A3k finding 2: distribution reads its caller list as mesh-config resolves it, not from an environment nothing renders
The node-engine fills ${config:} only in files, unit names, owners and groups, never in a
process's environment, so MESH_CONFIG_CALLERS was never set and design 51's defaults always held.
The tools bundle now reads `callers` through stdio.Config on each call (holder.ConfiguredCallers,
Verbs.WithCallers per call); the program, which has no runtime resolving config, asks its own
tools bundle's new registry_callers on each wake of the windows (manifest invokes). The
environment variable is gone.
Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
This commit is contained in:
@@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -30,7 +31,7 @@ func TestTheVerbsSpeakTheSeatsContract(t *testing.T) {
|
||||
reg.VerbVersions: reg.VersionsInput{}, reg.VerbRemove: reg.RemoveInput{}, reg.VerbHold: reg.HoldInput{},
|
||||
reg.VerbRelease: reg.ReleaseInput{}, reg.VerbMirror: reg.MirrorInput{}, reg.VerbTags: reg.TagsInput{}}
|
||||
seen := 0
|
||||
for _, tool := range tools(&holder.Verbs{}, kept[holder.Window]{}) {
|
||||
for _, tool := range tools(&holder.Verbs{}, kept[holder.Window]{}, func() (holder.Callers, error) { return holder.DefaultCallers(), nil }) {
|
||||
seat, verb, ok := strings.Cut(tool.Name, ".")
|
||||
in, isVerb := inputs[verb]
|
||||
if !ok || !isVerb || (seat != holder.ImageSeat && seat != holder.ArtifactSeat) {
|
||||
@@ -56,6 +57,28 @@ func TestTheVerbsSpeakTheSeatsContract(t *testing.T) {
|
||||
t.Errorf("%s's body %s is not the contract's: %v", event, b, err)
|
||||
}
|
||||
}
|
||||
// The caller list is read on each call: one that cannot be read answers no verb, and registry_callers says it as read.
|
||||
unread := func() (holder.Callers, error) { return nil, errors.New("config refused") }
|
||||
for _, tool := range tools(&holder.Verbs{}, kept[holder.Window]{}, unread) {
|
||||
if tool.Name == holder.ImageSeat+"."+holder.VerbVersions {
|
||||
if _, err := tool.Run(map[string]any{"name": "m/a"}); err == nil || !strings.Contains(err.Error(), "caller list") {
|
||||
t.Errorf("versions with an unread caller list = %v, want refused naming the caller list", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
answered := false
|
||||
for _, tool := range tools(&holder.Verbs{}, kept[holder.Window]{}, func() (holder.Callers, error) { return holder.Callers{holder.VerbPublish: {"mesh-build-go"}}, nil }) {
|
||||
if tool.Name == holder.CallersTool {
|
||||
answered = true
|
||||
got, err := tool.Run(map[string]any{})
|
||||
if b, _ := json.Marshal(got); err != nil || string(b) != `{"callers":{"publish":["mesh-build-go"]}}` {
|
||||
t.Errorf("%s = %s, %v", holder.CallersTool, b, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if !answered {
|
||||
t.Errorf("no tool %s: the program could not read the caller list", holder.CallersTool)
|
||||
}
|
||||
if a := publishAnswer(holder.PublishAnswer{Transfer: "t", Missing: []string{"sha256:bb"}}); !reflect.DeepEqual(a.Lacks, []string{"sha256:bb"}) || a.Held {
|
||||
t.Errorf("publish answers %+v", a)
|
||||
}
|
||||
|
||||
@@ -238,7 +238,16 @@ func arg[T any](args map[string]any) (T, error) {
|
||||
|
||||
func str(args map[string]any, k string) string { s, _ := args[k].(string); return strings.TrimSpace(s) }
|
||||
|
||||
func tools(v *holder.Verbs, windows kept[holder.Window]) []stdio.Tool {
|
||||
// tools are distribution's tools over v; each call that checks a caller reads the caller list on that call (callers:
|
||||
// the config value as mesh-config resolves it now), so an operator's change of it holds from the next call on.
|
||||
func tools(v *holder.Verbs, windows kept[holder.Window], callers func() (holder.Callers, error)) []stdio.Tool {
|
||||
now := func() (*holder.Verbs, error) {
|
||||
c, err := callers()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("the caller list cannot be read, so no verb is answered: %w", err)
|
||||
}
|
||||
return v.WithCallers(c), nil
|
||||
}
|
||||
obj := func(props map[string]any, req ...string) map[string]any {
|
||||
return map[string]any{"type": "object", "required": req, "properties": props}
|
||||
}
|
||||
@@ -249,6 +258,10 @@ func tools(v *holder.Verbs, windows kept[holder.Window]) []stdio.Tool {
|
||||
stdio.Tool{Name: seat + "." + holder.VerbPublish, Description: "Open a transfer of a publish (ADR 0611): its name, version, kind, digest and parts; answers the parts the holder lacks.",
|
||||
Input: obj(map[string]any{"transfer": s("the transfer's id"), "name": s("<module>/<artifact>"), "version": s("the builder's version"), "kind": s("image, bundle, archive or upstream"), "digest": s("sha256:…"), "parts": map[string]any{"type": "array"}}, "transfer", "name", "kind", "digest", "parts"),
|
||||
Run: func(args map[string]any) (any, error) {
|
||||
v, err := now()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
in, err := arg[reg.PublishInput](args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -266,6 +279,10 @@ func tools(v *holder.Verbs, windows kept[holder.Window]) []stdio.Tool {
|
||||
stdio.Tool{Name: seat + "." + holder.VerbPublishPart, Description: "Send one chunk (at most 1 MiB) of one part of an open transfer, from the caller that opened it.",
|
||||
Input: obj(map[string]any{"transfer": s("its id"), "part": s("the part's digest"), "sequence": map[string]any{"type": "integer"}, "last": map[string]any{"type": "boolean"}, "data": s("base64")}, "transfer", "part", "sequence", "last", "data"),
|
||||
Run: func(args map[string]any) (any, error) {
|
||||
v, err := now()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
in, err := arg[reg.PublishPartInput](args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -279,6 +296,10 @@ func tools(v *holder.Verbs, windows kept[holder.Window]) []stdio.Tool {
|
||||
}},
|
||||
stdio.Tool{Name: seat + "." + holder.VerbVersions, Description: "The versions the seat holds of a name.", Input: obj(map[string]any{"name": s("<module>/<artifact>")}, "name"),
|
||||
Run: func(args map[string]any) (any, error) {
|
||||
v, err := now()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a, err := v.Versions(seat, stdio.CallerOf(args), str(args, "name"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -288,6 +309,10 @@ func tools(v *holder.Verbs, windows kept[holder.Window]) []stdio.Tool {
|
||||
stdio.Tool{Name: seat + "." + holder.VerbRemove, Description: "Remove a tagged version (the builder), or for retention an untagged one by its digest (the artifact sweep); says removed.",
|
||||
Input: obj(map[string]any{"name": s("name"), "version": s("a tagged version"), "tag": s("its tag"), "digest": s("for retention"), "reason": s("why")}, "name", "reason"),
|
||||
Run: func(args map[string]any) (any, error) {
|
||||
v, err := now()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
in, err := arg[reg.RemoveInput](args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -301,6 +326,10 @@ func tools(v *holder.Verbs, windows kept[holder.Window]) []stdio.Tool {
|
||||
stdio.Tool{Name: seat + "." + holder.VerbHold, Description: "Keep one untagged bundle, archive or copied base from retention (the artifact sweep alone); says held.",
|
||||
Input: obj(map[string]any{"name": s("name"), "digest": s("sha256:…"), "reason": s("which of the sweep's rules keeps it")}, "name", "digest", "reason"),
|
||||
Run: func(args map[string]any) (any, error) {
|
||||
v, err := now()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
in, err := arg[reg.HoldInput](args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -313,6 +342,10 @@ func tools(v *holder.Verbs, windows kept[holder.Window]) []stdio.Tool {
|
||||
stdio.Tool{Name: seat + "." + holder.VerbRelease, Description: "Let a hold go (the artifact sweep, or the operator's session through registry_holds); says released.",
|
||||
Input: obj(map[string]any{"name": s("name"), "digest": s("sha256:…"), "reason": s("why it is let go")}, "name", "digest", "reason"),
|
||||
Run: func(args map[string]any) (any, error) {
|
||||
v, err := now()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
in, err := arg[reg.ReleaseInput](args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -327,6 +360,10 @@ func tools(v *holder.Verbs, windows kept[holder.Window]) []stdio.Tool {
|
||||
out = append(out,
|
||||
stdio.Tool{Name: holder.ImageSeat + "." + holder.VerbTags, Description: "The tags of an image.", Input: obj(map[string]any{"name": s("name")}, "name"),
|
||||
Run: func(args map[string]any) (any, error) {
|
||||
v, err := now()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a, err := v.Tags(holder.ImageSeat, stdio.CallerOf(args), str(args, "name"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -336,6 +373,10 @@ func tools(v *holder.Verbs, windows kept[holder.Window]) []stdio.Tool {
|
||||
stdio.Tool{Name: holder.ImageSeat + "." + holder.VerbMirror, Description: "Copy an outside image, pinned by its digest, into the store as upstream/<host>/<path> (every platform, only the parts it lacks): answers at once with the reference the copy will have, and says published (or refused, with why) on the seat's events when the copy ends; wait on that event (mesh-build-image alone).",
|
||||
Input: obj(map[string]any{"source": s("<host>/<path>"), "digest": s("sha256:…")}, "source", "digest"),
|
||||
Run: func(args map[string]any) (any, error) {
|
||||
v, err := now()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
in, err := arg[reg.MirrorInput](args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -346,6 +387,15 @@ func tools(v *holder.Verbs, windows kept[holder.Window]) []stdio.Tool {
|
||||
}
|
||||
return reg.MirrorAnswer{Digest: in.Digest, Reference: ref}, nil
|
||||
}},
|
||||
stdio.Tool{Name: holder.CallersTool, Description: "Who may call which verb of distribution's seats: the trusted config value callers as mesh-config resolves it on this machine now (design 51's list where unset). The program asks it at each wake of the windows, as it has no config of its own.",
|
||||
Input: obj(map[string]any{}),
|
||||
Run: func(map[string]any) (any, error) {
|
||||
c, err := callers()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return map[string]any{"callers": c}, nil
|
||||
}},
|
||||
stdio.Tool{Name: "registry_health", Description: "distribution's health check, run by the runtime each minute: versions of each seat asked through the store's read side (the program's port 5000); fails naming the program when it does not answer.",
|
||||
Input: obj(map[string]any{}),
|
||||
Run: func(map[string]any) (any, error) {
|
||||
@@ -355,6 +405,10 @@ func tools(v *holder.Verbs, windows kept[holder.Window]) []stdio.Tool {
|
||||
stdio.Tool{Name: "registry_incoming", Description: "Open transfers of incoming and its fill; with drop, drops one by hand (a hand act, why required).",
|
||||
Input: obj(map[string]any{"drop": s("a transfer id to drop"), "why": s("why it is dropped")}),
|
||||
Run: func(args map[string]any) (any, error) {
|
||||
v, err := now()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
idle, _ := v.DropIdle()
|
||||
if id := str(args, "drop"); id != "" {
|
||||
if str(args, "why") == "" {
|
||||
@@ -371,6 +425,10 @@ func tools(v *holder.Verbs, windows kept[holder.Window]) []stdio.Tool {
|
||||
stdio.Tool{Name: "registry_holds", Description: "What is held, since when and for whom; with release, lets one go through the seat's release as the operator's session (a hand act).",
|
||||
Input: obj(map[string]any{"seat": s("the seat"), "release": s("the name to release"), "digest": s("its digest"), "why": s("why")}),
|
||||
Run: func(args map[string]any) (any, error) {
|
||||
v, err := now()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if name := str(args, "release"); name != "" {
|
||||
if str(args, "why") == "" {
|
||||
return nil, errors.New("a release by hand is a hand act: say why")
|
||||
@@ -414,20 +472,15 @@ func tools(v *holder.Verbs, windows kept[holder.Window]) []stdio.Tool {
|
||||
}
|
||||
|
||||
func main() {
|
||||
callers, err := holder.ParseCallers(os.Getenv("MESH_CONFIG_CALLERS"))
|
||||
if err != nil {
|
||||
logf("%v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
base, _ := url.Parse("http://127.0.0.1:5001")
|
||||
store := &holder.OCI{Base: base, Registry: os.Getenv("MESH_REGISTRY_REACH"), Upstream: &holder.Upstream{}}
|
||||
h := &holder.Holder{Callers: callers, Objects: parts.BusObjects(holder.IncomingObjects), Builder: delivery.BuilderVersions{Ask: stdio.Ask}, Store: store, Say: say, Finding: finding,
|
||||
h := &holder.Holder{Objects: parts.BusObjects(holder.IncomingObjects), Builder: delivery.BuilderVersions{Ask: stdio.Ask}, Store: store, Say: say, Finding: finding,
|
||||
Transfers: parts.BusTransfers[holder.Opening](holder.TransfersState)}
|
||||
v := &holder.Verbs{Holder: h, Catalog: store, Holds: kept[holder.Hold]{state: "holds", key: holder.Hold.Key}, Mirrors: mirrors{}}
|
||||
go followMirrors(v)
|
||||
windows := kept[holder.Window]{state: "windows", key: func(w holder.Window) string { return w.Name }}
|
||||
closeBootstrap = bootstrapCloser(windows)
|
||||
if err := stdio.Serve("", tools(v, windows)); err != nil {
|
||||
if err := stdio.Serve("", tools(v, windows, holder.ConfiguredCallers)); err != nil {
|
||||
logf("%v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user