Files
mesh-host/internal/compose/resolution/several_secrets_test.go
T
jschoubben d68e241724 Take the catalogue's composition rules into the node-engine
ADR 0593 §7: the composer with its refusals leaves mesh-sdk, so mesh-sdk
carries no composition rule. The catalogue functions only the
node-engine's composition reaches (Resolve, Resolution's Compose,
Declaration, DataItems, LeftOut, Rules and the rest, the settings,
placeholder and template refusals, the rendering helpers) are copied
with their tests into internal/compose/resolution; a method of a
mesh-sdk shape becomes a function taking it first. The composition
calls its own copies, and the captured catalogue its tests read lives
here now, shared by mesh-compose's tests.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 19:15:55 +01:00

145 lines
5.2 KiB
Go

package resolution
import (
"strings"
"testing"
mcat "git.novox.be/novox/mesh-sdk/go/catalogue"
)
const twoSecrets = `{"module":"ca","version":"1","requires":["secret"],
"secrets":{"secret":{"root-key":"/var/lib/ca/root.key","root-pass":"/var/lib/ca/root.pass"}},
"resources":[{"id":"state","type":"directory","path":"/var/lib/ca","mode":"0700"}]}`
func vaultAndCA() map[string]mcat.Manifest {
ca, _ := decodeForTest([]byte(twoSecrets))
vault := mcat.Manifest{Module: "mesh-vault", Version: "1", Provides: mcat.FromAnywhere("secret"),
Grants: map[string]string{"secret": "/var/lib/vault/grants"},
Receives: map[string]string{"secret": "/var/lib/vault/grants/mesh.json"}}
// A second consumer of the same provision that keeps ONE file, mentioned before the one that
// keeps two: the lab found the expansion done on the first module to mention the provision,
// and the second consumer given one credential and no file.
cache := mcat.Manifest{Module: "cache", Version: "1", Requires: []string{"secret"},
Secrets: map[string]string{"secret": "/var/lib/cache/secret"}}
return shelf(vault, cache, ca)
}
func TestEachLocalNameIsANeedAFileAndAHolderOfItsOwn(t *testing.T) {
got, err := Resolve(vaultAndCA(), []string{"mesh-vault", "cache", "ca"}, workstation(), mcat.World{})
if err != nil {
t.Fatal(err)
}
var locals, cacheLocals []string
for _, n := range got.Needs {
if n.Name == "secret" && n.For == "ca" {
locals = append(locals, n.Local)
}
if n.Name == "secret" && n.For == "cache" {
cacheLocals = append(cacheLocals, n.Local)
}
}
if strings.Join(locals, ",") != "root-key,root-pass" {
t.Fatalf("two secrets from one provider are two needs: %v", got.Needs)
}
if len(cacheLocals) != 1 || cacheLocals[0] != "" {
t.Fatalf("the one-file consumer keeps one need with no local name: %v", got.Needs)
}
for i := range got.Needs {
got.Needs[i].Sealed = "sealed-" + got.Needs[i].Local
}
out, err := Declaration(got, mcat.Rendering{})
if err != nil {
t.Fatal(err)
}
seen := map[string]string{}
for _, r := range out {
if r["type"] == "file" && strings.HasPrefix(r["path"].(string), "/var/lib/ca/root.") {
seen[r["id"].(string)] = r["sealed"].(string)
}
}
if seen["ca."+mcat.SecretID("root-key")] != "sealed-root-key" || seen["ca."+mcat.SecretID("root-pass")] != "sealed-root-pass" {
t.Fatalf("each local name is its own file with its own credential: %v", seen)
}
}
func TestAProviderSeesEachLocalNameAsAHolderOfItsOwn(t *testing.T) {
r := mcat.Resolution{Modules: []mcat.Manifest{vaultAndCA()["mesh-vault"]}}
got, err := resolutionContributions(r, mcat.SettingsBy{}, []mcat.Grant{
{Provision: "secret", Consumer: "workstation", From: "ca", Local: "root-key", Sealed: "x"},
{Provision: "secret", Consumer: "workstation", From: "ca", Local: "root-pass", Sealed: "y"},
}, map[string]string{"secret": "/var/lib/vault/grants"})
if err != nil {
t.Fatal(err)
}
given := got["secret"]
if len(given) != 2 {
t.Fatalf("two holders: %+v", given)
}
if given[0].As != "mesh_workstation_ca_root_key" && given[0].As != "mesh_workstation_ca_root-key" {
t.Fatalf("the holder is the consumer's identity with the local name after it: %q", given[0].As)
}
if given[0].Secret == given[1].Secret {
t.Fatalf("two holders share one file on the provider: %q", given[0].Secret)
}
}
// And on the provider's machine, two files with two ids — the lab's first run had the declaration
// refused for two resources with one identity.
func TestAProviderKeepsOneFilePerHolder(t *testing.T) {
got, err := Resolve(vaultAndCA(), []string{"mesh-vault", "cache", "ca"}, workstation(), mcat.World{})
if err != nil {
t.Fatal(err)
}
out, err := Declaration(got, mcat.Rendering{Grants: []mcat.Grant{
{Provision: "secret", Consumer: "workstation", From: "ca", Local: "root-key", Sealed: "x"},
{Provision: "secret", Consumer: "workstation", From: "ca", Local: "root-pass", Sealed: "y"},
}})
if err != nil {
t.Fatal(err)
}
ids := map[string]string{}
for _, r := range out {
if id, _ := r["id"].(string); strings.Contains(id, "grant-secret") {
ids[id] = r["path"].(string)
}
}
if len(ids) != 2 {
t.Fatalf("two holders are two grant files: %v", ids)
}
}
// One file under a local name is still a local name (review C1): the need carries it, the file
// is written, and ${secret:<name>} is filled.
func TestOneLocalNameIsStillALocalName(t *testing.T) {
only, _ := decodeForTest([]byte(`{"module":"one","version":"1","requires":["secret"],
"secrets":{"secret":{"only":"/var/lib/one/only"}}}`))
vault := vaultAndCA()["mesh-vault"]
got, err := Resolve(shelf(vault, only), []string{"mesh-vault", "one"}, workstation(), mcat.World{})
if err != nil {
t.Fatal(err)
}
var found *mcat.Needed
for i, n := range got.Needs {
if n.For == "one" && n.Name == "secret" {
found = &got.Needs[i]
}
}
if found == nil || found.Local != "only" {
t.Fatalf("the one named file did not become a need under its name: %v", got.Needs)
}
found.Sealed = "sealed-only"
out, err := Declaration(got, mcat.Rendering{})
if err != nil {
t.Fatal(err)
}
var written bool
for _, r := range out {
if r["path"] == "/var/lib/one/only" && r["sealed"] == "sealed-only" {
written = true
}
}
if !written {
t.Fatal("the file under the one local name was not written")
}
}