Create an interface and implementation for Reason struct (#3868)

Intermediate step to a few different goals including
* moving interface definitions to better locations while avoid cycles
* adding a flag to disable kopia-assisted incrementals

Create an interface and implementation for the existing Reason
struct. The goal is to set stuff up so that eventually the kopia
package can ask the struct for the subtree path to work with
when merging the hierarchy instead of having the backup operation
pass that information in

Code changes are mostly just turning stuff into a struct and
fixing up compile errors. Some functions have been excluded from
the struct (i.e. `Key`) and made into functions in the kopia
package itself

---

#### 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

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

#### Issue(s)

* #2360

#### Test Plan

- [ ] 💪 Manual
- [x]  Unit test
- [x] 💚 E2E
This commit is contained in:
ashmrtn 2023-07-21 14:05:38 -07:00 committed by GitHub
parent 9bcf160187
commit 916bb0b27c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 343 additions and 492 deletions

View File

@ -24,7 +24,7 @@ type BackupBases interface {
MergeBackupBases( MergeBackupBases(
ctx context.Context, ctx context.Context,
other BackupBases, other BackupBases,
reasonToKey func(Reason) string, reasonToKey func(Reasoner) string,
) BackupBases ) BackupBases
} }
@ -109,10 +109,10 @@ func (bb *backupBases) ClearAssistBases() {
// some migration that disrupts lookup), and that the BackupBases used to call // some migration that disrupts lookup), and that the BackupBases used to call
// this function contains the current version. // this function contains the current version.
// //
// reasonToKey should be a function that, given a Reason, will produce some // reasonToKey should be a function that, given a Reasoner, will produce some
// string that represents Reason in the context of the merge operation. For // string that represents Reasoner in the context of the merge operation. For
// example, to merge BackupBases across a ResourceOwner migration, the Reason's // example, to merge BackupBases across a ProtectedResource migration, the
// service and category can be used as the key. // Reasoner's service and category can be used as the key.
// //
// Selection priority, for each reason key generated by reasonsToKey, follows // Selection priority, for each reason key generated by reasonsToKey, follows
// these rules: // these rules:
@ -125,7 +125,7 @@ func (bb *backupBases) ClearAssistBases() {
func (bb *backupBases) MergeBackupBases( func (bb *backupBases) MergeBackupBases(
ctx context.Context, ctx context.Context,
other BackupBases, other BackupBases,
reasonToKey func(reason Reason) string, reasonToKey func(reason Reasoner) string,
) BackupBases { ) BackupBases {
if other == nil || (len(other.MergeBases()) == 0 && len(other.AssistBases()) == 0) { if other == nil || (len(other.MergeBases()) == 0 && len(other.AssistBases()) == 0) {
return bb return bb
@ -159,7 +159,7 @@ func (bb *backupBases) MergeBackupBases(
// Calculate the set of mergeBases to pull from other into this one. // Calculate the set of mergeBases to pull from other into this one.
for _, m := range other.MergeBases() { for _, m := range other.MergeBases() {
useReasons := []Reason{} useReasons := []Reasoner{}
for _, r := range m.Reasons { for _, r := range m.Reasons {
k := reasonToKey(r) k := reasonToKey(r)
@ -210,7 +210,7 @@ func (bb *backupBases) MergeBackupBases(
// Add assistBases from other to this one as needed. // Add assistBases from other to this one as needed.
for _, m := range other.AssistBases() { for _, m := range other.AssistBases() {
useReasons := []Reason{} useReasons := []Reasoner{}
// Assume that all complete manifests in assist overlap with MergeBases. // Assume that all complete manifests in assist overlap with MergeBases.
if len(m.IncompleteReason) == 0 { if len(m.IncompleteReason) == 0 {
@ -267,8 +267,8 @@ func findNonUniqueManifests(
} }
for _, reason := range man.Reasons { for _, reason := range man.Reasons {
reasonKey := reason.ResourceOwner + reason.Service.String() + reason.Category.String() mapKey := reasonKey(reason)
reasons[reasonKey] = append(reasons[reasonKey], man) reasons[mapKey] = append(reasons[mapKey], man)
} }
} }

View File

@ -16,7 +16,7 @@ import (
"github.com/alcionai/corso/src/pkg/path" "github.com/alcionai/corso/src/pkg/path"
) )
func makeManifest(id, incmpl, bID string, reasons ...Reason) ManifestEntry { func makeManifest(id, incmpl, bID string, reasons ...Reasoner) ManifestEntry {
bIDKey, _ := makeTagKV(TagBackupID) bIDKey, _ := makeTagKV(TagBackupID)
return ManifestEntry{ return ManifestEntry{
@ -223,14 +223,10 @@ func (suite *BackupBasesUnitSuite) TestMergeBackupBases() {
ir = "checkpoint" ir = "checkpoint"
} }
reasons := make([]Reason, 0, len(i.cat)) reasons := make([]Reasoner, 0, len(i.cat))
for _, c := range i.cat { for _, c := range i.cat {
reasons = append(reasons, Reason{ reasons = append(reasons, NewReason("", ro, path.ExchangeService, c))
ResourceOwner: ro,
Service: path.ExchangeService,
Category: c,
})
} }
m := makeManifest(baseID, ir, "b"+baseID, reasons...) m := makeManifest(baseID, ir, "b"+baseID, reasons...)
@ -457,8 +453,8 @@ func (suite *BackupBasesUnitSuite) TestMergeBackupBases() {
got := bb.MergeBackupBases( got := bb.MergeBackupBases(
ctx, ctx,
other, other,
func(reason Reason) string { func(r Reasoner) string {
return reason.Service.String() + reason.Category.String() return r.Service().String() + r.Category().String()
}) })
AssertBackupBasesEqual(t, expect, got) AssertBackupBasesEqual(t, expect, got)
}) })
@ -469,13 +465,8 @@ func (suite *BackupBasesUnitSuite) TestFixupAndVerify() {
ro := "resource_owner" ro := "resource_owner"
makeMan := func(pct path.CategoryType, id, incmpl, bID string) ManifestEntry { makeMan := func(pct path.CategoryType, id, incmpl, bID string) ManifestEntry {
reason := Reason{ r := NewReason("", ro, path.ExchangeService, pct)
ResourceOwner: ro, return makeManifest(id, incmpl, bID, r)
Service: path.ExchangeService,
Category: pct,
}
return makeManifest(id, incmpl, bID, reason)
} }
// Make a function so tests can modify things without messing with each other. // Make a function so tests can modify things without messing with each other.
@ -606,11 +597,7 @@ func (suite *BackupBasesUnitSuite) TestFixupAndVerify() {
res := validMail1() res := validMail1()
res.mergeBases[0].Reasons = append( res.mergeBases[0].Reasons = append(
res.mergeBases[0].Reasons, res.mergeBases[0].Reasons,
Reason{ NewReason("", ro, path.ExchangeService, path.ContactsCategory))
ResourceOwner: ro,
Service: path.ExchangeService,
Category: path.ContactsCategory,
})
res.assistBases = res.mergeBases res.assistBases = res.mergeBases
return res return res
@ -619,11 +606,7 @@ func (suite *BackupBasesUnitSuite) TestFixupAndVerify() {
res := validMail1() res := validMail1()
res.mergeBases[0].Reasons = append( res.mergeBases[0].Reasons = append(
res.mergeBases[0].Reasons, res.mergeBases[0].Reasons,
Reason{ NewReason("", ro, path.ExchangeService, path.ContactsCategory))
ResourceOwner: ro,
Service: path.ExchangeService,
Category: path.ContactsCategory,
})
res.assistBases = res.mergeBases res.assistBases = res.mergeBases
return res return res

View File

@ -29,39 +29,98 @@ const (
userTagPrefix = "tag:" userTagPrefix = "tag:"
) )
type Reason struct { // TODO(ashmrtn): Move this into some inject package. Here to avoid import
ResourceOwner string // cycles.
Service path.ServiceType type Reasoner interface {
Category path.CategoryType Tenant() string
ProtectedResource() string
Service() path.ServiceType
Category() path.CategoryType
// SubtreePath returns the path prefix for data in existing backups that have
// parameters (tenant, protected resourced, etc) that match this Reasoner.
SubtreePath() (path.Path, error)
// TODO(ashmrtn): Remove this when kopia generates tags from Reasons.
TagKeys() []string
} }
func (r Reason) TagKeys() []string { func NewReason(
return []string{ tenant, resource string,
r.ResourceOwner, service path.ServiceType,
serviceCatString(r.Service, r.Category), category path.CategoryType,
) Reasoner {
return reason{
tenant: tenant,
resource: resource,
service: service,
category: category,
} }
} }
// Key is the concatenation of the ResourceOwner, Service, and Category. type reason struct {
func (r Reason) Key() string { // tenant appears here so that when this is moved to an inject package nothing
return r.ResourceOwner + r.Service.String() + r.Category.String() // needs changed. However, kopia itself is blind to the fields in the reason
// struct and relies on helper functions to get the information it needs.
tenant string
resource string
service path.ServiceType
category path.CategoryType
}
func (r reason) Tenant() string {
return r.tenant
}
func (r reason) ProtectedResource() string {
return r.resource
}
func (r reason) Service() path.ServiceType {
return r.service
}
func (r reason) Category() path.CategoryType {
return r.category
}
func (r reason) SubtreePath() (path.Path, error) {
p, err := path.ServicePrefix(
r.Tenant(),
r.ProtectedResource(),
r.Service(),
r.Category())
return p, clues.Wrap(err, "building path").OrNil()
}
// TODO(ashmrtn): Remove this when kopia generates tags based off Reasons. Here
// at the moment so things compile.
func (r reason) TagKeys() []string {
return []string{
r.ProtectedResource(),
serviceCatString(r.Service(), r.Category()),
}
}
// reasonKey returns the concatenation of the ProtectedResource, Service, and Category.
func reasonKey(r Reasoner) string {
return r.ProtectedResource() + r.Service().String() + r.Category().String()
} }
type BackupEntry struct { type BackupEntry struct {
*backup.Backup *backup.Backup
Reasons []Reason Reasons []Reasoner
} }
type ManifestEntry struct { type ManifestEntry struct {
*snapshot.Manifest *snapshot.Manifest
// Reason contains the ResourceOwners and Service/Categories that caused this // Reasons contains the ResourceOwners and Service/Categories that caused this
// snapshot to be selected as a base. We can't reuse OwnersCats here because // snapshot to be selected as a base. We can't reuse OwnersCats here because
// it's possible some ResourceOwners will have a subset of the Categories as // it's possible some ResourceOwners will have a subset of the Categories as
// the reason for selecting a snapshot. For example: // the reason for selecting a snapshot. For example:
// 1. backup user1 email,contacts -> B1 // 1. backup user1 email,contacts -> B1
// 2. backup user1 contacts -> B2 (uses B1 as base) // 2. backup user1 contacts -> B2 (uses B1 as base)
// 3. backup user1 email,contacts,events (uses B1 for email, B2 for contacts) // 3. backup user1 email,contacts,events (uses B1 for email, B2 for contacts)
Reasons []Reason Reasons []Reasoner
} }
func (me ManifestEntry) GetTag(key string) (string, bool) { func (me ManifestEntry) GetTag(key string) (string, bool) {
@ -157,7 +216,7 @@ func (b *baseFinder) getBackupModel(
// most recent complete backup as the base. // most recent complete backup as the base.
func (b *baseFinder) findBasesInSet( func (b *baseFinder) findBasesInSet(
ctx context.Context, ctx context.Context,
reason Reason, reason Reasoner,
metas []*manifest.EntryMetadata, metas []*manifest.EntryMetadata,
) (*BackupEntry, *ManifestEntry, []ManifestEntry, error) { ) (*BackupEntry, *ManifestEntry, []ManifestEntry, error) {
// Sort manifests by time so we can go through them sequentially. The code in // Sort manifests by time so we can go through them sequentially. The code in
@ -190,7 +249,7 @@ func (b *baseFinder) findBasesInSet(
kopiaAssistSnaps = append(kopiaAssistSnaps, ManifestEntry{ kopiaAssistSnaps = append(kopiaAssistSnaps, ManifestEntry{
Manifest: man, Manifest: man,
Reasons: []Reason{reason}, Reasons: []Reasoner{reason},
}) })
logger.Ctx(ictx).Info("found incomplete backup") logger.Ctx(ictx).Info("found incomplete backup")
@ -211,7 +270,7 @@ func (b *baseFinder) findBasesInSet(
kopiaAssistSnaps = append(kopiaAssistSnaps, ManifestEntry{ kopiaAssistSnaps = append(kopiaAssistSnaps, ManifestEntry{
Manifest: man, Manifest: man,
Reasons: []Reason{reason}, Reasons: []Reasoner{reason},
}) })
logger.Ctx(ictx).Info("found incomplete backup") logger.Ctx(ictx).Info("found incomplete backup")
@ -235,7 +294,7 @@ func (b *baseFinder) findBasesInSet(
kopiaAssistSnaps = append(kopiaAssistSnaps, ManifestEntry{ kopiaAssistSnaps = append(kopiaAssistSnaps, ManifestEntry{
Manifest: man, Manifest: man,
Reasons: []Reason{reason}, Reasons: []Reasoner{reason},
}) })
logger.Ctx(ictx).Infow( logger.Ctx(ictx).Infow(
@ -253,13 +312,13 @@ func (b *baseFinder) findBasesInSet(
me := ManifestEntry{ me := ManifestEntry{
Manifest: man, Manifest: man,
Reasons: []Reason{reason}, Reasons: []Reasoner{reason},
} }
kopiaAssistSnaps = append(kopiaAssistSnaps, me) kopiaAssistSnaps = append(kopiaAssistSnaps, me)
return &BackupEntry{ return &BackupEntry{
Backup: bup, Backup: bup,
Reasons: []Reason{reason}, Reasons: []Reasoner{reason},
}, &me, kopiaAssistSnaps, nil }, &me, kopiaAssistSnaps, nil
} }
@ -270,12 +329,12 @@ func (b *baseFinder) findBasesInSet(
func (b *baseFinder) getBase( func (b *baseFinder) getBase(
ctx context.Context, ctx context.Context,
reason Reason, r Reasoner,
tags map[string]string, tags map[string]string,
) (*BackupEntry, *ManifestEntry, []ManifestEntry, error) { ) (*BackupEntry, *ManifestEntry, []ManifestEntry, error) {
allTags := map[string]string{} allTags := map[string]string{}
for _, k := range reason.TagKeys() { for _, k := range r.TagKeys() {
allTags[k] = "" allTags[k] = ""
} }
@ -292,12 +351,12 @@ func (b *baseFinder) getBase(
return nil, nil, nil, nil return nil, nil, nil, nil
} }
return b.findBasesInSet(ctx, reason, metas) return b.findBasesInSet(ctx, r, metas)
} }
func (b *baseFinder) FindBases( func (b *baseFinder) FindBases(
ctx context.Context, ctx context.Context,
reasons []Reason, reasons []Reasoner,
tags map[string]string, tags map[string]string,
) BackupBases { ) BackupBases {
var ( var (
@ -310,14 +369,14 @@ func (b *baseFinder) FindBases(
kopiaAssistSnaps = map[manifest.ID]ManifestEntry{} kopiaAssistSnaps = map[manifest.ID]ManifestEntry{}
) )
for _, reason := range reasons { for _, searchReason := range reasons {
ictx := clues.Add( ictx := clues.Add(
ctx, ctx,
"search_service", reason.Service.String(), "search_service", searchReason.Service().String(),
"search_category", reason.Category.String()) "search_category", searchReason.Category().String())
logger.Ctx(ictx).Info("searching for previous manifests") logger.Ctx(ictx).Info("searching for previous manifests")
baseBackup, baseSnap, assistSnaps, err := b.getBase(ictx, reason, tags) baseBackup, baseSnap, assistSnaps, err := b.getBase(ictx, searchReason, tags)
if err != nil { if err != nil {
logger.Ctx(ctx).Info( logger.Ctx(ctx).Info(
"getting base, falling back to full backup for reason", "getting base, falling back to full backup for reason",

View File

@ -39,61 +39,24 @@ var (
testUser2 = "user2" testUser2 = "user2"
testUser3 = "user3" testUser3 = "user3"
testAllUsersAllCats = []Reason{ testAllUsersAllCats = []Reasoner{
{ // User1 email and events.
ResourceOwner: testUser1, NewReason("", testUser1, path.ExchangeService, path.EmailCategory),
Service: path.ExchangeService, NewReason("", testUser1, path.ExchangeService, path.EventsCategory),
Category: path.EmailCategory, // User2 email and events.
}, NewReason("", testUser2, path.ExchangeService, path.EmailCategory),
{ NewReason("", testUser2, path.ExchangeService, path.EventsCategory),
ResourceOwner: testUser1, // User3 email and events.
Service: path.ExchangeService, NewReason("", testUser3, path.ExchangeService, path.EmailCategory),
Category: path.EventsCategory, NewReason("", testUser3, path.ExchangeService, path.EventsCategory),
},
{
ResourceOwner: testUser2,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
{
ResourceOwner: testUser2,
Service: path.ExchangeService,
Category: path.EventsCategory,
},
{
ResourceOwner: testUser3,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
{
ResourceOwner: testUser3,
Service: path.ExchangeService,
Category: path.EventsCategory,
},
} }
testAllUsersMail = []Reason{ testAllUsersMail = []Reasoner{
{ NewReason("", testUser1, path.ExchangeService, path.EmailCategory),
ResourceOwner: testUser1, NewReason("", testUser2, path.ExchangeService, path.EmailCategory),
Service: path.ExchangeService, NewReason("", testUser3, path.ExchangeService, path.EmailCategory),
Category: path.EmailCategory,
},
{
ResourceOwner: testUser2,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
{
ResourceOwner: testUser3,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
} }
testUser1Mail = []Reason{ testUser1Mail = []Reasoner{
{ NewReason("", testUser1, path.ExchangeService, path.EmailCategory),
ResourceOwner: testUser1,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
} }
) )
@ -322,12 +285,8 @@ func (suite *BaseFinderUnitSuite) TestNoResult_NoBackupsOrSnapshots() {
sm: mockEmptySnapshotManager{}, sm: mockEmptySnapshotManager{},
bg: mockEmptyModelGetter{}, bg: mockEmptyModelGetter{},
} }
reasons := []Reason{ reasons := []Reasoner{
{ NewReason("", "a-user", path.ExchangeService, path.EmailCategory),
ResourceOwner: "a-user",
Service: path.ExchangeService,
Category: path.EmailCategory,
},
} }
bb := bf.FindBases(ctx, reasons, nil) bb := bf.FindBases(ctx, reasons, nil)
@ -345,12 +304,8 @@ func (suite *BaseFinderUnitSuite) TestNoResult_ErrorListingSnapshots() {
sm: &mockSnapshotManager{findErr: assert.AnError}, sm: &mockSnapshotManager{findErr: assert.AnError},
bg: mockEmptyModelGetter{}, bg: mockEmptyModelGetter{},
} }
reasons := []Reason{ reasons := []Reasoner{
{ NewReason("", "a-user", path.ExchangeService, path.EmailCategory),
ResourceOwner: "a-user",
Service: path.ExchangeService,
Category: path.EmailCategory,
},
} }
bb := bf.FindBases(ctx, reasons, nil) bb := bf.FindBases(ctx, reasons, nil)
@ -361,14 +316,14 @@ func (suite *BaseFinderUnitSuite) TestNoResult_ErrorListingSnapshots() {
func (suite *BaseFinderUnitSuite) TestGetBases() { func (suite *BaseFinderUnitSuite) TestGetBases() {
table := []struct { table := []struct {
name string name string
input []Reason input []Reasoner
manifestData []manifestInfo manifestData []manifestInfo
// Use this to denote the Reasons a base backup or base manifest is // Use this to denote the Reasons a base backup or base manifest is
// selected. The int maps to the index of the backup or manifest in data. // selected. The int maps to the index of the backup or manifest in data.
expectedBaseReasons map[int][]Reason expectedBaseReasons map[int][]Reasoner
// Use this to denote the Reasons a kopia assised incrementals manifest is // Use this to denote the Reasons a kopia assised incrementals manifest is
// selected. The int maps to the index of the manifest in data. // selected. The int maps to the index of the manifest in data.
expectedAssistManifestReasons map[int][]Reason expectedAssistManifestReasons map[int][]Reasoner
backupData []backupInfo backupData []backupInfo
}{ }{
{ {
@ -394,10 +349,10 @@ func (suite *BaseFinderUnitSuite) TestGetBases() {
testUser1, testUser1,
), ),
}, },
expectedBaseReasons: map[int][]Reason{ expectedBaseReasons: map[int][]Reasoner{
1: testUser1Mail, 1: testUser1Mail,
}, },
expectedAssistManifestReasons: map[int][]Reason{ expectedAssistManifestReasons: map[int][]Reasoner{
1: testUser1Mail, 1: testUser1Mail,
}, },
backupData: []backupInfo{ backupData: []backupInfo{
@ -428,10 +383,10 @@ func (suite *BaseFinderUnitSuite) TestGetBases() {
testUser1, testUser1,
), ),
}, },
expectedBaseReasons: map[int][]Reason{ expectedBaseReasons: map[int][]Reasoner{
1: testUser1Mail, 1: testUser1Mail,
}, },
expectedAssistManifestReasons: map[int][]Reason{ expectedAssistManifestReasons: map[int][]Reasoner{
0: testUser1Mail, 0: testUser1Mail,
1: testUser1Mail, 1: testUser1Mail,
}, },
@ -463,10 +418,10 @@ func (suite *BaseFinderUnitSuite) TestGetBases() {
testUser1, testUser1,
), ),
}, },
expectedBaseReasons: map[int][]Reason{ expectedBaseReasons: map[int][]Reasoner{
1: testUser1Mail, 1: testUser1Mail,
}, },
expectedAssistManifestReasons: map[int][]Reason{ expectedAssistManifestReasons: map[int][]Reasoner{
0: testUser1Mail, 0: testUser1Mail,
1: testUser1Mail, 1: testUser1Mail,
}, },
@ -492,10 +447,10 @@ func (suite *BaseFinderUnitSuite) TestGetBases() {
testUser3, testUser3,
), ),
}, },
expectedBaseReasons: map[int][]Reason{ expectedBaseReasons: map[int][]Reasoner{
0: testUser1Mail, 0: testUser1Mail,
}, },
expectedAssistManifestReasons: map[int][]Reason{ expectedAssistManifestReasons: map[int][]Reasoner{
0: testUser1Mail, 0: testUser1Mail,
}, },
backupData: []backupInfo{ backupData: []backupInfo{
@ -519,10 +474,10 @@ func (suite *BaseFinderUnitSuite) TestGetBases() {
testUser3, testUser3,
), ),
}, },
expectedBaseReasons: map[int][]Reason{ expectedBaseReasons: map[int][]Reasoner{
0: testAllUsersAllCats, 0: testAllUsersAllCats,
}, },
expectedAssistManifestReasons: map[int][]Reason{ expectedAssistManifestReasons: map[int][]Reasoner{
0: testAllUsersAllCats, 0: testAllUsersAllCats,
}, },
backupData: []backupInfo{ backupData: []backupInfo{
@ -557,76 +512,28 @@ func (suite *BaseFinderUnitSuite) TestGetBases() {
testUser3, testUser3,
), ),
}, },
expectedBaseReasons: map[int][]Reason{ expectedBaseReasons: map[int][]Reasoner{
0: { 0: {
{ NewReason("", testUser1, path.ExchangeService, path.EmailCategory),
ResourceOwner: testUser1, NewReason("", testUser2, path.ExchangeService, path.EmailCategory),
Service: path.ExchangeService, NewReason("", testUser3, path.ExchangeService, path.EmailCategory),
Category: path.EmailCategory,
},
{
ResourceOwner: testUser2,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
{
ResourceOwner: testUser3,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
}, },
1: { 1: {
Reason{ NewReason("", testUser1, path.ExchangeService, path.EventsCategory),
ResourceOwner: testUser1, NewReason("", testUser2, path.ExchangeService, path.EventsCategory),
Service: path.ExchangeService, NewReason("", testUser3, path.ExchangeService, path.EventsCategory),
Category: path.EventsCategory,
},
Reason{
ResourceOwner: testUser2,
Service: path.ExchangeService,
Category: path.EventsCategory,
},
Reason{
ResourceOwner: testUser3,
Service: path.ExchangeService,
Category: path.EventsCategory,
}, },
}, },
}, expectedAssistManifestReasons: map[int][]Reasoner{
expectedAssistManifestReasons: map[int][]Reason{
0: { 0: {
{ NewReason("", testUser1, path.ExchangeService, path.EmailCategory),
ResourceOwner: testUser1, NewReason("", testUser2, path.ExchangeService, path.EmailCategory),
Service: path.ExchangeService, NewReason("", testUser3, path.ExchangeService, path.EmailCategory),
Category: path.EmailCategory,
},
{
ResourceOwner: testUser2,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
{
ResourceOwner: testUser3,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
}, },
1: { 1: {
Reason{ NewReason("", testUser1, path.ExchangeService, path.EventsCategory),
ResourceOwner: testUser1, NewReason("", testUser2, path.ExchangeService, path.EventsCategory),
Service: path.ExchangeService, NewReason("", testUser3, path.ExchangeService, path.EventsCategory),
Category: path.EventsCategory,
},
Reason{
ResourceOwner: testUser2,
Service: path.ExchangeService,
Category: path.EventsCategory,
},
Reason{
ResourceOwner: testUser3,
Service: path.ExchangeService,
Category: path.EventsCategory,
},
}, },
}, },
backupData: []backupInfo{ backupData: []backupInfo{
@ -657,10 +564,10 @@ func (suite *BaseFinderUnitSuite) TestGetBases() {
testUser1, testUser1,
), ),
}, },
expectedBaseReasons: map[int][]Reason{ expectedBaseReasons: map[int][]Reasoner{
0: testUser1Mail, 0: testUser1Mail,
}, },
expectedAssistManifestReasons: map[int][]Reason{ expectedAssistManifestReasons: map[int][]Reasoner{
0: testUser1Mail, 0: testUser1Mail,
1: testUser1Mail, 1: testUser1Mail,
}, },
@ -693,10 +600,10 @@ func (suite *BaseFinderUnitSuite) TestGetBases() {
testUser1, testUser1,
), ),
}, },
expectedBaseReasons: map[int][]Reason{ expectedBaseReasons: map[int][]Reasoner{
1: testUser1Mail, 1: testUser1Mail,
}, },
expectedAssistManifestReasons: map[int][]Reason{ expectedAssistManifestReasons: map[int][]Reasoner{
1: testUser1Mail, 1: testUser1Mail,
}, },
backupData: []backupInfo{ backupData: []backupInfo{
@ -728,8 +635,8 @@ func (suite *BaseFinderUnitSuite) TestGetBases() {
testUser1, testUser1,
), ),
}, },
expectedBaseReasons: map[int][]Reason{}, expectedBaseReasons: map[int][]Reasoner{},
expectedAssistManifestReasons: map[int][]Reason{ expectedAssistManifestReasons: map[int][]Reasoner{
1: testUser1Mail, 1: testUser1Mail,
}, },
backupData: []backupInfo{ backupData: []backupInfo{
@ -752,10 +659,10 @@ func (suite *BaseFinderUnitSuite) TestGetBases() {
testUser1, testUser1,
), ),
}, },
expectedBaseReasons: map[int][]Reason{ expectedBaseReasons: map[int][]Reasoner{
0: testUser1Mail, 0: testUser1Mail,
}, },
expectedAssistManifestReasons: map[int][]Reason{ expectedAssistManifestReasons: map[int][]Reasoner{
0: testUser1Mail, 0: testUser1Mail,
}, },
backupData: []backupInfo{ backupData: []backupInfo{
@ -787,10 +694,10 @@ func (suite *BaseFinderUnitSuite) TestGetBases() {
testUser1, testUser1,
), ),
}, },
expectedBaseReasons: map[int][]Reason{ expectedBaseReasons: map[int][]Reasoner{
0: testUser1Mail, 0: testUser1Mail,
}, },
expectedAssistManifestReasons: map[int][]Reason{ expectedAssistManifestReasons: map[int][]Reasoner{
0: testUser1Mail, 0: testUser1Mail,
}, },
backupData: []backupInfo{ backupData: []backupInfo{
@ -857,17 +764,17 @@ func (suite *BaseFinderUnitSuite) TestFindBases_CustomTags() {
table := []struct { table := []struct {
name string name string
input []Reason input []Reasoner
tags map[string]string tags map[string]string
// Use this to denote which manifests in data should be expected. Allows // Use this to denote which manifests in data should be expected. Allows
// defining data in a table while not repeating things between data and // defining data in a table while not repeating things between data and
// expected. // expected.
expectedIdxs map[int][]Reason expectedIdxs map[int][]Reasoner
}{ }{
{ {
name: "no tags specified", name: "no tags specified",
tags: nil, tags: nil,
expectedIdxs: map[int][]Reason{ expectedIdxs: map[int][]Reasoner{
0: testUser1Mail, 0: testUser1Mail,
}, },
}, },
@ -877,14 +784,14 @@ func (suite *BaseFinderUnitSuite) TestFindBases_CustomTags() {
"fnords": "", "fnords": "",
"smarf": "", "smarf": "",
}, },
expectedIdxs: map[int][]Reason{ expectedIdxs: map[int][]Reasoner{
0: testUser1Mail, 0: testUser1Mail,
}, },
}, },
{ {
name: "subset of custom tags", name: "subset of custom tags",
tags: map[string]string{"fnords": ""}, tags: map[string]string{"fnords": ""},
expectedIdxs: map[int][]Reason{ expectedIdxs: map[int][]Reasoner{
0: testUser1Mail, 0: testUser1Mail,
}, },
}, },
@ -925,7 +832,7 @@ func checkManifestEntriesMatch(
t *testing.T, t *testing.T,
retSnaps []ManifestEntry, retSnaps []ManifestEntry,
allExpected []manifestInfo, allExpected []manifestInfo,
expectedIdxsAndReasons map[int][]Reason, expectedIdxsAndReasons map[int][]Reasoner,
) { ) {
// Check the proper snapshot manifests were returned. // Check the proper snapshot manifests were returned.
expected := make([]*snapshot.Manifest, 0, len(expectedIdxsAndReasons)) expected := make([]*snapshot.Manifest, 0, len(expectedIdxsAndReasons))
@ -941,7 +848,7 @@ func checkManifestEntriesMatch(
assert.ElementsMatch(t, expected, got) assert.ElementsMatch(t, expected, got)
// Check the reasons for selecting each manifest are correct. // Check the reasons for selecting each manifest are correct.
expectedReasons := make(map[manifest.ID][]Reason, len(expectedIdxsAndReasons)) expectedReasons := make(map[manifest.ID][]Reasoner, len(expectedIdxsAndReasons))
for idx, reasons := range expectedIdxsAndReasons { for idx, reasons := range expectedIdxsAndReasons {
expectedReasons[allExpected[idx].man.ID] = reasons expectedReasons[allExpected[idx].man.ID] = reasons
} }
@ -967,7 +874,7 @@ func checkBackupEntriesMatch(
t *testing.T, t *testing.T,
retBups []BackupEntry, retBups []BackupEntry,
allExpected []backupInfo, allExpected []backupInfo,
expectedIdxsAndReasons map[int][]Reason, expectedIdxsAndReasons map[int][]Reasoner,
) { ) {
// Check the proper snapshot manifests were returned. // Check the proper snapshot manifests were returned.
expected := make([]*backup.Backup, 0, len(expectedIdxsAndReasons)) expected := make([]*backup.Backup, 0, len(expectedIdxsAndReasons))
@ -983,7 +890,7 @@ func checkBackupEntriesMatch(
assert.ElementsMatch(t, expected, got) assert.ElementsMatch(t, expected, got)
// Check the reasons for selecting each manifest are correct. // Check the reasons for selecting each manifest are correct.
expectedReasons := make(map[model.StableID][]Reason, len(expectedIdxsAndReasons)) expectedReasons := make(map[model.StableID][]Reasoner, len(expectedIdxsAndReasons))
for idx, reasons := range expectedIdxsAndReasons { for idx, reasons := range expectedIdxsAndReasons {
expectedReasons[allExpected[idx].b.ID] = reasons expectedReasons[allExpected[idx].b.ID] = reasons
} }

View File

@ -37,7 +37,7 @@ type (
BaseFinder interface { BaseFinder interface {
FindBases( FindBases(
ctx context.Context, ctx context.Context,
reasons []kopia.Reason, reasons []kopia.Reasoner,
tags map[string]string, tags map[string]string,
) kopia.BackupBases ) kopia.BackupBases
} }

View File

@ -703,17 +703,19 @@ func (suite *KopiaIntegrationSuite) TestBackupCollections() {
"brunhilda": "", "brunhilda": "",
} }
reasons := []Reason{ reasons := []Reasoner{
{ NewReason(
ResourceOwner: suite.storePath1.ResourceOwner(), testTenant,
Service: suite.storePath1.Service(), suite.storePath1.ResourceOwner(),
Category: suite.storePath1.Category(), suite.storePath1.Service(),
}, suite.storePath1.Category(),
{ ),
ResourceOwner: suite.storePath2.ResourceOwner(), NewReason(
Service: suite.storePath2.Service(), testTenant,
Category: suite.storePath2.Category(), suite.storePath2.ResourceOwner(),
}, suite.storePath2.Service(),
suite.storePath2.Category(),
),
} }
for _, r := range reasons { for _, r := range reasons {
@ -837,12 +839,12 @@ func (suite *KopiaIntegrationSuite) TestBackupCollections_NoDetailsForMeta() {
"brunhilda": "", "brunhilda": "",
} }
reasons := []Reason{ reasons := []Reasoner{
{ NewReason(
ResourceOwner: storePath.ResourceOwner(), testTenant,
Service: storePath.Service(), storePath.ResourceOwner(),
Category: storePath.Category(), storePath.Service(),
}, storePath.Category()),
} }
for _, r := range reasons { for _, r := range reasons {
@ -1017,13 +1019,9 @@ func (suite *KopiaIntegrationSuite) TestRestoreAfterCompressionChange() {
w := &Wrapper{k} w := &Wrapper{k}
tags := map[string]string{} tags := map[string]string{}
reason := Reason{ r := NewReason(testTenant, testUser, path.ExchangeService, path.EmailCategory)
ResourceOwner: testUser,
Service: path.ExchangeService,
Category: path.EmailCategory,
}
for _, k := range reason.TagKeys() { for _, k := range r.TagKeys() {
tags[k] = "" tags[k] = ""
} }
@ -1113,13 +1111,9 @@ func (suite *KopiaIntegrationSuite) TestBackupCollections_ReaderError() {
loc1 := path.Builder{}.Append(suite.storePath1.Folders()...) loc1 := path.Builder{}.Append(suite.storePath1.Folders()...)
loc2 := path.Builder{}.Append(suite.storePath2.Folders()...) loc2 := path.Builder{}.Append(suite.storePath2.Folders()...)
tags := map[string]string{} tags := map[string]string{}
reason := Reason{ r := NewReason(testTenant, testUser, path.ExchangeService, path.EmailCategory)
ResourceOwner: testUser,
Service: path.ExchangeService,
Category: path.EmailCategory,
}
for _, k := range reason.TagKeys() { for _, k := range r.TagKeys() {
tags[k] = "" tags[k] = ""
} }
@ -1392,13 +1386,9 @@ func (suite *KopiaSimpleRepoIntegrationSuite) SetupTest() {
} }
tags := map[string]string{} tags := map[string]string{}
reason := Reason{ r := NewReason(testTenant, testUser, path.ExchangeService, path.EmailCategory)
ResourceOwner: testUser,
Service: path.ExchangeService,
Category: path.EmailCategory,
}
for _, k := range reason.TagKeys() { for _, k := range r.TagKeys() {
tags[k] = "" tags[k] = ""
} }
@ -1437,11 +1427,7 @@ func (c *i64counter) Count(i int64) {
} }
func (suite *KopiaSimpleRepoIntegrationSuite) TestBackupExcludeItem() { func (suite *KopiaSimpleRepoIntegrationSuite) TestBackupExcludeItem() {
reason := Reason{ r := NewReason(testTenant, testUser, path.ExchangeService, path.EmailCategory)
ResourceOwner: testUser,
Service: path.ExchangeService,
Category: path.EmailCategory,
}
subtreePathTmp, err := path.Build( subtreePathTmp, err := path.Build(
testTenant, testTenant,
@ -1459,7 +1445,7 @@ func (suite *KopiaSimpleRepoIntegrationSuite) TestBackupExcludeItem() {
tags := map[string]string{} tags := map[string]string{}
for _, k := range reason.TagKeys() { for _, k := range r.TagKeys() {
tags[k] = "" tags[k] = ""
} }

View File

@ -280,8 +280,8 @@ func (op *BackupOperation) do(
backupID model.StableID, backupID model.StableID,
) (*details.Builder, error) { ) (*details.Builder, error) {
var ( var (
reasons = selectorToReasons(op.Selectors, false) reasons = selectorToReasons(op.account.ID(), op.Selectors, false)
fallbackReasons = makeFallbackReasons(op.Selectors) fallbackReasons = makeFallbackReasons(op.account.ID(), op.Selectors)
lastBackupVersion = version.NoBackup lastBackupVersion = version.NoBackup
) )
@ -370,10 +370,10 @@ func (op *BackupOperation) do(
return deets, nil return deets, nil
} }
func makeFallbackReasons(sel selectors.Selector) []kopia.Reason { func makeFallbackReasons(tenant string, sel selectors.Selector) []kopia.Reasoner {
if sel.PathService() != path.SharePointService && if sel.PathService() != path.SharePointService &&
sel.DiscreteOwner != sel.DiscreteOwnerName { sel.DiscreteOwner != sel.DiscreteOwnerName {
return selectorToReasons(sel, true) return selectorToReasons(tenant, sel, true)
} }
return nil return nil
@ -420,9 +420,13 @@ func produceBackupDataCollections(
// Consumer funcs // Consumer funcs
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
func selectorToReasons(sel selectors.Selector, useOwnerNameForID bool) []kopia.Reason { func selectorToReasons(
tenant string,
sel selectors.Selector,
useOwnerNameForID bool,
) []kopia.Reasoner {
service := sel.PathService() service := sel.PathService()
reasons := []kopia.Reason{} reasons := []kopia.Reasoner{}
pcs, err := sel.PathCategories() pcs, err := sel.PathCategories()
if err != nil { if err != nil {
@ -438,28 +442,24 @@ func selectorToReasons(sel selectors.Selector, useOwnerNameForID bool) []kopia.R
for _, sl := range [][]path.CategoryType{pcs.Includes, pcs.Filters} { for _, sl := range [][]path.CategoryType{pcs.Includes, pcs.Filters} {
for _, cat := range sl { for _, cat := range sl {
reasons = append(reasons, kopia.Reason{ reasons = append(reasons, kopia.NewReason(tenant, owner, service, cat))
ResourceOwner: owner,
Service: service,
Category: cat,
})
} }
} }
return reasons return reasons
} }
func builderFromReason(ctx context.Context, tenant string, r kopia.Reason) (*path.Builder, error) { func builderFromReason(ctx context.Context, tenant string, r kopia.Reasoner) (*path.Builder, error) {
ctx = clues.Add(ctx, "category", r.Category.String()) ctx = clues.Add(ctx, "category", r.Category().String())
// This is hacky, but we want the path package to format the path the right // This is hacky, but we want the path package to format the path the right
// way (e.x. proper order for service, category, etc), but we don't care about // way (e.x. proper order for service, category, etc), but we don't care about
// the folders after the prefix. // the folders after the prefix.
p, err := path.Build( p, err := path.Build(
tenant, tenant,
r.ResourceOwner, r.ProtectedResource(),
r.Service, r.Service(),
r.Category, r.Category(),
false, false,
"tmp") "tmp")
if err != nil { if err != nil {
@ -474,7 +474,7 @@ func consumeBackupCollections(
ctx context.Context, ctx context.Context,
bc kinject.BackupConsumer, bc kinject.BackupConsumer,
tenantID string, tenantID string,
reasons []kopia.Reason, reasons []kopia.Reasoner,
bbs kopia.BackupBases, bbs kopia.BackupBases,
cs []data.BackupCollection, cs []data.BackupCollection,
pmr prefixmatcher.StringSetReader, pmr prefixmatcher.StringSetReader,
@ -530,8 +530,8 @@ func consumeBackupCollections(
} }
paths = append(paths, pb) paths = append(paths, pb)
services[reason.Service.String()] = struct{}{} services[reason.Service().String()] = struct{}{}
categories[reason.Category.String()] = struct{}{} categories[reason.Category().String()] = struct{}{}
} }
ids[m.ID] = struct{}{} ids[m.ID] = struct{}{}
@ -609,11 +609,11 @@ func consumeBackupCollections(
return kopiaStats, deets, itemsSourcedFromBase, err return kopiaStats, deets, itemsSourcedFromBase, err
} }
func matchesReason(reasons []kopia.Reason, p path.Path) bool { func matchesReason(reasons []kopia.Reasoner, p path.Path) bool {
for _, reason := range reasons { for _, reason := range reasons {
if p.ResourceOwner() == reason.ResourceOwner && if p.ResourceOwner() == reason.ProtectedResource() &&
p.Service() == reason.Service && p.Service() == reason.Service() &&
p.Category() == reason.Category { p.Category() == reason.Category() {
return true return true
} }
} }

View File

@ -395,25 +395,23 @@ func (suite *BackupOpUnitSuite) TestBackupOperation_ConsumeBackupDataCollections
tenant, tenant,
path.ExchangeService.String(), path.ExchangeService.String(),
resourceOwner, resourceOwner,
path.EmailCategory.String(), path.EmailCategory.String())
)
contactsBuilder = path.Builder{}.Append( contactsBuilder = path.Builder{}.Append(
tenant, tenant,
path.ExchangeService.String(), path.ExchangeService.String(),
resourceOwner, resourceOwner,
path.ContactsCategory.String(), path.ContactsCategory.String())
)
emailReason = kopia.Reason{ emailReason = kopia.NewReason(
ResourceOwner: resourceOwner, "",
Service: path.ExchangeService, resourceOwner,
Category: path.EmailCategory, path.ExchangeService,
} path.EmailCategory)
contactsReason = kopia.Reason{ contactsReason = kopia.NewReason(
ResourceOwner: resourceOwner, "",
Service: path.ExchangeService, resourceOwner,
Category: path.ContactsCategory, path.ExchangeService,
} path.ContactsCategory)
manifest1 = &snapshot.Manifest{ manifest1 = &snapshot.Manifest{
ID: "id1", ID: "id1",
@ -434,7 +432,7 @@ func (suite *BackupOpUnitSuite) TestBackupOperation_ConsumeBackupDataCollections
input: kopia.NewMockBackupBases().WithMergeBases( input: kopia.NewMockBackupBases().WithMergeBases(
kopia.ManifestEntry{ kopia.ManifestEntry{
Manifest: manifest1, Manifest: manifest1,
Reasons: []kopia.Reason{ Reasons: []kopia.Reasoner{
emailReason, emailReason,
}, },
}).ClearMockAssistBases(), }).ClearMockAssistBases(),
@ -452,7 +450,7 @@ func (suite *BackupOpUnitSuite) TestBackupOperation_ConsumeBackupDataCollections
input: kopia.NewMockBackupBases().WithMergeBases( input: kopia.NewMockBackupBases().WithMergeBases(
kopia.ManifestEntry{ kopia.ManifestEntry{
Manifest: manifest1, Manifest: manifest1,
Reasons: []kopia.Reason{ Reasons: []kopia.Reasoner{
emailReason, emailReason,
contactsReason, contactsReason,
}, },
@ -472,14 +470,14 @@ func (suite *BackupOpUnitSuite) TestBackupOperation_ConsumeBackupDataCollections
input: kopia.NewMockBackupBases().WithMergeBases( input: kopia.NewMockBackupBases().WithMergeBases(
kopia.ManifestEntry{ kopia.ManifestEntry{
Manifest: manifest1, Manifest: manifest1,
Reasons: []kopia.Reason{ Reasons: []kopia.Reasoner{
emailReason, emailReason,
contactsReason, contactsReason,
}, },
}, },
kopia.ManifestEntry{ kopia.ManifestEntry{
Manifest: manifest2, Manifest: manifest2,
Reasons: []kopia.Reason{ Reasons: []kopia.Reasoner{
emailReason, emailReason,
contactsReason, contactsReason,
}, },
@ -506,13 +504,13 @@ func (suite *BackupOpUnitSuite) TestBackupOperation_ConsumeBackupDataCollections
input: kopia.NewMockBackupBases().WithMergeBases( input: kopia.NewMockBackupBases().WithMergeBases(
kopia.ManifestEntry{ kopia.ManifestEntry{
Manifest: manifest1, Manifest: manifest1,
Reasons: []kopia.Reason{ Reasons: []kopia.Reasoner{
emailReason, emailReason,
}, },
}).WithAssistBases( }).WithAssistBases(
kopia.ManifestEntry{ kopia.ManifestEntry{
Manifest: manifest2, Manifest: manifest2,
Reasons: []kopia.Reason{ Reasons: []kopia.Reasoner{
contactsReason, contactsReason,
}, },
}), }),
@ -629,16 +627,16 @@ func (suite *BackupOpUnitSuite) TestBackupOperation_MergeBackupDetails_AddsItems
DetailsID: "did2", DetailsID: "did2",
} }
pathReason1 = kopia.Reason{ pathReason1 = kopia.NewReason(
ResourceOwner: itemPath1.ResourceOwner(), "",
Service: itemPath1.Service(), itemPath1.ResourceOwner(),
Category: itemPath1.Category(), itemPath1.Service(),
} itemPath1.Category())
pathReason3 = kopia.Reason{ pathReason3 = kopia.NewReason(
ResourceOwner: itemPath3.ResourceOwner(), "",
Service: itemPath3.Service(), itemPath3.ResourceOwner(),
Category: itemPath3.Category(), itemPath3.Service(),
} itemPath3.Category())
) )
itemParents1, err := path.GetDriveFolderPath(itemPath1) itemParents1, err := path.GetDriveFolderPath(itemPath1)
@ -684,7 +682,7 @@ func (suite *BackupOpUnitSuite) TestBackupOperation_MergeBackupDetails_AddsItems
}, },
DetailsID: "foo", DetailsID: "foo",
}, },
Reasons: []kopia.Reason{ Reasons: []kopia.Reasoner{
pathReason1, pathReason1,
}, },
}, },
@ -703,7 +701,7 @@ func (suite *BackupOpUnitSuite) TestBackupOperation_MergeBackupDetails_AddsItems
inputBackups: []kopia.BackupEntry{ inputBackups: []kopia.BackupEntry{
{ {
Backup: &backup1, Backup: &backup1,
Reasons: []kopia.Reason{ Reasons: []kopia.Reasoner{
pathReason1, pathReason1,
}, },
}, },
@ -730,13 +728,13 @@ func (suite *BackupOpUnitSuite) TestBackupOperation_MergeBackupDetails_AddsItems
inputBackups: []kopia.BackupEntry{ inputBackups: []kopia.BackupEntry{
{ {
Backup: &backup1, Backup: &backup1,
Reasons: []kopia.Reason{ Reasons: []kopia.Reasoner{
pathReason1, pathReason1,
}, },
}, },
{ {
Backup: &backup1, Backup: &backup1,
Reasons: []kopia.Reason{ Reasons: []kopia.Reasoner{
pathReason1, pathReason1,
}, },
}, },
@ -763,7 +761,7 @@ func (suite *BackupOpUnitSuite) TestBackupOperation_MergeBackupDetails_AddsItems
inputBackups: []kopia.BackupEntry{ inputBackups: []kopia.BackupEntry{
{ {
Backup: &backup1, Backup: &backup1,
Reasons: []kopia.Reason{ Reasons: []kopia.Reasoner{
pathReason1, pathReason1,
}, },
}, },
@ -822,7 +820,7 @@ func (suite *BackupOpUnitSuite) TestBackupOperation_MergeBackupDetails_AddsItems
inputBackups: []kopia.BackupEntry{ inputBackups: []kopia.BackupEntry{
{ {
Backup: &backup1, Backup: &backup1,
Reasons: []kopia.Reason{ Reasons: []kopia.Reasoner{
pathReason1, pathReason1,
}, },
}, },
@ -849,7 +847,7 @@ func (suite *BackupOpUnitSuite) TestBackupOperation_MergeBackupDetails_AddsItems
inputBackups: []kopia.BackupEntry{ inputBackups: []kopia.BackupEntry{
{ {
Backup: &backup1, Backup: &backup1,
Reasons: []kopia.Reason{ Reasons: []kopia.Reasoner{
pathReason1, pathReason1,
}, },
}, },
@ -879,7 +877,7 @@ func (suite *BackupOpUnitSuite) TestBackupOperation_MergeBackupDetails_AddsItems
inputBackups: []kopia.BackupEntry{ inputBackups: []kopia.BackupEntry{
{ {
Backup: &backup1, Backup: &backup1,
Reasons: []kopia.Reason{ Reasons: []kopia.Reasoner{
pathReason1, pathReason1,
}, },
}, },
@ -909,7 +907,7 @@ func (suite *BackupOpUnitSuite) TestBackupOperation_MergeBackupDetails_AddsItems
inputBackups: []kopia.BackupEntry{ inputBackups: []kopia.BackupEntry{
{ {
Backup: &backup1, Backup: &backup1,
Reasons: []kopia.Reason{ Reasons: []kopia.Reasoner{
pathReason1, pathReason1,
}, },
}, },
@ -940,7 +938,7 @@ func (suite *BackupOpUnitSuite) TestBackupOperation_MergeBackupDetails_AddsItems
inputBackups: []kopia.BackupEntry{ inputBackups: []kopia.BackupEntry{
{ {
Backup: &backup1, Backup: &backup1,
Reasons: []kopia.Reason{ Reasons: []kopia.Reasoner{
pathReason1, pathReason1,
}, },
}, },
@ -971,13 +969,13 @@ func (suite *BackupOpUnitSuite) TestBackupOperation_MergeBackupDetails_AddsItems
inputBackups: []kopia.BackupEntry{ inputBackups: []kopia.BackupEntry{
{ {
Backup: &backup1, Backup: &backup1,
Reasons: []kopia.Reason{ Reasons: []kopia.Reasoner{
pathReason1, pathReason1,
}, },
}, },
{ {
Backup: &backup2, Backup: &backup2,
Reasons: []kopia.Reason{ Reasons: []kopia.Reasoner{
pathReason3, pathReason3,
}, },
}, },
@ -1064,11 +1062,11 @@ func (suite *BackupOpUnitSuite) TestBackupOperation_MergeBackupDetails_AddsFolde
locPath1 = path.Builder{}.Append(itemPath1.Folders()...) locPath1 = path.Builder{}.Append(itemPath1.Folders()...)
pathReason1 = kopia.Reason{ pathReason1 = kopia.NewReason(
ResourceOwner: itemPath1.ResourceOwner(), "",
Service: itemPath1.Service(), itemPath1.ResourceOwner(),
Category: itemPath1.Category(), itemPath1.Service(),
} itemPath1.Category())
backup1 = kopia.BackupEntry{ backup1 = kopia.BackupEntry{
Backup: &backup.Backup{ Backup: &backup.Backup{
@ -1077,7 +1075,7 @@ func (suite *BackupOpUnitSuite) TestBackupOperation_MergeBackupDetails_AddsFolde
}, },
DetailsID: "did1", DetailsID: "did1",
}, },
Reasons: []kopia.Reason{ Reasons: []kopia.Reasoner{
pathReason1, pathReason1,
}, },
} }

View File

@ -23,7 +23,7 @@ func produceManifestsAndMetadata(
ctx context.Context, ctx context.Context,
bf inject.BaseFinder, bf inject.BaseFinder,
rp inject.RestoreProducer, rp inject.RestoreProducer,
reasons, fallbackReasons []kopia.Reason, reasons, fallbackReasons []kopia.Reasoner,
tenantID string, tenantID string,
getMetadata bool, getMetadata bool,
) (kopia.BackupBases, []data.RestoreCollection, bool, error) { ) (kopia.BackupBases, []data.RestoreCollection, bool, error) {
@ -47,8 +47,8 @@ func produceManifestsAndMetadata(
bb = bb.MergeBackupBases( bb = bb.MergeBackupBases(
ctx, ctx,
fbb, fbb,
func(r kopia.Reason) string { func(r kopia.Reasoner) string {
return r.Service.String() + r.Category.String() return r.Service().String() + r.Category().String()
}) })
if !getMetadata { if !getMetadata {
@ -115,9 +115,9 @@ func collectMetadata(
Append(fn). Append(fn).
ToServiceCategoryMetadataPath( ToServiceCategoryMetadataPath(
tenantID, tenantID,
reason.ResourceOwner, reason.ProtectedResource(),
reason.Service, reason.Service(),
reason.Category, reason.Category(),
true) true)
if err != nil { if err != nil {
return nil, clues. return nil, clues.

View File

@ -47,7 +47,7 @@ type mockBackupFinder struct {
func (bf *mockBackupFinder) FindBases( func (bf *mockBackupFinder) FindBases(
_ context.Context, _ context.Context,
reasons []kopia.Reason, reasons []kopia.Reasoner,
_ map[string]string, _ map[string]string,
) kopia.BackupBases { ) kopia.BackupBases {
if len(reasons) == 0 { if len(reasons) == 0 {
@ -58,7 +58,7 @@ func (bf *mockBackupFinder) FindBases(
return kopia.NewMockBackupBases() return kopia.NewMockBackupBases()
} }
b := bf.data[reasons[0].ResourceOwner] b := bf.data[reasons[0].ProtectedResource()]
if b == nil { if b == nil {
return kopia.NewMockBackupBases() return kopia.NewMockBackupBases()
} }
@ -102,7 +102,7 @@ func (suite *OperationsManifestsUnitSuite) TestCollectMetadata() {
table := []struct { table := []struct {
name string name string
manID string manID string
reasons []kopia.Reason reasons []kopia.Reasoner
fileNames []string fileNames []string
expectPaths func(*testing.T, []string) []path.Path expectPaths func(*testing.T, []string) []path.Path
expectErr error expectErr error
@ -110,12 +110,8 @@ func (suite *OperationsManifestsUnitSuite) TestCollectMetadata() {
{ {
name: "single reason, single file", name: "single reason, single file",
manID: "single single", manID: "single single",
reasons: []kopia.Reason{ reasons: []kopia.Reasoner{
{ kopia.NewReason(tid, ro, path.ExchangeService, path.EmailCategory),
ResourceOwner: ro,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
}, },
expectPaths: func(t *testing.T, files []string) []path.Path { expectPaths: func(t *testing.T, files []string) []path.Path {
ps := make([]path.Path, 0, len(files)) ps := make([]path.Path, 0, len(files))
@ -133,12 +129,8 @@ func (suite *OperationsManifestsUnitSuite) TestCollectMetadata() {
{ {
name: "single reason, multiple files", name: "single reason, multiple files",
manID: "single multi", manID: "single multi",
reasons: []kopia.Reason{ reasons: []kopia.Reasoner{
{ kopia.NewReason(tid, ro, path.ExchangeService, path.EmailCategory),
ResourceOwner: ro,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
}, },
expectPaths: func(t *testing.T, files []string) []path.Path { expectPaths: func(t *testing.T, files []string) []path.Path {
ps := make([]path.Path, 0, len(files)) ps := make([]path.Path, 0, len(files))
@ -156,17 +148,9 @@ func (suite *OperationsManifestsUnitSuite) TestCollectMetadata() {
{ {
name: "multiple reasons, single file", name: "multiple reasons, single file",
manID: "multi single", manID: "multi single",
reasons: []kopia.Reason{ reasons: []kopia.Reasoner{
{ kopia.NewReason(tid, ro, path.ExchangeService, path.EmailCategory),
ResourceOwner: ro, kopia.NewReason(tid, ro, path.ExchangeService, path.ContactsCategory),
Service: path.ExchangeService,
Category: path.EmailCategory,
},
{
ResourceOwner: ro,
Service: path.ExchangeService,
Category: path.ContactsCategory,
},
}, },
expectPaths: func(t *testing.T, files []string) []path.Path { expectPaths: func(t *testing.T, files []string) []path.Path {
ps := make([]path.Path, 0, len(files)) ps := make([]path.Path, 0, len(files))
@ -187,17 +171,9 @@ func (suite *OperationsManifestsUnitSuite) TestCollectMetadata() {
{ {
name: "multiple reasons, multiple file", name: "multiple reasons, multiple file",
manID: "multi multi", manID: "multi multi",
reasons: []kopia.Reason{ reasons: []kopia.Reasoner{
{ kopia.NewReason(tid, ro, path.ExchangeService, path.EmailCategory),
ResourceOwner: ro, kopia.NewReason(tid, ro, path.ExchangeService, path.ContactsCategory),
Service: path.ExchangeService,
Category: path.EmailCategory,
},
{
ResourceOwner: ro,
Service: path.ExchangeService,
Category: path.ContactsCategory,
},
}, },
expectPaths: func(t *testing.T, files []string) []path.Path { expectPaths: func(t *testing.T, files []string) []path.Path {
ps := make([]path.Path, 0, len(files)) ps := make([]path.Path, 0, len(files))
@ -243,17 +219,13 @@ func buildReasons(
ro string, ro string,
service path.ServiceType, service path.ServiceType,
cats ...path.CategoryType, cats ...path.CategoryType,
) []kopia.Reason { ) []kopia.Reasoner {
var reasons []kopia.Reason var reasons []kopia.Reasoner
for _, cat := range cats { for _, cat := range cats {
reasons = append( reasons = append(
reasons, reasons,
kopia.Reason{ kopia.NewReason("", ro, service, cat))
ResourceOwner: ro,
Service: service,
Category: cat,
})
} }
return reasons return reasons
@ -280,7 +252,7 @@ func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata() {
name string name string
bf *mockBackupFinder bf *mockBackupFinder
rp mockRestoreProducer rp mockRestoreProducer
reasons []kopia.Reason reasons []kopia.Reasoner
getMeta bool getMeta bool
assertErr assert.ErrorAssertionFunc assertErr assert.ErrorAssertionFunc
assertB assert.BoolAssertionFunc assertB assert.BoolAssertionFunc
@ -291,7 +263,7 @@ func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata() {
{ {
name: "don't get metadata, no mans", name: "don't get metadata, no mans",
rp: mockRestoreProducer{}, rp: mockRestoreProducer{},
reasons: []kopia.Reason{}, reasons: []kopia.Reasoner{},
getMeta: false, getMeta: false,
assertErr: assert.NoError, assertErr: assert.NoError,
assertB: assert.False, assertB: assert.False,
@ -308,12 +280,8 @@ func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata() {
}, },
}, },
rp: mockRestoreProducer{}, rp: mockRestoreProducer{},
reasons: []kopia.Reason{ reasons: []kopia.Reasoner{
{ kopia.NewReason("", ro, path.ExchangeService, path.EmailCategory),
ResourceOwner: ro,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
}, },
getMeta: false, getMeta: false,
assertErr: assert.NoError, assertErr: assert.NoError,
@ -333,12 +301,8 @@ func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata() {
}, },
}, },
rp: mockRestoreProducer{}, rp: mockRestoreProducer{},
reasons: []kopia.Reason{ reasons: []kopia.Reasoner{
{ kopia.NewReason("", ro, path.ExchangeService, path.EmailCategory),
ResourceOwner: ro,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
}, },
getMeta: true, getMeta: true,
assertErr: assert.NoError, assertErr: assert.NoError,
@ -365,17 +329,9 @@ func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata() {
"id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "id1"}}}, "id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "id1"}}},
}, },
}, },
reasons: []kopia.Reason{ reasons: []kopia.Reasoner{
{ kopia.NewReason("", ro, path.ExchangeService, path.EmailCategory),
ResourceOwner: ro, kopia.NewReason("", ro, path.ExchangeService, path.ContactsCategory),
Service: path.ExchangeService,
Category: path.EmailCategory,
},
{
ResourceOwner: ro,
Service: path.ExchangeService,
Category: path.ContactsCategory,
},
}, },
getMeta: true, getMeta: true,
assertErr: assert.NoError, assertErr: assert.NoError,
@ -421,12 +377,8 @@ func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata() {
"id2": {data.NoFetchRestoreCollection{Collection: mockColl{id: "id2"}}}, "id2": {data.NoFetchRestoreCollection{Collection: mockColl{id: "id2"}}},
}, },
}, },
reasons: []kopia.Reason{ reasons: []kopia.Reasoner{
{ kopia.NewReason("", ro, path.ExchangeService, path.EmailCategory),
ResourceOwner: ro,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
}, },
getMeta: true, getMeta: true,
assertErr: assert.NoError, assertErr: assert.NoError,
@ -454,12 +406,8 @@ func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata() {
"id2": {data.NoFetchRestoreCollection{Collection: mockColl{id: "id2"}}}, "id2": {data.NoFetchRestoreCollection{Collection: mockColl{id: "id2"}}},
}, },
}, },
reasons: []kopia.Reason{ reasons: []kopia.Reasoner{
{ kopia.NewReason("", ro, path.ExchangeService, path.EmailCategory),
ResourceOwner: ro,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
}, },
getMeta: true, getMeta: true,
assertErr: assert.NoError, assertErr: assert.NoError,
@ -480,12 +428,8 @@ func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata() {
}, },
}, },
rp: mockRestoreProducer{err: assert.AnError}, rp: mockRestoreProducer{err: assert.AnError},
reasons: []kopia.Reason{ reasons: []kopia.Reasoner{
{ kopia.NewReason("", ro, path.ExchangeService, path.EmailCategory),
ResourceOwner: ro,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
}, },
getMeta: true, getMeta: true,
assertErr: assert.Error, assertErr: assert.Error,
@ -588,24 +532,24 @@ func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata_Fallb
} }
} }
emailReason := kopia.Reason{ emailReason := kopia.NewReason(
ResourceOwner: ro, "",
Service: path.ExchangeService, ro,
Category: path.EmailCategory, path.ExchangeService,
} path.EmailCategory)
fbEmailReason := kopia.Reason{ fbEmailReason := kopia.NewReason(
ResourceOwner: fbro, "",
Service: path.ExchangeService, fbro,
Category: path.EmailCategory, path.ExchangeService,
} path.EmailCategory)
table := []struct { table := []struct {
name string name string
bf *mockBackupFinder bf *mockBackupFinder
rp mockRestoreProducer rp mockRestoreProducer
reasons []kopia.Reason reasons []kopia.Reasoner
fallbackReasons []kopia.Reason fallbackReasons []kopia.Reasoner
getMeta bool getMeta bool
assertErr assert.ErrorAssertionFunc assertErr assert.ErrorAssertionFunc
assertB assert.BoolAssertionFunc assertB assert.BoolAssertionFunc
@ -624,7 +568,7 @@ func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata_Fallb
}, },
}, },
rp: mockRestoreProducer{}, rp: mockRestoreProducer{},
fallbackReasons: []kopia.Reason{fbEmailReason}, fallbackReasons: []kopia.Reasoner{fbEmailReason},
getMeta: false, getMeta: false,
assertErr: assert.NoError, assertErr: assert.NoError,
assertB: assert.False, assertB: assert.False,
@ -649,7 +593,7 @@ func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata_Fallb
"fb_id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id1"}}}, "fb_id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id1"}}},
}, },
}, },
fallbackReasons: []kopia.Reason{fbEmailReason}, fallbackReasons: []kopia.Reasoner{fbEmailReason},
getMeta: true, getMeta: true,
assertErr: assert.NoError, assertErr: assert.NoError,
assertB: assert.True, assertB: assert.True,
@ -680,8 +624,8 @@ func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata_Fallb
"fb_id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id1"}}}, "fb_id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id1"}}},
}, },
}, },
reasons: []kopia.Reason{emailReason}, reasons: []kopia.Reasoner{emailReason},
fallbackReasons: []kopia.Reason{fbEmailReason}, fallbackReasons: []kopia.Reasoner{fbEmailReason},
getMeta: true, getMeta: true,
assertErr: assert.NoError, assertErr: assert.NoError,
assertB: assert.True, assertB: assert.True,
@ -708,8 +652,8 @@ func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata_Fallb
"fb_id2": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id2"}}}, "fb_id2": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id2"}}},
}, },
}, },
reasons: []kopia.Reason{emailReason}, reasons: []kopia.Reasoner{emailReason},
fallbackReasons: []kopia.Reason{fbEmailReason}, fallbackReasons: []kopia.Reasoner{fbEmailReason},
getMeta: true, getMeta: true,
assertErr: assert.NoError, assertErr: assert.NoError,
assertB: assert.True, assertB: assert.True,
@ -744,8 +688,8 @@ func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata_Fallb
"fb_id2": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id2"}}}, "fb_id2": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id2"}}},
}, },
}, },
reasons: []kopia.Reason{emailReason}, reasons: []kopia.Reasoner{emailReason},
fallbackReasons: []kopia.Reason{fbEmailReason}, fallbackReasons: []kopia.Reasoner{fbEmailReason},
getMeta: true, getMeta: true,
assertErr: assert.NoError, assertErr: assert.NoError,
assertB: assert.True, assertB: assert.True,
@ -776,8 +720,8 @@ func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata_Fallb
"fb_id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id1"}}}, "fb_id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id1"}}},
}, },
}, },
reasons: []kopia.Reason{emailReason}, reasons: []kopia.Reasoner{emailReason},
fallbackReasons: []kopia.Reason{fbEmailReason}, fallbackReasons: []kopia.Reasoner{fbEmailReason},
getMeta: true, getMeta: true,
assertErr: assert.NoError, assertErr: assert.NoError,
assertB: assert.True, assertB: assert.True,
@ -808,8 +752,8 @@ func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata_Fallb
"fb_id2": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id2"}}}, "fb_id2": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id2"}}},
}, },
}, },
reasons: []kopia.Reason{emailReason}, reasons: []kopia.Reasoner{emailReason},
fallbackReasons: []kopia.Reason{fbEmailReason}, fallbackReasons: []kopia.Reasoner{fbEmailReason},
getMeta: true, getMeta: true,
assertErr: assert.NoError, assertErr: assert.NoError,
assertB: assert.True, assertB: assert.True,
@ -838,21 +782,13 @@ func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata_Fallb
"fb_id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id1"}}}, "fb_id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id1"}}},
}, },
}, },
reasons: []kopia.Reason{ reasons: []kopia.Reasoner{
emailReason, emailReason,
{ kopia.NewReason("", ro, path.ExchangeService, path.ContactsCategory),
ResourceOwner: ro,
Service: path.ExchangeService,
Category: path.ContactsCategory,
}, },
}, fallbackReasons: []kopia.Reasoner{
fallbackReasons: []kopia.Reason{
fbEmailReason, fbEmailReason,
{ kopia.NewReason("", fbro, path.ExchangeService, path.ContactsCategory),
ResourceOwner: fbro,
Service: path.ExchangeService,
Category: path.ContactsCategory,
},
}, },
getMeta: true, getMeta: true,
assertErr: assert.NoError, assertErr: assert.NoError,
@ -882,13 +818,9 @@ func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata_Fallb
"fb_id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id1"}}}, "fb_id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id1"}}},
}, },
}, },
reasons: []kopia.Reason{emailReason}, reasons: []kopia.Reasoner{emailReason},
fallbackReasons: []kopia.Reason{ fallbackReasons: []kopia.Reasoner{
{ kopia.NewReason("", fbro, path.ExchangeService, path.ContactsCategory),
ResourceOwner: fbro,
Service: path.ExchangeService,
Category: path.ContactsCategory,
},
}, },
getMeta: true, getMeta: true,
assertErr: assert.NoError, assertErr: assert.NoError,
@ -921,21 +853,13 @@ func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata_Fallb
"fb_id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id1"}}}, "fb_id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id1"}}},
}, },
}, },
reasons: []kopia.Reason{ reasons: []kopia.Reasoner{
emailReason, emailReason,
{ kopia.NewReason("", ro, path.ExchangeService, path.ContactsCategory),
ResourceOwner: ro,
Service: path.ExchangeService,
Category: path.ContactsCategory,
}, },
}, fallbackReasons: []kopia.Reasoner{
fallbackReasons: []kopia.Reason{
fbEmailReason, fbEmailReason,
{ kopia.NewReason("", fbro, path.ExchangeService, path.ContactsCategory),
ResourceOwner: fbro,
Service: path.ExchangeService,
Category: path.ContactsCategory,
},
}, },
getMeta: true, getMeta: true,
assertErr: assert.NoError, assertErr: assert.NoError,

