Keep the original of any file the host writes over without a record of it, on every node, and name where (hq ADR 0100)
This commit is contained in:
+27
-4
@@ -322,7 +322,14 @@ func ApplyKeeping(
|
|||||||
if o, isOpening := resource.(*declaration.Opening); isOpening {
|
if o, isOpening := resource.(*declaration.Opening); isOpening {
|
||||||
outcome, err = applyOpening(ctx, o, run, fw)
|
outcome, err = applyOpening(ctx, o, run, fw)
|
||||||
} else {
|
} else {
|
||||||
outcome, err = applyOne(ctx, sys, resource, run, changed, declares, was, unseal)
|
// A file this host has no record of, under any id, is the machine's until the mesh
|
||||||
|
// writes over it — on any node, adopted or not: its original is kept first.
|
||||||
|
var keepFound Keep
|
||||||
|
if f, isFile := resource.(*declaration.File); isFile && was.ID == "" &&
|
||||||
|
!known.Recorded(string(declaration.TypeFile), f.Path) {
|
||||||
|
keepFound = keep
|
||||||
|
}
|
||||||
|
outcome, err = applyOne(ctx, sys, resource, run, changed, declares, was, unseal, keepFound)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
failed := &Error{Resource: resource.Identity(), Err: err, Done: report}
|
failed := &Error{Resource: resource.Identity(), Err: err, Done: report}
|
||||||
@@ -423,12 +430,12 @@ type Unseal func(sealed string) ([]byte, error)
|
|||||||
|
|
||||||
func applyOne(ctx context.Context, sys system.System, r declaration.Resource, run Runner,
|
func applyOne(ctx context.Context, sys system.System, r declaration.Resource, run Runner,
|
||||||
changed map[string]bool, declares map[string]string, previous store.Applied,
|
changed map[string]bool, declares map[string]string, previous store.Applied,
|
||||||
unseal Unseal) (Outcome, error) {
|
unseal Unseal, keepFound Keep) (Outcome, error) {
|
||||||
switch res := r.(type) {
|
switch res := r.(type) {
|
||||||
case *declaration.Directory:
|
case *declaration.Directory:
|
||||||
return applyDirectory(res)
|
return applyDirectory(res)
|
||||||
case *declaration.File:
|
case *declaration.File:
|
||||||
return applyFile(res, previous, unseal)
|
return applyFile(res, previous, unseal, keepFound)
|
||||||
case *declaration.Service:
|
case *declaration.Service:
|
||||||
return applyService(ctx, sys, res, run, changed)
|
return applyService(ctx, sys, res, run, changed)
|
||||||
case *declaration.Package:
|
case *declaration.Package:
|
||||||
@@ -573,7 +580,9 @@ func applyAccess(r *declaration.Access) (Outcome, error) {
|
|||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func applyFile(r *declaration.File, previous store.Applied, unseal Unseal) (Outcome, error) {
|
// keepFound, when not nil, is where the original of a file this host has no record of is kept
|
||||||
|
// before it is written over (novox/hq ADR 0100): once, never overwritten, and named in the outcome.
|
||||||
|
func applyFile(r *declaration.File, previous store.Applied, unseal Unseal, keepFound Keep) (Outcome, error) {
|
||||||
if r.Into != "" {
|
if r.Into != "" {
|
||||||
return applyInto(r, previous)
|
return applyInto(r, previous)
|
||||||
}
|
}
|
||||||
@@ -670,7 +679,15 @@ func applyFile(r *declaration.File, previous store.Applied, unseal Unseal) (Outc
|
|||||||
drifted := existed && previous.Wrote != "" && digestOf(string(existing)) != previous.Wrote
|
drifted := existed && previous.Wrote != "" && digestOf(string(existing)) != previous.Wrote
|
||||||
modeSame := existed && beforeMode == mode.Perm()
|
modeSame := existed && beforeMode == mode.Perm()
|
||||||
|
|
||||||
|
kept := ""
|
||||||
if !contentSame {
|
if !contentSame {
|
||||||
|
if existed && keepFound != nil {
|
||||||
|
// Before anything is written: a keep that fails stops the write, since the
|
||||||
|
// original could not be had back otherwise.
|
||||||
|
if kept, err = keepFound(r.Path, existing, beforeMode); err != nil {
|
||||||
|
return out, fmt.Errorf("keeping the original of %s before writing over it: %w", r.Path, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
if err := os.MkdirAll(filepath.Dir(r.Path), 0o755); err != nil {
|
if err := os.MkdirAll(filepath.Dir(r.Path), 0o755); err != nil {
|
||||||
return out, err
|
return out, err
|
||||||
}
|
}
|
||||||
@@ -734,6 +751,12 @@ func applyFile(r *declaration.File, previous store.Applied, unseal Unseal) (Outc
|
|||||||
default:
|
default:
|
||||||
out.Action = "unchanged"
|
out.Action = "unchanged"
|
||||||
}
|
}
|
||||||
|
if kept != "" {
|
||||||
|
if out.Detail != "" {
|
||||||
|
out.Detail += "; "
|
||||||
|
}
|
||||||
|
out.Detail += "the file found here, which the mesh had no record of, was kept at " + kept
|
||||||
|
}
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -472,8 +472,40 @@ func TestAConvergedNodeStillReplacesWhatItFinds(t *testing.T) {
|
|||||||
if len(state.Held) != 0 || outcomeOf(report, "hello-web.page").Action == "held" {
|
if len(state.Held) != 0 || outcomeOf(report, "hello-web.page").Action == "held" {
|
||||||
t.Errorf("a converged node held something: %+v", state.Held)
|
t.Errorf("a converged node held something: %+v", state.Held)
|
||||||
}
|
}
|
||||||
if _, err := os.Stat(filepath.Join(dir, "kept")); !errors.Is(err, os.ErrNotExist) {
|
// What it writes over that it has no record of, it keeps first — on any node.
|
||||||
t.Error("a converged node kept originals")
|
o := outcomeOf(report, "hello-web.page")
|
||||||
|
kept := o.Detail[strings.Index(o.Detail, "kept at ")+len("kept at "):]
|
||||||
|
if got, err := os.ReadFile(kept); err != nil || string(got) != "the predecessor's page\n" {
|
||||||
|
t.Errorf("the file written over was not kept, or not named: %q (%s) %v", got, o.Detail, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAFileWrittenOverIsKeptOnceAndOnlyWhenTheHostHasNoRecordOfIt(t *testing.T) {
|
||||||
|
dir := t.TempDir()
|
||||||
|
conf := filepath.Join(dir, "nftables.conf")
|
||||||
|
_ = os.WriteFile(conf, []byte("# the distribution's own\n"), 0o644)
|
||||||
|
decl := func(content string) *declaration.Declaration {
|
||||||
|
return parse(t, `{"declaration":1,"resources":[{"id":"nftables.config","type":"file","path":"`+conf+
|
||||||
|
`","content":"`+content+`"}]}`)
|
||||||
|
}
|
||||||
|
m := &machine{containers: map[string]*fakeContainer{}}
|
||||||
|
report, state := applyAdopted(t, decl("table inet mesh {}\\n"), store.State{}, m, dir)
|
||||||
|
o := outcomeOf(report, "nftables.config")
|
||||||
|
if !strings.Contains(o.Detail, "had no record of, was kept at ") {
|
||||||
|
t.Fatalf("writing over an unrecorded file did not keep it: %+v", o)
|
||||||
|
}
|
||||||
|
kept := o.Detail[strings.Index(o.Detail, "kept at ")+len("kept at "):]
|
||||||
|
if got, _ := os.ReadFile(kept); string(got) != "# the distribution's own\n" {
|
||||||
|
t.Errorf("the kept original is %q", got)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now the mesh's: a later change keeps nothing more, and the first original stays.
|
||||||
|
report, _ = applyAdopted(t, decl("table inet mesh { }\\n"), state, m, dir)
|
||||||
|
if o := outcomeOf(report, "nftables.config"); o.Action != "updated" || strings.Contains(o.Detail, "kept at") {
|
||||||
|
t.Errorf("a file the mesh wrote was kept again: %+v", o)
|
||||||
|
}
|
||||||
|
if got, _ := os.ReadFile(kept); string(got) != "# the distribution's own\n" {
|
||||||
|
t.Errorf("the first original was overwritten: %q", got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user