Let the seed carry the differences its nodes found, each named in its brief for the operator to approve knowingly; a refusal or silence still fails (issue 1126)
This commit is contained in:
@@ -121,7 +121,7 @@ func Intended(current, laidInput *compose.Input, currentPlaced map[string]delive
|
||||
// shown in the ask's brief. The seed alone fails on any difference.
|
||||
func JudgeAnswers(p delivery.PlacementProposal, intended map[string]map[string]bool, answers map[string]*delivery.CandidateAnswer,
|
||||
revisions map[string]uint64, online map[string]bool, pulsing map[string]bool, now time.Time) delivery.PlacementVerdict {
|
||||
v := delivery.PlacementVerdict{At: now.UTC(), Intended: map[string][]string{}, Unintended: map[string][]string{},
|
||||
v := delivery.PlacementVerdict{At: now.UTC(), Intended: map[string][]string{}, Unintended: map[string][]string{}, Carried: map[string][]string{},
|
||||
Answers: map[string]delivery.CandidateAnswer{}}
|
||||
for node, set := range intended {
|
||||
for m := range set {
|
||||
@@ -150,7 +150,8 @@ func JudgeAnswers(p delivery.PlacementProposal, intended map[string]map[string]b
|
||||
want := intended[node]
|
||||
differs := func(module, words string) {
|
||||
if p.Seed {
|
||||
v.Fails = append(v.Fails, fmt.Sprintf("%s %s with the seed, which must be the same as what runs", node, words))
|
||||
// The seed carries what its nodes found, each named in the brief for the operator who approves it.
|
||||
v.Carried[node] = append(v.Carried[node], words)
|
||||
return
|
||||
}
|
||||
if !want[module] {
|
||||
@@ -158,7 +159,7 @@ func JudgeAnswers(p delivery.PlacementProposal, intended map[string]map[string]b
|
||||
}
|
||||
}
|
||||
for _, d := range a.Differs {
|
||||
differs(d.Module, fmt.Sprintf("composes %s differently (%s)", d.Module, d.Why))
|
||||
differs(d.Module, differenceWords(d))
|
||||
}
|
||||
for _, d := range a.Added {
|
||||
differs(d.Module, "would run "+d.Module)
|
||||
@@ -174,6 +175,9 @@ func JudgeAnswers(p delivery.PlacementProposal, intended map[string]map[string]b
|
||||
if len(v.Unintended) == 0 {
|
||||
v.Unintended = nil
|
||||
}
|
||||
if len(v.Carried) == 0 {
|
||||
v.Carried = nil
|
||||
}
|
||||
if len(v.Answers) == 0 {
|
||||
v.Answers = nil
|
||||
}
|
||||
@@ -228,6 +232,9 @@ func Brief(p delivery.PlacementProposal, v delivery.PlacementVerdict) string {
|
||||
nodes[n] = true
|
||||
}
|
||||
}
|
||||
for n := range v.Carried {
|
||||
nodes[n] = true
|
||||
}
|
||||
names := make([]string, 0, len(nodes))
|
||||
for n := range nodes {
|
||||
names = append(names, n)
|
||||
@@ -243,6 +250,9 @@ func Brief(p delivery.PlacementProposal, v delivery.PlacementVerdict) string {
|
||||
if m := v.Unintended[n]; len(m) > 0 {
|
||||
fmt.Fprintf(&b, "; also differs, not computed: %s", strings.Join(m, ", "))
|
||||
}
|
||||
if m := v.Carried[n]; len(m) > 0 {
|
||||
fmt.Fprintf(&b, "; the seed carries: %s", strings.Join(m, "; "))
|
||||
}
|
||||
if held := v.Answers[n].Held; len(held) > 0 {
|
||||
var names []string
|
||||
for _, h := range held {
|
||||
@@ -253,3 +263,21 @@ func Brief(p delivery.PlacementProposal, v delivery.PlacementVerdict) string {
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// differenceWords names one module's difference: the module, the resource and the first path that differs in it, and how
|
||||
// many more, or why when no body is shown.
|
||||
func differenceWords(d delivery.ModuleDiffers) string {
|
||||
for _, b := range d.Bodies {
|
||||
if len(b.Paths) > 0 {
|
||||
words := fmt.Sprintf("%s: %s at %s", d.Module, b.ID, b.Paths[0])
|
||||
if n := len(b.Paths) - 1; n > 0 {
|
||||
words += fmt.Sprintf(" (and %d more)", n)
|
||||
}
|
||||
return words
|
||||
}
|
||||
}
|
||||
if d.Resource != "" {
|
||||
return fmt.Sprintf("%s: %s (%s)", d.Module, d.Resource, d.Why)
|
||||
}
|
||||
return fmt.Sprintf("%s: %s", d.Module, d.Why)
|
||||
}
|
||||
|
||||
@@ -47,7 +47,9 @@ func TestJudge(t *testing.T) {
|
||||
{"a module added where intended", false, map[string]*delivery.CandidateAnswer{"anchor": {Proposal: "p-one", Revision: 7, Verdict: delivery.CandidateDiffers,
|
||||
Added: []delivery.ModuleAdded{{Module: "web", Unissued: []string{"its own secret"}}}}, "laptop": same(8)}, nil, delivery.VerdictPass, "", ""},
|
||||
{"a difference not computed passes, marked", false, map[string]*delivery.CandidateAnswer{"anchor": differs(7, "mail"), "laptop": same(8)}, nil, delivery.VerdictPass, "", "mail"},
|
||||
{"the seed with a difference", true, map[string]*delivery.CandidateAnswer{"anchor": same(7), "laptop": differs(8, "web")}, nil, delivery.VerdictFail, "laptop composes web differently", ""},
|
||||
{"the seed carries a difference, named", true, map[string]*delivery.CandidateAnswer{"anchor": same(7), "laptop": {Proposal: "p-one", Revision: 8,
|
||||
Verdict: delivery.CandidateDiffers, Differs: []delivery.ModuleDiffers{{Module: "node-tools", Resource: "node-tools.runtime", Why: "the resource differs",
|
||||
Bodies: []delivery.ResourceBodies{{ID: "node-tools.runtime", Paths: []string{"env.MESH_OWNED_PATHS./etc/resolv.conf", "env.MESH_TOOL_MODULES"}}}}}}}, nil, delivery.VerdictPass, "", ""},
|
||||
{"a refusal", false, map[string]*delivery.CandidateAnswer{"anchor": same(7), "laptop": {Proposal: "p-one", Revision: 8, Verdict: delivery.CandidateRefused,
|
||||
Refusals: []delivery.ModuleRefused{{Module: "web", Why: "no own secret"}}}}, nil, delivery.VerdictFail, "laptop refuses", ""},
|
||||
{"a node not answering, within its bound", false, map[string]*delivery.CandidateAnswer{"anchor": same(7)}, map[string]bool{"laptop": true}, delivery.VerdictWaits, "laptop (offline)", ""},
|
||||
@@ -63,6 +65,9 @@ func TestJudge(t *testing.T) {
|
||||
if v.State != row.state || !strings.Contains(all, row.words) {
|
||||
t.Errorf("%s: %s, %q", row.name, v.State, all)
|
||||
}
|
||||
if row.seed && row.state == delivery.VerdictPass && !strings.Contains(Brief(q, v), "the seed carries: node-tools: node-tools.runtime at env.MESH_OWNED_PATHS./etc/resolv.conf (and 1 more)") {
|
||||
t.Errorf("%s: the brief does not name it: %s", row.name, Brief(q, v))
|
||||
}
|
||||
if strings.HasPrefix(row.name, "a module held as today") && !strings.Contains(Brief(q, v), "stays held as today: spiegel") {
|
||||
t.Errorf("%s: the brief does not show it: %s", row.name, Brief(q, v))
|
||||
}
|
||||
|
||||
@@ -356,15 +356,19 @@ func NewProposalID(at time.Time) string {
|
||||
// is shown by node: Intended is what the holder computed the proposal changes, Unintended what a node answered beside
|
||||
// it, marked as such in the brief.
|
||||
type PlacementVerdict struct {
|
||||
State string `json:"state"`
|
||||
At time.Time `json:"at"`
|
||||
Fails []string `json:"fails,omitempty"`
|
||||
Silent []string `json:"silent,omitempty"`
|
||||
Until time.Time `json:"until,omitempty"`
|
||||
Intended map[string][]string `json:"intended,omitempty"`
|
||||
Unintended map[string][]string `json:"unintended,omitempty"`
|
||||
Answers map[string]CandidateAnswer `json:"answers,omitempty"`
|
||||
Notes []string `json:"notes,omitempty"`
|
||||
State string `json:"state"`
|
||||
At time.Time `json:"at"`
|
||||
Fails []string `json:"fails,omitempty"`
|
||||
Silent []string `json:"silent,omitempty"`
|
||||
Until time.Time `json:"until,omitempty"`
|
||||
Intended map[string][]string `json:"intended,omitempty"`
|
||||
Unintended map[string][]string `json:"unintended,omitempty"`
|
||||
// Carried is, for the seed, by node each difference a node found that the seed carries, named for the operator who
|
||||
// approves it knowingly ("<module>: <resource> at <path>"): the seed fails on no difference, only on a refusal or
|
||||
// silence (issue 1126).
|
||||
Carried map[string][]string `json:"carried,omitempty"`
|
||||
Answers map[string]CandidateAnswer `json:"answers,omitempty"`
|
||||
Notes []string `json:"notes,omitempty"`
|
||||
}
|
||||
|
||||
// PlacementProposal is an open proposal, the value of its key in PlacementProposalsBucket.
|
||||
|
||||
Reference in New Issue
Block a user