Create and tag preview backups (#4595)
Add an option to request a preview backup and tag the resulting backup as a preview if the flag is set. Preview backups must complete successfully with no errors in order to be tagged This does not update the item selection logic, so right now preview backups will contain all items that normal backups do. Item selection will be refined in upcoming PRs --- #### 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 - [ ] ⚡ Unit test - [x] 💚 E2E
This commit is contained in:
parent
77a417b1ce
commit
e46cf645e5
@ -934,24 +934,49 @@ func (op *BackupOperation) createBackupModels(
|
|||||||
model.ServiceTag: op.Selectors.PathService().String(),
|
model.ServiceTag: op.Selectors.PathService().String(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add tags to mark this backup as either assist or merge. This is used to:
|
// Add tags to mark this backup as preview, assist, or merge. This is used to:
|
||||||
// 1. Filter assist backups by tag during base selection process
|
// 1. Filter assist backups by tag during base selection process
|
||||||
// 2. Differentiate assist backups from merge backups
|
// 2. Differentiate assist backups, merge backups, and preview backups.
|
||||||
if isMergeBackup(
|
//
|
||||||
|
// model.BackupTypeTag has more info about how these tags are used.
|
||||||
|
switch {
|
||||||
|
case op.Options.ToggleFeatures.PreviewBackup:
|
||||||
|
// Preview backups need to be successful and without errors to be considered
|
||||||
|
// valid. Just reuse the merge base check for that since it has the same
|
||||||
|
// requirements.
|
||||||
|
if !isMergeBackup(
|
||||||
snapID,
|
snapID,
|
||||||
ssid,
|
ssid,
|
||||||
op.Options.FailureHandling,
|
op.Options.FailureHandling,
|
||||||
op.Errors) {
|
op.Errors) {
|
||||||
|
return clues.New("failed preview backup").WithClues(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
tags[model.BackupTypeTag] = model.PreviewBackup
|
||||||
|
|
||||||
|
case isMergeBackup(
|
||||||
|
snapID,
|
||||||
|
ssid,
|
||||||
|
op.Options.FailureHandling,
|
||||||
|
op.Errors):
|
||||||
tags[model.BackupTypeTag] = model.MergeBackup
|
tags[model.BackupTypeTag] = model.MergeBackup
|
||||||
} else if isAssistBackup(
|
|
||||||
|
case isAssistBackup(
|
||||||
opStats.hasNewDetailEntries,
|
opStats.hasNewDetailEntries,
|
||||||
snapID,
|
snapID,
|
||||||
ssid,
|
ssid,
|
||||||
op.Options.FailureHandling,
|
op.Options.FailureHandling,
|
||||||
op.Errors) {
|
op.Errors):
|
||||||
tags[model.BackupTypeTag] = model.AssistBackup
|
tags[model.BackupTypeTag] = model.AssistBackup
|
||||||
} else {
|
|
||||||
return clues.New("backup is neither assist nor merge").WithClues(ctx)
|
default:
|
||||||
|
return clues.New("unable to determine backup type due to operation errors").
|
||||||
|
WithClues(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Additional defensive check to make sure we tag things as expected above.
|
||||||
|
if len(tags[model.BackupTypeTag]) == 0 {
|
||||||
|
return clues.New("empty backup type tag").WithClues(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx = clues.Add(ctx, model.BackupTypeTag, tags[model.BackupTypeTag])
|
ctx = clues.Add(ctx, model.BackupTypeTag, tags[model.BackupTypeTag])
|
||||||
|
|||||||
@ -1649,7 +1649,6 @@ func (suite *AssistBackupIntegrationSuite) TestBackupTypesForFailureModes() {
|
|||||||
var (
|
var (
|
||||||
acct = tconfig.NewM365Account(suite.T())
|
acct = tconfig.NewM365Account(suite.T())
|
||||||
tenantID = acct.Config[account.AzureTenantIDKey]
|
tenantID = acct.Config[account.AzureTenantIDKey]
|
||||||
opts = control.DefaultOptions()
|
|
||||||
osel = selectors.NewOneDriveBackup([]string{userID})
|
osel = selectors.NewOneDriveBackup([]string{userID})
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1667,6 +1666,7 @@ func (suite *AssistBackupIntegrationSuite) TestBackupTypesForFailureModes() {
|
|||||||
collFunc func() []data.BackupCollection
|
collFunc func() []data.BackupCollection
|
||||||
injectNonRecoverableErr bool
|
injectNonRecoverableErr bool
|
||||||
failurePolicy control.FailurePolicy
|
failurePolicy control.FailurePolicy
|
||||||
|
previewBackup bool
|
||||||
expectRunErr assert.ErrorAssertionFunc
|
expectRunErr assert.ErrorAssertionFunc
|
||||||
expectBackupTag string
|
expectBackupTag string
|
||||||
expectFaults func(t *testing.T, errs *fault.Bus)
|
expectFaults func(t *testing.T, errs *fault.Bus)
|
||||||
@ -1829,6 +1829,67 @@ func (suite *AssistBackupIntegrationSuite) TestBackupTypesForFailureModes() {
|
|||||||
assert.Greater(t, len(errs.Recovered()), 0, "recovered errors")
|
assert.Greater(t, len(errs.Recovered()), 0, "recovered errors")
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "preview, fail after recovery, no errors",
|
||||||
|
collFunc: func() []data.BackupCollection {
|
||||||
|
bc := []data.BackupCollection{
|
||||||
|
makeBackupCollection(
|
||||||
|
tmp,
|
||||||
|
locPath,
|
||||||
|
[]dataMock.Item{
|
||||||
|
makeMockItem("file1", nil, time.Now(), false, nil),
|
||||||
|
makeMockItem("file2", nil, time.Now(), false, nil),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
return bc
|
||||||
|
},
|
||||||
|
failurePolicy: control.FailAfterRecovery,
|
||||||
|
previewBackup: true,
|
||||||
|
expectRunErr: assert.NoError,
|
||||||
|
expectBackupTag: model.PreviewBackup,
|
||||||
|
expectFaults: func(t *testing.T, errs *fault.Bus) {
|
||||||
|
assert.NoError(t, errs.Failure(), clues.ToCore(errs.Failure()))
|
||||||
|
assert.Empty(t, errs.Recovered(), "recovered errors")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "preview, fail after recovery, non-recoverable errors",
|
||||||
|
collFunc: func() []data.BackupCollection {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
injectNonRecoverableErr: true,
|
||||||
|
failurePolicy: control.FailAfterRecovery,
|
||||||
|
previewBackup: true,
|
||||||
|
expectRunErr: assert.Error,
|
||||||
|
expectFaults: func(t *testing.T, errs *fault.Bus) {
|
||||||
|
assert.Error(t, errs.Failure(), clues.ToCore(errs.Failure()))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "preview, fail after recovery, recoverable errors",
|
||||||
|
collFunc: func() []data.BackupCollection {
|
||||||
|
bc := []data.BackupCollection{
|
||||||
|
makeBackupCollection(
|
||||||
|
tmp,
|
||||||
|
locPath,
|
||||||
|
[]dataMock.Item{
|
||||||
|
makeMockItem("file1", nil, time.Now(), false, nil),
|
||||||
|
makeMockItem("file2", nil, time.Now(), false, assert.AnError),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
return bc
|
||||||
|
},
|
||||||
|
failurePolicy: control.FailAfterRecovery,
|
||||||
|
previewBackup: true,
|
||||||
|
expectRunErr: assert.Error,
|
||||||
|
expectFaults: func(t *testing.T, errs *fault.Bus) {
|
||||||
|
assert.Error(t, errs.Failure(), clues.ToCore(errs.Failure()))
|
||||||
|
assert.Greater(t, len(errs.Recovered()), 0, "recovered errors")
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, test := range table {
|
for _, test := range table {
|
||||||
suite.Run(test.name, func() {
|
suite.Run(test.name, func() {
|
||||||
@ -1856,7 +1917,9 @@ func (suite *AssistBackupIntegrationSuite) TestBackupTypesForFailureModes() {
|
|||||||
cs = append(cs, mc)
|
cs = append(cs, mc)
|
||||||
bp := opMock.NewMockBackupProducer(cs, data.CollectionStats{}, test.injectNonRecoverableErr)
|
bp := opMock.NewMockBackupProducer(cs, data.CollectionStats{}, test.injectNonRecoverableErr)
|
||||||
|
|
||||||
|
opts := control.DefaultOptions()
|
||||||
opts.FailureHandling = test.failurePolicy
|
opts.FailureHandling = test.failurePolicy
|
||||||
|
opts.ToggleFeatures.PreviewBackup = test.previewBackup
|
||||||
|
|
||||||
bo, err := NewBackupOperation(
|
bo, err := NewBackupOperation(
|
||||||
ctx,
|
ctx,
|
||||||
|
|||||||
@ -86,4 +86,9 @@ type Toggles struct {
|
|||||||
// DisableConcurrencyLimiter removes concurrency limits when communicating with
|
// DisableConcurrencyLimiter removes concurrency limits when communicating with
|
||||||
// graph API. This flag is only relevant for exchange backups for now
|
// graph API. This flag is only relevant for exchange backups for now
|
||||||
DisableConcurrencyLimiter bool `json:"disableConcurrencyLimiter,omitempty"`
|
DisableConcurrencyLimiter bool `json:"disableConcurrencyLimiter,omitempty"`
|
||||||
|
|
||||||
|
// PreviewBackup denotes that this backup contains a subset of information for
|
||||||
|
// the protected resource. PreviewBackups are used to demonstrate value by
|
||||||
|
// being quick to create.
|
||||||
|
PreviewBackup bool `json:"previewBackup"`
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user