Remove fallback reasons and merge code for legacy UPN tagged backups

This commit is contained in:
Abhishek Pandey 2023-08-22 18:01:29 +05:30
parent 0e6ef90e41
commit fdc9145f61
5 changed files with 4 additions and 929 deletions

View File

@ -8,7 +8,6 @@ import (
"golang.org/x/exp/slices"
"github.com/alcionai/corso/src/internal/version"
"github.com/alcionai/corso/src/pkg/backup/identity"
"github.com/alcionai/corso/src/pkg/logger"
)
@ -23,11 +22,6 @@ type BackupBases interface {
ClearMergeBases()
AssistBases() []ManifestEntry
ClearAssistBases()
MergeBackupBases(
ctx context.Context,
other BackupBases,
reasonToKey func(identity.Reasoner) string,
) BackupBases
}
type backupBases struct {
@ -111,119 +105,6 @@ func (bb *backupBases) ClearAssistBases() {
bb.assistBases = nil
}
// MergeBackupBases reduces the two BackupBases into a single BackupBase.
// Assumes the passed in BackupBases represents a prior backup version (across
// some migration that disrupts lookup), and that the BackupBases used to call
// this function contains the current version.
//
// reasonToKey should be a function that, given a Reasoner, will produce some
// string that represents Reasoner in the context of the merge operation. For
// example, to merge BackupBases across a ProtectedResource migration, the
// Reasoner's service and category can be used as the key.
//
// Selection priority, for each reason key generated by reasonsToKey, follows
// these rules:
// 1. If the called BackupBases has an entry for a given reason, ignore the
// other BackupBases matching that reason.
// 2. If the called BackupBases has only AssistBases, look for a matching
// MergeBase manifest in the other BackupBases.
// 3. If the called BackupBases has no entry for a reason, look for a matching
// MergeBase in the other BackupBases.
func (bb *backupBases) MergeBackupBases(
ctx context.Context,
other BackupBases,
reasonToKey func(reason identity.Reasoner) string,
) BackupBases {
if other == nil || (len(other.MergeBases()) == 0 && len(other.AssistBases()) == 0) {
return bb
}
if bb == nil || (len(bb.MergeBases()) == 0 && len(bb.AssistBases()) == 0) {
return other
}
toMerge := map[string]struct{}{}
assist := map[string]struct{}{}
// Track the bases in bb.
for _, m := range bb.mergeBases {
for _, r := range m.Reasons {
k := reasonToKey(r)
toMerge[k] = struct{}{}
assist[k] = struct{}{}
}
}
for _, m := range bb.assistBases {
for _, r := range m.Reasons {
k := reasonToKey(r)
assist[k] = struct{}{}
}
}
var toAdd []ManifestEntry
// Calculate the set of mergeBases to pull from other into this one.
for _, m := range other.MergeBases() {
useReasons := []identity.Reasoner{}
for _, r := range m.Reasons {
k := reasonToKey(r)
if _, ok := toMerge[k]; ok {
// Assume other contains prior manifest versions.
// We don't want to stack a prior version incomplete onto
// a current version's complete snapshot.
continue
}
useReasons = append(useReasons, r)
}
if len(useReasons) > 0 {
m.Reasons = useReasons
toAdd = append(toAdd, m)
}
}
res := &backupBases{
backups: bb.Backups(),
mergeBases: bb.MergeBases(),
assistBases: bb.AssistBases(),
// Note that assistBackups are a new feature and don't exist
// in prior versions where we were using UPN based reasons i.e.
// other won't have any assistBackups.
assistBackups: bb.AssistBackups(),
}
// Add new mergeBases and backups.
for _, man := range toAdd {
// Will get empty string if not found which is fine, it'll fail one of the
// other checks.
bID, _ := man.GetTag(TagBackupID)
bup, ok := getBackupByID(other.Backups(), bID)
if !ok {
logger.Ctx(ctx).Infow(
"not unioning snapshot missing backup",
"other_manifest_id", man.ID,
"other_backup_id", bID)
continue
}
bup.Reasons = man.Reasons
res.backups = append(res.backups, bup)
res.mergeBases = append(res.mergeBases, man)
// TODO(pandeyabs): Remove this once we remove overlap between
// between merge and assist bases as part of #3943.
res.assistBases = append(res.assistBases, man)
}
return res
}
func findNonUniqueManifests(
ctx context.Context,
manifests []ManifestEntry,

View File

@ -1,7 +1,6 @@
package kopia
import (
"fmt"
"testing"
"github.com/kopia/kopia/repo/manifest"
@ -203,279 +202,6 @@ func (suite *BackupBasesUnitSuite) TestClearAssistBases() {
assert.Empty(suite.T(), bb.AssistBases())
}
func (suite *BackupBasesUnitSuite) TestMergeBackupBases() {
ro := "resource_owner"
type testInput struct {
id int
cat []path.CategoryType
}
// Make a function so tests can modify things without messing with each other.
makeBackupBases := func(mergeInputs []testInput, assistInputs []testInput) *backupBases {
res := &backupBases{}
for _, i := range mergeInputs {
baseID := fmt.Sprintf("id%d", i.id)
reasons := make([]identity.Reasoner, 0, len(i.cat))
for _, c := range i.cat {
reasons = append(reasons, NewReason("", ro, path.ExchangeService, c))
}
m := makeManifest(baseID, "", "b"+baseID, reasons...)
res.assistBases = append(res.assistBases, m)
b := BackupEntry{
Backup: &backup.Backup{
BaseModel: model.BaseModel{ID: model.StableID("b" + baseID)},
SnapshotID: baseID,
StreamStoreID: "ss" + baseID,
},
Reasons: reasons,
}
res.backups = append(res.backups, b)
res.mergeBases = append(res.mergeBases, m)
}
for _, i := range assistInputs {
baseID := fmt.Sprintf("id%d", i.id)
reasons := make([]identity.Reasoner, 0, len(i.cat))
for _, c := range i.cat {
reasons = append(reasons, NewReason("", ro, path.ExchangeService, c))
}
m := makeManifest(baseID, "", "a"+baseID, reasons...)
b := BackupEntry{
Backup: &backup.Backup{
BaseModel: model.BaseModel{
ID: model.StableID("a" + baseID),
Tags: map[string]string{model.BackupTypeTag: model.AssistBackup},
},
SnapshotID: baseID,
StreamStoreID: "ss" + baseID,
},
Reasons: reasons,
}
res.assistBackups = append(res.assistBackups, b)
res.assistBases = append(res.assistBases, m)
}
return res
}
table := []struct {
name string
merge []testInput
assist []testInput
otherMerge []testInput
otherAssist []testInput
expect func() *backupBases
}{
{
name: "Other Empty",
merge: []testInput{
{cat: []path.CategoryType{path.EmailCategory}},
},
assist: []testInput{
{cat: []path.CategoryType{path.EmailCategory}},
},
expect: func() *backupBases {
bs := makeBackupBases([]testInput{
{cat: []path.CategoryType{path.EmailCategory}},
}, []testInput{
{cat: []path.CategoryType{path.EmailCategory}},
})
return bs
},
},
{
name: "current Empty",
otherMerge: []testInput{
{cat: []path.CategoryType{path.EmailCategory}},
},
otherAssist: []testInput{
{cat: []path.CategoryType{path.EmailCategory}},
},
expect: func() *backupBases {
bs := makeBackupBases([]testInput{
{cat: []path.CategoryType{path.EmailCategory}},
}, []testInput{
{cat: []path.CategoryType{path.EmailCategory}},
})
return bs
},
},
{
name: "Other overlaps merge and assist",
merge: []testInput{
{
id: 1,
cat: []path.CategoryType{path.EmailCategory},
},
},
assist: []testInput{
{
id: 4,
cat: []path.CategoryType{path.EmailCategory},
},
},
otherMerge: []testInput{
{
id: 2,
cat: []path.CategoryType{path.EmailCategory},
},
{
id: 3,
cat: []path.CategoryType{path.EmailCategory},
},
},
otherAssist: []testInput{
{
id: 5,
cat: []path.CategoryType{path.EmailCategory},
},
},
expect: func() *backupBases {
bs := makeBackupBases([]testInput{
{
id: 1,
cat: []path.CategoryType{path.EmailCategory},
},
}, []testInput{
{
id: 4,
cat: []path.CategoryType{path.EmailCategory},
},
})
return bs
},
},
{
name: "Other overlaps merge",
merge: []testInput{
{
id: 1,
cat: []path.CategoryType{path.EmailCategory},
},
},
otherMerge: []testInput{
{
id: 2,
cat: []path.CategoryType{path.EmailCategory},
},
},
expect: func() *backupBases {
bs := makeBackupBases([]testInput{
{
id: 1,
cat: []path.CategoryType{path.EmailCategory},
},
}, nil)
return bs
},
},
{
name: "Current assist overlaps with Other merge",
assist: []testInput{
{
id: 3,
cat: []path.CategoryType{path.EmailCategory},
},
},
otherMerge: []testInput{
{
id: 1,
cat: []path.CategoryType{path.EmailCategory},
},
},
otherAssist: []testInput{
{
id: 2,
cat: []path.CategoryType{path.EmailCategory},
},
},
expect: func() *backupBases {
bs := makeBackupBases([]testInput{
{
id: 1,
cat: []path.CategoryType{path.EmailCategory},
},
}, []testInput{
{
id: 3,
cat: []path.CategoryType{path.EmailCategory},
},
})
return bs
},
},
{
name: "Other Disjoint",
merge: []testInput{
{cat: []path.CategoryType{path.EmailCategory}},
{
id: 1,
cat: []path.CategoryType{path.EmailCategory},
},
},
otherMerge: []testInput{
{
id: 2,
cat: []path.CategoryType{path.ContactsCategory},
},
},
expect: func() *backupBases {
bs := makeBackupBases([]testInput{
{cat: []path.CategoryType{path.EmailCategory}},
{
id: 1,
cat: []path.CategoryType{path.EmailCategory},
},
{
id: 2,
cat: []path.CategoryType{path.ContactsCategory},
},
}, nil)
return bs
},
},
}
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
bb := makeBackupBases(test.merge, test.assist)
other := makeBackupBases(test.otherMerge, test.otherAssist)
expected := test.expect()
ctx, flush := tester.NewContext(t)
defer flush()
got := bb.MergeBackupBases(
ctx,
other,
func(r identity.Reasoner) string {
return r.Service().String() + r.Category().String()
})
AssertBackupBasesEqual(t, expected, got)
})
}
}
func (suite *BackupBasesUnitSuite) TestFixupAndVerify() {
ro := "resource_owner"

View File

@ -333,11 +333,6 @@ func (op *BackupOperation) do(
return nil, clues.Wrap(err, "getting reasons")
}
fallbackReasons, err := makeFallbackReasons(op.account.ID(), op.Selectors)
if err != nil {
return nil, clues.Wrap(err, "getting fallback reasons")
}
logger.Ctx(ctx).With(
"control_options", op.Options,
"selectors", op.Selectors).
@ -355,7 +350,7 @@ func (op *BackupOperation) do(
ctx,
kbf,
op.kopia,
reasons, fallbackReasons,
reasons,
op.account.ID(),
op.incremental,
op.disableAssistBackup)
@ -430,16 +425,6 @@ func (op *BackupOperation) do(
return deets, nil
}
func makeFallbackReasons(tenant string, sel selectors.Selector) ([]identity.Reasoner, error) {
if sel.PathService() != path.SharePointService &&
sel.DiscreteOwner != sel.DiscreteOwnerName {
return sel.Reasons(tenant, true)
}
// return nil for fallback reasons since a nil value will no-op.
return nil, nil
}
// checker to see if conditions are correct for incremental backup behavior such as
// retrieving metadata like delta tokens and previous paths.
func useIncrementalBackup(sel selectors.Selector, opts control.Options) bool {

View File

@ -20,7 +20,7 @@ func produceManifestsAndMetadata(
ctx context.Context,
bf inject.BaseFinder,
rp inject.RestoreProducer,
reasons, fallbackReasons []identity.Reasoner,
reasons []identity.Reasoner,
tenantID string,
getMetadata, dropAssistBases bool,
) (kopia.BackupBases, []data.RestoreCollection, bool, error) {
@ -29,7 +29,6 @@ func produceManifestsAndMetadata(
bf,
rp,
reasons,
fallbackReasons,
tenantID,
getMetadata)
if err != nil {
@ -57,7 +56,7 @@ func getManifestsAndMetadata(
ctx context.Context,
bf inject.BaseFinder,
rp inject.RestoreProducer,
reasons, fallbackReasons []identity.Reasoner,
reasons []identity.Reasoner,
tenantID string,
getMetadata bool,
) (kopia.BackupBases, []data.RestoreCollection, bool, error) {
@ -68,24 +67,6 @@ func getManifestsAndMetadata(
)
bb := bf.FindBases(ctx, reasons, tags)
// TODO(ashmrtn): Only fetch these if we haven't already covered all the
// reasons for this backup.
fbb := bf.FindBases(ctx, fallbackReasons, tags)
// one of three cases can occur when retrieving backups across reason migrations:
// 1. the current reasons don't match any manifests, and we use the fallback to
// look up the previous reason version.
// 2. the current reasons only contain an incomplete manifest, and the fallback
// can find a complete manifest.
// 3. the current reasons contain all the necessary manifests.
// Note: This is not relevant for assist backups, since they are newly introduced
// and they don't exist with fallback reasons.
bb = bb.MergeBackupBases(
ctx,
fbb,
func(r identity.Reasoner) string {
return r.Service().String() + r.Category().String()
})
if !getMetadata {
return bb, nil, false, nil

View File

@ -12,9 +12,7 @@ import (
"github.com/alcionai/corso/src/internal/data"
"github.com/alcionai/corso/src/internal/kopia"
"github.com/alcionai/corso/src/internal/model"
"github.com/alcionai/corso/src/internal/tester"
"github.com/alcionai/corso/src/pkg/backup"
"github.com/alcionai/corso/src/pkg/backup/identity"
"github.com/alcionai/corso/src/pkg/fault"
"github.com/alcionai/corso/src/pkg/path"
@ -482,7 +480,7 @@ func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata() {
ctx,
test.bf,
&test.rp,
test.reasons, nil,
test.reasons,
tid,
test.getMeta,
test.dropAssist)
@ -532,499 +530,3 @@ func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata() {
})
}
}
func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata_FallbackReasons() {
const (
ro = "resourceowner"
fbro = "fb_resourceowner"
tid = "tenantid"
did = "detailsid"
)
makeMan := func(ro, id, incmpl string, cats ...path.CategoryType) kopia.ManifestEntry {
return kopia.ManifestEntry{
Manifest: &snapshot.Manifest{
ID: manifest.ID(id),
IncompleteReason: incmpl,
Tags: map[string]string{"tag:" + kopia.TagBackupID: id + "bup"},
},
Reasons: buildReasons(ro, path.ExchangeService, cats...),
}
}
makeBackup := func(ro, snapID string, cats ...path.CategoryType) kopia.BackupEntry {
return kopia.BackupEntry{
Backup: &backup.Backup{
BaseModel: model.BaseModel{
ID: model.StableID(snapID + "bup"),
},
SnapshotID: snapID,
StreamStoreID: snapID + "store",
},
Reasons: buildReasons(ro, path.ExchangeService, cats...),
}
}
emailReason := kopia.NewReason(
"",
ro,
path.ExchangeService,
path.EmailCategory)
fbEmailReason := kopia.NewReason(
"",
fbro,
path.ExchangeService,
path.EmailCategory)
table := []struct {
name string
bf *mockBackupFinder
rp mockRestoreProducer
reasons []identity.Reasoner
fallbackReasons []identity.Reasoner
getMeta bool
dropAssist bool
assertErr assert.ErrorAssertionFunc
assertB assert.BoolAssertionFunc
expectDCS []mockColl
expectMans kopia.BackupBases
}{
{
name: "don't get metadata, only fallbacks",
bf: &mockBackupFinder{
data: map[string]kopia.BackupBases{
fbro: kopia.NewMockBackupBases().WithMergeBases(
makeMan(fbro, "fb_id1", "", path.EmailCategory),
).WithBackups(
makeBackup(fbro, "fb_id1", path.EmailCategory),
),
},
},
rp: mockRestoreProducer{},
fallbackReasons: []identity.Reasoner{fbEmailReason},
getMeta: false,
assertErr: assert.NoError,
assertB: assert.False,
expectDCS: nil,
expectMans: kopia.NewMockBackupBases().WithAssistBases(
makeMan(fbro, "fb_id1", "", path.EmailCategory),
),
},
{
name: "only fallbacks",
bf: &mockBackupFinder{
data: map[string]kopia.BackupBases{
fbro: kopia.NewMockBackupBases().WithMergeBases(
makeMan(fbro, "fb_id1", "", path.EmailCategory),
).WithBackups(
makeBackup(fbro, "fb_id1", path.EmailCategory),
),
},
},
rp: mockRestoreProducer{
collsByID: map[string][]data.RestoreCollection{
"fb_id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id1"}}},
},
},
fallbackReasons: []identity.Reasoner{fbEmailReason},
getMeta: true,
assertErr: assert.NoError,
assertB: assert.True,
expectDCS: []mockColl{{id: "fb_id1"}},
expectMans: kopia.NewMockBackupBases().WithMergeBases(
makeMan(fbro, "fb_id1", "", path.EmailCategory),
).WithBackups(
makeBackup(fbro, "fb_id1", path.EmailCategory),
),
},
{
name: "only fallbacks, no assist",
bf: &mockBackupFinder{
data: map[string]kopia.BackupBases{
fbro: kopia.NewMockBackupBases().WithMergeBases(
makeMan(fbro, "fb_id1", "", path.EmailCategory),
).WithBackups(
makeBackup(fbro, "fb_id1", path.EmailCategory),
),
},
},
rp: mockRestoreProducer{
collsByID: map[string][]data.RestoreCollection{
"fb_id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id1"}}},
},
},
fallbackReasons: []identity.Reasoner{fbEmailReason},
getMeta: true,
dropAssist: true,
assertErr: assert.NoError,
assertB: assert.True,
expectDCS: []mockColl{{id: "fb_id1"}},
expectMans: kopia.NewMockBackupBases().WithMergeBases(
makeMan(fbro, "fb_id1", "", path.EmailCategory),
).WithBackups(
makeBackup(fbro, "fb_id1", path.EmailCategory),
).
ClearMockAssistBases(),
},
{
name: "complete mans and fallbacks",
bf: &mockBackupFinder{
data: map[string]kopia.BackupBases{
ro: kopia.NewMockBackupBases().WithMergeBases(
makeMan(ro, "id1", "", path.EmailCategory),
),
fbro: kopia.NewMockBackupBases().WithMergeBases(
makeMan(fbro, "fb_id1", "", path.EmailCategory),
).WithBackups(
makeBackup(fbro, "fb_id1", path.EmailCategory),
),
},
},
rp: mockRestoreProducer{
collsByID: map[string][]data.RestoreCollection{
"id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "id1"}}},
"fb_id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id1"}}},
},
},
reasons: []identity.Reasoner{emailReason},
fallbackReasons: []identity.Reasoner{fbEmailReason},
getMeta: true,
assertErr: assert.NoError,
assertB: assert.True,
expectDCS: []mockColl{{id: "id1"}},
expectMans: kopia.NewMockBackupBases().WithMergeBases(
makeMan(ro, "id1", "", path.EmailCategory),
),
},
{
name: "incomplete mans and fallbacks",
bf: &mockBackupFinder{
data: map[string]kopia.BackupBases{
ro: kopia.NewMockBackupBases().WithAssistBases(
makeMan(ro, "id2", "checkpoint", path.EmailCategory),
),
fbro: kopia.NewMockBackupBases().WithAssistBases(
makeMan(fbro, "fb_id2", "checkpoint", path.EmailCategory),
),
},
},
rp: mockRestoreProducer{
collsByID: map[string][]data.RestoreCollection{
"id2": {data.NoFetchRestoreCollection{Collection: mockColl{id: "id2"}}},
"fb_id2": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id2"}}},
},
},
reasons: []identity.Reasoner{emailReason},
fallbackReasons: []identity.Reasoner{fbEmailReason},
getMeta: true,
assertErr: assert.NoError,
assertB: assert.True,
expectDCS: nil,
expectMans: kopia.NewMockBackupBases().WithAssistBases(
makeMan(ro, "id2", "checkpoint", path.EmailCategory),
),
},
{
name: "complete and incomplete mans and fallbacks",
bf: &mockBackupFinder{
data: map[string]kopia.BackupBases{
ro: kopia.NewMockBackupBases().WithMergeBases(
makeMan(ro, "id1", "", path.EmailCategory),
).WithAssistBases(
makeMan(ro, "id2", "checkpoint", path.EmailCategory),
),
fbro: kopia.NewMockBackupBases().WithMergeBases(
makeMan(fbro, "fb_id1", "", path.EmailCategory),
).WithBackups(
makeBackup(fbro, "fb_id1", path.EmailCategory),
).WithAssistBases(
makeMan(fbro, "fb_id2", "checkpoint", path.EmailCategory),
),
},
},
rp: mockRestoreProducer{
collsByID: map[string][]data.RestoreCollection{
"id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "id1"}}},
"id2": {data.NoFetchRestoreCollection{Collection: mockColl{id: "id2"}}},
"fb_id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id1"}}},
"fb_id2": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id2"}}},
},
},
reasons: []identity.Reasoner{emailReason},
fallbackReasons: []identity.Reasoner{fbEmailReason},
getMeta: true,
assertErr: assert.NoError,
assertB: assert.True,
expectDCS: []mockColl{{id: "id1"}},
expectMans: kopia.NewMockBackupBases().WithMergeBases(
makeMan(ro, "id1", "", path.EmailCategory),
).WithAssistBases(
makeMan(ro, "id2", "checkpoint", path.EmailCategory),
),
},
{
name: "incomplete mans and complete fallbacks",
bf: &mockBackupFinder{
data: map[string]kopia.BackupBases{
ro: kopia.NewMockBackupBases().WithAssistBases(
makeMan(ro, "id2", "checkpoint", path.EmailCategory),
),
fbro: kopia.NewMockBackupBases().WithMergeBases(
makeMan(fbro, "fb_id1", "", path.EmailCategory),
).WithBackups(
makeBackup(fbro, "fb_id1", path.EmailCategory),
),
},
},
rp: mockRestoreProducer{
collsByID: map[string][]data.RestoreCollection{
"id2": {data.NoFetchRestoreCollection{Collection: mockColl{id: "id2"}}},
"fb_id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id1"}}},
},
},
reasons: []identity.Reasoner{emailReason},
fallbackReasons: []identity.Reasoner{fbEmailReason},
getMeta: true,
assertErr: assert.NoError,
assertB: assert.True,
expectDCS: []mockColl{{id: "fb_id1"}},
expectMans: kopia.NewMockBackupBases().WithMergeBases(
makeMan(fbro, "fb_id1", "", path.EmailCategory),
).WithBackups(
makeBackup(fbro, "fb_id1", path.EmailCategory),
).WithAssistBases(
makeMan(ro, "id2", "checkpoint", path.EmailCategory),
),
},
{
name: "incomplete mans and complete fallbacks, no assist bases",
bf: &mockBackupFinder{
data: map[string]kopia.BackupBases{
ro: kopia.NewMockBackupBases().WithAssistBases(
makeMan(ro, "id2", "checkpoint", path.EmailCategory),
),
fbro: kopia.NewMockBackupBases().WithMergeBases(
makeMan(fbro, "fb_id1", "", path.EmailCategory),
).WithBackups(
makeBackup(fbro, "fb_id1", path.EmailCategory),
),
},
},
rp: mockRestoreProducer{
collsByID: map[string][]data.RestoreCollection{
"id2": {data.NoFetchRestoreCollection{Collection: mockColl{id: "id2"}}},
"fb_id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id1"}}},
},
},
reasons: []identity.Reasoner{emailReason},
fallbackReasons: []identity.Reasoner{fbEmailReason},
getMeta: true,
dropAssist: true,
assertErr: assert.NoError,
assertB: assert.True,
expectDCS: []mockColl{{id: "fb_id1"}},
expectMans: kopia.NewMockBackupBases().WithMergeBases(
makeMan(fbro, "fb_id1", "", path.EmailCategory),
).WithBackups(
makeBackup(fbro, "fb_id1", path.EmailCategory),
).
ClearMockAssistBases(),
},
{
name: "complete mans and incomplete fallbacks",
bf: &mockBackupFinder{
data: map[string]kopia.BackupBases{
ro: kopia.NewMockBackupBases().WithMergeBases(
makeMan(ro, "id1", "", path.EmailCategory),
),
fbro: kopia.NewMockBackupBases().WithAssistBases(
makeMan(fbro, "fb_id2", "checkpoint", path.EmailCategory),
),
},
},
rp: mockRestoreProducer{
collsByID: map[string][]data.RestoreCollection{
"id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "id1"}}},
"fb_id2": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id2"}}},
},
},
reasons: []identity.Reasoner{emailReason},
fallbackReasons: []identity.Reasoner{fbEmailReason},
getMeta: true,
assertErr: assert.NoError,
assertB: assert.True,
expectDCS: []mockColl{{id: "id1"}},
expectMans: kopia.NewMockBackupBases().WithMergeBases(
makeMan(ro, "id1", "", path.EmailCategory),
),
},
{
name: "complete mans and complete fallbacks, multiple reasons",
bf: &mockBackupFinder{
data: map[string]kopia.BackupBases{
ro: kopia.NewMockBackupBases().WithMergeBases(
makeMan(ro, "id1", "", path.EmailCategory, path.ContactsCategory),
),
fbro: kopia.NewMockBackupBases().WithMergeBases(
makeMan(fbro, "fb_id1", "", path.EmailCategory, path.ContactsCategory),
).WithBackups(
makeBackup(fbro, "fb_id1", path.EmailCategory, path.ContactsCategory),
),
},
},
rp: mockRestoreProducer{
collsByID: map[string][]data.RestoreCollection{
"id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "id1"}}},
"fb_id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id1"}}},
},
},
reasons: []identity.Reasoner{
emailReason,
kopia.NewReason("", ro, path.ExchangeService, path.ContactsCategory),
},
fallbackReasons: []identity.Reasoner{
fbEmailReason,
kopia.NewReason("", fbro, path.ExchangeService, path.ContactsCategory),
},
getMeta: true,
assertErr: assert.NoError,
assertB: assert.True,
expectDCS: []mockColl{{id: "id1"}},
expectMans: kopia.NewMockBackupBases().WithMergeBases(
makeMan(ro, "id1", "", path.EmailCategory, path.ContactsCategory),
),
},
{
name: "complete mans and complete fallbacks, distinct reasons",
bf: &mockBackupFinder{
data: map[string]kopia.BackupBases{
ro: kopia.NewMockBackupBases().WithMergeBases(
makeMan(ro, "id1", "", path.EmailCategory),
),
fbro: kopia.NewMockBackupBases().WithMergeBases(
makeMan(fbro, "fb_id1", "", path.ContactsCategory),
).WithBackups(
makeBackup(fbro, "fb_id1", path.ContactsCategory),
),
},
},
rp: mockRestoreProducer{
collsByID: map[string][]data.RestoreCollection{
"id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "id1"}}},
"fb_id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id1"}}},
},
},
reasons: []identity.Reasoner{emailReason},
fallbackReasons: []identity.Reasoner{
kopia.NewReason("", fbro, path.ExchangeService, path.ContactsCategory),
},
getMeta: true,
assertErr: assert.NoError,
assertB: assert.True,
expectDCS: []mockColl{{id: "id1"}, {id: "fb_id1"}},
expectMans: kopia.NewMockBackupBases().WithMergeBases(
makeMan(ro, "id1", "", path.EmailCategory),
makeMan(fbro, "fb_id1", "", path.ContactsCategory),
).WithBackups(
makeBackup(fbro, "fb_id1", path.ContactsCategory),
),
},
{
name: "complete mans and complete fallbacks, fallback has superset of reasons",
bf: &mockBackupFinder{
data: map[string]kopia.BackupBases{
ro: kopia.NewMockBackupBases().WithMergeBases(
makeMan(ro, "id1", "", path.EmailCategory),
),
fbro: kopia.NewMockBackupBases().WithMergeBases(
makeMan(fbro, "fb_id1", "", path.EmailCategory, path.ContactsCategory),
).WithBackups(
makeBackup(fbro, "fb_id1", path.EmailCategory, path.ContactsCategory),
),
},
},
rp: mockRestoreProducer{
collsByID: map[string][]data.RestoreCollection{
"id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "id1"}}},
"fb_id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id1"}}},
},
},
reasons: []identity.Reasoner{
emailReason,
kopia.NewReason("", ro, path.ExchangeService, path.ContactsCategory),
},
fallbackReasons: []identity.Reasoner{
fbEmailReason,
kopia.NewReason("", fbro, path.ExchangeService, path.ContactsCategory),
},
getMeta: true,
assertErr: assert.NoError,
assertB: assert.True,
expectDCS: []mockColl{{id: "id1"}, {id: "fb_id1"}},
expectMans: kopia.NewMockBackupBases().WithMergeBases(
makeMan(ro, "id1", "", path.EmailCategory),
makeMan(fbro, "fb_id1", "", path.ContactsCategory),
).WithBackups(
makeBackup(fbro, "fb_id1", path.ContactsCategory),
),
},
}
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
mans, dcs, b, err := produceManifestsAndMetadata(
ctx,
test.bf,
&test.rp,
test.reasons, test.fallbackReasons,
tid,
test.getMeta,
test.dropAssist)
test.assertErr(t, err, clues.ToCore(err))
test.assertB(t, b)
kopia.AssertBackupBasesEqual(t, test.expectMans, mans)
expect, got := []string{}, []string{}
for _, dc := range test.expectDCS {
expect = append(expect, dc.id)
}
for _, dc := range dcs {
if !assert.IsTypef(
t,
data.NoFetchRestoreCollection{},
dc,
"unexpected type returned [%T]",
dc,
) {
continue
}
tmp := dc.(data.NoFetchRestoreCollection)
if !assert.IsTypef(
t,
mockColl{},
tmp.Collection,
"unexpected type returned [%T]",
tmp.Collection,
) {
continue
}
mc := tmp.Collection.(mockColl)
got = append(got, mc.id)
}
assert.ElementsMatch(t, expect, got, "expected collections are present")
})
}
}