Retention update op (#3865)

Create new operation type to update retention parameters
for immutable backups

---

#### Does this PR need a docs update or release note?

- [ ]  Yes, it's included
- [ ] 🕐 Yes, but in a later PR
- [x]  No

#### Type of change

- [x] 🌻 Feature
- [ ] 🐛 Bugfix
- [ ] 🗺️ Documentation
- [ ] 🤖 Supportability/Tests
- [ ] 💻 CI/Deployment
- [ ] 🧹 Tech Debt/Cleanup


#### Test Plan

- [ ] 💪 Manual
- [x]  Unit test
- [ ] 💚 E2E
This commit is contained in:
ashmrtn 2023-07-28 11:27:54 -07:00 committed by GitHub
parent a836e877af
commit b12bb50a4a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 167 additions and 0 deletions

View File

@ -0,0 +1,77 @@
package operations
import (
"context"
"time"
"github.com/alcionai/clues"
"github.com/alcionai/corso/src/internal/common/crash"
"github.com/alcionai/corso/src/internal/events"
"github.com/alcionai/corso/src/internal/kopia"
"github.com/alcionai/corso/src/internal/stats"
"github.com/alcionai/corso/src/pkg/control"
"github.com/alcionai/corso/src/pkg/control/repository"
"github.com/alcionai/corso/src/pkg/count"
)
// RetentionConfigOperation wraps an operation with restore-specific props.
type RetentionConfigOperation struct {
operation
Results RetentionConfigResults
rcOpts repository.Retention
}
// RetentionConfigResults aggregate the details of the results of the operation.
type RetentionConfigResults struct {
stats.StartAndEndTime
}
// NewRetentionConfigOperation constructs and validates an operation to change
// retention parameters.
func NewRetentionConfigOperation(
ctx context.Context,
opts control.Options,
kw *kopia.Wrapper,
rcOpts repository.Retention,
bus events.Eventer,
) (RetentionConfigOperation, error) {
op := RetentionConfigOperation{
operation: newOperation(opts, bus, count.New(), kw, nil),
rcOpts: rcOpts,
}
// Don't run validation because we don't populate the model store.
return op, nil
}
func (op *RetentionConfigOperation) Run(ctx context.Context) (err error) {
defer func() {
if crErr := crash.Recovery(ctx, recover(), "retention_config"); crErr != nil {
err = crErr
}
}()
op.Results.StartedAt = time.Now()
// TODO(ashmrtn): Send telemetry?
return op.do(ctx)
}
func (op *RetentionConfigOperation) do(ctx context.Context) error {
defer func() {
op.Results.CompletedAt = time.Now()
}()
err := op.operation.kopia.SetRetentionParameters(ctx, op.rcOpts)
if err != nil {
op.Status = Failed
return clues.Wrap(err, "running retention config operation")
}
op.Status = Completed
return nil
}

View File

@ -0,0 +1,74 @@
package operations
import (
"testing"
"github.com/alcionai/clues"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/alcionai/corso/src/internal/common/ptr"
evmock "github.com/alcionai/corso/src/internal/events/mock"
"github.com/alcionai/corso/src/internal/kopia"
"github.com/alcionai/corso/src/internal/tester"
"github.com/alcionai/corso/src/pkg/control"
"github.com/alcionai/corso/src/pkg/control/repository"
storeTD "github.com/alcionai/corso/src/pkg/storage/testdata"
)
type RetentionConfigOpIntegrationSuite struct {
tester.Suite
}
func TestRetentionConfigOpIntegrationSuite(t *testing.T) {
suite.Run(t, &RetentionConfigOpIntegrationSuite{
Suite: tester.NewIntegrationSuite(
t,
[][]string{storeTD.AWSStorageCredEnvs}),
})
}
func (suite *RetentionConfigOpIntegrationSuite) TestRepoRetentionConfig() {
var (
t = suite.T()
// need to initialize the repository before we can test connecting to it.
st = storeTD.NewPrefixedS3Storage(t)
k = kopia.NewConn(st)
)
ctx, flush := tester.NewContext(t)
defer flush()
err := k.Initialize(ctx, repository.Options{}, repository.Retention{})
require.NoError(t, err, clues.ToCore(err))
kw, err := kopia.NewWrapper(k)
// kopiaRef comes with a count of 1 and Wrapper bumps it again so safe
// to close here.
k.Close(ctx)
require.NoError(t, err, clues.ToCore(err))
defer kw.Close(ctx)
// Only set extend locks parameter as other retention options require a bucket
// with object locking enabled. There's more complete tests in the kopia
// package.
rco, err := NewRetentionConfigOperation(
ctx,
control.DefaultOptions(),
kw,
repository.Retention{
Extend: ptr.To(true),
},
evmock.NewBus())
require.NoError(t, err, clues.ToCore(err))
err = rco.Run(ctx)
assert.NoError(t, err, clues.ToCore(err))
assert.Equal(t, Completed, rco.Status)
assert.NotZero(t, rco.Results.StartedAt)
assert.NotZero(t, rco.Results.CompletedAt)
assert.NotEqual(t, rco.Results.StartedAt, rco.Results.CompletedAt)
}

View File

@ -84,6 +84,10 @@ type Repository interface {
ctx context.Context,
mOpts ctrlRepo.Maintenance,
) (operations.MaintenanceOperation, error)
NewRetentionConfig(
ctx context.Context,
rcOpts ctrlRepo.Retention,
) (operations.RetentionConfigOperation, error)
DeleteBackup(ctx context.Context, id string) error
BackupGetter
// ConnectToM365 establishes graph api connections
@ -420,6 +424,18 @@ func (r repository) NewMaintenance(
r.Bus)
}
func (r repository) NewRetentionConfig(
ctx context.Context,
rcOpts ctrlRepo.Retention,
) (operations.RetentionConfigOperation, error) {
return operations.NewRetentionConfigOperation(
ctx,
r.Opts,
r.dataLayer,
rcOpts,
r.Bus)
}
// Backup retrieves a backup by id.
func (r repository) Backup(ctx context.Context, id string) (*backup.Backup, error) {
return getBackup(ctx, id, store.NewKopiaStore(r.modelStore))