184 lines
8.0 KiB
Go
184 lines
8.0 KiB
Go
// Package frombustest is a bus in memory for frombus's Reader, and a mesh on it whose composition hands a module to the
|
|
// provisioner with a provision to make, so a module that composes plans from the bus (mesh-provisioner, novox/hq ADR
|
|
// 0570 clause 2) tests against a real plan rather than one written by hand. The manifests are the catalogue's (mesh-catalog
|
|
// c05aa5e6: restic and the provisioner's client) as frombus's own tests read them, restic given one requirement more.
|
|
package frombustest
|
|
|
|
import (
|
|
"archive/tar"
|
|
"bytes"
|
|
"compress/gzip"
|
|
"context"
|
|
"embed"
|
|
"encoding/json"
|
|
"errors"
|
|
"strings"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/compose"
|
|
"git.novox.be/novox/mesh-sdk/go/compose/frombus"
|
|
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
)
|
|
|
|
//go:embed testdata
|
|
var testdata embed.FS
|
|
|
|
// Bus is a frombus.Reader over values held in memory. A credential asked is answered held when Held is set.
|
|
type Bus struct {
|
|
Placement map[string]delivery.Placement
|
|
Record map[string]delivery.NodeRecord
|
|
Module map[string]delivery.Module
|
|
Manifests map[string][]byte
|
|
Value map[string]map[string]any // by node.module
|
|
Own []compose.OwnSecretRow
|
|
Builds map[string]*delivery.BuilderBuild // by "<module> <fp>"
|
|
Archives map[string][]byte // by digest
|
|
Serves map[string]map[string]any // another node's values, by node.module
|
|
Held bool
|
|
}
|
|
|
|
var _ frombus.Reader = (*Bus)(nil)
|
|
|
|
func (b *Bus) Placements(context.Context) (map[string]delivery.Placement, error) {
|
|
return b.Placement, nil
|
|
}
|
|
func (b *Bus) Records(context.Context) (map[string]delivery.NodeRecord, error) { return b.Record, nil }
|
|
func (b *Bus) Modules(context.Context) (map[string]delivery.Module, error) { return b.Module, nil }
|
|
func (b *Bus) VaultAuthority(context.Context) string { return "" }
|
|
|
|
func (b *Bus) Manifest(_ context.Context, digest string) ([]byte, error) {
|
|
raw, ok := b.Manifests[digest]
|
|
if !ok {
|
|
return nil, errors.New("no such manifest")
|
|
}
|
|
return raw, nil
|
|
}
|
|
|
|
func (b *Bus) Values(_ context.Context, node, module string) (map[string]any, error) {
|
|
return b.Value[node+"."+module], nil
|
|
}
|
|
|
|
func (b *Bus) OwnSecrets(context.Context, string) ([]compose.OwnSecretRow, error) { return b.Own, nil }
|
|
|
|
func (b *Bus) Credentials(_ context.Context, _ string, asConsumer, asProvider []compose.CredentialKey) ([]compose.Credential, []compose.Issued, error) {
|
|
var out []compose.Credential
|
|
var issued []compose.Issued
|
|
for _, k := range append(append([]compose.CredentialKey{}, asConsumer...), asProvider...) {
|
|
if b.Held {
|
|
out = append(out, compose.Credential{Provision: k.Provision, Consumer: k.Consumer, ConsumerModule: k.ConsumerModule,
|
|
Provider: k.Provider, Local: k.Local, State: compose.CredentialHeld, Sealed: `"box"`})
|
|
issued = append(issued, compose.Issued{Provision: k.Provision, Consumer: k.Consumer, ConsumerModule: k.ConsumerModule,
|
|
Provider: k.Provider, Local: k.Local, ForProvider: `"box"`})
|
|
}
|
|
}
|
|
return out, issued, nil
|
|
}
|
|
|
|
func (b *Bus) Build(_ context.Context, module, fp string) (*delivery.BuilderBuild, error) {
|
|
return b.Builds[module+" "+fp], nil
|
|
}
|
|
|
|
func (b *Bus) Archive(_ context.Context, _, digest string) ([]byte, error) {
|
|
body, ok := b.Archives[digest]
|
|
if !ok {
|
|
return nil, errors.New("the store holds no such blob")
|
|
}
|
|
return body, nil
|
|
}
|
|
|
|
func (b *Bus) Served(_ context.Context, node, module string, keys []string) (map[string]any, error) {
|
|
out := map[string]any{}
|
|
for _, k := range keys {
|
|
if v, ok := b.Serves[node+"."+module][k]; ok {
|
|
out[k] = v
|
|
}
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// Node is the one node of the mesh Provisioned composes.
|
|
const Node = "n1"
|
|
|
|
// Provisioned is one node, n1, that runs the artifact store, a postgres providing postgres-database, the provisioner's
|
|
// client and restic: restic built and handed to the provisioner, and requiring postgres-database. Its plans (frombus.Plans
|
|
// with Scope{Node: Node}) hand restic, whose plan names that provision with postgres on n1 as its provider.
|
|
func Provisioned() (*Bus, error) {
|
|
read := func(name string) ([]byte, error) { return testdata.ReadFile("testdata/" + name) }
|
|
resticRaw, err := read("catalogue-c05aa5e6-restic.module.json")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var restic map[string]any
|
|
if err := json.Unmarshal(resticRaw, &restic); err != nil {
|
|
return nil, err
|
|
}
|
|
restic["requires"] = []string{"postgres-database"}
|
|
if resticRaw, err = json.Marshal(restic); err != nil {
|
|
return nil, err
|
|
}
|
|
clientRaw, err := read("catalogue-c05aa5e6-mesh-provisioner-client.module.json")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
store := []byte(`{"module":"store","version":"1","provides":[{"name":"artifact-store","scope":"mesh"}],` +
|
|
`"serves":{"artifact-store":{"port":5000}}}`)
|
|
postgres := []byte(`{"module":"postgres","version":"1","provides":[{"name":"postgres-database","scope":"mesh"}],` +
|
|
`"serves":{"postgres-database":{"port":5432}}}`)
|
|
|
|
var packed bytes.Buffer
|
|
zw := gzip.NewWriter(&packed)
|
|
tw := tar.NewWriter(zw)
|
|
for _, f := range []string{"backups.conf", "data.conf", "retention.conf"} {
|
|
text, err := read("catalogue-c05aa5e6-restic.files/" + f)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := tw.WriteHeader(&tar.Header{Name: "./" + f, Mode: 0o644, Size: int64(len(text)), Typeflag: tar.TypeReg}); err != nil {
|
|
return nil, err
|
|
}
|
|
if _, err := tw.Write(text); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
if err := tw.Close(); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := zw.Close(); err != nil {
|
|
return nil, err
|
|
}
|
|
archive := delivery.ManifestDigest(packed.Bytes())
|
|
digestOf := func(c string) string { return "sha256:" + strings.Repeat(c, 64) }
|
|
fps := FPs()
|
|
b := &Bus{
|
|
Placement: map[string]delivery.Placement{Node: {Node: Node,
|
|
Decided: delivery.NodeDecided{Hub: true, Address: "10.77.0.1", Account: "operator", AccountHome: "/home/operator"},
|
|
Modules: []delivery.PlacedModule{{Module: "store"}, {Module: "postgres"}, {Module: "mesh-provisioner-client"}, {Module: "restic"}}}},
|
|
Record: map[string]delivery.NodeRecord{Node: {Node: Node, TunnelKey: "dGVzdGtleXRlc3RrZXl0ZXN0a2V5dGVzdGtleXRlc3Q=",
|
|
SealingKey: "sk", Contract: compose.HandedContract, Capabilities: []string{"systemd", "package-manager"}}},
|
|
Module: map[string]delivery.Module{}, Manifests: map[string][]byte{},
|
|
Own: []compose.OwnSecretRow{{Node: Node, Module: "mesh-provisioner-client", Name: "provisioner-broker", Origin: "made", SealedTo: "sk", Sealed: `"box"`}},
|
|
Builds: map[string]*delivery.BuilderBuild{
|
|
"restic " + fps["restic"]: {State: delivery.BuildDone, Manifest: delivery.ManifestDigest(resticRaw), Artifacts: []delivery.BuiltArtifact{
|
|
{Name: "tools", Kind: "bundle", Reference: "artifact-store://restic/tools/blobs/" + digestOf("6"), Digest: digestOf("6")},
|
|
{Name: "files", Kind: "archive", Reference: "artifact-store://restic/files/blobs/" + archive, Digest: archive}}},
|
|
"mesh-provisioner-client " + fps["mesh-provisioner-client"]: {State: delivery.BuildDone, Manifest: delivery.ManifestDigest(clientRaw),
|
|
Artifacts: []delivery.BuiltArtifact{{Name: "client", Kind: "bundle",
|
|
Reference: "artifact-store://mesh-provisioner-client/client/blobs/" + digestOf("7"), Digest: digestOf("7")}}},
|
|
"store " + fps["store"]: {State: delivery.BuildDone, Manifest: delivery.ManifestDigest(store)},
|
|
"postgres " + fps["postgres"]: {State: delivery.BuildDone, Manifest: delivery.ManifestDigest(postgres)},
|
|
},
|
|
Archives: map[string][]byte{archive: packed.Bytes()},
|
|
Held: true,
|
|
}
|
|
for module, raw := range map[string][]byte{"restic": resticRaw, "mesh-provisioner-client": clientRaw, "store": store, "postgres": postgres} {
|
|
b.Module[module] = delivery.Module{FP2: fps[module], Manifest: delivery.ManifestDigest(raw)}
|
|
b.Manifests[delivery.ManifestDigest(raw)] = raw
|
|
}
|
|
return b, nil
|
|
}
|
|
|
|
// FPs are the fingerprints Provisioned's modules are built at.
|
|
func FPs() map[string]string {
|
|
return map[string]string{"restic": "src2:" + strings.Repeat("3", 64), "mesh-provisioner-client": "src2:" + strings.Repeat("4", 64),
|
|
"store": "src2:" + strings.Repeat("5", 64), "postgres": "src2:" + strings.Repeat("8", 64)}
|
|
}
|