View File

@ -242,13 +242,7 @@ func checkBackupIsInManifests(
for _, category := range categories { for _, category := range categories {
t.Run(category.String(), func(t *testing.T) { t.Run(category.String(), func(t *testing.T) {
var ( var (
reasons = []kopia.Reason{ r = kopia.NewReason("", resourceOwner, sel.PathService(), category)
{
ResourceOwner: resourceOwner,
Service: sel.PathService(),
Category: category,
},
}
tags = map[string]string{kopia.TagBackupCategory: ""} tags = map[string]string{kopia.TagBackupCategory: ""}
found bool found bool
) )
@ -256,7 +250,7 @@ func checkBackupIsInManifests(
bf, err := kw.NewBaseFinder(sw) bf, err := kw.NewBaseFinder(sw)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
mans := bf.FindBases(ctx, reasons, tags) mans := bf.FindBases(ctx, []kopia.Reasoner{r}, tags)
for _, man := range mans.MergeBases() { for _, man := range mans.MergeBases() {
bID, ok := man.GetTag(kopia.TagBackupID) bID, ok := man.GetTag(kopia.TagBackupID)
if !assert.Truef(t, ok, "snapshot manifest %s missing backup ID tag", man.ID) { if !assert.Truef(t, ok, "snapshot manifest %s missing backup ID tag", man.ID) {