checks/credentialform: a module that provides and keeps a bound state must be read by mesh-provisioner (issue 1952)

This commit is contained in:
2026-10-29 22:50:06 +01:00
parent 1f0ff6ed98
commit dd9b6a0485
2 changed files with 41 additions and 4 deletions
+34
View File
@@ -21,6 +21,8 @@ func Problems(root string) ([]string, error) {
} }
sort.Strings(files) sort.Strings(files)
var out []string var out []string
reads := map[string]bool{}
var binders []string
for _, f := range files { for _, f := range files {
raw, err := os.ReadFile(f) raw, err := os.ReadFile(f)
if err != nil { if err != nil {
@@ -31,6 +33,38 @@ func Problems(root string) ([]string, error) {
return nil, fmt.Errorf("%s: %w", f, err) return nil, fmt.Errorf("%s: %w", f, err)
} }
out = append(out, catalogue.CredentialFormProblems(m)...) out = append(out, catalogue.CredentialFormProblems(m)...)
var kept struct {
Reads []string `json:"reads"`
State []json.RawMessage `json:"state"`
}
_ = json.Unmarshal(raw, &kept) // reads may hold objects in another module; only the provisioner's are read
if m.Module == "mesh-provisioner" {
for _, r := range kept.Reads {
reads[r] = true
}
}
if len(m.Provides) > 0 && keepsBound(kept.State) {
binders = append(binders, m.Module)
}
}
for _, module := range binders {
if !reads[module+".bound"] {
out = append(out, fmt.Sprintf("%s provides and keeps a `bound` state, and mesh-provisioner's manifest does not read %s.bound", module, module))
}
} }
return out, nil return out, nil
} }
// keepsBound says whether a manifest's `state` names `bound`, as a plain name or an object with a name.
func keepsBound(state []json.RawMessage) bool {
for _, s := range state {
var name string
var obj struct {
Name string `json:"name"`
}
if json.Unmarshal(s, &name) == nil && name == "bound" || json.Unmarshal(s, &obj) == nil && obj.Name == "bound" {
return true
}
}
return false
}
+5 -2
View File
@@ -27,14 +27,17 @@ func aCatalogue(t *testing.T, manifests map[string]string) string {
func TestEveryOfferSaysTheFormOfItsCredential(t *testing.T) { func TestEveryOfferSaysTheFormOfItsCredential(t *testing.T) {
root := aCatalogue(t, map[string]string{ root := aCatalogue(t, map[string]string{
"silent": `{"module":"silent","provides":[{"name":"thing","scope":"mesh"}]}`, "silent": `{"module":"silent","provides":[{"name":"thing","scope":"mesh"}]}`,
"binder": `{"module":"binder","provides":[{"name":"d","credential":{"form":"pair"}}],"state":["bound"]}`,
"mesh-provisioner": `{"module":"mesh-provisioner","reads":["other.bound"]}`,
"says": `{"module":"says","provides":[{"name":"a","credential":{"form":"pair"}},{"name":"b","credential":{"form":"none"}},{"name":"c","credential":{"own":"jwt"}}]}`, "says": `{"module":"says","provides":[{"name":"a","credential":{"form":"pair"}},{"name":"b","credential":{"form":"none"}},{"name":"c","credential":{"own":"jwt"}}]}`,
}) })
got, err := Problems(root) got, err := Problems(root)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if len(got) != 1 || !strings.Contains(got[0], `silent provides "thing"`) { if len(got) != 2 || !strings.Contains(got[0], `silent provides "thing"`) ||
t.Fatalf("refused %v, wanted the one silent offer", got) !strings.Contains(got[1], "binder") || !strings.Contains(got[1], "mesh-provisioner") {
t.Fatalf("refused %v, wanted the silent offer and the binder the provisioner does not read", got)
} }
real, err := Problems(filepath.Join("..", "..")) real, err := Problems(filepath.Join("..", ".."))
if err != nil { if err != nil {