fix: make daemon reset conflict safe

This commit is contained in:
proxyctl coordinator
2026-07-17 11:11:56 +08:00
parent 613fe56540
commit 9e2418f45e
6 changed files with 283 additions and 73 deletions
+120
View File
@@ -355,6 +355,126 @@ func TestDockerDaemonEnableOrdersManagedWriteReloadRestartAndPostInspect(t *test
}
}
func TestDockerDaemonDisableAndResetRejectDriftWithoutMutatingDaemonOrRecovery(t *testing.T) {
for _, test := range []struct {
name string
mutate func(t *testing.T, managed []byte) []byte
}{
{
name: "unrelated administrator edit",
mutate: func(t *testing.T, managed []byte) []byte {
t.Helper()
var document map[string]any
if err := json.Unmarshal(managed, &document); err != nil {
t.Fatal(err)
}
document["log-level"] = "debug"
changed, err := json.MarshalIndent(document, "", " ")
if err != nil {
t.Fatal(err)
}
return append(changed, '\n')
},
},
{
name: "managed proxy edit",
mutate: func(t *testing.T, managed []byte) []byte {
t.Helper()
var document map[string]any
if err := json.Unmarshal(managed, &document); err != nil {
t.Fatal(err)
}
document["proxies"].(map[string]any)["http-proxy"] = "http://administrator.example:3128"
changed, err := json.MarshalIndent(document, "", " ")
if err != nil {
t.Fatal(err)
}
return append(changed, '\n')
},
},
} {
t.Run(test.name, func(t *testing.T) {
a, record, daemonPath, recoveryPath, _ := fixture(t)
if result := a.SetDesired(context.Background(), true, activeProfile()); result.Err != nil {
t.Fatal(result.Err)
}
managed, err := os.ReadFile(daemonPath)
if err != nil {
t.Fatal(err)
}
changed := test.mutate(t, managed)
if err := os.WriteFile(daemonPath, changed, 0o600); err != nil {
t.Fatal(err)
}
recoveryBefore, err := os.ReadFile(recoveryPath)
if err != nil {
t.Fatal(err)
}
record.events = nil
reset := app.NewMaintenance(profile.State{Profiles: []profile.Profile{*activeProfile()}, ActiveProfile: activeProfile().Name}, []target.Target{a}, immediateLock{}, nil).Reset(context.Background(), true)
if reset.Err == nil || len(reset.Targets) != 1 || reset.Targets[0].Err == nil || reset.Targets[0].Inspection.State != target.Drifted {
t.Fatalf("conflicting reset = %#v", reset)
}
if events := mutationEvents(record.events); len(events) != 0 {
t.Fatalf("conflicting reset mutated daemon state: %#v", events)
}
if got, err := os.ReadFile(daemonPath); err != nil || !reflect.DeepEqual(got, changed) {
t.Fatalf("current daemon configuration changed to %q: %v", got, err)
}
if got, err := os.ReadFile(recoveryPath); err != nil || !reflect.DeepEqual(got, recoveryBefore) {
t.Fatalf("recovery metadata changed to %q: %v", got, err)
}
})
}
}
func TestDockerDaemonResetCleansAlreadyRestoredRecoveryWithoutDaemonWriteOrSystemctl(t *testing.T) {
a, record, daemonPath, recoveryPath, _ := fixture(t)
original := []byte("{\n \"log-level\": \"debug\",\n \"proxies\": {\"http-proxy\": \"http://prior.example:3128\"}\n}\n")
if err := os.MkdirAll(filepath.Dir(daemonPath), 0o700); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(daemonPath, original, 0o640); err != nil {
t.Fatal(err)
}
if result := a.SetDesired(context.Background(), true, activeProfile()); result.Err != nil {
t.Fatal(result.Err)
}
if err := os.WriteFile(daemonPath, original, 0o640); err != nil {
t.Fatal(err)
}
record.events = nil
reset := app.NewMaintenance(profile.State{Profiles: []profile.Profile{*activeProfile()}, ActiveProfile: activeProfile().Name}, []target.Target{a}, immediateLock{}, nil).Reset(context.Background(), true)
if reset.Err != nil || len(reset.Targets) != 1 || reset.Targets[0].Err != nil || reset.Targets[0].Inspection.State != target.External {
t.Fatalf("already-restored reset = %#v", reset)
}
for _, event := range record.events {
if event == "write daemon.json" || strings.Contains(event, "daemon-reload") || strings.Contains(event, " restart ") {
t.Fatalf("already-restored reset changed daemon or systemd state: %#v", record.events)
}
}
if !contains(record.events, "remove docker-daemon.json") {
t.Fatalf("already-restored reset did not clean recovery metadata: %#v", record.events)
}
if got, err := os.ReadFile(daemonPath); err != nil || !reflect.DeepEqual(got, original) {
t.Fatalf("already-restored daemon configuration changed to %q: %v", got, err)
}
if info, err := os.Stat(daemonPath); err != nil || info.Mode().Perm() != 0o640 {
t.Fatalf("already-restored daemon metadata changed: info=%v err=%v", info, err)
}
if _, err := os.Stat(recoveryPath); !errors.Is(err, os.ErrNotExist) {
t.Fatalf("recovery metadata remains: %v", err)
}
record.events = nil
retry := a.SetDesired(context.Background(), false, nil)
if retry.Err != nil || retry.Inspection.State != target.External || len(mutationEvents(record.events)) != 0 {
t.Fatalf("already-restored cleanup retry = %#v events=%#v", retry, record.events)
}
}
func TestDockerDaemonPreservesAndRestoresExistingConfiguration(t *testing.T) {
a, _, daemonPath, recoveryPath, _ := fixture(t)
original := []byte("{\n \"log-level\": \"debug\",\n \"proxies\": {\"http-proxy\": \"http://prior.example:3128\"}\n}\n")