From 64c1996a5e9dd273c058890d2c68b4022bd5de68 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 21 Oct 2026 11:53:24 +0200 Subject: [PATCH] Prove the controller composes and checks shares on an s3-bucket requirement (issue 1593) --- cmd/mesh-controller/shares_test.go | 104 +++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 cmd/mesh-controller/shares_test.go diff --git a/cmd/mesh-controller/shares_test.go b/cmd/mesh-controller/shares_test.go new file mode 100644 index 00000000..294a38e8 --- /dev/null +++ b/cmd/mesh-controller/shares_test.go @@ -0,0 +1,104 @@ +package main + +import ( + "bytes" + "crypto/ecdh" + "crypto/rand" + "encoding/base64" + "encoding/json" + "os" + "path/filepath" + "strings" + "testing" + + "git.novox.be/novox/mesh-sdk/go/catalogue" + "git.novox.be/novox/mesh-sdk/go/compose" + "git.novox.be/novox/mesh-sdk/go/overlay" +) + +// Issue 1593 (novox/hq ADR 0468 §8): `shares` on a requirement of s3-bucket, taken in with mesh-sdk 465beb4. The fixture's +// shape is mesh-sdk's go/catalogue/bucket_shares_test.go (issue 1591) and go/compose/resolve_test.go theObjectsMesh. + +const filesRequiringShares = `{"module":"files","version":"1","requires":[ + {"name":"s3-bucket","shares":[{"to":"seat:node-files","methods":["put"]}]}]}` + +func aKey(t *testing.T) string { + t.Helper() + k, err := ecdh.X25519().GenerateKey(rand.Reader) + if err != nil { + t.Fatal(err) + } + return base64.StdEncoding.EncodeToString(k.PublicKey().Bytes()) +} + +// 1593/AC1 unit: composing the provider's machine, the file it receives for the consumer carries the shares. +func TestTheProvidersMachineReceivesTheSharesOfAnS3BucketRequirement(t *testing.T) { + var overlayModule catalogue.Manifest + raw, err := json.Marshal(overlay.Manifest()) + if err != nil { + t.Fatal(err) + } + if err := json.Unmarshal(raw, &overlayModule); err != nil { + t.Fatal(err) + } + consumer, err := catalogue.ParseManifest([]byte(filesRequiringShares)) + if err != nil { + t.Fatal(err) + } + provider := catalogue.Manifest{Module: "objects", Version: "1", + Provides: []catalogue.Offer{{Name: "s3-bucket", Scope: catalogue.ScopeMesh, + Identity: &catalogue.OfferIdentity{Max: 20, In: "an S3 access key"}}}, + Receives: map[string]string{"s3-bucket": "/var/lib/mesh/objects/mesh.json"}} + in := compose.Input{ + Catalogue: map[string]catalogue.Manifest{overlay.Name: overlayModule, "objects": provider, "files": consumer}, + Issued: []compose.Issued{{Provision: "s3-bucket", Consumer: "laptop", ConsumerModule: "files", + Provider: "anchor", ForProvider: "sealed-to-anchor"}}, + } + for i, name := range []string{"anchor", "laptop"} { + assigned := []string{overlay.Name, "objects"} + if name == "laptop" { + assigned = []string{overlay.Name, "files"} + } + in.Nodes = append(in.Nodes, compose.Node{Name: name, SealingKey: name + "-key", Assigned: assigned, + Place: compose.Place{Key: aKey(t), Endpoint: name + ".example:51820", Site: "here", Hub: name == "anchor", + Address: []string{"10.77.0.1", "10.77.0.2"}[i]}, + Capabilities: map[string]bool{"container-runtime": true, "wireguard": true, "systemd": true}}) + } + rendered, err := compose.Render(in, "anchor", compose.Foreseeing) + if err != nil { + t.Fatal(err) + } + if len(rendered.Rendering.Grants) != 1 { + t.Fatalf("grants are %+v", rendered.Rendering.Grants) + } + got, err := json.Marshal(rendered.Rendering.Grants[0].Values["shares"]) + if err != nil { + t.Fatal(err) + } + if want := `[{"methods":["put"],"to":"seat:node-files"}]`; string(got) != want { + t.Fatalf("the provider's grant for the consumer carries shares %s, want %s", got, want) + } +} + +// 1593/AC2 unit: the module check accepts a manifest with shares and refuses an unknown method, citing ADR 0468 §8. +func TestModuleCheckAcceptsSharesAndRefusesAnUnknownMethod(t *testing.T) { + dir := t.TempDir() + write := func(name, body string) string { + p := filepath.Join(dir, name+".json") + if err := os.WriteFile(p, []byte(body), 0o600); err != nil { + t.Fatal(err) + } + return p + } + var out bytes.Buffer + if err := moduleCheckFor([]string{write("files", filesRequiringShares)}, 6, &out); err != nil { + t.Fatalf("a manifest with shares was refused: %v\n%s", err, out.String()) + } + out.Reset() + bad := write("bad", `{"module":"bad","version":"1","requires":[ + {"name":"s3-bucket","shares":[{"to":"seat:node-files","methods":["delete"]}]}]}`) + err := moduleCheckFor([]string{bad}, 6, &out) + if err == nil || !strings.Contains(out.String(), "ADR 0468 §8") { + t.Fatalf("an unknown method passed or the refusal does not cite the ADR: %v\n%s", err, out.String()) + } +}