test(auth): gate local and OIDC authentication release
This commit is contained in:
@@ -120,7 +120,7 @@ func TestRestoreStoppedInstallationRunsCheckpointRestoreAndVerification(t *testi
|
||||
var events []string
|
||||
var checkpointRequest CreateRequest
|
||||
deps := restoreTestDependencies(t, runner)
|
||||
deps.checkpointLocked = func(_ context.Context, _ config.Installation, request CreateRequest) (Result, error) {
|
||||
deps.checkpoint = func(_ context.Context, _ *lifecycle.Transaction, _ config.Installation, request CreateRequest) (Result, error) {
|
||||
events = append(events, "checkpoint")
|
||||
checkpointRequest = request
|
||||
return Result{Path: "/tmp/checkpoint.zip"}, nil
|
||||
@@ -133,9 +133,9 @@ func TestRestoreStoppedInstallationRunsCheckpointRestoreAndVerification(t *testi
|
||||
events = append(events, "cleanup-checkpoint")
|
||||
return nil
|
||||
}
|
||||
deps.acquireLock = func(config.Installation) (restoreLock, error) {
|
||||
deps.acquireTransaction = func(target config.Installation) (*lifecycle.Transaction, error) {
|
||||
events = append(events, "lock")
|
||||
return fakeRestoreLock{release: func() { events = append(events, "unlock") }}, nil
|
||||
return lifecycle.AcquireTransaction(target)
|
||||
}
|
||||
deps.restoreFile = func(_ context.Context, _ config.Installation, entry ArchiveEntryMetadata, _ io.Reader) error {
|
||||
events = append(events, "file:"+entry.Path)
|
||||
@@ -159,9 +159,12 @@ func TestRestoreStoppedInstallationRunsCheckpointRestoreAndVerification(t *testi
|
||||
if !checkpointRequest.IncludeSecrets || !checkpointRequest.Confirm {
|
||||
t.Fatalf("checkpoint request = %#v, want private confirmed secret-aware checkpoint", checkpointRequest)
|
||||
}
|
||||
if got, want := events, []string{"lock", "checkpoint", "prepare-recovery", "file:configuration/operator.env", "health", "doctor", "pi", "workspace", "cleanup-checkpoint", "unlock"}; !equalStrings(got, want) {
|
||||
if got, want := events, []string{"lock", "checkpoint", "prepare-recovery", "file:configuration/operator.env", "health", "doctor", "pi", "workspace", "cleanup-checkpoint"}; !equalStrings(got, want) {
|
||||
t.Fatalf("restore events = %v, want %v", got, want)
|
||||
}
|
||||
if err := lifecycleLockFreeAfterTerminalRestore(installation); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRestoreClosesTargetArchiveBeforeReleasingLifecycleLock(t *testing.T) {
|
||||
@@ -179,22 +182,85 @@ func TestRestoreClosesTargetArchiveBeforeReleasingLifecycleLock(t *testing.T) {
|
||||
target = result
|
||||
return result, err
|
||||
}
|
||||
unlockBeforeTargetClose := false
|
||||
deps.acquireLock = func(config.Installation) (restoreLock, error) {
|
||||
return fakeRestoreLock{release: func() {
|
||||
if target.archive != nil && target.archive.file != nil {
|
||||
if _, err := target.archive.file.Stat(); err == nil {
|
||||
unlockBeforeTargetClose = true
|
||||
}
|
||||
}
|
||||
}}, nil
|
||||
}
|
||||
deps.acquireTransaction = lifecycle.AcquireTransaction
|
||||
|
||||
if _, err := restoreWithDependencies(context.Background(), installation, RestoreRequest{Archive: archive, Confirm: true}, deps); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if unlockBeforeTargetClose {
|
||||
t.Fatal("restore released its lifecycle lock before closing the target archive")
|
||||
if target.archive != nil && target.archive.file != nil {
|
||||
if _, err := target.archive.file.Stat(); err == nil {
|
||||
t.Fatal("restore did not close the target archive")
|
||||
}
|
||||
}
|
||||
if err := lifecycleLockFreeAfterTerminalRestore(installation); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRestoreAcquiresLifecycleLockBeforeTargetDependentPreflight(t *testing.T) {
|
||||
installation := preflightTestInstallation(t)
|
||||
archive := filepath.Join(t.TempDir(), "restore.zip")
|
||||
writePreflightArchive(t, archive, preflightArchiveSpec{
|
||||
entries: []preflightArchiveEntry{{path: "configuration/operator.env", body: []byte("safe")}},
|
||||
})
|
||||
|
||||
runner := newBackupRunner(installation, false)
|
||||
deps := restoreTestDependencies(t, runner)
|
||||
deps.acquireTransaction = lifecycle.AcquireTransaction
|
||||
deps.preflight = func(ctx context.Context, target config.Installation, request PreflightRequest) (PreflightResult, error) {
|
||||
probe, err := lifecycle.Acquire(target)
|
||||
if err == nil {
|
||||
_ = probe.Release()
|
||||
return PreflightResult{}, errors.New("target-dependent preflight ran before lifecycle lock acquisition")
|
||||
}
|
||||
if !errors.Is(err, lifecycle.ErrLocked) {
|
||||
return PreflightResult{}, fmt.Errorf("probe lifecycle lock: %w", err)
|
||||
}
|
||||
return Preflight(ctx, target, request, permissivePreflightDependencies())
|
||||
}
|
||||
|
||||
if _, err := restoreWithDependencies(context.Background(), installation, RestoreRequest{Archive: archive, Confirm: true}, deps); err != nil {
|
||||
t.Fatalf("Restore() error = %v, want preflight protected by lifecycle lock", err)
|
||||
}
|
||||
if err := lifecycleLockFreeAfterTerminalRestore(installation); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRestoreStagesArchiveAfterCheckpointAndRejectsMutation(t *testing.T) {
|
||||
installation := preflightTestInstallation(t)
|
||||
archive := filepath.Join(t.TempDir(), "restore.zip")
|
||||
writePreflightArchive(t, archive, preflightArchiveSpec{
|
||||
entries: []preflightArchiveEntry{{path: "configuration/operator.env", body: []byte("before")}},
|
||||
})
|
||||
|
||||
runner := newBackupRunner(installation, false)
|
||||
deps := restoreTestDependencies(t, runner)
|
||||
deps.acquireTransaction = lifecycle.AcquireTransaction
|
||||
deps.checkpoint = func(context.Context, *lifecycle.Transaction, config.Installation, CreateRequest) (Result, error) {
|
||||
writePreflightArchive(t, archive, preflightArchiveSpec{
|
||||
entries: []preflightArchiveEntry{{path: "configuration/operator.env", body: []byte("after!")}},
|
||||
})
|
||||
return Result{Path: filepath.Join(t.TempDir(), "checkpoint.zip")}, nil
|
||||
}
|
||||
var restored [][]byte
|
||||
deps.restoreFile = func(_ context.Context, _ config.Installation, _ ArchiveEntryMetadata, stream io.Reader) error {
|
||||
body, err := io.ReadAll(stream)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
restored = append(restored, body)
|
||||
return nil
|
||||
}
|
||||
|
||||
if _, err := restoreWithDependencies(context.Background(), installation, RestoreRequest{Archive: archive, Confirm: true}, deps); err == nil || !strings.Contains(err.Error(), "changed") {
|
||||
t.Fatalf("Restore() error = %v, want archive mutation refusal", err)
|
||||
}
|
||||
if len(restored) != 0 {
|
||||
t.Fatalf("restore applied mutated archive payloads: %q", restored)
|
||||
}
|
||||
if err := lifecycleLockFreeAfterTerminalRestore(installation); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,8 +363,8 @@ func TestRestoreLifecycleLockExcludesCompetingTransactionsUntilTerminalCleanup(t
|
||||
caller, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
deps := restoreTestDependencies(t, runner)
|
||||
deps.acquireLock = func(target config.Installation) (restoreLock, error) { return lifecycle.Acquire(target) }
|
||||
deps.checkpointLocked = func(context.Context, config.Installation, CreateRequest) (Result, error) {
|
||||
deps.acquireTransaction = lifecycle.AcquireTransaction
|
||||
deps.checkpoint = func(context.Context, *lifecycle.Transaction, config.Installation, CreateRequest) (Result, error) {
|
||||
gate("checkpoint")
|
||||
return Result{Path: filepath.Join(t.TempDir(), "checkpoint.zip")}, nil
|
||||
}
|
||||
@@ -369,8 +435,8 @@ func competingRestoreAndBackupEntry(installation config.Installation, archive st
|
||||
|
||||
checkpointCalled := false
|
||||
restoreDeps := restoreTestDependencies(t, newBackupRunner(installation, false))
|
||||
restoreDeps.acquireLock = func(target config.Installation) (restoreLock, error) { return lifecycle.Acquire(target) }
|
||||
restoreDeps.checkpointLocked = func(context.Context, config.Installation, CreateRequest) (Result, error) {
|
||||
restoreDeps.acquireTransaction = lifecycle.AcquireTransaction
|
||||
restoreDeps.checkpoint = func(context.Context, *lifecycle.Transaction, config.Installation, CreateRequest) (Result, error) {
|
||||
checkpointCalled = true
|
||||
return Result{Path: filepath.Join(t.TempDir(), "competing-checkpoint.zip")}, nil
|
||||
}
|
||||
@@ -408,8 +474,8 @@ func TestRestoreCannotApplyAStaleCheckpointOverAnInterleavedRestore(t *testing.T
|
||||
continueCheckpoint := make(chan struct{})
|
||||
firstRunner := newBackupRunner(installation, true)
|
||||
firstDeps := restoreTestDependencies(t, firstRunner)
|
||||
firstDeps.acquireLock = func(target config.Installation) (restoreLock, error) { return lifecycle.Acquire(target) }
|
||||
firstDeps.checkpointLocked = func(context.Context, config.Installation, CreateRequest) (Result, error) {
|
||||
firstDeps.acquireTransaction = lifecycle.AcquireTransaction
|
||||
firstDeps.checkpoint = func(context.Context, *lifecycle.Transaction, config.Installation, CreateRequest) (Result, error) {
|
||||
checkpointState = targetState
|
||||
close(checkpointEntered)
|
||||
<-continueCheckpoint
|
||||
@@ -439,8 +505,8 @@ func TestRestoreCannotApplyAStaleCheckpointOverAnInterleavedRestore(t *testing.T
|
||||
interleavedCheckpoint := false
|
||||
interleavedMutation := false
|
||||
interleavedDeps := restoreTestDependencies(t, newBackupRunner(installation, false))
|
||||
interleavedDeps.acquireLock = func(target config.Installation) (restoreLock, error) { return lifecycle.Acquire(target) }
|
||||
interleavedDeps.checkpointLocked = func(context.Context, config.Installation, CreateRequest) (Result, error) {
|
||||
interleavedDeps.acquireTransaction = lifecycle.AcquireTransaction
|
||||
interleavedDeps.checkpoint = func(context.Context, *lifecycle.Transaction, config.Installation, CreateRequest) (Result, error) {
|
||||
interleavedCheckpoint = true
|
||||
return Result{Path: filepath.Join(t.TempDir(), "interleaved-checkpoint.zip")}, nil
|
||||
}
|
||||
@@ -542,7 +608,7 @@ func TestRestorePreflightFailureDoesNotMutateTarget(t *testing.T) {
|
||||
return PreflightResult{}, preflightErr
|
||||
}
|
||||
checkpointCalls := 0
|
||||
deps.checkpointLocked = func(context.Context, config.Installation, CreateRequest) (Result, error) {
|
||||
deps.checkpoint = func(context.Context, *lifecycle.Transaction, config.Installation, CreateRequest) (Result, error) {
|
||||
checkpointCalls++
|
||||
return Result{}, nil
|
||||
}
|
||||
@@ -567,7 +633,7 @@ func TestRestoreCheckpointFailureDoesNotMutateTarget(t *testing.T) {
|
||||
runner := newBackupRunner(installation, true)
|
||||
deps := restoreTestDependencies(t, runner)
|
||||
checkpointErr := errors.New("checkpoint unavailable")
|
||||
deps.checkpointLocked = func(context.Context, config.Installation, CreateRequest) (Result, error) {
|
||||
deps.checkpoint = func(context.Context, *lifecycle.Transaction, config.Installation, CreateRequest) (Result, error) {
|
||||
return Result{}, checkpointErr
|
||||
}
|
||||
restoredFiles := 0
|
||||
@@ -591,7 +657,7 @@ func TestRestoreFileFailureRollsBackSecretAwareCheckpointBeforeCleanup(t *testin
|
||||
runner := newBackupRunner(installation, true)
|
||||
deps := restoreTestDependencies(t, runner)
|
||||
fileErr := errors.New("cannot restore operator configuration")
|
||||
deps.checkpointLocked = func(context.Context, config.Installation, CreateRequest) (Result, error) {
|
||||
deps.checkpoint = func(context.Context, *lifecycle.Transaction, config.Installation, CreateRequest) (Result, error) {
|
||||
return Result{Path: "/tmp/recovery.zip"}, nil
|
||||
}
|
||||
var events []string
|
||||
@@ -1095,7 +1161,7 @@ func TestRestoreReleasesBarrierOnlyAfterVerifiedRecoveryFromLostResponse(t *test
|
||||
}
|
||||
runner := &commandFailureRunner{fakeBackupRunner: backing, failures: []*commandFailure{&failure}}
|
||||
deps := restoreTestDependencies(t, runner)
|
||||
deps.checkpointLocked = func(context.Context, config.Installation, CreateRequest) (Result, error) {
|
||||
deps.checkpoint = func(context.Context, *lifecycle.Transaction, config.Installation, CreateRequest) (Result, error) {
|
||||
return Result{Path: "/tmp/recovery.zip"}, nil
|
||||
}
|
||||
deps.prepareRecovery = func(context.Context, config.Installation, string) (PreflightResult, error) {
|
||||
@@ -1322,10 +1388,6 @@ func (runner *lifecycleGateRunner) SessionInventoryScope() string {
|
||||
return runner.fakeBackupRunner.SessionInventoryScope()
|
||||
}
|
||||
|
||||
type fakeRestoreLock struct {
|
||||
release func()
|
||||
}
|
||||
|
||||
type authenticationStateResetRunner struct {
|
||||
args []string
|
||||
result compose.Result
|
||||
@@ -1431,13 +1493,6 @@ func TestVerifyRestoreWorkspaceRejectsInvalidOperatorResults(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func (lock fakeRestoreLock) Release() error {
|
||||
if lock.release != nil {
|
||||
lock.release()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func equalStrings(got, want []string) bool {
|
||||
if len(got) != len(want) {
|
||||
return false
|
||||
@@ -1456,18 +1511,18 @@ func restoreTestDependencies(t *testing.T, runner archiveRunner) restoreDependen
|
||||
preflight: func(ctx context.Context, installation config.Installation, request PreflightRequest) (PreflightResult, error) {
|
||||
return Preflight(ctx, installation, request, permissivePreflightDependencies())
|
||||
},
|
||||
checkpointLocked: func(context.Context, config.Installation, CreateRequest) (Result, error) {
|
||||
checkpoint: func(context.Context, *lifecycle.Transaction, config.Installation, CreateRequest) (Result, error) {
|
||||
return Result{Path: "/tmp/default-checkpoint.zip"}, nil
|
||||
},
|
||||
prepareRecovery: func(context.Context, config.Installation, string) (PreflightResult, error) {
|
||||
return PreflightResult{}, nil
|
||||
},
|
||||
recover: func(context.Context, config.Installation, PreflightResult, bool) error { return nil },
|
||||
cleanupCheckpoint: func(string) error { return nil },
|
||||
acquireLock: func(config.Installation) (restoreLock, error) { return fakeRestoreLock{}, nil },
|
||||
runner: runner,
|
||||
sleep: func(time.Duration) {},
|
||||
restoreFile: func(context.Context, config.Installation, ArchiveEntryMetadata, io.Reader) error { return nil },
|
||||
recover: func(context.Context, config.Installation, PreflightResult, bool) error { return nil },
|
||||
cleanupCheckpoint: func(string) error { return nil },
|
||||
acquireTransaction: lifecycle.AcquireTransaction,
|
||||
runner: runner,
|
||||
sleep: func(time.Duration) {},
|
||||
restoreFile: func(context.Context, config.Installation, ArchiveEntryMetadata, io.Reader) error { return nil },
|
||||
restoreVolume: func(context.Context, config.Installation, VolumeMetadata, io.Reader) error {
|
||||
return nil
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user