Tell the operator when the answer to a plan's ask is refused (issue 1316, ADR 0449)
This commit is contained in:
@@ -77,6 +77,15 @@ type askerRig struct {
|
||||
silenced []string
|
||||
acts []link.HandAct
|
||||
now time.Time
|
||||
// refuses, when set, is what every verb the asker calls answers; told is every notice the operator was told.
|
||||
refuses error
|
||||
told []told
|
||||
}
|
||||
|
||||
// told is one plain notice the asker told the operator.
|
||||
type told struct {
|
||||
headline string
|
||||
lines []string
|
||||
}
|
||||
|
||||
func newAskerRig(t *testing.T) *askerRig {
|
||||
@@ -102,6 +111,10 @@ func newAskerRig(t *testing.T) *askerRig {
|
||||
call: func(_ context.Context, a conditions.Action, args map[string]string) error {
|
||||
raw, _ := json.Marshal(args)
|
||||
r.called = append(r.called, a.Verb+"@"+a.Machine+" "+string(raw))
|
||||
return r.refuses
|
||||
},
|
||||
tell: func(_ context.Context, headline string, lines []string) error {
|
||||
r.told = append(r.told, told{headline, lines})
|
||||
return nil
|
||||
},
|
||||
record: func(_ context.Context, act link.HandAct) error { r.acts = append(r.acts, act); return nil },
|
||||
@@ -1074,3 +1087,76 @@ func TestAWarrantForRetryCallsPlansRetryOnceAndIsRecorded(t *testing.T) {
|
||||
t.Errorf("kept %+v", got)
|
||||
}
|
||||
}
|
||||
|
||||
// 1316/AC1 and AC2: a Retry or a Stop on a plan that moved on is refused by `plans`; the operator who pressed it is
|
||||
// told once, with the ask's headline and the refusal's words, and the hand act records the failure as before.
|
||||
func TestARefusedAnswerToAPlansAskIsToldOnceAndStillRecorded(t *testing.T) {
|
||||
plan := "plan-1792433168676870614"
|
||||
waiting := conditions.Condition{Key: "plan." + plan + ".waiting", Severity: conditions.Urgent,
|
||||
Headline: "openrazer delivery waiting to start", Needs: "start it, or stop it.",
|
||||
Explanation: "Needs you: start it, or stop it.", Actions: waitingActions(plan, conditions.Urgent)}
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
c conditions.Condition
|
||||
label string
|
||||
}{
|
||||
{"retry", failedPlanCondition(plan), "Retry"},
|
||||
{"stop", waiting, "Stop"},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
r := newAskerRig(t)
|
||||
r.refuses = errors.New("mesh-controller.plans refused: " + plan + " is done; there is nothing to retry")
|
||||
r.open = []conditions.Condition{tc.c}
|
||||
_ = r.a.reconcile(context.Background())
|
||||
w := r.warrantFor(t, tc.c.Key, tc.label)
|
||||
answerWith(t, r, w)
|
||||
answerWith(t, r, w) // heard again: told once
|
||||
if len(r.told) != 1 {
|
||||
t.Fatalf("told %+v", r.told)
|
||||
}
|
||||
got := r.told[0]
|
||||
if got.headline != tc.c.Headline {
|
||||
t.Errorf("the headline is %q, not the ask's %q", got.headline, tc.c.Headline)
|
||||
}
|
||||
said := strings.Join(got.lines, " ")
|
||||
if !strings.Contains(said, "the plan is done; there is nothing to retry") || !strings.Contains(said, tc.label) {
|
||||
t.Errorf("the lines do not carry the answer and the refusal's words: %q", said)
|
||||
}
|
||||
if why, ok := conditions.Plain(said); !ok {
|
||||
t.Errorf("the notice is not plain, so the channel would withhold it: %s: %q", why, said)
|
||||
}
|
||||
if len(r.acts) != 1 || !strings.HasPrefix(r.acts[0].Outcome, "failed: ") {
|
||||
t.Errorf("the failure is not recorded as before: %+v", r.acts)
|
||||
}
|
||||
if kept := r.store[w.Ask]; !strings.HasPrefix(kept.Acted, "failed: ") {
|
||||
t.Errorf("kept %+v", kept)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 1316/AC3: an answer that is done tells nothing; and a refusal of an answer that is no plan's is not told here.
|
||||
func TestADoneAnswerTellsNothingAndOnlyAPlansRefusalIsTold(t *testing.T) {
|
||||
r := newAskerRig(t)
|
||||
c := failedPlanCondition("plan-1792433168676870614")
|
||||
r.open = []conditions.Condition{c}
|
||||
_ = r.a.reconcile(context.Background())
|
||||
answerWith(t, r, r.warrantFor(t, c.Key, "Retry"))
|
||||
if len(r.told) != 0 {
|
||||
t.Fatalf("a retry that was done told %+v", r.told)
|
||||
}
|
||||
|
||||
r = newAskerRig(t)
|
||||
r.refuses = errors.New("node-service-manager.restart refused: unit not found")
|
||||
module := conditions.Condition{Key: "module.openrazer.g14.unhealthy", Severity: conditions.Warning,
|
||||
Headline: "openrazer not working on g14", Needs: "restart its service openrazer-daemon on g14.",
|
||||
Explanation: "Needs you: restart it.", Actions: []conditions.Action{{Label: "Restart",
|
||||
Verb: "node-service-manager.restart", Machine: "g14", Level: conditions.LevelApprove,
|
||||
Arguments: map[string]string{"unit": "openrazer-daemon.service", "scope": "user"}}}}
|
||||
r.open = []conditions.Condition{module}
|
||||
_ = r.a.reconcile(context.Background())
|
||||
answerWith(t, r, r.warrantFor(t, module.Key, "Restart"))
|
||||
if len(r.told) != 0 || len(r.acts) != 1 {
|
||||
t.Fatalf("a refused restart told %+v, acts %+v", r.told, r.acts)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user