No change to a machine takes effect unseen (hq ADR 0217)
Three guards, each silent when nothing is at stake: - settings: show reads a layer; set says what it adds, changes and removes, refuses a removal without --replace, and keeps the layer it replaced (settings_history, migration 0057). - push: plan --diff compares with what the machine was last sent, now kept as a summary that holds no file content; push with no machine needs --all. - a running container whose mount would point at another directory holds that machine's push until --move names the module; the other machines go ahead.
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
package inventory
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/novox/mesh-controller/internal/catalogue"
|
||||
)
|
||||
|
||||
// novox/hq ADR 0217: a settings layer can be read, and the one a set or a clear replaced is kept, so
|
||||
// a previous value is one command away rather than in a database backup (issue 246).
|
||||
func TestTheLayerASetReplacesIsKeptAndReadable(t *testing.T) {
|
||||
inv := fresh(t)
|
||||
ctx := t.Context()
|
||||
if _, err := inv.AddNode(ctx, "anchor"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
web := catalogue.Manifest{Module: "web", Version: "1",
|
||||
Resources: []map[string]any{
|
||||
{"id": "server", "type": "container", "name": "web", "image": "x"},
|
||||
{"id": "conf", "type": "file", "path": "/etc/web.json", "content": "{}", "merge": "json"},
|
||||
}}
|
||||
if err := inv.RegisterModule(ctx, web, Source{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, has, err := inv.Layer(ctx, "anchor", "web"); err != nil || has {
|
||||
t.Fatalf("a layer nobody set: %v %v", has, err)
|
||||
}
|
||||
first := map[string]any{"colour": "blue", "size": "large"}
|
||||
if err := inv.SetSettings(ctx, "anchor", "web", first); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, has, err := inv.Layer(ctx, "anchor", "web")
|
||||
if err != nil || !has || got["colour"] != "blue" || got["size"] != "large" {
|
||||
t.Fatalf("the layer read back: %v %v %v", got, has, err)
|
||||
}
|
||||
if past, err := inv.SettingsHistory(ctx, "anchor", "web"); err != nil || len(past) != 0 {
|
||||
t.Fatalf("a first set replaced something: %v %v", past, err)
|
||||
}
|
||||
|
||||
if err := inv.SetSettings(ctx, "anchor", "web", map[string]any{"colour": "red"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := inv.ClearSettings(ctx, "anchor", "web"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
past, err := inv.SettingsHistory(ctx, "anchor", "web")
|
||||
if err != nil || len(past) != 2 {
|
||||
t.Fatalf("history %v %v", past, err)
|
||||
}
|
||||
// The latest first: the clear took the red layer, the set took the first.
|
||||
if past[0].ReplacedBy != "clear" || past[0].Values["colour"] != "red" {
|
||||
t.Errorf("the cleared layer: %+v", past[0])
|
||||
}
|
||||
if past[1].ReplacedBy != "set" || past[1].Values["size"] != "large" {
|
||||
t.Errorf("the replaced layer still has what the set dropped: %+v", past[1])
|
||||
}
|
||||
// The mesh-wide layer keeps its own history, apart from the node's.
|
||||
if err := inv.SetSettings(ctx, "", "web", map[string]any{"colour": "green"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := inv.SetSettings(ctx, "", "web", map[string]any{"colour": "grey"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mesh, err := inv.SettingsHistory(ctx, "", "web")
|
||||
if err != nil || len(mesh) != 1 || mesh[0].Values["colour"] != "green" {
|
||||
t.Fatalf("the mesh-wide history %v %v", mesh, err)
|
||||
}
|
||||
}
|
||||
|
||||
// What a machine was last sent is kept, summarised, and read back by its name; a machine sent
|
||||
// nothing since has nothing to compare with.
|
||||
func TestWhatAMachineWasSentIsKeptForComparison(t *testing.T) {
|
||||
inv := fresh(t)
|
||||
ctx := t.Context()
|
||||
n, err := inv.AddNode(ctx, "anchor")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got, err := inv.SentSummary(ctx, "anchor"); err != nil || len(got) != 0 {
|
||||
t.Fatalf("a machine never sent anything: %s %v", got, err)
|
||||
}
|
||||
if err := inv.RecordSentSummary(ctx, n.ID, []byte(`[{"id":"web.server","type":"container"}]`)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, err := inv.SentSummary(ctx, "anchor")
|
||||
if err != nil || len(got) == 0 {
|
||||
t.Fatalf("read back: %s %v", got, err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user