diff --git a/cmd/mesh-controller/adoption.go b/cmd/mesh-controller/adoption.go index 411ae05d..afa90a07 100644 --- a/cmd/mesh-controller/adoption.go +++ b/cmd/mesh-controller/adoption.go @@ -424,8 +424,9 @@ func comparisonOf(module string, c comparison, opts takeOptions) (preview string } // And its settings on this machine, composed against its definition (rule 1, rule 6). for _, layer := range c.layers { - keys := make([]string, 0, len(layer.Values)) - for k := range layer.Values { + said := layer.Whole() + keys := make([]string, 0, len(said)) + for k := range said { keys = append(keys, k) } sort.Strings(keys) diff --git a/cmd/mesh-controller/facts.go b/cmd/mesh-controller/facts.go index 50b891cf..af04f202 100644 --- a/cmd/mesh-controller/facts.go +++ b/cmd/mesh-controller/facts.go @@ -348,11 +348,11 @@ func gatherFacts(ctx context.Context, open *stores, busVersion string) (snapshot if layer.From == catalogue.MeshWideLayer { if !meshWide[module] { meshWide[module] = true - f.Settings = append(f.Settings, snapshot.Settings{Module: module, Values: scrub.Values(layer.Values)}) + f.Settings = append(f.Settings, snapshot.Settings{Module: module, Values: scrub.Values(layer.Whole())}) } continue } - m.Settings = append(m.Settings, snapshot.Settings{Module: module, Values: scrub.Values(layer.Values)}) + m.Settings = append(m.Settings, snapshot.Settings{Module: module, Values: scrub.Values(layer.Whole())}) } } m.Declaration = declarationFacts(ctx, open, n.Name, gens, gensErr, scrub) diff --git a/cmd/mesh-controller/mesh_for_test.go b/cmd/mesh-controller/mesh_for_test.go index b2e76be4..eea81d13 100644 --- a/cmd/mesh-controller/mesh_for_test.go +++ b/cmd/mesh-controller/mesh_for_test.go @@ -33,7 +33,21 @@ import ( func aMesh(t *testing.T) *stores { t.Helper() inventory.ForTest(t) // raises the store, migrates it, and points the environment at it - licences.ForTest(t) // planning reaches this one too, by name and never by connection + return aMeshOnTheStore(t) +} + +// aMeshBefore is aMesh on a store migrated only below migration `number`, with the function that runs the +// rest: for a test of what a migration that moves data does to what the mesh composes (novox/hq issue 651). +func aMeshBefore(t *testing.T, number int) (*stores, func()) { + t.Helper() + _, migrate := inventory.ForTestBefore(t, number) + return aMeshOnTheStore(t), migrate +} + +// aMeshOnTheStore is the mesh aMesh makes, on the store the environment points at. +func aMeshOnTheStore(t *testing.T) *stores { + t.Helper() + licences.ForTest(t) // planning reaches this one too, by name and never by connection open, err := openStores(t.Context()) if err != nil { diff --git a/cmd/mesh-controller/modules.go b/cmd/mesh-controller/modules.go index 66804655..dc3ea06c 100644 --- a/cmd/mesh-controller/modules.go +++ b/cmd/mesh-controller/modules.go @@ -537,6 +537,10 @@ func settingsCommand(ctx context.Context, args []string) error { if p.SetBy != "" { by = "; " + p.SetBy } + if p.Why != "" { + // A layer whose placement moved out of it (migration 0097): still the layer, as it stands. + by += "; " + p.Why + } fmt.Printf("%s on %s, until %s (%s%s):\n %s\n", positionals[0], where, p.ReplacedAt.Local().Format("2006-01-02 15:04:05"), p.ReplacedBy, by, shown) } diff --git a/internal/catalogue/data_test.go b/internal/catalogue/data_test.go index fde16ca2..d60f3356 100644 --- a/internal/catalogue/data_test.go +++ b/internal/catalogue/data_test.go @@ -173,7 +173,7 @@ func TestTheBackupHoldersLinesAreDerivedFromTheData(t *testing.T) { {"id":"meta","path":"${dir:meta}","class":"rebuildable"}]}, "resources":[{"id":"meta","type":"directory","mode":"0700"}]}`) facts := map[string]string{"account-home": "/home/op"} - with := Rendering{Settings: SettingsBy{"media": {{From: "node", Values: map[string]any{AccessesSetting: map[string]any{"films": "/tank/films"}}}}}} + with := Rendering{Settings: SettingsBy{"media": {LayerOf("node", map[string]any{AccessesSetting: map[string]any{"films": "/tank/films"}})}}} backup, _, err := seatContributions([]Manifest{pg, agent, media}, Manifest{}, BackupSeat+":"+BackupKindBackup, with, facts, nil) if err != nil { t.Fatal(err) diff --git a/internal/catalogue/setting_defaults_test.go b/internal/catalogue/setting_defaults_test.go index f23fb5e1..d5e50dad 100644 --- a/internal/catalogue/setting_defaults_test.go +++ b/internal/catalogue/setting_defaults_test.go @@ -427,7 +427,7 @@ func TestALeftOutModulesUnplaceableBackupLineCostsOnlyThatLine(t *testing.T) { r := anAdoptedAnchor() r.Modules = append(r.Modules, holder, media, placedBadly) with := anchorRendering(false) - with.Settings["notes"] = []Layer{{From: "anchor", Values: map[string]any{PlacesSetting: "not a map"}}} + with.Settings["notes"] = []Layer{LayerOf("anchor", map[string]any{PlacesSetting: "not a map"})} composed, err := r.Compose(with) if err != nil { t.Fatalf("an unplaceable line of a left-out module failed the machine: %v", err) diff --git a/internal/catalogue/settings_judge_test.go b/internal/catalogue/settings_judge_test.go index 50d96222..cbb494c9 100644 --- a/internal/catalogue/settings_judge_test.go +++ b/internal/catalogue/settings_judge_test.go @@ -27,7 +27,7 @@ func TestASettingThatCannotComposeIsRefusedByNameAndLeavesOnlyItsModuleOut(t *te if c.name == "a mesh-wide port" { from = MeshWideLayer } - err := JudgeSettings(web, []Layer{{From: from, Values: c.layer}}, true) + err := JudgeSettings(web, []Layer{LayerOf(from, c.layer)}, true) if err == nil || !strings.Contains(err.Error(), c.refuse) { t.Errorf("%s: judged %v, want %q", c.name, err, c.refuse) } @@ -69,8 +69,8 @@ func TestASettingThatCannotComposeIsRefusedByNameAndLeavesOnlyItsModuleOut(t *te // declaration as the networks it also joins. func TestAKeptNetworkReachesTheContainerOnAnAdoptedMachineOnly(t *testing.T) { r := anAdoptedAnchor() - keep := SettingsBy{"hello-web": {{From: "anchor", - Values: map[string]any{NetworksSetting: map[string]any{"server": []any{"predecessor_default"}}}}}} + keep := SettingsBy{"hello-web": {LayerOf("anchor", + map[string]any{NetworksSetting: map[string]any{"server": []any{"predecessor_default"}}})}} with := anchorRendering(true) with.Settings = keep @@ -107,13 +107,13 @@ func TestAKeptNetworkReachesTheContainerOnAnAdoptedMachineOnly(t *testing.T) { layer Layer want string }{ - {"mesh-wide", Layer{From: MeshWideLayer, Values: map[string]any{NetworksSetting: map[string]any{"server": []any{"x"}}}}, + {"mesh-wide", LayerOf(MeshWideLayer, map[string]any{NetworksSetting: map[string]any{"server": []any{"x"}}}), "a found network is a fact about one machine"}, - {"an unknown container", Layer{From: "anchor", Values: map[string]any{NetworksSetting: map[string]any{"db": []any{"x"}}}}, + {"an unknown container", LayerOf("anchor", map[string]any{NetworksSetting: map[string]any{"db": []any{"x"}}}), `names the container "db", which it does not declare`}, - {"not a list", Layer{From: "anchor", Values: map[string]any{NetworksSetting: map[string]any{"server": "x"}}}, + {"not a list", LayerOf("anchor", map[string]any{NetworksSetting: map[string]any{"server": "x"}}), "is a list of network names"}, - {"not a network name", Layer{From: "anchor", Values: map[string]any{NetworksSetting: map[string]any{"server": []any{"a/b"}}}}, + {"not a network name", LayerOf("anchor", map[string]any{NetworksSetting: map[string]any{"server": []any{"a/b"}}}), "which is not a network name"}, } { _, err := KeptNetworks(web, []Layer{c.layer}, true) diff --git a/internal/catalogue/terminal_settings_test.go b/internal/catalogue/terminal_settings_test.go index ac767cfd..d822a896 100644 --- a/internal/catalogue/terminal_settings_test.go +++ b/internal/catalogue/terminal_settings_test.go @@ -13,22 +13,22 @@ func TestAPlacementOrAnAccessAtTheMachinesOwnIsRefused(t *testing.T) { for _, path := range []string{"/", "/etc", "/etc/", "/etc/sudoers.d", "/usr/bin", "/root", "/var", "/var/lib", "/home", "/home/", "/run", "/run/user/1000", "/var/lib/mesh/x", "/var/lib/mesh-host", "/var/lib/mesh-host/identity", "/srv/../etc", "//etc", "/proc/1", "/sys", "/dev", "/boot/efi", "/bin", "/sbin", "/lib", "/lib64"} { - layers := []Layer{{From: "laptop", Values: map[string]any{PlacesSetting: map[string]any{"data": path}}}} + layers := []Layer{LayerOf("laptop", map[string]any{PlacesSetting: map[string]any{"data": path}})} if _, err := Places(m, layers); err == nil || !strings.Contains(err.Error(), "issue 339") { t.Errorf("a place at %s: %v", path, err) } - layers = []Layer{{From: "laptop", Values: map[string]any{AccessesSetting: map[string]any{"media": path}}}} + layers = []Layer{LayerOf("laptop", map[string]any{AccessesSetting: map[string]any{"media": path}})} if _, err := AccessPlaces(m, layers); err == nil || !strings.Contains(err.Error(), "issue 339") { t.Errorf("an access at %s: %v", path, err) } } for _, path := range []string{"/srv/notes", "/mnt/plex/data", "/storage/media", "/services/media/movies", "/var/lib/notes/data", "/home/restic/repo", "/var/lib/mesh-store"} { - layers := []Layer{{From: "laptop", Values: map[string]any{PlacesSetting: map[string]any{"data": path}}}} + layers := []Layer{LayerOf("laptop", map[string]any{PlacesSetting: map[string]any{"data": path}})} if got, err := Places(m, layers); err != nil || got["data"].Path != path { t.Errorf("a place at %s: %v %v", path, got, err) } - layers = []Layer{{From: "laptop", Values: map[string]any{AccessesSetting: map[string]any{"media": path}}}} + layers = []Layer{LayerOf("laptop", map[string]any{AccessesSetting: map[string]any{"media": path}})} if got, err := AccessPlaces(m, layers); err != nil || got["media"] != path { t.Errorf("an access at %s: %v %v", path, got, err) } @@ -111,17 +111,17 @@ func TestTheRuntimesDataAndAnyHomesSSHAreTheMachinesOwn(t *testing.T) { for _, path := range []string{"/var/spool", "/var/spool/cron", "/var/lib/docker", "/var/lib/docker/volumes", "/var/lib/containers/storage", "/var/lib/containerd", "/var/lib/containerd/io.containerd.snapshotter.v1", "/opt", "/opt/app", "/home/alice/.ssh", "/home/alice/.ssh/keys", "/srv/backup/.ssh", "/root/.ssh"} { - layers := []Layer{{From: "laptop", Values: map[string]any{PlacesSetting: map[string]any{"data": path}}}} + layers := []Layer{LayerOf("laptop", map[string]any{PlacesSetting: map[string]any{"data": path}})} if _, err := Places(m, layers); err == nil || !strings.Contains(err.Error(), "issue 339") { t.Errorf("a place at %s: %v", path, err) } - layers = []Layer{{From: "laptop", Values: map[string]any{AccessesSetting: map[string]any{"media": path}}}} + layers = []Layer{LayerOf("laptop", map[string]any{AccessesSetting: map[string]any{"media": path}})} if _, err := AccessPlaces(m, layers); err == nil || !strings.Contains(err.Error(), "issue 339") { t.Errorf("an access at %s: %v", path, err) } } for _, path := range []string{"/var/lib/dockerish", "/home/alice/ssh", "/home/alice/.sshd-notes", "/storage/media"} { - layers := []Layer{{From: "laptop", Values: map[string]any{PlacesSetting: map[string]any{"data": path}}}} + layers := []Layer{LayerOf("laptop", map[string]any{PlacesSetting: map[string]any{"data": path}})} if _, err := Places(m, layers); err != nil { t.Errorf("a place at %s: %v", path, err) } diff --git a/testdata/store-tests b/testdata/store-tests index bbfc1467..d3af9659 100644 --- a/testdata/store-tests +++ b/testdata/store-tests @@ -61,3 +61,6 @@ ./cmd/mesh-controller TestAProviderElsewhereServesItsOwnComposedName ./cmd/mesh-controller TestAReusedCheckBuildIsRegisteredByItsDigestAndGatedAsABuiltOne ./internal/inventory TestAReportOfABusyContainerIsKeptAndReadBack +./internal/inventory TestPlacementMovesOutOfTheSettingsLayers +./internal/inventory TestASettingRoutesPlacementBesideTheLayer +./cmd/mesh-controller TestADeclarationIsTheSameAfterPlacementMoves