Files
mesh-host/internal/apply/into_handover_test.go
T

130 lines
5.2 KiB
Go

package apply
import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
"testing"
"git.novox.be/novox/mesh-sdk/go/declaration"
"github.com/novox/mesh-host/internal/store"
)
// novox/hq issue 190, ADR 0222: the private network stops writing the mesh's registry into the
// container runtime's file, and the runtime's own module writes the same member. Both are moved in
// one apply, and the machine never loses the member: the network's record goes first (its member
// leaves the list), the runtime's module is applied after (the member is added back, now recorded
// as the runtime's), and the daemon is reloaded once, never stopped, disabled or restarted — even
// though the record going says the unit was found stopped and disabled.
const theRegistry = "anchor.internal:5100"
func runtimeDecl(t *testing.T, path string, network bool) *declaration.Declaration {
t.Helper()
var resources []string
if network {
resources = append(resources,
fmt.Sprintf(`{"id":"networking.registry-trust","type":"file","path":%q,"into":"json","content":%q}`,
path, `{"insecure-registries":["`+theRegistry+`"]}`),
`{"id":"networking.registry-trust-reload","type":"service","unit":"docker.service","state":"running",
"reload-on":["networking.registry-trust"]}`)
}
resources = append(resources,
fmt.Sprintf(`{"id":"docker.daemon","type":"file","path":%q,"into":"json","content":%q}`,
path, `{"live-restore":true,"insecure-registries":["`+theRegistry+`"]}`),
`{"id":"docker.runtime","type":"service","unit":"docker.service","state":"running","boot":"enabled",
"reload-on":["docker.daemon"]}`)
return parse(t, `{"declaration":1,"resources":[`+strings.Join(resources, ",")+`]}`)
}
func TestTheRegistryMovesToTheRuntimesModuleWithoutLeavingTheList(t *testing.T) {
path := filepath.Join(t.TempDir(), "daemon.json")
if err := os.WriteFile(path, []byte(`{"insecure-registries":["192.0.2.7:5000"]}`), 0o644); err != nil {
t.Fatal(err)
}
var commands []string
run := recordingServices(&commands)
// Both declare it: the network's record added the member, so the runtime's finds it there.
_, state, err := Apply(context.Background(), archHost(t), runtimeDecl(t, path, true), store.State{},
store.OriginDeclared, run, nil, nil)
if err != nil {
t.Fatal(err)
}
// Say the network's record found the runtime stopped and disabled: giving that back would stop
// every container on the machine.
for i := range state.Resources {
if state.Resources[i].ID == "networking.registry-trust-reload" {
state.Resources[i].Found = &store.FoundUnit{Unit: "docker.service", State: "stopped", Boot: "disabled"}
}
}
commands = nil
report, state, err := Apply(context.Background(), archHost(t), runtimeDecl(t, path, false), state,
store.OriginDeclared, run, nil, nil)
if err != nil {
t.Fatal(err)
}
if got := fmt.Sprint(readObject(t, path)["insecure-registries"]); got != "[192.0.2.7:5000 "+theRegistry+"]" {
t.Fatalf("the list after the move: %s", got)
}
rec, _ := state.Find("docker.daemon")
if added := rec.Into.Added["insecure-registries"]; len(added) != 1 || canonical(added[0]) != `"`+theRegistry+`"` {
t.Errorf("the member is not recorded as the runtime's module's: %s", added)
}
reloads := 0
for _, c := range commands {
if strings.Contains(c, "docker.service") && (strings.Contains(c, " stop ") || strings.Contains(c, " restart ") ||
strings.Contains(c, " disable ")) {
t.Errorf("the runtime was given back as the network's record found it: %q", c)
}
if strings.Contains(c, "reload docker.service") {
reloads++
}
}
if reloads != 1 {
t.Errorf("the runtime was reloaded %d times; commands were %v", reloads, commands)
}
for _, o := range report.Outcomes {
if o.ID == "networking.registry-trust-reload" && o.Action != "forgotten" {
t.Errorf("the network's record of the unit was %q: %s", o.Action, o.Detail)
}
}
// Steady from here on, and undeclaring the runtime's module takes only what it added.
report, state, err = Apply(context.Background(), archHost(t), runtimeDecl(t, path, false), state,
store.OriginDeclared, run, nil, nil)
if err != nil {
t.Fatal(err)
}
for _, o := range report.Outcomes {
if o.ID == "docker.daemon" && o.Action != "unchanged" {
t.Errorf("a second apply was %q", o.Action)
}
}
if _, _, err := Apply(context.Background(), archHost(t), somethingElse(t), state, store.OriginDeclared, run, nil, nil); err != nil {
t.Fatal(err)
}
if got := fmt.Sprint(readObject(t, path)["insecure-registries"]); got != "[192.0.2.7:5000]" {
t.Errorf("undeclaring the runtime's module left %s", got)
}
}
// And the preview says the same before it happens.
func TestThePlanForgetsARecordOfAUnitStillHeld(t *testing.T) {
path := filepath.Join(t.TempDir(), "daemon.json")
var commands []string
_, state, err := Apply(context.Background(), archHost(t), runtimeDecl(t, path, true), store.State{},
store.OriginDeclared, recordingServices(&commands), nil, nil)
if err != nil {
t.Fatal(err)
}
for _, step := range Plan(runtimeDecl(t, path, false), state, store.OriginDeclared) {
if step.ID == "networking.registry-trust-reload" && step.Verb != "forget" {
t.Errorf("the plan would %s the network's record of a unit docker.runtime holds: %s", step.Verb, step.Why)
}
}
}