package apply import ( "context" "fmt" "os" "path/filepath" "testing" "github.com/novox/mesh-host/internal/store" ) // Defends novox/hq ADR 0349 §5 (issue 667): a file that says who owns it at runtime is written as the // form it names always was — owned as a whole file whose drift is written back, seed as create-once, // managed as only its keys or its marked block. Each is applied beside its old form, through the same // drift, and the two must end the same: the same actions, the same bytes on disk. // ownershipCase is one ownership word and the old form it maps onto. type ownershipCase struct { name string // said and old are the resource's fields after id, type and path: with the word, and in the old form. said, old string // before is what is at the path before the first apply; "" leaves the path absent. before string // drift is what the machine writes there between the two applies. drift string // first and second are the actions the old form is known to report. first, second string } func TestEachOwnershipBehavesAsItsOldForm(t *testing.T) { for _, c := range []ownershipCase{ { name: "owned is a whole file whose drift is written back", said: `"content":"correct\n","mode":"0644","owner-at-runtime":"owned"`, old: `"content":"correct\n","mode":"0644"`, drift: "someone edited this\n", first: "created", second: "corrected", }, { name: "seed is written once and then the program's", said: `"content":"user default on\n","mode":"0600","owner-at-runtime":"seed"`, old: `"content":"user default on\n","mode":"0600","create-once":true`, drift: "user default on\nuser app-one on\n", first: "created", second: "kept", }, { name: "managed json sets only its keys", said: `"into":"json","content":"{\"insecure-registries\":[\"10.42.0.1:5000\"]}","owner-at-runtime":"managed"`, old: `"into":"json","content":"{\"insecure-registries\":[\"10.42.0.1:5000\"]}"`, before: machinesOwn, drift: `{"data-root":"/srv/other","log-opts":{"max-size":"10m"},"insecure-registries":["192.0.2.7:5000"]}`, first: "updated", second: "corrected", }, { name: "managed block holds only its region", said: `"into":"block","content":"10.42.0.1 ace\n","owner-at-runtime":"managed"`, old: `"into":"block","content":"10.42.0.1 ace\n"`, before: workstationHosts, drift: workstationHosts + "\n" + marked("f", "6.6.6.6 evil\n") + "192.0.2.9 the-machines-own\n", first: "updated", second: "corrected", }, } { t.Run(c.name, func(t *testing.T) { saidActions, saidBytes := applyThroughDrift(t, c, c.said) oldActions, oldBytes := applyThroughDrift(t, c, c.old) if saidActions != oldActions { t.Errorf("with the word the actions were %s; the old form's were %s", saidActions, oldActions) } if want := c.first + " " + c.second; oldActions != want { t.Errorf("the old form's actions were %s; known to be %s", oldActions, want) } if saidBytes != oldBytes { t.Errorf("with the word the file ends\n%q\nthe old form's ends\n%q", saidBytes, oldBytes) } }) } } // applyThroughDrift applies one file, lets the machine write the case's drift into it, applies again, // and says the two actions and what the file holds at the end. func applyThroughDrift(t *testing.T, c ownershipCase, fields string) (string, string) { t.Helper() path := filepath.Join(t.TempDir(), "the-file") if c.before != "" { if err := os.WriteFile(path, []byte(c.before), 0o644); err != nil { t.Fatal(err) } } d := parse(t, fmt.Sprintf(`{"declaration":1,"resources":[{"id":"f","type":"file","path":%q,%s}]}`, path, fields)) first, state, err := Apply(context.Background(), archHost(t), d, store.State{}, store.OriginDeclared, noServices, nil, nil) if err != nil { t.Fatal(err) } if err := os.WriteFile(path, []byte(c.drift), 0o644); err != nil { t.Fatal(err) } second, _, err := Apply(context.Background(), archHost(t), d, state, store.OriginDeclared, noServices, nil, nil) if err != nil { t.Fatal(err) } return first.Outcomes[0].Action + " " + second.Outcomes[0].Action, readText(t, path) } // A seed said by the word alone, with no create-once beside it, is still kept once present: the word is // enough, and the file the program grew is not written back. func TestASeedByTheWordAloneKeepsWhatGrew(t *testing.T) { path := filepath.Join(t.TempDir(), "acl.conf") d := parse(t, fmt.Sprintf(`{"declaration":1,"resources":[ {"id":"acl","type":"file","path":%q,"content":"user default on\n","owner-at-runtime":"seed"} ]}`, path)) _, state, err := Apply(context.Background(), archHost(t), d, store.State{}, store.OriginDeclared, noServices, nil, nil) if err != nil { t.Fatal(err) } if err := os.WriteFile(path, []byte("grown\n"), 0o600); err != nil { t.Fatal(err) } report, _, err := Apply(context.Background(), archHost(t), d, state, store.OriginDeclared, noServices, nil, nil) if err != nil { t.Fatal(err) } if got := report.Outcomes[0].Action; got != "kept" { t.Errorf("a seed was %q", got) } if got := readText(t, path); got != "grown\n" { t.Errorf("what grew in the seed was written over: %q", got) } }