Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Fargher <jfargher@gitlab.com>2023-08-07 07:26:10 +0300
committerJames Fargher <jfargher@gitlab.com>2023-08-10 01:06:39 +0300
commitf2bc26bbbb0a9781d36ee6a72bd1786c6060c502 (patch)
tree2e400ed703860c34c2a5ca2b73ba782f012e8453
parent9c19257b770a75fdc73e700f8c1891d5420f1370 (diff)
backup: Restore empty repository backups
Reads the refs file to detect empty repository backups. When there are no references in the refs file, the bundle can be skipped. In the future the loaded refs will be used to remove deleted refs. https://gitlab.com/gitlab-org/gitaly/-/issues/3857 Changelog: changed
-rw-r--r--internal/backup/backup.go69
-rw-r--r--internal/backup/backup_test.go10
-rw-r--r--internal/backup/server_side_test.go2
-rw-r--r--internal/gitaly/service/repository/restore_repository_test.go2
4 files changed, 59 insertions, 24 deletions
diff --git a/internal/backup/backup.go b/internal/backup/backup.go
index 01b48d60c..fd308abd2 100644
--- a/internal/backup/backup.go
+++ b/internal/backup/backup.go
@@ -270,23 +270,33 @@ func (mgr *Manager) Restore(ctx context.Context, req *RestoreRequest) error {
}
for _, step := range backup.Steps {
- if err := mgr.restoreBundle(ctx, repo, step.BundlePath); err != nil {
- if 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 := repo.Remove(ctx); err != nil {
- return fmt.Errorf("manager: remove on skipped: %w", err)
- }
-
- return fmt.Errorf("manager: %w: %s", ErrSkipped, err.Error())
+ refs, err := mgr.readRefs(ctx, step.RefPath)
+ switch {
+ case 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 := repo.Remove(ctx); err != nil {
+ return fmt.Errorf("manager: remove on skipped: %w", err)
+ }
+
+ return fmt.Errorf("manager: %w: %s", ErrSkipped, err.Error())
+ case err != nil:
+ return fmt.Errorf("manager: %w", err)
+ }
+
+ // Git bundles can not be created for empty repositories. Since empty
+ // repository backups do not contain a bundle, skip bundle restoration.
+ if len(refs) > 0 {
+ if err := mgr.restoreBundle(ctx, repo, step.BundlePath); err != nil {
+ return fmt.Errorf("manager: %w", err)
}
}
if err := mgr.restoreCustomHooks(ctx, repo, step.CustomHooksPath); err != nil {
@@ -401,6 +411,31 @@ func (mgr *Manager) negatedKnownRefs(ctx context.Context, step *Step) (io.ReadCl
return r, nil
}
+func (mgr *Manager) readRefs(ctx context.Context, path string) ([]git.Reference, error) {
+ reader, err := mgr.sink.GetReader(ctx, path)
+ if err != nil {
+ return nil, fmt.Errorf("read refs: %w", err)
+ }
+ defer reader.Close()
+
+ var refs []git.Reference
+
+ d := git.NewShowRefDecoder(reader)
+ for {
+ var ref git.Reference
+
+ if err := d.Decode(&ref); err == io.EOF {
+ break
+ } else if err != nil {
+ return refs, fmt.Errorf("read refs: %w", err)
+ }
+
+ refs = append(refs, ref)
+ }
+
+ return refs, nil
+}
+
func (mgr *Manager) restoreBundle(ctx context.Context, repo Repository, path string) error {
reader, err := mgr.sink.GetReader(ctx, path)
if err != nil {
diff --git a/internal/backup/backup_test.go b/internal/backup/backup_test.go
index d9eeb80c6..fb156d732 100644
--- a/internal/backup/backup_test.go
+++ b/internal/backup/backup_test.go
@@ -518,9 +518,9 @@ func TestManager_Restore_latest(t *testing.T) {
relativePath + ".refs": "",
})
- return repo, nil
+ return repo, new(git.Checksum)
},
- expectedErrAs: backup.ErrSkipped,
+ expectExists: true,
},
{
desc: "empty backup, always create",
@@ -533,7 +533,7 @@ func TestManager_Restore_latest(t *testing.T) {
relativePath + ".refs": "",
})
- return repo, nil
+ return repo, new(git.Checksum)
},
alwaysCreate: true,
expectExists: true,
@@ -590,9 +590,9 @@ func TestManager_Restore_latest(t *testing.T) {
filepath.Join(relativePath, backupID, "001.refs"): "",
})
- return repo, nil
+ return repo, new(git.Checksum)
},
- expectedErrAs: backup.ErrSkipped,
+ expectExists: true,
},
{
desc: "many incrementals",
diff --git a/internal/backup/server_side_test.go b/internal/backup/server_side_test.go
index 91dfe9784..3329c8a78 100644
--- a/internal/backup/server_side_test.go
+++ b/internal/backup/server_side_test.go
@@ -194,7 +194,7 @@ func TestServerSideAdapter_Restore(t *testing.T) {
backupID: "",
}
},
- expectedErr: fmt.Errorf("server-side restore: %w: rpc error: code = FailedPrecondition desc = restore repository: manager: repository skipped: restore bundle: \"@test/restore/latest/missing.bundle\": doesn't exist", backup.ErrSkipped),
+ expectedErr: fmt.Errorf("server-side restore: %w: rpc error: code = FailedPrecondition desc = restore repository: manager: repository skipped: read refs: doesn't exist", backup.ErrSkipped),
},
} {
tc := tc
diff --git a/internal/gitaly/service/repository/restore_repository_test.go b/internal/gitaly/service/repository/restore_repository_test.go
index df0043a69..2551ffeb7 100644
--- a/internal/gitaly/service/repository/restore_repository_test.go
+++ b/internal/gitaly/service/repository/restore_repository_test.go
@@ -138,7 +138,7 @@ func TestRestoreRepository(t *testing.T) {
backupID: "",
}
},
- expectedErr: structerr.NewFailedPrecondition("restore repository: manager: repository skipped: restore bundle: \"@test/restore/latest/missing.bundle\": doesn't exist").WithDetail(
+ expectedErr: structerr.NewFailedPrecondition("restore repository: manager: repository skipped: read refs: doesn't exist").WithDetail(
&gitalypb.RestoreRepositoryResponse_SkippedError{},
),
},