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 

## Type of change

- [x] 🐛 Bugfix

## Issue(s)

* #2062

## Test Plan

- [x]  Unit test
- [x] 💚 E2E
This commit is contained in:
Keepers 2023-01-09 15:01:21 -07:00 committed by GitHub
parent 04d25e171e
commit f3bf09ba8a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -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 { if err != nil {
opStats.readErr = errors.Wrap(err, "connecting to M365") opStats.readErr = errors.Wrap(err, "connecting to M365")
return opStats.readErr return opStats.readErr
@ -178,7 +178,7 @@ func (op *BackupOperation) Run(ctx context.Context) (err error) {
mans, mans,
cs, cs,
op.Results.BackupID, op.Results.BackupID,
uib) uib && canUseMetaData)
if err != nil { if err != nil {
opStats.writeErr = errors.Wrap(err, "backing up service data") opStats.writeErr = errors.Wrap(err, "backing up service data")
return opStats.writeErr return opStats.writeErr
@ -301,7 +301,7 @@ func produceManifestsAndMetadata(
oc *kopia.OwnersCats, oc *kopia.OwnersCats,
tenantID string, tenantID string,
getMetadata bool, getMetadata bool,
) ([]*kopia.ManifestEntry, []data.Collection, error) { ) ([]*kopia.ManifestEntry, []data.Collection, bool, error) {
var ( var (
metadataFiles = graph.AllMetadataFileNames() metadataFiles = graph.AllMetadataFileNames()
collections []data.Collection collections []data.Collection
@ -312,11 +312,11 @@ func produceManifestsAndMetadata(
oc, oc,
map[string]string{kopia.TagBackupCategory: ""}) map[string]string{kopia.TagBackupCategory: ""})
if err != nil { if err != nil {
return nil, nil, err return nil, nil, false, err
} }
if !getMetadata { 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 // We only need to check that we have 1:1 reason:base if we're doing an
@ -332,7 +332,7 @@ func produceManifestsAndMetadata(
err, err,
) )
return ms, nil, nil return ms, nil, false, nil
} }
for _, man := range ms { for _, man := range ms {
@ -344,12 +344,22 @@ func produceManifestsAndMetadata(
bID := man.Tags[tk] bID := man.Tags[tk]
if len(bID) == 0 { 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)) dID, _, err := sw.GetDetailsIDFromBackupID(ctx, model.StableID(bID))
if err != nil { 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 // 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 missing details ID, falling back to full backup",
"backup_id", bID) "backup_id", bID)
return ms, nil, nil return ms, nil, false, nil
} }
colls, err := collectMetadata(ctx, kw, man, metadataFiles, tenantID) colls, err := collectMetadata(ctx, kw, man, metadataFiles, tenantID)
@ -370,13 +380,13 @@ func produceManifestsAndMetadata(
// prior metadata isn't guaranteed to exist. // prior metadata isn't guaranteed to exist.
// if it doesn't, we'll just have to do a // if it doesn't, we'll just have to do a
// full backup for that data. // full backup for that data.
return nil, nil, err return nil, nil, false, err
} }
collections = append(collections, colls...) collections = append(collections, colls...)
} }
return ms, collections, err return ms, collections, true, err
} }
func collectMetadata( func collectMetadata(