An unchanged process keeps its record, so the node's runtime is not re-created every other cycle (hq issue 210)
The process applier's unchanged path returned an outcome that said nothing about what was written; the loop recorded it like any other, erasing the digest. The next cycle found no record and re-created the daemon, the one after found a record again, and so on: the node's runtime restarted every ten minutes on every machine since it arrived. The outcome now carries the digest forward, as a file's does. The test applies one process three times and asserts the record survives an unchanged apply and no restart is asked.
This commit is contained in:
@@ -77,6 +77,11 @@ func applyProcess(ctx context.Context, r *declaration.Process, run Runner,
|
|||||||
// Everything about it is as declared. Still asked whether it is RUNNING, because a
|
// Everything about it is as declared. Still asked whether it is RUNNING, because a
|
||||||
// declaration that is satisfied by a record rather than by the machine is how a stopped
|
// declaration that is satisfied by a record rather than by the machine is how a stopped
|
||||||
// service reports success.
|
// service reports success.
|
||||||
|
// **The record is carried forward, not re-derived.** An unchanged outcome is recorded
|
||||||
|
// like any other, so one that said nothing about what was written erased the digest; the
|
||||||
|
// next apply then found no record, re-created the daemon, and the one after that found a
|
||||||
|
// record again — the node's runtime restarted every other cycle (novox/hq 04-ISSUES/210).
|
||||||
|
out.wrote = want
|
||||||
if active, err := run(ctx, "systemctl", "is-active", "--quiet", r.Name+".service"); err == nil {
|
if active, err := run(ctx, "systemctl", "is-active", "--quiet", r.Name+".service"); err == nil {
|
||||||
_ = active
|
_ = active
|
||||||
return out, nil
|
return out, nil
|
||||||
|
|||||||
@@ -256,3 +256,60 @@ func TestAProcessRecordedUnderAPathlikeNameIsRefusedNotRemoved(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// novox/hq 04-ISSUES/210: the node's runtime was re-created — and restarted — on every reconcile,
|
||||||
|
// because the host did not find what it wrote for a process the cycle before. Applying the same
|
||||||
|
// process declaration twice must do no work the second time.
|
||||||
|
func TestAProcessAppliedAgainIsUnchangedAndNotRestarted(t *testing.T) {
|
||||||
|
units, bundles := t.TempDir(), t.TempDir()
|
||||||
|
wasUnits, wasBundles := unitDir, daemonRoot
|
||||||
|
unitDir, daemonRoot = units, bundles
|
||||||
|
t.Cleanup(func() { unitDir, daemonRoot = wasUnits, wasBundles })
|
||||||
|
|
||||||
|
body, digest := anArchive(t, map[string]string{"main.js": "console.log(1)\n"})
|
||||||
|
var commands []string
|
||||||
|
run := func(ctx context.Context, name string, args ...string) (string, error) {
|
||||||
|
commands = append(commands, name+" "+strings.Join(args, " "))
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
d := declare(t, `{"id":"node-tools.runtime","type":"process","name":"node-tools","source":"`+serving(t, body)+
|
||||||
|
`","digest":"`+digest+`","run":["node","main.js"],"env":{"MESH_TOOL_MODULES":"a=/x/index.js"}}`)
|
||||||
|
|
||||||
|
first, state, err := Apply(context.Background(), archHost(t), d, store.State{}, store.OriginDeclared, run, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if o := outcomeOf(first, "node-tools.runtime"); o.Action != "created" {
|
||||||
|
t.Fatalf("first apply: %+v, want created", o)
|
||||||
|
}
|
||||||
|
rec, ok := state.Find("node-tools.runtime")
|
||||||
|
if !ok || rec.Wrote == "" {
|
||||||
|
t.Fatalf("the host did not record what it wrote for the process: %+v", rec)
|
||||||
|
}
|
||||||
|
commands = nil
|
||||||
|
again, state, err := Apply(context.Background(), archHost(t), d, state, store.OriginDeclared, run, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if o := outcomeOf(again, "node-tools.runtime"); o.Action != "unchanged" {
|
||||||
|
t.Errorf("second apply: %+v, want unchanged", o)
|
||||||
|
}
|
||||||
|
for _, c := range commands {
|
||||||
|
if strings.Contains(c, "restart") {
|
||||||
|
t.Errorf("the second apply restarted the process: %v", commands)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// And the record survives an unchanged apply: the third cycle is unchanged too. This is the
|
||||||
|
// cycle the live mesh showed — created, unchanged, created — before the record was carried.
|
||||||
|
if rec, _ := state.Find("node-tools.runtime"); rec.Wrote == "" {
|
||||||
|
t.Fatalf("an unchanged apply dropped the digest from the record: %+v", rec)
|
||||||
|
}
|
||||||
|
commands = nil
|
||||||
|
third, _, err := Apply(context.Background(), archHost(t), d, state, store.OriginDeclared, run, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if o := outcomeOf(third, "node-tools.runtime"); o.Action != "unchanged" {
|
||||||
|
t.Errorf("third apply: %+v, want unchanged", o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user