diff --git a/go/composer/composer.go b/go/composer/composer.go index 69b729e..dd224cf 100644 --- a/go/composer/composer.go +++ b/go/composer/composer.go @@ -135,13 +135,17 @@ type ManifestRead struct { } // Machine is one machine composed: whether it composes (resolves, at DepthResolve), why not, the node-engine -// validator's problems and the composition's refusals (DepthCompose), and its declared paths when asked. +// validator's problems and the composition's refusals (DepthCompose), and its declared paths when asked. NotJudged says, +// in words, why the composer did not judge the machine (an older node-engine's input that does not say how it composes, +// novox/hq ADR 0593 §9): the machine is then neither composed nor refused, and a reader decides by this field alone, +// never by the words of Reason. type Machine struct { - Composes bool `json:"composes"` - Reason string `json:"reason,omitempty"` - Problems []string `json:"problems,omitempty"` - Refusals []string `json:"refusals,omitempty"` - Declared []Path `json:"declared,omitempty"` + Composes bool `json:"composes"` + NotJudged string `json:"not_judged,omitempty"` + Reason string `json:"reason,omitempty"` + Problems []string `json:"problems,omitempty"` + Refusals []string `json:"refusals,omitempty"` + Declared []Path `json:"declared,omitempty"` } // Path is one path a declaration puts on the machine, with its kind (the resource's type), owner, group and mode as diff --git a/go/delivery/compose_candidate.go b/go/delivery/compose_candidate.go index 25d2a17..84cdefd 100644 --- a/go/delivery/compose_candidate.go +++ b/go/delivery/compose_candidate.go @@ -31,6 +31,12 @@ const ( // starting): no module is named, Refused says why in words, and the checker takes it as a node that did not answer // (ComposeNoAnswer, ADR 0593 §9), deciding by this verdict and never by the words. ComposeCandidateUnready = "unready" + // ComposeCandidateNotJudged is the verdict for a node the checker's sandbox composed and could not judge (an older + // node-engine's input that does not say how it composes, novox/hq ADR 0593 §9): no module is named, Refused says why in + // words, and the checker names the node with them and passes the check with that warning, never silent and never a + // failure of the change. The checker decides by this verdict, never by the words (ADR 0601 §4's kind: a contract is a + // field, not a phrase). + ComposeCandidateNotJudged = "not-judged" // The states of one module's composition with the candidate: composed (Digest set), refused by the candidate // (Words set), or held, refused without the candidate too (Words set), which counts toward no verdict. @@ -115,13 +121,13 @@ func (a ComposeCandidateAnswer) check() error { if a.Took < 0 { return fmt.Errorf("delivery: the answer of %s took %d ms", a.Node, a.Took) } - if a.Verdict == ComposeCandidateUnready && a.Refused == "" { - return fmt.Errorf("delivery: %s is not ready to compose and its answer says no why", a.Node) + if (a.Verdict == ComposeCandidateUnready || a.Verdict == ComposeCandidateNotJudged) && a.Refused == "" { + return fmt.Errorf("delivery: %s's answer is %s and says no why", a.Node, a.Verdict) } if a.Refused != "" { - if len(a.Modules) > 0 || a.Verdict != ComposeCandidateRefused && a.Verdict != ComposeCandidateUnready { - return fmt.Errorf("delivery: %s could not compose the candidate (%s), and its answer names modules or is neither %s nor %s", - a.Node, a.Refused, ComposeCandidateRefused, ComposeCandidateUnready) + if len(a.Modules) > 0 || a.Verdict != ComposeCandidateRefused && a.Verdict != ComposeCandidateUnready && a.Verdict != ComposeCandidateNotJudged { + return fmt.Errorf("delivery: %s could not compose the candidate (%s), and its answer names modules or is neither %s, %s nor %s", + a.Node, a.Refused, ComposeCandidateRefused, ComposeCandidateUnready, ComposeCandidateNotJudged) } return nil } diff --git a/go/delivery/compose_candidate_test.go b/go/delivery/compose_candidate_test.go index 7c66dce..63e4e1d 100644 --- a/go/delivery/compose_candidate_test.go +++ b/go/delivery/compose_candidate_test.go @@ -78,6 +78,23 @@ func TestComposeCandidateAndComposedPlanRoundTripAndRefuse(t *testing.T) { if _, err := EncodeComposeCandidateAnswer(unready); err == nil { t.Error("an unready answer naming modules was encoded") } + // A node the sandbox did not judge: its own verdict, with words and no module (ADR 0593 §9, ADR 0601 §4). + notJudged := ComposeCandidateAnswer{Node: "ace", Candidate: key, Verdict: ComposeCandidateNotJudged, Refused: "its node-engine sends no running state"} + if raw, err := EncodeComposeCandidateAnswer(notJudged); err != nil { + t.Errorf("a not-judged answer was not encoded: %v", err) + } else if back, err := DecodeComposeCandidateAnswer(raw); err != nil || back.Verdict != ComposeCandidateNotJudged { + t.Errorf("a not-judged answer read back as %+v, %v", back, err) + } + for name, mutate := range map[string]func(*ComposeCandidateAnswer){ + "no why": func(a *ComposeCandidateAnswer) { a.Refused = "" }, + "modules too": func(a *ComposeCandidateAnswer) { a.Modules = good.Modules }, + } { + a := notJudged + mutate(&a) + if _, err := EncodeComposeCandidateAnswer(a); err == nil { + t.Errorf("a not-judged answer with %s was encoded", name) + } + } if _, err := DecodeComposeCandidateAnswer([]byte(strings.Replace(string(raw), `"took"`, `"other":1,"took"`, 1))); err == nil { t.Error("an answer with an unknown key was read") }