diff --git a/src/pkg/backup/backup.go b/src/pkg/backup/backup.go index a0b83864d..76e068562 100644 --- a/src/pkg/backup/backup.go +++ b/src/pkg/backup/backup.go @@ -7,6 +7,7 @@ import ( "strings" "time" + "github.com/alcionai/clues" "github.com/dustin/go-humanize" "github.com/alcionai/corso/src/cli/print" @@ -14,6 +15,7 @@ import ( "github.com/alcionai/corso/src/internal/common/str" "github.com/alcionai/corso/src/internal/model" "github.com/alcionai/corso/src/internal/stats" + "github.com/alcionai/corso/src/pkg/backup/identity" "github.com/alcionai/corso/src/pkg/fault" "github.com/alcionai/corso/src/pkg/selectors" ) @@ -180,6 +182,66 @@ func New( return b } +// PersistedBaseSet contains information extracted from the backup model +// relating to its lineage. It only contains the backup ID and Reasons each +// base was selected instead of the full set of information contained in other +// structs like BackupBases. +type PersistedBaseSet struct { + Merge map[model.StableID][]identity.Reasoner + Assist map[model.StableID][]identity.Reasoner +} + +func (b Backup) Bases() (PersistedBaseSet, error) { + res := PersistedBaseSet{ + Merge: map[model.StableID][]identity.Reasoner{}, + Assist: map[model.StableID][]identity.Reasoner{}, + } + + for id, reasons := range b.MergeBases { + for _, reason := range reasons { + service, cat, err := serviceCatStringToTypes(reason) + if err != nil { + return res, clues.Wrap(err, "getting Reason info").With( + "base_type", "merge", + "base_backup_id", id, + "input_string", reason) + } + + res.Merge[id] = append(res.Merge[id], identity.NewReason( + // Tenant ID not currently stored in backup model. + "", + str.First( + b.ProtectedResourceID, + b.Selector.DiscreteOwner), + service, + cat)) + } + } + + for id, reasons := range b.AssistBases { + for _, reason := range reasons { + service, cat, err := serviceCatStringToTypes(reason) + if err != nil { + return res, clues.Wrap(err, "getting Reason info").With( + "base_type", "assist", + "base_backup_id", id, + "input_string", reason) + } + + res.Assist[id] = append(res.Assist[id], identity.NewReason( + // Tenant ID not currently stored in backup model. + "", + str.First( + b.ProtectedResourceID, + b.Selector.DiscreteOwner), + service, + cat)) + } + } + + return res, nil +} + // -------------------------------------------------------------------------------- // CLI Output // -------------------------------------------------------------------------------- diff --git a/src/pkg/backup/backup_bases.go b/src/pkg/backup/backup_bases.go index 981f61827..da6403f1f 100644 --- a/src/pkg/backup/backup_bases.go +++ b/src/pkg/backup/backup_bases.go @@ -2,7 +2,9 @@ package backup import ( "context" + "strings" + "github.com/alcionai/clues" "github.com/kopia/kopia/repo/manifest" "github.com/kopia/kopia/snapshot" @@ -40,6 +42,41 @@ func serviceCatString( return serviceCatPrefix + service.String() + separator + category.String() } +func serviceCatStringToTypes( + input string, +) (path.ServiceType, path.CategoryType, error) { + trimmed := strings.TrimPrefix(input, serviceCatPrefix) + // No prefix found -> unexpected format. + if trimmed == input { + return path.UnknownService, + path.UnknownCategory, + clues.New("missing tag prefix") + } + + parts := strings.Split(trimmed, separator) + if len(parts) != 2 { + return path.UnknownService, + path.UnknownCategory, + clues.New("missing tag separator") + } + + cat := path.ToCategoryType(parts[1]) + if cat == path.UnknownCategory { + return path.UnknownService, + path.UnknownCategory, + clues.New("parsing category").With("input_category", parts[1]) + } + + service := path.ToServiceType(parts[0]) + if service == path.UnknownService { + return path.UnknownService, + path.UnknownCategory, + clues.New("parsing service").With("input_service", parts[0]) + } + + return service, cat, nil +} + // reasonTags returns the set of key-value pairs that can be used as tags to // represent this Reason. // nolint