From f3bf09ba8a081fb643edb30cd40a2f0f9950671c Mon Sep 17 00:00:00 2001 From: Keepers Date: Mon, 9 Jan 2023 15:01:21 -0700 Subject: [PATCH] protect against missing backups in manifests (#2063) ## Description If a prior manifest is missing a backup, it should be handled the same way as when a manifest's backup is missing a details ID: the metadata is skipped, and a full backup is performed. ## Does this PR need a docs update or release note? - [x] :no_entry: No ## Type of change - [x] :bug: Bugfix ## Issue(s) * #2062 ## Test Plan - [x] :zap: Unit test - [x] :green_heart: E2E --- src/internal/operations/backup.go | 32 ++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/internal/operations/backup.go b/src/internal/operations/backup.go index 65e8bc037..718b964bc 100644 --- a/src/internal/operations/backup.go +++ b/src/internal/operations/backup.go @@ -151,7 +151,7 @@ func (op *BackupOperation) Run(ctx context.Context) (err error) { } }() - mans, mdColls, err := produceManifestsAndMetadata(ctx, op.kopia, op.store, oc, tenantID, uib) + mans, mdColls, canUseMetaData, err := produceManifestsAndMetadata(ctx, op.kopia, op.store, oc, tenantID, uib) if err != nil { opStats.readErr = errors.Wrap(err, "connecting to M365") return opStats.readErr @@ -178,7 +178,7 @@ func (op *BackupOperation) Run(ctx context.Context) (err error) { mans, cs, op.Results.BackupID, - uib) + uib && canUseMetaData) if err != nil { opStats.writeErr = errors.Wrap(err, "backing up service data") return opStats.writeErr @@ -301,7 +301,7 @@ func produceManifestsAndMetadata( oc *kopia.OwnersCats, tenantID string, getMetadata bool, -) ([]*kopia.ManifestEntry, []data.Collection, error) { +) ([]*kopia.ManifestEntry, []data.Collection, bool, error) { var ( metadataFiles = graph.AllMetadataFileNames() collections []data.Collection @@ -312,11 +312,11 @@ func produceManifestsAndMetadata( oc, map[string]string{kopia.TagBackupCategory: ""}) if err != nil { - return nil, nil, err + return nil, nil, false, err } if !getMetadata { - return ms, nil, nil + return ms, nil, false, nil } // We only need to check that we have 1:1 reason:base if we're doing an @@ -332,7 +332,7 @@ func produceManifestsAndMetadata( err, ) - return ms, nil, nil + return ms, nil, false, nil } for _, man := range ms { @@ -344,12 +344,22 @@ func produceManifestsAndMetadata( bID := man.Tags[tk] if len(bID) == 0 { - return nil, nil, errors.New("missing backup id in prior manifest") + return nil, nil, false, errors.New("snapshot manifest missing backup ID") } dID, _, err := sw.GetDetailsIDFromBackupID(ctx, model.StableID(bID)) if err != nil { - return nil, nil, errors.Wrap(err, "retrieving prior backup data") + // if no backup exists for any of the complete manifests, we want + // to fall back to a complete backup. + if errors.Is(err, kopia.ErrNotFound) { + logger.Ctx(ctx).Infow( + "backup missing, falling back to full backup", + "backup_id", bID) + + return ms, nil, false, nil + } + + return nil, nil, false, errors.Wrap(err, "retrieving prior backup data") } // if no detailsID exists for any of the complete manifests, we want @@ -362,7 +372,7 @@ func produceManifestsAndMetadata( "backup missing details ID, falling back to full backup", "backup_id", bID) - return ms, nil, nil + return ms, nil, false, nil } colls, err := collectMetadata(ctx, kw, man, metadataFiles, tenantID) @@ -370,13 +380,13 @@ func produceManifestsAndMetadata( // prior metadata isn't guaranteed to exist. // if it doesn't, we'll just have to do a // full backup for that data. - return nil, nil, err + return nil, nil, false, err } collections = append(collections, colls...) } - return ms, collections, err + return ms, collections, true, err } func collectMetadata(