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 <proglottis@gmail.com>2021-04-22 05:33:13 +0300
committerJames Fargher <proglottis@gmail.com>2021-05-04 01:44:34 +0300
commit12aa02e6cf4ee69f4ab0f6af8948022e9e90f429 (patch)
tree688e6fbad86fb23f8eece4cac02f77d6da77a39b
parentefaec4f88b7660801693759cbca43e80f35af605 (diff)
gitaly-backup: Add restore command
-rw-r--r--cmd/gitaly-backup/main.go3
-rw-r--r--cmd/gitaly-backup/restore.go74
-rw-r--r--cmd/gitaly-backup/restore_test.go77
3 files changed, 153 insertions, 1 deletions
diff --git a/cmd/gitaly-backup/main.go b/cmd/gitaly-backup/main.go
index 12cf21396..57173ca31 100644
--- a/cmd/gitaly-backup/main.go
+++ b/cmd/gitaly-backup/main.go
@@ -16,7 +16,8 @@ type subcmd interface {
}
var subcommands = map[string]subcmd{
- "create": &createSubcommand{},
+ "create": &createSubcommand{},
+ "restore": &restoreSubcommand{},
}
func main() {
diff --git a/cmd/gitaly-backup/restore.go b/cmd/gitaly-backup/restore.go
new file mode 100644
index 000000000..619192a8a
--- /dev/null
+++ b/cmd/gitaly-backup/restore.go
@@ -0,0 +1,74 @@
+package main
+
+import (
+ "context"
+ "encoding/json"
+ "errors"
+ "flag"
+ "fmt"
+ "io"
+
+ log "github.com/sirupsen/logrus"
+ "gitlab.com/gitlab-org/gitaly/internal/backup"
+ "gitlab.com/gitlab-org/gitaly/internal/storage"
+ "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
+)
+
+type restoreRequest struct {
+ storage.ServerInfo
+ StorageName string `json:"storage_name"`
+ RelativePath string `json:"relative_path"`
+ GlProjectPath string `json:"gl_project_path"`
+ AlwaysCreate bool `json:"always_create"`
+}
+
+type restoreSubcommand struct {
+ backupPath string
+}
+
+func (cmd *restoreSubcommand) Flags(fs *flag.FlagSet) {
+ fs.StringVar(&cmd.backupPath, "path", "", "repository backup path")
+}
+
+func (cmd *restoreSubcommand) Run(ctx context.Context, stdin io.Reader, stdout io.Writer) error {
+ fsBackup := backup.NewFilesystem(cmd.backupPath)
+
+ var failed int
+ decoder := json.NewDecoder(stdin)
+ for {
+ var req restoreRequest
+ if err := decoder.Decode(&req); errors.Is(err, io.EOF) {
+ break
+ } else if err != nil {
+ return fmt.Errorf("restore: %w", err)
+ }
+
+ repoLog := log.WithFields(log.Fields{
+ "storage_name": req.StorageName,
+ "relative_path": req.RelativePath,
+ "gl_project_path": req.GlProjectPath,
+ })
+ repo := gitalypb.Repository{
+ StorageName: req.StorageName,
+ RelativePath: req.RelativePath,
+ GlProjectPath: req.GlProjectPath,
+ }
+ repoLog.Info("started restore")
+ if err := fsBackup.RestoreRepository(ctx, req.ServerInfo, &repo, req.AlwaysCreate); err != nil {
+ if errors.Is(err, backup.ErrSkipped) {
+ repoLog.WithError(err).Warn("skipped restore")
+ } else {
+ repoLog.WithError(err).Error("restore failed")
+ failed++
+ }
+ continue
+ }
+
+ repoLog.Info("completed restore")
+ }
+
+ if failed > 0 {
+ return fmt.Errorf("restore: %d failures encountered", failed)
+ }
+ return nil
+}
diff --git a/cmd/gitaly-backup/restore_test.go b/cmd/gitaly-backup/restore_test.go
new file mode 100644
index 000000000..efccf131f
--- /dev/null
+++ b/cmd/gitaly-backup/restore_test.go
@@ -0,0 +1,77 @@
+package main
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "path/filepath"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+ "gitlab.com/gitlab-org/gitaly/internal/git/gittest"
+ "gitlab.com/gitlab-org/gitaly/internal/gitaly/service/setup"
+ "gitlab.com/gitlab-org/gitaly/internal/testhelper"
+ "gitlab.com/gitlab-org/gitaly/internal/testhelper/testcfg"
+ "gitlab.com/gitlab-org/gitaly/internal/testhelper/testserver"
+ "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
+)
+
+func TestRestoreSubcommand(t *testing.T) {
+ cfg := testcfg.Build(t)
+ testhelper.ConfigureGitalyHooksBinary(cfg.BinDir)
+
+ gitalyAddr := testserver.RunGitalyServer(t, cfg, nil, setup.RegisterAll)
+
+ path := testhelper.TempDir(t)
+
+ existingRepo, existRepoPath, _ := gittest.CloneRepoAtStorage(t, cfg.Storages[0], "existing_repo")
+ existingRepoBundlePath := filepath.Join(path, existingRepo.RelativePath+".bundle")
+ gittest.Exec(t, cfg, "-C", existRepoPath, "bundle", "create", existingRepoBundlePath, "--all")
+
+ repos := []*gitalypb.Repository{existingRepo}
+ for i := 0; i < 2; i++ {
+ repo := gittest.InitRepoDir(t, cfg.Storages[0].Path, fmt.Sprintf("repo-%d", i))
+ repoBundlePath := filepath.Join(path, repo.RelativePath+".bundle")
+ testhelper.CopyFile(t, existingRepoBundlePath, repoBundlePath)
+ repos = append(repos, repo)
+ }
+
+ var stdin bytes.Buffer
+
+ encoder := json.NewEncoder(&stdin)
+ for _, repo := range repos {
+ require.NoError(t, encoder.Encode(map[string]string{
+ "address": gitalyAddr,
+ "token": cfg.Auth.Token,
+ "storage_name": repo.StorageName,
+ "relative_path": repo.RelativePath,
+ "gl_project_path": repo.GlProjectPath,
+ }))
+ }
+
+ require.NoError(t, encoder.Encode(map[string]string{
+ "address": "invalid",
+ "token": "invalid",
+ }))
+
+ cmd := restoreSubcommand{}
+
+ fs := flag.NewFlagSet("restore", flag.ContinueOnError)
+ cmd.Flags(fs)
+
+ require.NoError(t, fs.Parse([]string{"-path", path}))
+ require.EqualError(t,
+ cmd.Run(context.Background(), &stdin, ioutil.Discard),
+ "restore: 1 failures encountered")
+
+ for _, repo := range repos {
+ repoPath := filepath.Join(cfg.Storages[0].Path, repo.RelativePath)
+ bundlePath := filepath.Join(path, repo.RelativePath+".bundle")
+
+ output := gittest.Exec(t, cfg, "-C", repoPath, "bundle", "verify", bundlePath)
+ require.Contains(t, string(output), "The bundle records a complete history")
+ }
+}