// 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 " " 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)} }