diff --git a/src/internal/kopia/retention/opts.go b/src/internal/kopia/retention/opts.go index 07371797a..1e23270f3 100644 --- a/src/internal/kopia/retention/opts.go +++ b/src/internal/kopia/retention/opts.go @@ -136,3 +136,34 @@ func (r *Opts) setBlobConfigMode( return nil } + +// Verify checks that the config info in r passes kopia's retention validation +// checks when it comes to locking durations and that if retention is requested +// in the blob config blob then lock extension is also configured to run during +// maintenance. If rentention is not enabled in the blob config blob then lock +// extension should be disabled during maintenance. +func (r Opts) Verify(ctx context.Context) error { + if !r.blobCfg.IsRetentionEnabled() { + if r.params.ExtendObjectLocks { + return clues.NewWC( + ctx, + "retention disabled but maintenance lock extension enabled") + } + + // Both disabled. + return nil + } + + // Rest of function handles case where retention is enabled in the blob config + // blob. + if !r.params.ExtendObjectLocks { + return clues.NewWC( + ctx, + "retention enabled but maintenance lock extension disabled") + } + + return clues.Stack(maintenance.CheckExtendRetention( + ctx, + r.blobCfg, + &r.params)).OrNil() +} diff --git a/src/internal/kopia/retention/opts_test.go b/src/internal/kopia/retention/opts_test.go index 8b250c79a..5de4cf68d 100644 --- a/src/internal/kopia/retention/opts_test.go +++ b/src/internal/kopia/retention/opts_test.go @@ -202,3 +202,97 @@ func (suite *OptsUnitSuite) TestSet() { }) } } + +func (suite *OptsUnitSuite) TestVerify() { + mode := blob.Governance + fullCycleInterval := time.Hour * 24 + duration := 2 * fullCycleInterval + + table := []struct { + name string + input *retention.Opts + expectErr assert.ErrorAssertionFunc + }{ + { + name: "ValidDisabled", + input: retention.OptsFromConfigs( + format.BlobStorageConfiguration{}, + maintenance.Params{ + FullCycle: maintenance.CycleParams{ + Interval: fullCycleInterval, + }, + ExtendObjectLocks: false, + }), + expectErr: assert.NoError, + }, + { + name: "ValidEnabled", + input: retention.OptsFromConfigs( + format.BlobStorageConfiguration{ + RetentionMode: mode, + RetentionPeriod: duration, + }, + maintenance.Params{ + FullCycle: maintenance.CycleParams{ + Interval: fullCycleInterval, + }, + ExtendObjectLocks: true, + }), + expectErr: assert.NoError, + }, + { + name: "InvalidDuration", + input: retention.OptsFromConfigs( + format.BlobStorageConfiguration{ + RetentionMode: mode, + RetentionPeriod: fullCycleInterval, + }, + maintenance.Params{ + FullCycle: maintenance.CycleParams{ + Interval: fullCycleInterval, + }, + ExtendObjectLocks: true, + }), + expectErr: assert.Error, + }, + { + name: "InvalidNotExtending", + input: retention.OptsFromConfigs( + format.BlobStorageConfiguration{ + RetentionMode: mode, + RetentionPeriod: duration, + }, + maintenance.Params{ + FullCycle: maintenance.CycleParams{ + Interval: fullCycleInterval, + }, + ExtendObjectLocks: false, + }), + expectErr: assert.Error, + }, + { + name: "InvalidNotConfigured", + input: retention.OptsFromConfigs( + format.BlobStorageConfiguration{}, + maintenance.Params{ + FullCycle: maintenance.CycleParams{ + Interval: fullCycleInterval, + }, + ExtendObjectLocks: true, + }), + expectErr: assert.Error, + }, + } + + for _, test := range table { + suite.Run(test.name, func() { + t := suite.T() + + ctx, flush := tester.NewContext(t) + t.Cleanup(flush) + + err := test.input.Verify(ctx) + test.expectErr(t, err, clues.ToCore(err)) + }) + } +}