Add builder_build and builder_builds read tools to the view, so the builder's keys can be checked live (issue 1723)
mesh/merge-gate head f2379dfeea2d merged into trunk 113f7aa93385 — pass: builds mesh-delivery-view → shanks; no bus step; every machine composes with the c…
mesh/repo-check head f2379dfeea2d merged into trunk 113f7aa93385 — pass: its merge-check.sh passed
mesh/delivery delivered
mesh/check failed: g14: fetch of novox/mesh-delivery#18: build_go_fetch on g14: nats: no responders available for request | f2379dfeea2d on 113f7aa933…
mesh/merge-gate head f2379dfeea2d merged into trunk 113f7aa93385 — pass: builds mesh-delivery-view → shanks; no bus step; every machine composes with the c…
mesh/repo-check head f2379dfeea2d merged into trunk 113f7aa93385 — pass: its merge-check.sh passed
mesh/delivery delivered
mesh/check failed: g14: fetch of novox/mesh-delivery#18: build_go_fetch on g14: nats: no responders available for request | f2379dfeea2d on 113f7aa933…
This commit is contained in:
@@ -35,18 +35,47 @@ func Tools(r view.Reader) []stdio.Tool {
|
||||
mods, err := r.Modules(only)
|
||||
return map[string]any{"modules": mods}, err
|
||||
}},
|
||||
{Name: view.BuilderBuildsReadTool,
|
||||
Description: "The builder's build keys, each as stored in mesh-delivery-builder_builds: key, module, fingerprint (src2) and build " +
|
||||
"(state, by, step with the nodes it was built on, manifest, artifacts with their digests, toolchains, error). Narrow by module " +
|
||||
"or state (queued, building, done, failed). Read-only.",
|
||||
Input: map[string]any{"type": "object", "properties": map[string]any{
|
||||
"module": map[string]any{"type": "string", "description": "only this module's builds"},
|
||||
"state": map[string]any{"type": "string", "description": "only builds in this state"}}},
|
||||
Run: func(args map[string]any) (any, error) {
|
||||
module, _ := args["module"].(string)
|
||||
state, _ := args["state"].(string)
|
||||
rows, err := r.BuilderBuilds(module, state)
|
||||
return map[string]any{"builds": rows}, err
|
||||
}},
|
||||
{Name: view.BuilderBuildReadTool,
|
||||
Description: "One build of the builder as stored. Give the module and its fingerprint (fp2, src2:...), or the module alone for the build " +
|
||||
"at the fingerprint the indexer holds for it now, joined through modules.<module>; with commit, only if the indexer holds the module at that commit. Read-only.",
|
||||
Input: map[string]any{"type": "object", "required": []string{"module"}, "properties": map[string]any{
|
||||
"module": map[string]any{"type": "string", "description": "the module's name"},
|
||||
"fp2": map[string]any{"type": "string", "description": "its new-form fingerprint; empty means the indexer's current one"},
|
||||
"commit": map[string]any{"type": "string", "description": "the commit the indexer must hold the module at"}}},
|
||||
Run: func(args map[string]any) (any, error) {
|
||||
module, _ := args["module"].(string)
|
||||
fp2, _ := args["fp2"].(string)
|
||||
commit, _ := args["commit"].(string)
|
||||
return r.BuilderBuild(module, fp2, commit)
|
||||
}},
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
state := stdio.State("mesh-delivery-indexer.modules")
|
||||
r := view.Reader{State: view.StateRead{Keys: state.Keys, Get: func(key string) (json.RawMessage, error) {
|
||||
func read(state stdio.KeptState) view.StateRead {
|
||||
return view.StateRead{Keys: state.Keys, Get: func(key string) (json.RawMessage, error) {
|
||||
e, err := state.Get(key)
|
||||
if err != nil || e == nil {
|
||||
return nil, err
|
||||
}
|
||||
return e.Value, nil
|
||||
}}}
|
||||
}}
|
||||
}
|
||||
|
||||
func main() {
|
||||
r := view.Reader{State: read(stdio.State("mesh-delivery-indexer.modules")), Builds: read(stdio.State("mesh-delivery-builder.builds"))}
|
||||
if err := stdio.Serve("", Tools(r)); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "[mesh-delivery-view-tools] %v\n", err)
|
||||
os.Exit(1)
|
||||
|
||||
@@ -94,3 +94,65 @@ func TestModulesToolAnswersEveryKeyAsStored(t *testing.T) {
|
||||
t.Errorf("none2 modules = %v", only)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
fp2A = "src2:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
fp2B = "src2:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
|
||||
keyA = "netcheck.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
keyB = "zsh.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
|
||||
doneBuild = `{"v":1,"state":"done","by":"novox.mesh-delivery-builder","step":"built on shanks, ace","manifest":"m","artifacts":[{"name":"program","kind":"bundle","digest":"sha256:d1"}]}`
|
||||
failBuild = `{"v":1,"state":"failed","by":"novox.mesh-delivery-builder","error":"refused kind: package"}`
|
||||
)
|
||||
|
||||
func both(builds, modules fake) view.Reader {
|
||||
r := modules.reader()
|
||||
r.Builds = builds.reader().State
|
||||
return r
|
||||
}
|
||||
|
||||
// 1723/AC1: builder_builds lists every key as stored (module, fingerprint, state, digests, error), and narrows by module and state.
|
||||
func TestBuilderBuildsListsKeysAsStored(t *testing.T) {
|
||||
r := both(fake{keyA: doneBuild, keyB: failBuild, "junk": "{}"}, fake{})
|
||||
out, err := run(t, r, view.BuilderBuildsReadTool, map[string]any{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rows := out.(map[string]any)["builds"].([]view.BuilderRow)
|
||||
if len(rows) != 2 || rows[0].Key != keyA || rows[0].Module != "netcheck" || rows[0].Fingerprint != fp2A || string(rows[0].Build) != doneBuild || string(rows[1].Build) != failBuild {
|
||||
t.Errorf("rows = %+v", rows)
|
||||
}
|
||||
for args, want := range map[string]string{"state": keyB, "module": keyA} {
|
||||
out, _ := run(t, r, view.BuilderBuildsReadTool, map[string]any{args: map[string]string{keyB: "failed", keyA: "netcheck"}[want]})
|
||||
got := out.(map[string]any)["builds"].([]view.BuilderRow)
|
||||
if len(got) != 1 || got[0].Key != want {
|
||||
t.Errorf("%s filter = %+v, want %s", args, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 1723/AC2: builder_build answers one key by module and fp2, and by module alone through the indexer's fp2 (joined at its commit).
|
||||
func TestBuilderBuildJoinsTheIndexersFingerprint(t *testing.T) {
|
||||
idx := `{"v":3,"fp2":"` + fp2A + `","commit":"c1","path":"."}`
|
||||
r := both(fake{keyA: doneBuild}, fake{"netcheck": idx, "zsh": `{"v":3,"none2":"not known","commit":"c9"}`})
|
||||
for _, args := range []map[string]any{{"module": "netcheck", "fp2": fp2A}, {"module": "netcheck"}, {"module": "netcheck", "commit": "c1"}} {
|
||||
out, err := run(t, r, view.BuilderBuildReadTool, args)
|
||||
if err != nil {
|
||||
t.Fatalf("%v: %v", args, err)
|
||||
}
|
||||
if row := out.(view.BuilderRow); row.Key != keyA || string(row.Build) != doneBuild {
|
||||
t.Errorf("%v = %+v", args, row)
|
||||
}
|
||||
}
|
||||
for args, want := range map[string]string{"netcheck/c2": "not c2", "zsh": "not known", "gone": "no module gone"} {
|
||||
a := map[string]any{"module": strings.Split(args, "/")[0]}
|
||||
if strings.Contains(args, "/") {
|
||||
a["commit"] = "c2"
|
||||
}
|
||||
if _, err := run(t, r, view.BuilderBuildReadTool, a); err == nil || !strings.Contains(err.Error(), want) {
|
||||
t.Errorf("%v: err = %v, want %q", a, err, want)
|
||||
}
|
||||
}
|
||||
if _, err := run(t, r, view.BuilderBuildReadTool, map[string]any{"module": "netcheck", "fp2": fp2B}); err == nil || !strings.Contains(err.Error(), "holds no build") {
|
||||
t.Errorf("an unbuilt fingerprint: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user