Third review of mesh-catalog #147. A unit's name can now come from a setting (a template's instance), so every systemctl call that names a unit passes `--` before it, and a name can never be read as an option. On removal, a unit is started or enabled again only when it is exactly the stopped and disabled the mesh held it to. A masked unit, or one somebody started or enabled since, is left as it is, and the report says why. A restore that fails (the start refused, or the unit not running after it) puts back what this attempt changed and is reported as waiting, so no apply is made fatal by it and the next one tries again. The tests' fake service managers read systemctl's arguments in the shape they were written for (oldShape); internal/system's own test holds the `--`.
70 lines
2.5 KiB
Go
70 lines
2.5 KiB
Go
package apply
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/novox/mesh-host/internal/store"
|
|
)
|
|
|
|
// The host's own former archive stops nothing (novox/hq issue 194). A new host's first apply finds
|
|
// the version before it as a former target of the archive that delivered it; an archive has no
|
|
// removal then (issue 162), and the refusal stopped every machine applying anything. A former
|
|
// target of an archive is left in place, said, and forgotten: the version before is what a rollback
|
|
// starts (ADR 0141).
|
|
func TestTheHostsOwnFormerArchiveIsLeftInPlaceNotFatal(t *testing.T) {
|
|
run := func(_ context.Context, name string, args ...string) (string, error) {
|
|
args = oldShape(name, args)
|
|
if name == "docker" && args[0] == "info" {
|
|
return "29.0.0\n", nil
|
|
}
|
|
return "", nil
|
|
}
|
|
dir := t.TempDir()
|
|
former := store.FormerID("mesh-host.next", dir+"/versions/old")
|
|
known := store.State{Resources: []store.Applied{
|
|
{ID: "mesh-host.next", Type: "archive", Target: dir + "/versions/new", Origin: store.OriginDeclared},
|
|
{ID: former, Type: "archive", Target: dir + "/versions/old", Origin: store.OriginDeclared},
|
|
}}
|
|
body, digest := anArchive(t, map[string]string{"nox-mesh-host": "#!/bin/sh\n"})
|
|
d := parse(t, `{"declaration":1,"resources":[
|
|
{"id":"mesh-host.next","type":"archive","path":"`+dir+`/versions/new","source":"`+serving(t, body)+`","digest":"`+digest+`"},
|
|
{"id":"notes.conf","type":"file","path":"`+dir+`/notes.conf","content":"x"}
|
|
]}`)
|
|
report, state, err := Apply(context.Background(), archHost(t), d, known, store.OriginDeclared, run, nil, nil)
|
|
if err != nil {
|
|
t.Fatalf("the apply failed: %v", err)
|
|
}
|
|
if _, still := state.HeldAt(former); still {
|
|
t.Fatal("held?")
|
|
}
|
|
for _, r := range state.Resources {
|
|
if r.ID == former {
|
|
t.Fatal("the former archive is still on record")
|
|
}
|
|
}
|
|
said := false
|
|
for _, o := range report.Outcomes {
|
|
if o.ID == former && o.Action == "forgotten" && strings.Contains(o.Detail, "left in place") {
|
|
said = true
|
|
}
|
|
}
|
|
if !said {
|
|
t.Fatalf("leaving the former archive was not said: %+v", report.Outcomes)
|
|
}
|
|
applied := false
|
|
for _, o := range report.Outcomes {
|
|
if o.ID == "notes.conf" && o.Action == "created" {
|
|
applied = true
|
|
}
|
|
}
|
|
if !applied {
|
|
t.Fatalf("the rest of the declaration was not applied: %+v", report.Outcomes)
|
|
}
|
|
|
|
// An archive the declaration dropped is taken away since issue 162; one recorded before the
|
|
// host kept what it unpacked is left in place and forgotten, never fatal — that case is
|
|
// archive_removal_test.go's.
|
|
}
|