diff options
Diffstat (limited to 'internal/backup/backup.go')
-rw-r--r-- | internal/backup/backup.go | 80 |
1 files changed, 34 insertions, 46 deletions
diff --git a/internal/backup/backup.go b/internal/backup/backup.go index c59591b59..70a9b6d91 100644 --- a/internal/backup/backup.go +++ b/internal/backup/backup.go @@ -38,20 +38,10 @@ type Sink interface { GetReader(ctx context.Context, relativePath string) (io.ReadCloser, error) } -// Backup represents all the information needed to restore a backup for a repository -type Backup struct { - // Steps are the ordered list of steps required to restore this backup - Steps []Step -} - -// Step represents an incremental step that makes up a complete backup for a repository -type Step struct { +// Full represents all paths required for a full backup +type Full struct { // BundlePath is the path of the bundle BundlePath string - // SkippableOnNotFound defines if the bundle can be skipped when it does - // not exist. This allows us to maintain legacy behaviour where we always - // check a specific location for a bundle without knowing if it exists. - SkippableOnNotFound bool // RefPath is the path of the ref file RefPath string // CustomHooksPath is the path of the custom hooks archive @@ -60,14 +50,14 @@ type Step struct { // Locator finds sink backup paths for repositories type Locator interface { - // BeginFull returns a tentative first step needed to create a new full backup. - BeginFull(ctx context.Context, repo *gitalypb.Repository, backupID string) *Step + // BeginFull returns paths for a new full backup + BeginFull(ctx context.Context, repo *gitalypb.Repository, backupID string) *Full - // CommitFull marks the step returned by `BeginFull` as the latest backup. - CommitFull(ctx context.Context, step *Step) error + // CommitFull persists the paths for a new backup so that it can be looked up by FindLatestFull + CommitFull(ctx context.Context, full *Full) error - // FindLatest returns the latest backup that was written by CommitFull - FindLatest(ctx context.Context, repo *gitalypb.Repository) (*Backup, error) + // FindLatestFull returns the paths committed by the latest call to CommitFull + FindLatestFull(ctx context.Context, repo *gitalypb.Repository) (*Full, error) } // ResolveSink returns a sink implementation based on the provided path. @@ -182,40 +172,38 @@ func (mgr *Manager) Restore(ctx context.Context, req *RestoreRequest) error { return fmt.Errorf("manager: %w", err) } - backup, err := mgr.locator.FindLatest(ctx, req.Repository) + full, err := mgr.locator.FindLatestFull(ctx, req.Repository) if err != nil { - return fmt.Errorf("manager: %w", err) + return mgr.checkRestoreSkip(ctx, err, req) } - if err := mgr.createRepository(ctx, req.Server, req.Repository); err != nil { + if err := mgr.restoreBundle(ctx, full.BundlePath, req.Server, req.Repository); err != nil { + return mgr.checkRestoreSkip(ctx, err, req) + } + if err := mgr.restoreCustomHooks(ctx, full.CustomHooksPath, req.Server, req.Repository); err != nil { return fmt.Errorf("manager: %w", err) } + return nil +} - for _, step := range backup.Steps { - if err := mgr.restoreBundle(ctx, step.BundlePath, req.Server, req.Repository); err != nil { - if step.SkippableOnNotFound && errors.Is(err, ErrDoesntExist) { - // For compatibility with existing backups we need to make sure the - // repository exists even if there's no bundle for project - // repositories (not wiki or snippet repositories). Gitaly does - // not know which repository is which type so here we accept a - // parameter to tell us to employ this behaviour. Since the - // repository has already been created, we simply skip cleaning up. - if req.AlwaysCreate { - return nil - } - - if err := mgr.removeRepository(ctx, req.Server, req.Repository); err != nil { - return fmt.Errorf("manager: remove on skipped: %w", err) - } - - return fmt.Errorf("manager: %w: %s", ErrSkipped, err.Error()) +func (mgr *Manager) checkRestoreSkip(ctx context.Context, err error, req *RestoreRequest) error { + if errors.Is(err, ErrDoesntExist) { + // For compatibility with existing backups we need to always create the + // repository even if there's no bundle for project repositories + // (not wiki or snippet repositories). Gitaly does not know which + // repository is which type so here we accept a parameter to tell us + // to employ this behaviour. + if req.AlwaysCreate { + if err := mgr.createRepository(ctx, req.Server, req.Repository); err != nil { + return fmt.Errorf("manager: %w", err) } + return nil } - if err := mgr.restoreCustomHooks(ctx, step.CustomHooksPath, req.Server, req.Repository); err != nil { - return fmt.Errorf("manager: %w", err) - } + + return fmt.Errorf("manager: %w: %s", ErrSkipped, err.Error()) } - return nil + + return fmt.Errorf("manager: %w", err) } func (mgr *Manager) isEmpty(ctx context.Context, server storage.ServerInfo, repo *gitalypb.Repository) (bool, error) { @@ -329,11 +317,11 @@ func (mgr *Manager) restoreBundle(ctx context.Context, path string, server stora if err != nil { return fmt.Errorf("restore bundle: %q: %w", path, err) } - stream, err := repoClient.FetchBundle(ctx) + stream, err := repoClient.CreateRepositoryFromBundle(ctx) if err != nil { return fmt.Errorf("restore bundle: %q: %w", path, err) } - request := &gitalypb.FetchBundleRequest{Repository: repo} + request := &gitalypb.CreateRepositoryFromBundleRequest{Repository: repo} bundle := streamio.NewWriter(func(p []byte) error { request.Data = p if err := stream.Send(request); err != nil { @@ -341,7 +329,7 @@ func (mgr *Manager) restoreBundle(ctx context.Context, path string, server stora } // Only set `Repository` on the first `Send` of the stream - request = &gitalypb.FetchBundleRequest{} + request = &gitalypb.CreateRepositoryFromBundleRequest{} return nil }) |