Merge pull request 'The seat calls a gate that raised no machine the mesh composes an error, whatever judged it (hq issue 285)' (#106) from fix/seat-refuses-a-gate-that-raised-nothing into main
This commit was merged in pull request #106.
This commit is contained in:
@@ -298,6 +298,13 @@ func Check(ctx context.Context, run Runner, spec CheckSpec, workspace, registry
|
||||
say("check", "the facts of %s (%s): %d machine(s), the bus at %s, the store at %s", f.Taken.Format(time.RFC3339),
|
||||
short(strings.TrimPrefix(digest, "sha256:")), len(f.Machines), f.Versions.Bus, f.Versions.Store)
|
||||
|
||||
// **What an earlier delivery of this same ask left is removed first** (novox/hq issue 285): an ask is
|
||||
// redelivered when the holder that took it stopped mid-check — the build agent itself updated by the
|
||||
// rollout it is checking — and its containers, named by the ask's id, are still there. Raising the
|
||||
// store again under that name was refused, and the check said it could not run.
|
||||
if n, err := RemoveContainersOf(ctx, run, spec.ID); err == nil && n > 0 {
|
||||
say("check", "removed %d throwaway container(s) an earlier delivery of this check left", n)
|
||||
}
|
||||
// The throwaway store and bus, of the versions the mesh runs, removed whatever happens.
|
||||
defer func() {
|
||||
removing, done := context.WithTimeout(context.Background(), time.Minute)
|
||||
@@ -526,14 +533,17 @@ func gateLayer(ctx context.Context, spec CheckSpec, tree, root, gate, verdictFil
|
||||
// The gate could not judge: not the change's fault, and never a pass.
|
||||
return "error", "the merge gate could not judge the change: " + lastLine(out.String())
|
||||
}
|
||||
var said struct {
|
||||
Verdict string `json:"verdict"`
|
||||
Summary string `json:"summary"`
|
||||
}
|
||||
raw, err := os.ReadFile(verdictFile)
|
||||
if err != nil || json.Unmarshal(raw, &said) != nil || said.Verdict == "" {
|
||||
if err != nil {
|
||||
return "error", "the merge gate said no verdict"
|
||||
}
|
||||
said, ok := readGateVerdict(raw)
|
||||
if !ok {
|
||||
return "error", "the merge gate said no verdict"
|
||||
}
|
||||
if verdict, summary, raised := gateRaisedTheMesh(said); !raised {
|
||||
return verdict, summary
|
||||
}
|
||||
|
||||
// 3. **The replays of what the mesh runs** (to-be 45 §9, M9): mesh-lab's, from its main — reviewed code,
|
||||
// so given the container runtime the resolver replay raises containers with — against the bus of the
|
||||
@@ -683,6 +693,58 @@ func newProblems(change, base string) []string {
|
||||
return out
|
||||
}
|
||||
|
||||
// gateVerdict is what of the gate's verdict the seat reads: the verdict, and every machine — whether it
|
||||
// composes on the mesh and whether it composed in the gate's store without the change.
|
||||
type gateVerdict struct {
|
||||
Verdict string `json:"verdict"`
|
||||
Summary string `json:"summary"`
|
||||
Machines []struct {
|
||||
Described string `json:"described"`
|
||||
Live bool `json:"live-composes"`
|
||||
Base struct {
|
||||
Composes bool `json:"composes"`
|
||||
} `json:"base"`
|
||||
} `json:"machines"`
|
||||
}
|
||||
|
||||
// readGateVerdict reads the verdict the gate wrote, from its first line that opens a JSON document: a
|
||||
// judge from before the verdict was alone on its output printed what composition said ahead of it.
|
||||
func readGateVerdict(raw []byte) (gateVerdict, bool) {
|
||||
var v gateVerdict
|
||||
text := string(raw)
|
||||
if i := strings.Index(text, "\n{"); i >= 0 && !strings.HasPrefix(strings.TrimSpace(text), "{") {
|
||||
text = text[i+1:]
|
||||
}
|
||||
if json.Unmarshal([]byte(text), &v) != nil || v.Verdict == "" {
|
||||
return gateVerdict{}, false
|
||||
}
|
||||
return v, true
|
||||
}
|
||||
|
||||
// gateRaisedTheMesh is the seat's own reading of a verdict that passes (novox/hq issue 285): **a machine
|
||||
// the mesh composes that did not compose in the gate's store without the change makes the gate an error,
|
||||
// never a pass** — the change was judged against a machine that is not the mesh's, broken against broken.
|
||||
// The judge says so itself since issue 285, but the judge is the controller the mesh runs, and one from
|
||||
// before it passed such a verdict; read here too, the rule holds whatever judged. It answers false, with
|
||||
// the verdict to report, when the gate did not raise the mesh.
|
||||
func gateRaisedTheMesh(v gateVerdict) (string, string, bool) {
|
||||
if v.Verdict != "pass" && v.Verdict != "warning" {
|
||||
return "", "", true
|
||||
}
|
||||
var unraised []string
|
||||
for _, m := range v.Machines {
|
||||
if m.Live && !m.Base.Composes {
|
||||
unraised = append(unraised, m.Described)
|
||||
}
|
||||
}
|
||||
if len(unraised) == 0 {
|
||||
return "", "", true
|
||||
}
|
||||
return "error", fmt.Sprintf("the mesh as it is could not be raised, so the change cannot be judged against it: "+
|
||||
"%d of %d machines compose on the mesh and not in the gate (the first: %s) — novox/hq issue 285",
|
||||
len(unraised), len(v.Machines), unraised[0]), false
|
||||
}
|
||||
|
||||
// gitShow is a file as a ref has it.
|
||||
func gitShow(ctx context.Context, dir, ref, file string) ([]byte, error) {
|
||||
cmd := exec.CommandContext(ctx, "git", "show", ref+":"+filepath.ToSlash(file))
|
||||
|
||||
@@ -429,3 +429,54 @@ func TestAFailedScriptIsSaidByWhatFailed(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// **Issue 285**: a judge from before the rule passed "every machine composes with the change as it did
|
||||
// without (0 of 4 compose)". The seat reads the machines itself: a machine the mesh composes that did not
|
||||
// compose in the gate's store makes the gate an error, whatever judged it.
|
||||
func TestTheSeatCallsAGateThatRaisedNoMachineAnError(t *testing.T) {
|
||||
old := "bus users without a credential: controller\n" + `{"verdict":"pass","summary":"every machine composes with the change as it did without (0 of 2 compose)",
|
||||
"machines":[{"described":"the hub","live-composes":true,"base":{"composes":false}},
|
||||
{"described":"a machine","live-composes":true,"base":{"composes":false}}]}`
|
||||
v, ok := readGateVerdict([]byte(old))
|
||||
if !ok {
|
||||
t.Fatal("a verdict after a line of composition's was not read")
|
||||
}
|
||||
verdict, summary, raised := gateRaisedTheMesh(v)
|
||||
if raised || verdict != "error" || !strings.Contains(summary, "2 of 2 machines") || !strings.Contains(summary, "the hub") {
|
||||
t.Errorf("0 of 2 composing is %q %q", verdict, summary)
|
||||
}
|
||||
good := `{"verdict":"pass","summary":"(2 of 2 compose)","machines":[{"described":"the hub","live-composes":true,"base":{"composes":true}},
|
||||
{"described":"a laptop","live-composes":false,"base":{"composes":false}}]}`
|
||||
v, _ = readGateVerdict([]byte(good))
|
||||
if _, _, raised := gateRaisedTheMesh(v); !raised {
|
||||
t.Error("a machine that does not compose on the mesh either was read as the gate's failure")
|
||||
}
|
||||
failed := `{"verdict":"fail","summary":"x","machines":[{"described":"the hub","live-composes":true,"base":{"composes":false}}]}`
|
||||
v, _ = readGateVerdict([]byte(failed))
|
||||
if _, _, raised := gateRaisedTheMesh(v); !raised {
|
||||
t.Error("a failing verdict is the change's, and stands")
|
||||
}
|
||||
}
|
||||
|
||||
// **Issue 285**: an ask redelivered after its holder stopped mid-check found its own throwaway store still
|
||||
// there under its name, and the check said it could not run. What an earlier delivery left goes first.
|
||||
func TestARedeliveredCheckRemovesWhatItsEarlierDeliveryLeft(t *testing.T) {
|
||||
registry := checkEnvironment(t)
|
||||
repo, head := aCheckedRepository(t, map[string]string{CheckScript: "echo checked\n"})
|
||||
id := fmt.Sprintf("check-again-%d", time.Now().UnixNano())
|
||||
if out, err := exec.Command("docker", "run", "-d", "--rm", "--label", BuildLabel+"="+id, "--name", id+"-store",
|
||||
"postgres:17-alpine", "sleep", "300").CombinedOutput(); err != nil {
|
||||
t.Skipf("no container for the earlier delivery: %v %s", err, out)
|
||||
}
|
||||
v, err := Check(t.Context(), Command, CheckSpec{ID: id, Repository: repo, Ref: head, Owner: "novox",
|
||||
Repo: "hq", Number: 7, Toolchain: goToolchain}, t.TempDir(), registry, GitCredential{}, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("a redelivered check could not run: %v", err)
|
||||
}
|
||||
if v.Repo == nil || v.Repo.Verdict != "pass" {
|
||||
t.Errorf("the repository's check answered %+v", v.Repo)
|
||||
}
|
||||
if left := labelled(id); len(left) > 0 {
|
||||
t.Errorf("the check left %d container(s) behind", len(left))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user