review: rebuild a file the mesh once wrote whole, keep links, give back a missing line end, and release a hold only after the write (hq issue 128)
This commit is contained in:
@@ -469,3 +469,150 @@ func TestTheRecordOfABlockSurvivesTheStateFile(t *testing.T) {
|
||||
t.Errorf("undeclaring from a saved state left %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
// The mesh's old whole hosts file, as the controller composed it before issue 128.
|
||||
const oldWholeHosts = "# Generated by the mesh. Do not edit — this file is replaced whenever a machine\n" +
|
||||
"# joins or leaves, and an edit would survive until then and vanish.\n\n" +
|
||||
"127.0.0.1\tlocalhost\n" +
|
||||
"::1\t\tlocalhost ip6-localhost ip6-loopback\n" +
|
||||
"127.0.1.1\tg14\n" +
|
||||
"\n" +
|
||||
"10.42.0.1\tace.internal\tace\n" +
|
||||
"10.42.0.9\tg14.internal\tg14\t# this machine\n"
|
||||
|
||||
func wholeDecl(path, content string) string {
|
||||
return fmt.Sprintf(`{"declaration":1,"resources":[
|
||||
{"id":%q,"type":"file","path":%q,"content":%q}
|
||||
]}`, namesID, path, content)
|
||||
}
|
||||
|
||||
func applyKeepingIn(t *testing.T, raw string, known store.State, keepDir string) (Report, store.State) {
|
||||
t.Helper()
|
||||
report, state, err := ApplyKeeping(context.Background(), archHost(t), parse(t, raw), known,
|
||||
store.OriginDeclared, (&machine{}).run, nil, nil, KeepIn(keepDir))
|
||||
if err != nil {
|
||||
t.Fatalf("apply failed: %v", err)
|
||||
}
|
||||
return report, state
|
||||
}
|
||||
|
||||
func TestAFileTheMeshWroteWholeAndMadeItselfKeepsOnlyItsLoopbackLines(t *testing.T) {
|
||||
// Written whole into a file that was not there, then declared as a block under the same id:
|
||||
// the old names must not stay above the region, where a resolver would answer from them first.
|
||||
path := filepath.Join(t.TempDir(), "hosts")
|
||||
_, state := applyKeepingIn(t, wholeDecl(path, oldWholeHosts), store.State{}, t.TempDir())
|
||||
report, state := applyKeepingIn(t, blockDecl(t, path, meshNames), state, t.TempDir())
|
||||
floor := "127.0.0.1\tlocalhost\n::1\t\tlocalhost ip6-localhost ip6-loopback\n127.0.1.1\tg14\n"
|
||||
if got := readText(t, path); got != floor+"\n"+marked(namesID, meshNames) {
|
||||
t.Fatalf("the old whole file became:\n%q", got)
|
||||
}
|
||||
o := outcomeOf(report, namesID)
|
||||
if o.Action != "updated" || !strings.Contains(o.Detail, "loopback lines") {
|
||||
t.Errorf("the rebuild was reported as %q: %s", o.Action, o.Detail)
|
||||
}
|
||||
if rec, _ := state.Find(namesID); !rec.Into.Created {
|
||||
t.Error("a file the mesh made itself was not recorded as the mesh's")
|
||||
}
|
||||
report, _ = applyKeepingIn(t, blockDecl(t, path, meshNames), state, t.TempDir())
|
||||
if got := outcomeOf(report, namesID).Action; got != "unchanged" {
|
||||
t.Errorf("applied again, the rebuilt file was %q", got)
|
||||
}
|
||||
undeclare(t, state)
|
||||
if got := readText(t, path); got != floor {
|
||||
t.Errorf("undeclared, the file holds %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAFileTheMeshWroteWholeOverAnOriginalGetsTheOriginalBack(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "hosts")
|
||||
_ = os.WriteFile(path, []byte(workstationHosts), 0o644)
|
||||
keep := t.TempDir()
|
||||
_, state := applyKeepingIn(t, wholeDecl(path, oldWholeHosts), store.State{}, keep)
|
||||
if rec, _ := state.Find(namesID); rec.Kept == "" {
|
||||
t.Fatal("where the original was kept was not recorded")
|
||||
}
|
||||
report, state := applyKeepingIn(t, blockDecl(t, path, meshNames), state, keep)
|
||||
if got := readText(t, path); got != workstationHosts+"\n"+marked(namesID, meshNames) {
|
||||
t.Fatalf("the old whole file became:\n%q", got)
|
||||
}
|
||||
if d := outcomeOf(report, namesID).Detail; !strings.Contains(d, "original kept at") {
|
||||
t.Errorf("the rebuild was reported as: %s", d)
|
||||
}
|
||||
undeclare(t, state)
|
||||
if got := readText(t, path); got != workstationHosts {
|
||||
t.Errorf("undeclared, the machine did not get its original back: %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAFileTheMeshWroteWholeAndSomebodyChangedIsWrittenIntoAsItStands(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "hosts")
|
||||
_, state := applyKeepingIn(t, wholeDecl(path, oldWholeHosts), store.State{}, t.TempDir())
|
||||
edited := oldWholeHosts + "192.168.1.20 printer\n"
|
||||
_ = os.WriteFile(path, []byte(edited), 0o644)
|
||||
report, _ := applyKeepingIn(t, blockDecl(t, path, meshNames), state, t.TempDir())
|
||||
if got := readText(t, path); got != edited+"\n"+marked(namesID, meshNames) {
|
||||
t.Fatalf("an edited whole file became:\n%q", got)
|
||||
}
|
||||
if d := outcomeOf(report, namesID).Detail; !strings.Contains(d, "changed since; its old lines were kept") {
|
||||
t.Errorf("the outcome does not say so: %s", d)
|
||||
}
|
||||
}
|
||||
|
||||
func TestALinkedFileStaysALink(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
real := filepath.Join(dir, "static", "hosts")
|
||||
_ = os.MkdirAll(filepath.Dir(real), 0o755)
|
||||
_ = os.WriteFile(real, []byte(workstationHosts), 0o644)
|
||||
link := filepath.Join(dir, "hosts")
|
||||
if err := os.Symlink(real, link); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, state := applyBlockDecl(t, blockDecl(t, link, meshNames), store.State{})
|
||||
if info, err := os.Lstat(link); err != nil || info.Mode()&os.ModeSymlink == 0 {
|
||||
t.Fatalf("the link was replaced by a file")
|
||||
}
|
||||
if got := readText(t, real); got != workstationHosts+"\n"+marked(namesID, meshNames) {
|
||||
t.Errorf("the file the link names holds %q", got)
|
||||
}
|
||||
undeclare(t, state)
|
||||
if info, err := os.Lstat(link); err != nil || info.Mode()&os.ModeSymlink == 0 {
|
||||
t.Fatalf("undeclaring replaced the link with a file")
|
||||
}
|
||||
if got := readText(t, real); got != workstationHosts {
|
||||
t.Errorf("undeclared, the file the link names holds %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestALastLineWithNoEndIsGivenBackWithNone(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "hosts")
|
||||
_ = os.WriteFile(path, []byte("x"), 0o644)
|
||||
_, state := applyBlockDecl(t, blockDecl(t, path, meshNames), store.State{})
|
||||
if got := readText(t, path); got != "x\n\n"+marked(namesID, meshNames) {
|
||||
t.Fatalf("got %q", got)
|
||||
}
|
||||
undeclare(t, state)
|
||||
if got := readText(t, path); got != "x" {
|
||||
t.Errorf("undeclaring left %q, the machine had %q", got, "x")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAFailedBlockWriteKeepsItsHold(t *testing.T) {
|
||||
// Held from when it was declared whole, then declared as a block into a file whose markers do
|
||||
// not pair: the write is refused, and the hold — with where its original is — stays.
|
||||
path := filepath.Join(t.TempDir(), "hosts")
|
||||
broken := "a\n# BEGIN mesh " + namesID + "\n"
|
||||
_ = os.WriteFile(path, []byte(broken), 0o644)
|
||||
known := store.State{Held: []store.Held{{ID: namesID, Module: "mesh-wireguard", Kind: "file",
|
||||
Target: path, Kept: "/var/lib/mesh/kept/hosts"}}}
|
||||
resource := fmt.Sprintf(`{"id":%q,"type":"file","path":%q,"into":"block","content":%q}`, namesID, path, meshNames)
|
||||
d := adopted(t, `{"taken":[],"untaken":{"mesh-wireguard":["`+namesID+`"]}}`, resource)
|
||||
_, state, err := ApplyKeeping(context.Background(), archHost(t), d, known,
|
||||
store.OriginDeclared, (&machine{}).run, nil, nil, KeepIn(t.TempDir()))
|
||||
if err == nil {
|
||||
t.Fatal("a write into unpaired markers was not refused")
|
||||
}
|
||||
h, held := state.HeldAt(namesID)
|
||||
if !held || h.Kept != "/var/lib/mesh/kept/hosts" {
|
||||
t.Errorf("a failed write released the hold: %+v", state.Held)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user