fix: close Pi management final review findings
This commit is contained in:
@@ -114,7 +114,7 @@ func TestSuccessfulUpdateAndRollbackRemainSelectedOnFreshRecreate(t *testing.T)
|
||||
if fake.currentImage != "sha256:candidate" {
|
||||
t.Fatalf("fresh recreate image = %q, want verified candidate", fake.currentImage)
|
||||
}
|
||||
if _, err := Rollback(context.Background(), fake, statePath, true); err != nil {
|
||||
if _, err := Rollback(context.Background(), fake, statePath, pairedRestartStatePath(statePath), true); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fake.currentImage = "sha256:candidate"
|
||||
@@ -209,7 +209,7 @@ func TestDigestPinnedConfiguredImageIsNeverUsedAsARollbackTagTarget(t *testing.T
|
||||
if _, err := Update(context.Background(), fake, Request{StatePath: statePath, Version: "0.81.0", Source: BuildSource, Confirm: true}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := Rollback(context.Background(), fake, statePath, true); err != nil {
|
||||
if _, err := Rollback(context.Background(), fake, statePath, pairedRestartStatePath(statePath), true); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assertNotCalled(t, fake.calls, "image tag sha256:old "+fake.configuredImage)
|
||||
@@ -306,7 +306,7 @@ func TestSuccessfulLifecycleCommandsPreserveTypedRecoveryErrorFromMaintenanceCle
|
||||
Confirm: true,
|
||||
})
|
||||
} else {
|
||||
result, err = Rollback(context.Background(), fake, statePath, true)
|
||||
result, err = Rollback(context.Background(), fake, statePath, pairedRestartStatePath(statePath), true)
|
||||
}
|
||||
|
||||
if !fake.maintenance {
|
||||
@@ -382,7 +382,7 @@ func TestManualRollbackSurvivesADeadCandidateCore(t *testing.T) {
|
||||
fake.coreRunning = false
|
||||
fake.maintenance = false
|
||||
|
||||
result, err := Rollback(context.Background(), fake, statePath, true)
|
||||
result, err := Rollback(context.Background(), fake, statePath, pairedRestartStatePath(statePath), true)
|
||||
|
||||
if err != nil || result.Phase != PhaseRolledBack {
|
||||
t.Fatalf("Rollback() = %+v, %v; want restored previous core", result, err)
|
||||
@@ -657,7 +657,7 @@ func TestRollbackFinalStateWriteFailureKeepsMaintenanceAndOverrideForRecovery(t
|
||||
hooks := defaultLifecycleHooks
|
||||
hooks.writeState = func(string, State) error { return errors.New("injected rollback state write failure") }
|
||||
|
||||
result, err := rollbackWithHooks(context.Background(), fake, statePath, true, hooks)
|
||||
result, err := rollbackWithHooks(context.Background(), fake, statePath, pairedRestartStatePath(statePath), true, hooks)
|
||||
if err == nil || result.Phase != PhaseFailed {
|
||||
t.Fatalf("rollbackWithHooks() = %+v, %v; want failed durable finalization", result, err)
|
||||
}
|
||||
@@ -783,7 +783,7 @@ func TestRollbackRestoresInterruptedOrPreviouslyRecordedState(t *testing.T) {
|
||||
previous.Reference = "thothii-core:thothctl-test-previous"
|
||||
fake.tags[previous.Reference] = previous.ID
|
||||
writeStateForTest(t, statePath, State{Version: 1, Phase: PhaseRecreated, Previous: previous})
|
||||
result, err := Rollback(context.Background(), fake, statePath, true)
|
||||
result, err := Rollback(context.Background(), fake, statePath, pairedRestartStatePath(statePath), true)
|
||||
if err != nil {
|
||||
t.Fatalf("Rollback() error = %v", err)
|
||||
}
|
||||
@@ -805,6 +805,100 @@ func TestUpdateRefusesToOverwriteInterruptedRecoveryState(t *testing.T) {
|
||||
assertNotCalled(t, fake.calls, "compose")
|
||||
}
|
||||
|
||||
func TestFailedRestartBlocksUpdateAndRollbackAndMalformedStateAlsoRejects(t *testing.T) {
|
||||
for _, operation := range []string{"update", "rollback"} {
|
||||
for _, restartState := range []string{"failed", "malformed"} {
|
||||
t.Run(operation+"_"+restartState, func(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
fake := newFakeRunner()
|
||||
updateStatePath := filepath.Join(dir, "update-state.json")
|
||||
restartStatePath := filepath.Join(dir, "restart-state.json")
|
||||
if restartState == "failed" {
|
||||
fake.fail = "health"
|
||||
_, restartErr := Restart(context.Background(), fake, RestartRequest{
|
||||
StatePath: restartStatePath,
|
||||
UpdateStatePath: updateStatePath,
|
||||
Confirm: true,
|
||||
})
|
||||
var recovery *RecoveryRequiredError
|
||||
if !errors.As(restartErr, &recovery) {
|
||||
t.Fatalf("Restart() error = %v, want failed restart recovery state", restartErr)
|
||||
}
|
||||
fake.fail = ""
|
||||
} else if err := os.WriteFile(restartStatePath, []byte("{malformed"), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if operation == "rollback" {
|
||||
previous := stateImageForTest(t, fake)
|
||||
writeStateForTest(t, updateStatePath, State{
|
||||
Transaction: "rollback-target",
|
||||
Phase: PhaseRecreated,
|
||||
Target: Target{Version: fake.version, Source: string(BuildSource)},
|
||||
Previous: previous,
|
||||
MutationStarted: true,
|
||||
})
|
||||
}
|
||||
fake.calls = nil
|
||||
|
||||
var err error
|
||||
if operation == "update" {
|
||||
_, err = Update(context.Background(), fake, Request{
|
||||
StatePath: updateStatePath,
|
||||
RestartStatePath: restartStatePath,
|
||||
Version: "0.81.0",
|
||||
Source: BuildSource,
|
||||
Confirm: true,
|
||||
})
|
||||
} else {
|
||||
_, err = Rollback(context.Background(), fake, updateStatePath, restartStatePath, true)
|
||||
}
|
||||
if err == nil {
|
||||
t.Fatalf("%s accepted %s restart recovery state", operation, restartState)
|
||||
}
|
||||
if restartState == "failed" && !errors.Is(err, ErrInterruptedRestart) {
|
||||
t.Fatalf("%s error = %v, want ErrInterruptedRestart", operation, err)
|
||||
}
|
||||
assertNotCalled(t, fake.calls, "compose")
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateCleansVerifiedRestartStateBeforeNormalLifecycleWork(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
fake := newFakeRunner()
|
||||
updateStatePath := filepath.Join(dir, "update-state.json")
|
||||
restartStatePath := filepath.Join(dir, "restart-state.json")
|
||||
state := State{
|
||||
Transaction: "verified-restart",
|
||||
Phase: PhaseVerified,
|
||||
Target: Target{Version: fake.version, Source: "restart"},
|
||||
Previous: stateImageForTest(t, fake),
|
||||
MutationStarted: true,
|
||||
}
|
||||
writeStateForTest(t, restartStatePath, state)
|
||||
overridePath := lifecycleOverridePath(restartStatePath, state.Transaction)
|
||||
if err := writeLifecycleOverride(overridePath, state.Previous.Reference); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
result, err := Update(context.Background(), fake, Request{
|
||||
StatePath: updateStatePath,
|
||||
RestartStatePath: restartStatePath,
|
||||
Version: fake.version,
|
||||
Source: BuildSource,
|
||||
Confirm: true,
|
||||
})
|
||||
if err != nil || result.Phase != PhaseNoop {
|
||||
t.Fatalf("Update() = %+v, %v; want normal no-op after verified restart", result, err)
|
||||
}
|
||||
for _, path := range []string{restartStatePath, overridePath} {
|
||||
if _, err := os.Stat(path); !errors.Is(err, os.ErrNotExist) {
|
||||
t.Fatalf("terminal restart artifact %s still exists: %v", path, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunningImageCapturesServerBindAndNamedMountIdentity(t *testing.T) {
|
||||
fake := newFakeRunner()
|
||||
fake.mountsJSON = `[{"Type":"bind","Source":"/srv/thothii/data","Destination":"/data","RW":true},{"Type":"bind","Source":"/srv/thothii/pi","Destination":"/home/thoth/.pi","RW":true},{"Type":"volume","Name":"sessions","Source":"/var/lib/docker/volumes/sessions/_data","Destination":"/data/sessions","RW":true}]`
|
||||
|
||||
Reference in New Issue
Block a user