diff options
author | Sami Hiltunen <shiltunen@gitlab.com> | 2023-06-22 15:52:24 +0300 |
---|---|---|
committer | Sami Hiltunen <shiltunen@gitlab.com> | 2023-06-22 15:52:24 +0300 |
commit | 8d7e76612ceb321f8868d9c7d6ba2fc9b237ada3 (patch) | |
tree | b79320ee9d4291a882b70dfcdc39241e614527f5 | |
parent | fa12d031b2ee607a9b7519f8610a6b83a21fb52e (diff) | |
parent | c63a4a908100edccb8591f1be251a5f15d9ef983 (diff) |
Merge branch 'backup_repository_rpc' into 'master'
Add BackupRepository RPC
Closes #4940
See merge request https://gitlab.com/gitlab-org/gitaly/-/merge_requests/5925
Merged-by: Sami Hiltunen <shiltunen@gitlab.com>
Approved-by: Sami Hiltunen <shiltunen@gitlab.com>
Reviewed-by: Justin Tobler <jtobler@gitlab.com>
Co-authored-by: James Fargher <jfargher@gitlab.com>
33 files changed, 895 insertions, 166 deletions
diff --git a/internal/backup/backup.go b/internal/backup/backup.go index 8d1ef1abe..79117bf24 100644 --- a/internal/backup/backup.go +++ b/internal/backup/backup.go @@ -202,13 +202,22 @@ func (mgr *Manager) RemoveAllRepositories(ctx context.Context, req *RemoveAllRep // CreateRequest is the request to create a backup type CreateRequest struct { - Server storage.ServerInfo - Repository *gitalypb.Repository + // Server contains gitaly server connection information required to call + // RPCs in the non-local backup.Manager configuration. + Server storage.ServerInfo + // Repository is the repository to be backed up. + Repository *gitalypb.Repository + // VanityRepository is used to determine the backup path. + VanityRepository *gitalypb.Repository + // Incremental when true will create an increment on the specified full backup. Incremental bool } // Create creates a repository backup. func (mgr *Manager) Create(ctx context.Context, req *CreateRequest) error { + if req.VanityRepository == nil { + req.VanityRepository = req.Repository + } repo, err := mgr.repositoryFactory(ctx, req.Repository, req.Server) if err != nil { return fmt.Errorf("manager: %w", err) @@ -223,12 +232,12 @@ func (mgr *Manager) Create(ctx context.Context, req *CreateRequest) error { var step *Step if req.Incremental { var err error - step, err = mgr.locator.BeginIncremental(ctx, req.Repository, mgr.backupID) + step, err = mgr.locator.BeginIncremental(ctx, req.VanityRepository, mgr.backupID) if err != nil { return fmt.Errorf("manager: %w", err) } } else { - step = mgr.locator.BeginFull(ctx, req.Repository, mgr.backupID) + step = mgr.locator.BeginFull(ctx, req.VanityRepository, mgr.backupID) } refs, err := repo.ListRefs(ctx) diff --git a/internal/backup/backup_test.go b/internal/backup/backup_test.go index bc5b6676b..b10820b56 100644 --- a/internal/backup/backup_test.go +++ b/internal/backup/backup_test.go @@ -159,10 +159,14 @@ func TestManager_Create(t *testing.T) { t.Run(tc.desc, func(t *testing.T) { repo, repoPath := tc.setup(t) backupRoot := testhelper.TempDir(t) + vanityRepo := &gitalypb.Repository{ + RelativePath: "some/path.git", + StorageName: "some_storage", + } - refsPath := joinBackupPath(t, backupRoot, repo, backupID, "001.refs") - bundlePath := joinBackupPath(t, backupRoot, repo, backupID, "001.bundle") - customHooksPath := joinBackupPath(t, backupRoot, repo, backupID, "001.custom_hooks.tar") + refsPath := joinBackupPath(t, backupRoot, vanityRepo, backupID, "001.refs") + bundlePath := joinBackupPath(t, backupRoot, vanityRepo, backupID, "001.bundle") + customHooksPath := joinBackupPath(t, backupRoot, vanityRepo, backupID, "001.custom_hooks.tar") sink := backup.NewFilesystemSink(backupRoot) locator, err := backup.ResolveLocator("pointer", sink) @@ -170,8 +174,9 @@ func TestManager_Create(t *testing.T) { fsBackup := managerTC.setup(t, sink, locator) err = fsBackup.Create(ctx, &backup.CreateRequest{ - Server: storage.ServerInfo{Address: cfg.SocketPath, Token: cfg.Auth.Token}, - Repository: repo, + Server: storage.ServerInfo{Address: cfg.SocketPath, Token: cfg.Auth.Token}, + Repository: repo, + VanityRepository: vanityRepo, }) if tc.err == nil { require.NoError(t, err) diff --git a/internal/cli/gitaly/serve.go b/internal/cli/gitaly/serve.go index a4ae23e81..5706adf6b 100644 --- a/internal/cli/gitaly/serve.go +++ b/internal/cli/gitaly/serve.go @@ -14,6 +14,7 @@ import ( "github.com/urfave/cli/v2" "gitlab.com/gitlab-org/gitaly/v16" "gitlab.com/gitlab-org/gitaly/v16/client" + "gitlab.com/gitlab-org/gitaly/v16/internal/backup" "gitlab.com/gitlab-org/gitaly/v16/internal/bootstrap" "gitlab.com/gitlab-org/gitaly/v16/internal/bootstrap/starter" "gitlab.com/gitlab-org/gitaly/v16/internal/cache" @@ -306,6 +307,20 @@ func run(cfg config.Cfg) error { concurrencyTracker := hook.NewConcurrencyTracker() prometheus.MustRegister(concurrencyTracker) + var backupSink backup.Sink + var backupLocator backup.Locator + if cfg.Backup.GoCloudURL != "" { + var err error + backupSink, err = backup.ResolveSink(ctx, cfg.Backup.GoCloudURL) + if err != nil { + return fmt.Errorf("resolve backup sink: %w", err) + } + backupLocator, err = backup.ResolveLocator(cfg.Backup.Layout, backupSink) + if err != nil { + return fmt.Errorf("resolve backup locator: %w", err) + } + } + for _, c := range []starter.Config{ {Name: starter.Unix, Addr: cfg.SocketPath, HandoverOnUpgrade: true}, {Name: starter.Unix, Addr: cfg.InternalSocketPath(), HandoverOnUpgrade: false}, @@ -344,6 +359,8 @@ func run(cfg config.Cfg) error { Git2goExecutor: git2goExecutor, UpdaterWithHooks: updaterWithHooks, HousekeepingManager: housekeepingManager, + BackupSink: backupSink, + BackupLocator: backupLocator, }) b.RegisterStarter(starter.New(c, srv)) } diff --git a/internal/git/localrepo/remote_extra_test.go b/internal/git/localrepo/remote_extra_test.go index 47bb4172b..743d438d7 100644 --- a/internal/git/localrepo/remote_extra_test.go +++ b/internal/git/localrepo/remote_extra_test.go @@ -51,6 +51,8 @@ func TestRepo_FetchInternal(t *testing.T) { deps.GetConnsPool(), deps.GetGit2goExecutor(), deps.GetHousekeepingManager(), + deps.GetBackupSink(), + deps.GetBackupLocator(), )) }, testserver.WithGitCommandFactory(protocolDetectingFactory)) diff --git a/internal/git/remoterepo/testhelper_test.go b/internal/git/remoterepo/testhelper_test.go index 9e9645487..32b95f78e 100644 --- a/internal/git/remoterepo/testhelper_test.go +++ b/internal/git/remoterepo/testhelper_test.go @@ -32,6 +32,8 @@ func setupGitalyServer(t *testing.T) config.Cfg { deps.GetConnsPool(), deps.GetGit2goExecutor(), deps.GetHousekeepingManager(), + deps.GetBackupSink(), + deps.GetBackupLocator(), )) gitalypb.RegisterCommitServiceServer(srv, commit.NewServer( deps.GetCfg(), diff --git a/internal/gitaly/config/config.go b/internal/gitaly/config/config.go index 3854a9f63..ffd0a4272 100644 --- a/internal/gitaly/config/config.go +++ b/internal/gitaly/config/config.go @@ -112,6 +112,7 @@ type Cfg struct { Cgroups cgroups.Config `toml:"cgroups,omitempty" json:"cgroups"` PackObjectsCache StreamCacheConfig `toml:"pack_objects_cache,omitempty" json:"pack_objects_cache"` PackObjectsLimiting PackObjectsLimiting `toml:"pack_objects_limiting,omitempty" json:"pack_objects_limiting"` + Backup BackupConfig `toml:"backup,omitempty" json:"backup"` } // TLS configuration @@ -419,6 +420,31 @@ func (pol PackObjectsLimiting) Validate() error { AsError() } +// BackupConfig configures server-side backups. +type BackupConfig struct { + // GoCloudURL is the blob storage GoCloud URL that will be used to store + // server-side backups. + GoCloudURL string `toml:"go_cloud_url,omitempty" json:"go_cloud_url,omitempty"` + // Layout determines how backup files are located. + Layout string `toml:"layout,omitempty" json:"layout,omitempty"` +} + +// Validate runs validation on all fields and returns any errors found. +func (bc BackupConfig) Validate() error { + if bc.GoCloudURL == "" { + return nil + } + var errs cfgerror.ValidationErrors + + if _, err := url.Parse(bc.GoCloudURL); err != nil { + errs = errs.Append(err, "go_cloud_url") + } + + return errs. + Append(cfgerror.NotBlank(bc.Layout), "layout"). + AsError() +} + // StreamCacheConfig contains settings for a streamcache instance. type StreamCacheConfig struct { Enabled bool `toml:"enabled" json:"enabled"` // Default: false @@ -577,6 +603,7 @@ func (cfg *Cfg) ValidateV2() error { {field: "cgroups", validate: cfg.Cgroups.Validate}, {field: "pack_objects_cache", validate: cfg.PackObjectsCache.Validate}, {field: "pack_objects_limiting", validate: cfg.PackObjectsLimiting.Validate}, + {field: "backup", validate: cfg.Backup.Validate}, } { var fields []string if check.field != "" { @@ -615,6 +642,10 @@ func (cfg *Cfg) setDefaults() error { cfg.Cgroups.FallbackToOldVersion() + if cfg.Backup.Layout == "" { + cfg.Backup.Layout = "pointer" + } + return nil } diff --git a/internal/gitaly/config/config_test.go b/internal/gitaly/config/config_test.go index 7697bf0d3..c9962ab88 100644 --- a/internal/gitaly/config/config_test.go +++ b/internal/gitaly/config/config_test.go @@ -2056,6 +2056,66 @@ func TestGitlab_Validate(t *testing.T) { } } +func TestBackupConfig_Validate(t *testing.T) { + t.Parallel() + + for _, tc := range []struct { + name string + backupConfig BackupConfig + expectedErr error + }{ + { + name: "empty", + }, + { + name: "valid", + backupConfig: BackupConfig{ + GoCloudURL: "s3://my-bucket", + Layout: "pointer", + }, + }, + { + name: "go_cloud_url invalid", + backupConfig: BackupConfig{ + GoCloudURL: "%invalid%", + Layout: "pointer", + }, + expectedErr: cfgerror.ValidationErrors{ + cfgerror.NewValidationError( + &url.Error{ + Op: "parse", + URL: "%invalid%", + Err: url.EscapeError("%in"), + }, + "go_cloud_url", + ), + }, + }, + { + name: "layout missing", + backupConfig: BackupConfig{ + GoCloudURL: "s3://my-bucket", + Layout: "", + }, + expectedErr: cfgerror.ValidationErrors{ + cfgerror.NewValidationError( + cfgerror.ErrBlankOrEmpty, + "layout", + ), + }, + }, + } { + tc := tc + + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + err := tc.backupConfig.Validate() + require.Equal(t, tc.expectedErr, err) + }) + } +} + func TestStreamCacheConfig_Validate(t *testing.T) { t.Parallel() diff --git a/internal/gitaly/service/blob/testhelper_test.go b/internal/gitaly/service/blob/testhelper_test.go index eb34361fe..aeb43915f 100644 --- a/internal/gitaly/service/blob/testhelper_test.go +++ b/internal/gitaly/service/blob/testhelper_test.go @@ -38,6 +38,8 @@ func setup(tb testing.TB, ctx context.Context) (config.Cfg, gitalypb.BlobService deps.GetConnsPool(), deps.GetGit2goExecutor(), deps.GetHousekeepingManager(), + deps.GetBackupSink(), + deps.GetBackupLocator(), )) }) cfg.SocketPath = addr diff --git a/internal/gitaly/service/cleanup/testhelper_test.go b/internal/gitaly/service/cleanup/testhelper_test.go index aeb621d3f..a37bf796e 100644 --- a/internal/gitaly/service/cleanup/testhelper_test.go +++ b/internal/gitaly/service/cleanup/testhelper_test.go @@ -57,6 +57,8 @@ func runCleanupServiceServer(t *testing.T, cfg config.Cfg) string { deps.GetConnsPool(), deps.GetGit2goExecutor(), deps.GetHousekeepingManager(), + deps.GetBackupSink(), + deps.GetBackupLocator(), )) }) } diff --git a/internal/gitaly/service/commit/testhelper_test.go b/internal/gitaly/service/commit/testhelper_test.go index 9428bddd8..cf22b52e3 100644 --- a/internal/gitaly/service/commit/testhelper_test.go +++ b/internal/gitaly/service/commit/testhelper_test.go @@ -85,6 +85,8 @@ func startTestServices(tb testing.TB, cfg config.Cfg, opts ...testserver.GitalyS deps.GetConnsPool(), deps.GetGit2goExecutor(), deps.GetHousekeepingManager(), + deps.GetBackupSink(), + deps.GetBackupLocator(), )) }, opts...) } diff --git a/internal/gitaly/service/conflicts/testhelper_test.go b/internal/gitaly/service/conflicts/testhelper_test.go index 75eaf1cfa..413eef849 100644 --- a/internal/gitaly/service/conflicts/testhelper_test.go +++ b/internal/gitaly/service/conflicts/testhelper_test.go @@ -58,6 +58,8 @@ func runConflictsServer(tb testing.TB, cfg config.Cfg, hookManager hook.Manager) deps.GetConnsPool(), deps.GetGit2goExecutor(), deps.GetHousekeepingManager(), + deps.GetBackupSink(), + deps.GetBackupLocator(), )) gitalypb.RegisterSSHServiceServer(srv, ssh.NewServer( deps.GetLocator(), diff --git a/internal/gitaly/service/dependencies.go b/internal/gitaly/service/dependencies.go index 78563c647..136ec6288 100644 --- a/internal/gitaly/service/dependencies.go +++ b/internal/gitaly/service/dependencies.go @@ -2,6 +2,7 @@ package service import ( "gitlab.com/gitlab-org/gitaly/v16/client" + "gitlab.com/gitlab-org/gitaly/v16/internal/backup" "gitlab.com/gitlab-org/gitaly/v16/internal/cache" "gitlab.com/gitlab-org/gitaly/v16/internal/git" "gitlab.com/gitlab-org/gitaly/v16/internal/git/catfile" @@ -39,6 +40,8 @@ type Dependencies struct { UpdaterWithHooks *updateref.UpdaterWithHooks HousekeepingManager housekeeping.Manager PartitionManager *storagemgr.PartitionManager + BackupSink backup.Sink + BackupLocator backup.Locator } // GetCfg returns service configuration. @@ -130,3 +133,13 @@ func (dc *Dependencies) GetPackObjectsLimiter() limithandler.Limiter { func (dc *Dependencies) GetPartitionManager() *storagemgr.PartitionManager { return dc.PartitionManager } + +// GetBackupSink returns the backup.Sink. +func (dc *Dependencies) GetBackupSink() backup.Sink { + return dc.BackupSink +} + +// GetBackupLocator returns the backup.Locator. +func (dc *Dependencies) GetBackupLocator() backup.Locator { + return dc.BackupLocator +} diff --git a/internal/gitaly/service/diff/testhelper_test.go b/internal/gitaly/service/diff/testhelper_test.go index 234aae21b..c13fa72ef 100644 --- a/internal/gitaly/service/diff/testhelper_test.go +++ b/internal/gitaly/service/diff/testhelper_test.go @@ -49,6 +49,8 @@ func setupDiffServiceWithoutRepo(tb testing.TB, opt ...testserver.GitalyServerOp deps.GetConnsPool(), deps.GetGit2goExecutor(), deps.GetHousekeepingManager(), + deps.GetBackupSink(), + deps.GetBackupLocator(), )) }, opt...) cfg.SocketPath = addr diff --git a/internal/gitaly/service/hook/testhelper_test.go b/internal/gitaly/service/hook/testhelper_test.go index dae029546..7e579a701 100644 --- a/internal/gitaly/service/hook/testhelper_test.go +++ b/internal/gitaly/service/hook/testhelper_test.go @@ -76,6 +76,8 @@ func runHooksServer(tb testing.TB, cfg config.Cfg, opts []serverOption, serverOp deps.GetConnsPool(), deps.GetGit2goExecutor(), deps.GetHousekeepingManager(), + deps.GetBackupSink(), + deps.GetBackupLocator(), )) }, serverOpts...) } diff --git a/internal/gitaly/service/objectpool/testhelper_test.go b/internal/gitaly/service/objectpool/testhelper_test.go index d35966459..62b713f56 100644 --- a/internal/gitaly/service/objectpool/testhelper_test.go +++ b/internal/gitaly/service/objectpool/testhelper_test.go @@ -87,6 +87,8 @@ func runObjectPoolServer(t *testing.T, cfg config.Cfg, locator storage.Locator, deps.GetConnsPool(), deps.GetGit2goExecutor(), deps.GetHousekeepingManager(), + deps.GetBackupSink(), + deps.GetBackupLocator(), )) }, append(opts, testserver.WithLocator(locator), testserver.WithLogger(logger))...) } diff --git a/internal/gitaly/service/operations/testhelper_test.go b/internal/gitaly/service/operations/testhelper_test.go index 893ce149d..90b0d873f 100644 --- a/internal/gitaly/service/operations/testhelper_test.go +++ b/internal/gitaly/service/operations/testhelper_test.go @@ -119,6 +119,8 @@ func runOperationServiceServer(tb testing.TB, cfg config.Cfg, options ...testser deps.GetConnsPool(), deps.GetGit2goExecutor(), deps.GetHousekeepingManager(), + deps.GetBackupSink(), + deps.GetBackupLocator(), )) gitalypb.RegisterRefServiceServer(srv, ref.NewServer( deps.GetLocator(), diff --git a/internal/gitaly/service/ref/delete_refs_test.go b/internal/gitaly/service/ref/delete_refs_test.go index e770cd095..bba8b94ad 100644 --- a/internal/gitaly/service/ref/delete_refs_test.go +++ b/internal/gitaly/service/ref/delete_refs_test.go @@ -121,6 +121,8 @@ func TestDeleteRefs_transaction(t *testing.T) { deps.GetConnsPool(), deps.GetGit2goExecutor(), deps.GetHousekeepingManager(), + deps.GetBackupSink(), + deps.GetBackupLocator(), )) gitalypb.RegisterHookServiceServer(srv, hookservice.NewServer( deps.GetHookManager(), diff --git a/internal/gitaly/service/ref/testhelper_test.go b/internal/gitaly/service/ref/testhelper_test.go index 80e4b0b8d..9b0a77852 100644 --- a/internal/gitaly/service/ref/testhelper_test.go +++ b/internal/gitaly/service/ref/testhelper_test.go @@ -82,6 +82,8 @@ func runRefServiceServer(tb testing.TB, cfg config.Cfg) string { deps.GetConnsPool(), deps.GetGit2goExecutor(), deps.GetHousekeepingManager(), + deps.GetBackupSink(), + deps.GetBackupLocator(), )) }) } diff --git a/internal/gitaly/service/remote/testhelper_test.go b/internal/gitaly/service/remote/testhelper_test.go index d1c03eeca..da5681147 100644 --- a/internal/gitaly/service/remote/testhelper_test.go +++ b/internal/gitaly/service/remote/testhelper_test.go @@ -41,6 +41,8 @@ func setupRemoteService(t *testing.T, ctx context.Context, opts ...testserver.Gi deps.GetConnsPool(), deps.GetGit2goExecutor(), deps.GetHousekeepingManager(), + deps.GetBackupSink(), + deps.GetBackupLocator(), )) }, opts...) cfg.SocketPath = addr diff --git a/internal/gitaly/service/remote/update_remote_mirror_test.go b/internal/gitaly/service/remote/update_remote_mirror_test.go index ea007b60f..6a1f58a55 100644 --- a/internal/gitaly/service/remote/update_remote_mirror_test.go +++ b/internal/gitaly/service/remote/update_remote_mirror_test.go @@ -589,6 +589,8 @@ func TestUpdateRemoteMirror(t *testing.T) { deps.GetConnsPool(), deps.GetGit2goExecutor(), deps.GetHousekeepingManager(), + deps.GetBackupSink(), + deps.GetBackupLocator(), )) }) cfg.SocketPath = addr diff --git a/internal/gitaly/service/repository/backup_repository.go b/internal/gitaly/service/repository/backup_repository.go new file mode 100644 index 000000000..8e9eea55f --- /dev/null +++ b/internal/gitaly/service/repository/backup_repository.go @@ -0,0 +1,62 @@ +package repository + +import ( + "context" + "errors" + "fmt" + + "gitlab.com/gitlab-org/gitaly/v16/internal/backup" + "gitlab.com/gitlab-org/gitaly/v16/internal/gitaly/storage" + "gitlab.com/gitlab-org/gitaly/v16/internal/structerr" + "gitlab.com/gitlab-org/gitaly/v16/proto/go/gitalypb" +) + +func (s *server) BackupRepository(ctx context.Context, in *gitalypb.BackupRepositoryRequest) (*gitalypb.BackupRepositoryResponse, error) { + if s.backupSink == nil || s.backupLocator == nil { + return nil, structerr.NewFailedPrecondition("backup repository: server-side backups are not configured") + } + if err := s.validateBackupRepositoryRequest(in); err != nil { + return nil, structerr.NewInvalidArgument("backup repository: %w", err) + } + + manager := backup.NewManagerLocal( + s.backupSink, + s.backupLocator, + s.locator, + s.gitCmdFactory, + s.catfileCache, + s.txManager, + in.BackupId, + ) + + err := manager.Create(ctx, &backup.CreateRequest{ + Repository: in.Repository, + VanityRepository: in.VanityRepository, + }) + + switch { + case errors.Is(err, backup.ErrSkipped): + return nil, structerr.NewFailedPrecondition("backup repository: %w", err).WithDetail( + &gitalypb.BackupRepositoryResponse_SkippedError{}, + ) + case err != nil: + return nil, structerr.NewInternal("backup repository: %w", err) + } + + return &gitalypb.BackupRepositoryResponse{}, nil +} + +func (s *server) validateBackupRepositoryRequest(in *gitalypb.BackupRepositoryRequest) error { + if in.GetBackupId() == "" { + return fmt.Errorf("empty BackupId") + } + if err := s.locator.ValidateRepository(in.GetRepository()); err != nil { + return err + } + if err := s.locator.ValidateRepository(in.GetVanityRepository(), + storage.WithSkipRepositoryExistenceCheck(), + ); err != nil { + return err + } + return nil +} diff --git a/internal/gitaly/service/repository/backup_repository_test.go b/internal/gitaly/service/repository/backup_repository_test.go new file mode 100644 index 000000000..82d76ce91 --- /dev/null +++ b/internal/gitaly/service/repository/backup_repository_test.go @@ -0,0 +1,188 @@ +package repository + +import ( + "context" + "path/filepath" + "strings" + "testing" + + "github.com/stretchr/testify/require" + "gitlab.com/gitlab-org/gitaly/v16/internal/backup" + "gitlab.com/gitlab-org/gitaly/v16/internal/git" + "gitlab.com/gitlab-org/gitaly/v16/internal/git/gittest" + "gitlab.com/gitlab-org/gitaly/v16/internal/gitaly/config" + "gitlab.com/gitlab-org/gitaly/v16/internal/structerr" + "gitlab.com/gitlab-org/gitaly/v16/internal/testhelper" + "gitlab.com/gitlab-org/gitaly/v16/internal/testhelper/testserver" + "gitlab.com/gitlab-org/gitaly/v16/proto/go/gitalypb" +) + +func TestServerBackupRepository(t *testing.T) { + t.Parallel() + ctx := testhelper.Context(t) + + type setupData struct { + cfg config.Cfg + client gitalypb.RepositoryServiceClient + repo *gitalypb.Repository + backupID string + } + + for _, tc := range []struct { + desc string + setup func(t *testing.T, ctx context.Context, backupSink backup.Sink, backupLocator backup.Locator) setupData + expectedErr error + }{ + { + desc: "success", + setup: func(t *testing.T, ctx context.Context, backupSink backup.Sink, backupLocator backup.Locator) setupData { + cfg, client := setupRepositoryServiceWithoutRepo(t, + testserver.WithBackupSink(backupSink), + testserver.WithBackupLocator(backupLocator), + ) + + repo, repoPath := gittest.CreateRepository(t, ctx, cfg) + gittest.WriteCommit(t, cfg, repoPath, gittest.WithBranch(git.DefaultBranch)) + + return setupData{ + cfg: cfg, + client: client, + repo: repo, + backupID: "abc123", + } + }, + }, + { + desc: "missing backup ID", + setup: func(t *testing.T, ctx context.Context, backupSink backup.Sink, backupLocator backup.Locator) setupData { + cfg, client := setupRepositoryServiceWithoutRepo(t, + testserver.WithBackupSink(backupSink), + testserver.WithBackupLocator(backupLocator), + ) + + repo, repoPath := gittest.CreateRepository(t, ctx, cfg) + gittest.WriteCommit(t, cfg, repoPath, gittest.WithBranch(git.DefaultBranch)) + + return setupData{ + cfg: cfg, + client: client, + repo: repo, + backupID: "", + } + }, + expectedErr: structerr.NewInvalidArgument("backup repository: empty BackupId"), + }, + { + desc: "missing repository", + setup: func(t *testing.T, ctx context.Context, backupSink backup.Sink, backupLocator backup.Locator) setupData { + cfg, client := setupRepositoryServiceWithoutRepo(t, + testserver.WithBackupSink(backupSink), + testserver.WithBackupLocator(backupLocator), + ) + + return setupData{ + cfg: cfg, + client: client, + repo: nil, + backupID: "abc123", + } + }, + expectedErr: structerr.NewInvalidArgument(testhelper.GitalyOrPraefect( + "backup repository: repository not set", + "repo scoped: repository not set", + )), + }, + { + desc: "repository with no branches", + setup: func(t *testing.T, ctx context.Context, backupSink backup.Sink, backupLocator backup.Locator) setupData { + cfg, client := setupRepositoryServiceWithoutRepo(t, + testserver.WithBackupSink(backupSink), + testserver.WithBackupLocator(backupLocator), + ) + + repo, _ := gittest.CreateRepository(t, ctx, cfg) + + return setupData{ + cfg: cfg, + client: client, + repo: repo, + backupID: "abc123", + } + }, + expectedErr: structerr.NewFailedPrecondition("backup repository: manager: repository empty: repository skipped").WithDetail( + &gitalypb.BackupRepositoryResponse_SkippedError{}, + ), + }, + { + desc: "missing backup sink", + setup: func(t *testing.T, ctx context.Context, backupSink backup.Sink, backupLocator backup.Locator) setupData { + cfg, client := setupRepositoryServiceWithoutRepo(t, + testserver.WithBackupLocator(backupLocator), + ) + + repo, _ := gittest.CreateRepository(t, ctx, cfg) + + return setupData{ + cfg: cfg, + client: client, + repo: repo, + backupID: "abc123", + } + }, + expectedErr: structerr.NewFailedPrecondition("backup repository: server-side backups are not configured"), + }, + { + desc: "missing backup locator", + setup: func(t *testing.T, ctx context.Context, backupSink backup.Sink, backupLocator backup.Locator) setupData { + cfg, client := setupRepositoryServiceWithoutRepo(t, + testserver.WithBackupSink(backupSink), + ) + + repo, _ := gittest.CreateRepository(t, ctx, cfg) + + return setupData{ + cfg: cfg, + client: client, + repo: repo, + backupID: "abc123", + } + }, + expectedErr: structerr.NewFailedPrecondition("backup repository: server-side backups are not configured"), + }, + } { + tc := tc + + t.Run(tc.desc, func(t *testing.T) { + t.Parallel() + + backupRoot := testhelper.TempDir(t) + backupSink, err := backup.ResolveSink(ctx, backupRoot) + require.NoError(t, err) + + backupLocator, err := backup.ResolveLocator("pointer", backupSink) + require.NoError(t, err) + + data := tc.setup(t, ctx, backupSink, backupLocator) + + response, err := data.client.BackupRepository(ctx, &gitalypb.BackupRepositoryRequest{ + Repository: data.repo, + VanityRepository: data.repo, + BackupId: data.backupID, + }) + if tc.expectedErr != nil { + testhelper.RequireGrpcError(t, tc.expectedErr, err) + return + } + + require.NoError(t, err) + testhelper.ProtoEqual(t, &gitalypb.BackupRepositoryResponse{}, response) + + relativePath := strings.TrimSuffix(data.repo.GetRelativePath(), ".git") + bundlePath := filepath.Join(relativePath, data.backupID, "001.bundle") + + bundle, err := backupSink.GetReader(ctx, bundlePath) + require.NoError(t, err) + testhelper.MustClose(t, bundle) + }) + } +} diff --git a/internal/gitaly/service/repository/server.go b/internal/gitaly/service/repository/server.go index efaa0e929..0265fadb6 100644 --- a/internal/gitaly/service/repository/server.go +++ b/internal/gitaly/service/repository/server.go @@ -4,6 +4,7 @@ import ( "context" "gitlab.com/gitlab-org/gitaly/v16/client" + "gitlab.com/gitlab-org/gitaly/v16/internal/backup" "gitlab.com/gitlab-org/gitaly/v16/internal/git" "gitlab.com/gitlab-org/gitaly/v16/internal/git/catfile" "gitlab.com/gitlab-org/gitaly/v16/internal/git/housekeeping" @@ -29,6 +30,8 @@ type server struct { catfileCache catfile.Cache git2goExecutor *git2go.Executor housekeepingManager housekeeping.Manager + backupSink backup.Sink + backupLocator backup.Locator licenseCache *unarycache.Cache[git.ObjectID, *gitalypb.FindLicenseResponse] } @@ -43,6 +46,8 @@ func NewServer( connsPool *client.Pool, git2goExecutor *git2go.Executor, housekeepingManager housekeeping.Manager, + backupSink backup.Sink, + backupLocator backup.Locator, ) gitalypb.RepositoryServiceServer { return &server{ locator: locator, @@ -54,6 +59,8 @@ func NewServer( catfileCache: catfileCache, git2goExecutor: git2goExecutor, housekeepingManager: housekeepingManager, + backupSink: backupSink, + backupLocator: backupLocator, licenseCache: newLicenseCache(), } diff --git a/internal/gitaly/service/repository/testhelper_test.go b/internal/gitaly/service/repository/testhelper_test.go index 5df7c55b4..757fb0f7d 100644 --- a/internal/gitaly/service/repository/testhelper_test.go +++ b/internal/gitaly/service/repository/testhelper_test.go @@ -66,6 +66,8 @@ func runRepositoryService(tb testing.TB, cfg config.Cfg, opts ...testserver.Gita deps.GetConnsPool(), deps.GetGit2goExecutor(), deps.GetHousekeepingManager(), + deps.GetBackupSink(), + deps.GetBackupLocator(), )) gitalypb.RegisterHookServiceServer(srv, hookservice.NewServer( deps.GetHookManager(), diff --git a/internal/gitaly/service/setup/register.go b/internal/gitaly/service/setup/register.go index a9a50863f..a51219a76 100644 --- a/internal/gitaly/service/setup/register.go +++ b/internal/gitaly/service/setup/register.go @@ -99,6 +99,8 @@ func RegisterAll(srv *grpc.Server, deps *service.Dependencies) { deps.GetConnsPool(), deps.GetGit2goExecutor(), deps.GetHousekeepingManager(), + deps.GetBackupSink(), + deps.GetBackupLocator(), )) gitalypb.RegisterSSHServiceServer(srv, ssh.NewServer( deps.GetLocator(), diff --git a/internal/gitaly/service/smarthttp/testhelper_test.go b/internal/gitaly/service/smarthttp/testhelper_test.go index cb55094d1..585a4bc6a 100644 --- a/internal/gitaly/service/smarthttp/testhelper_test.go +++ b/internal/gitaly/service/smarthttp/testhelper_test.go @@ -46,6 +46,8 @@ func startSmartHTTPServerWithOptions(t *testing.T, cfg config.Cfg, opts []Server deps.GetConnsPool(), deps.GetGit2goExecutor(), deps.GetHousekeepingManager(), + deps.GetBackupSink(), + deps.GetBackupLocator(), )) gitalypb.RegisterObjectPoolServiceServer(srv, objectpool.NewServer( deps.GetLocator(), diff --git a/internal/gitaly/service/ssh/testhelper_test.go b/internal/gitaly/service/ssh/testhelper_test.go index f869ccbcb..c7ededf68 100644 --- a/internal/gitaly/service/ssh/testhelper_test.go +++ b/internal/gitaly/service/ssh/testhelper_test.go @@ -52,6 +52,8 @@ func startSSHServerWithOptions(t *testing.T, cfg config.Cfg, opts []ServerOpt, s deps.GetConnsPool(), deps.GetGit2goExecutor(), deps.GetHousekeepingManager(), + deps.GetBackupSink(), + deps.GetBackupLocator(), )) gitalypb.RegisterObjectPoolServiceServer(srv, objectpool.NewServer( deps.GetLocator(), diff --git a/internal/praefect/info_service_test.go b/internal/praefect/info_service_test.go index 882f95f4f..45cf4c589 100644 --- a/internal/praefect/info_service_test.go +++ b/internal/praefect/info_service_test.go @@ -45,6 +45,8 @@ func TestInfoService_RepositoryReplicas(t *testing.T) { deps.GetConnsPool(), deps.GetGit2goExecutor(), deps.GetHousekeepingManager(), + deps.GetBackupSink(), + deps.GetBackupLocator(), )) }, testserver.WithDisablePraefect()) cfgNodes = append(cfgNodes, &config.Node{ diff --git a/internal/praefect/verifier_test.go b/internal/praefect/verifier_test.go index f7938c34a..d4f6a1ca2 100644 --- a/internal/praefect/verifier_test.go +++ b/internal/praefect/verifier_test.go @@ -486,6 +486,8 @@ func TestVerifier(t *testing.T) { deps.GetConnsPool(), deps.GetGit2goExecutor(), deps.GetHousekeepingManager(), + deps.GetBackupSink(), + deps.GetBackupLocator(), )}) } } diff --git a/internal/testhelper/testserver/gitaly.go b/internal/testhelper/testserver/gitaly.go index f515249d0..251b037b4 100644 --- a/internal/testhelper/testserver/gitaly.go +++ b/internal/testhelper/testserver/gitaly.go @@ -11,6 +11,7 @@ import ( "github.com/stretchr/testify/require" gitalyauth "gitlab.com/gitlab-org/gitaly/v16/auth" "gitlab.com/gitlab-org/gitaly/v16/client" + "gitlab.com/gitlab-org/gitaly/v16/internal/backup" "gitlab.com/gitlab-org/gitaly/v16/internal/cache" "gitlab.com/gitlab-org/gitaly/v16/internal/git" "gitlab.com/gitlab-org/gitaly/v16/internal/git/catfile" @@ -268,6 +269,8 @@ type gitalyServerDeps struct { git2goExecutor *git2go.Executor updaterWithHooks *updateref.UpdaterWithHooks housekeepingManager housekeeping.Manager + backupSink backup.Sink + backupLocator backup.Locator } func (gsd *gitalyServerDeps) createDependencies(tb testing.TB, cfg config.Cfg) *service.Dependencies { @@ -382,6 +385,8 @@ func (gsd *gitalyServerDeps) createDependencies(tb testing.TB, cfg config.Cfg) * UpdaterWithHooks: gsd.updaterWithHooks, HousekeepingManager: gsd.housekeepingManager, PartitionManager: partitionManager, + BackupSink: gsd.backupSink, + BackupLocator: gsd.backupLocator, } } @@ -487,3 +492,19 @@ func WithHousekeepingManager(manager housekeeping.Manager) GitalyServerOpt { return deps } } + +// WithBackupSink sets the backup.Sink that will be used for Gitaly services +func WithBackupSink(backupSink backup.Sink) GitalyServerOpt { + return func(deps gitalyServerDeps) gitalyServerDeps { + deps.backupSink = backupSink + return deps + } +} + +// WithBackupLocator sets the backup.Locator that will be used for Gitaly services +func WithBackupLocator(backupLocator backup.Locator) GitalyServerOpt { + return func(deps gitalyServerDeps) gitalyServerDeps { + deps.backupLocator = backupLocator + return deps + } +} diff --git a/proto/go/gitalypb/repository.pb.go b/proto/go/gitalypb/repository.pb.go index 9cac82eb7..5ac1b82f7 100644 --- a/proto/go/gitalypb/repository.pb.go +++ b/proto/go/gitalypb/repository.pb.go @@ -4762,6 +4762,112 @@ func (*RemoveAllResponse) Descriptor() ([]byte, []int) { return file_repository_proto_rawDescGZIP(), []int{82} } +// BackupRepository is a request for the BackupRepository RPC. +type BackupRepositoryRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Repository is the repository to be backed up. + Repository *Repository `protobuf:"bytes,1,opt,name=repository,proto3" json:"repository,omitempty"` + // VanityRepository is used to determine the backup path. + VanityRepository *Repository `protobuf:"bytes,2,opt,name=vanity_repository,json=vanityRepository,proto3" json:"vanity_repository,omitempty"` + // BackupId is the label used to identify this backup when restoring. + BackupId string `protobuf:"bytes,3,opt,name=backup_id,json=backupId,proto3" json:"backup_id,omitempty"` +} + +func (x *BackupRepositoryRequest) Reset() { + *x = BackupRepositoryRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_repository_proto_msgTypes[83] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BackupRepositoryRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BackupRepositoryRequest) ProtoMessage() {} + +func (x *BackupRepositoryRequest) ProtoReflect() protoreflect.Message { + mi := &file_repository_proto_msgTypes[83] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BackupRepositoryRequest.ProtoReflect.Descriptor instead. +func (*BackupRepositoryRequest) Descriptor() ([]byte, []int) { + return file_repository_proto_rawDescGZIP(), []int{83} +} + +func (x *BackupRepositoryRequest) GetRepository() *Repository { + if x != nil { + return x.Repository + } + return nil +} + +func (x *BackupRepositoryRequest) GetVanityRepository() *Repository { + if x != nil { + return x.VanityRepository + } + return nil +} + +func (x *BackupRepositoryRequest) GetBackupId() string { + if x != nil { + return x.BackupId + } + return "" +} + +// BackupRepositoryResponse is a response for the BackupRepository RPC. +type BackupRepositoryResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *BackupRepositoryResponse) Reset() { + *x = BackupRepositoryResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_repository_proto_msgTypes[84] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BackupRepositoryResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BackupRepositoryResponse) ProtoMessage() {} + +func (x *BackupRepositoryResponse) ProtoReflect() protoreflect.Message { + mi := &file_repository_proto_msgTypes[84] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BackupRepositoryResponse.ProtoReflect.Descriptor instead. +func (*BackupRepositoryResponse) Descriptor() ([]byte, []int) { + return file_repository_proto_rawDescGZIP(), []int{84} +} + // ReferencesInfo hosts information about references. type RepositoryInfoResponse_ReferencesInfo struct { state protoimpl.MessageState @@ -4786,7 +4892,7 @@ type RepositoryInfoResponse_ReferencesInfo struct { func (x *RepositoryInfoResponse_ReferencesInfo) Reset() { *x = RepositoryInfoResponse_ReferencesInfo{} if protoimpl.UnsafeEnabled { - mi := &file_repository_proto_msgTypes[83] + mi := &file_repository_proto_msgTypes[85] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4799,7 +4905,7 @@ func (x *RepositoryInfoResponse_ReferencesInfo) String() string { func (*RepositoryInfoResponse_ReferencesInfo) ProtoMessage() {} func (x *RepositoryInfoResponse_ReferencesInfo) ProtoReflect() protoreflect.Message { - mi := &file_repository_proto_msgTypes[83] + mi := &file_repository_proto_msgTypes[85] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4865,7 +4971,7 @@ type RepositoryInfoResponse_ObjectsInfo struct { func (x *RepositoryInfoResponse_ObjectsInfo) Reset() { *x = RepositoryInfoResponse_ObjectsInfo{} if protoimpl.UnsafeEnabled { - mi := &file_repository_proto_msgTypes[84] + mi := &file_repository_proto_msgTypes[86] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4878,7 +4984,7 @@ func (x *RepositoryInfoResponse_ObjectsInfo) String() string { func (*RepositoryInfoResponse_ObjectsInfo) ProtoMessage() {} func (x *RepositoryInfoResponse_ObjectsInfo) ProtoReflect() protoreflect.Message { - mi := &file_repository_proto_msgTypes[84] + mi := &file_repository_proto_msgTypes[86] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4949,7 +5055,7 @@ type GetRawChangesResponse_RawChange struct { func (x *GetRawChangesResponse_RawChange) Reset() { *x = GetRawChangesResponse_RawChange{} if protoimpl.UnsafeEnabled { - mi := &file_repository_proto_msgTypes[85] + mi := &file_repository_proto_msgTypes[87] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4962,7 +5068,7 @@ func (x *GetRawChangesResponse_RawChange) String() string { func (*GetRawChangesResponse_RawChange) ProtoMessage() {} func (x *GetRawChangesResponse_RawChange) ProtoReflect() protoreflect.Message { - mi := &file_repository_proto_msgTypes[85] + mi := &file_repository_proto_msgTypes[87] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5034,6 +5140,45 @@ func (x *GetRawChangesResponse_RawChange) GetOldPathBytes() []byte { return nil } +// SkippedError is returned when the repository backup has been skipped. +type BackupRepositoryResponse_SkippedError struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *BackupRepositoryResponse_SkippedError) Reset() { + *x = BackupRepositoryResponse_SkippedError{} + if protoimpl.UnsafeEnabled { + mi := &file_repository_proto_msgTypes[88] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BackupRepositoryResponse_SkippedError) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BackupRepositoryResponse_SkippedError) ProtoMessage() {} + +func (x *BackupRepositoryResponse_SkippedError) ProtoReflect() protoreflect.Message { + mi := &file_repository_proto_msgTypes[88] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BackupRepositoryResponse_SkippedError.ProtoReflect.Descriptor instead. +func (*BackupRepositoryResponse_SkippedError) Descriptor() ([]byte, []int) { + return file_repository_proto_rawDescGZIP(), []int{84, 0} +} + var File_repository_proto protoreflect.FileDescriptor var file_repository_proto_rawDesc = []byte{ @@ -5564,7 +5709,21 @@ var file_repository_proto_rawDesc = []byte{ 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x13, 0x0a, 0x11, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x41, 0x6c, 0x6c, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x8f, 0x1e, 0x0a, 0x11, 0x52, 0x65, 0x70, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xb1, 0x01, 0x0a, 0x17, 0x42, 0x61, 0x63, + 0x6b, 0x75, 0x70, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, + 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, + 0x79, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x42, 0x04, 0x98, 0xc6, + 0x2c, 0x01, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x3f, + 0x0a, 0x11, 0x76, 0x61, 0x6e, 0x69, 0x74, 0x79, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x69, 0x74, 0x61, + 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x10, 0x76, + 0x61, 0x6e, 0x69, 0x74, 0x79, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, + 0x1b, 0x0a, 0x09, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x49, 0x64, 0x22, 0x2a, 0x0a, 0x18, + 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x0e, 0x0a, 0x0c, 0x53, 0x6b, 0x69, 0x70, + 0x70, 0x65, 0x64, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x32, 0xee, 0x1e, 0x0a, 0x11, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5d, 0x0a, 0x10, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x70, 0x6f, @@ -5805,11 +5964,17 @@ var file_repository_proto_rawDesc = []byte{ 0x18, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x04, 0x80, 0x98, 0x28, 0x01, 0x42, 0x34, 0x5a, 0x32, 0x67, 0x69, - 0x74, 0x6c, 0x61, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2d, - 0x6f, 0x72, 0x67, 0x2f, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2f, 0x76, 0x31, 0x36, 0x2f, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x70, 0x62, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x04, 0x80, 0x98, 0x28, 0x01, 0x12, 0x5d, 0x0a, 0x10, 0x42, 0x61, + 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x1f, + 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x20, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, + 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x02, 0x42, 0x34, 0x5a, 0x32, 0x67, 0x69, 0x74, + 0x6c, 0x61, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2d, 0x6f, + 0x72, 0x67, 0x2f, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2f, 0x76, 0x31, 0x36, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x70, 0x62, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -5825,7 +5990,7 @@ func file_repository_proto_rawDescGZIP() []byte { } var file_repository_proto_enumTypes = make([]protoimpl.EnumInfo, 3) -var file_repository_proto_msgTypes = make([]protoimpl.MessageInfo, 86) +var file_repository_proto_msgTypes = make([]protoimpl.MessageInfo, 89) var file_repository_proto_goTypes = []interface{}{ (GetArchiveRequest_Format)(0), // 0: gitaly.GetArchiveRequest.Format (GetRawChangesResponse_RawChange_Operation)(0), // 1: gitaly.GetRawChangesResponse.RawChange.Operation @@ -5913,152 +6078,159 @@ var file_repository_proto_goTypes = []interface{}{ (*FullPathResponse)(nil), // 83: gitaly.FullPathResponse (*RemoveAllRequest)(nil), // 84: gitaly.RemoveAllRequest (*RemoveAllResponse)(nil), // 85: gitaly.RemoveAllResponse - (*RepositoryInfoResponse_ReferencesInfo)(nil), // 86: gitaly.RepositoryInfoResponse.ReferencesInfo - (*RepositoryInfoResponse_ObjectsInfo)(nil), // 87: gitaly.RepositoryInfoResponse.ObjectsInfo - (*GetRawChangesResponse_RawChange)(nil), // 88: gitaly.GetRawChangesResponse.RawChange - (*Repository)(nil), // 89: gitaly.Repository - (ObjectFormat)(0), // 90: gitaly.ObjectFormat + (*BackupRepositoryRequest)(nil), // 86: gitaly.BackupRepositoryRequest + (*BackupRepositoryResponse)(nil), // 87: gitaly.BackupRepositoryResponse + (*RepositoryInfoResponse_ReferencesInfo)(nil), // 88: gitaly.RepositoryInfoResponse.ReferencesInfo + (*RepositoryInfoResponse_ObjectsInfo)(nil), // 89: gitaly.RepositoryInfoResponse.ObjectsInfo + (*GetRawChangesResponse_RawChange)(nil), // 90: gitaly.GetRawChangesResponse.RawChange + (*BackupRepositoryResponse_SkippedError)(nil), // 91: gitaly.BackupRepositoryResponse.SkippedError + (*Repository)(nil), // 92: gitaly.Repository + (ObjectFormat)(0), // 93: gitaly.ObjectFormat } var file_repository_proto_depIdxs = []int32{ - 89, // 0: gitaly.RepositoryExistsRequest.repository:type_name -> gitaly.Repository - 89, // 1: gitaly.RepositorySizeRequest.repository:type_name -> gitaly.Repository - 89, // 2: gitaly.RepositoryInfoRequest.repository:type_name -> gitaly.Repository - 86, // 3: gitaly.RepositoryInfoResponse.references:type_name -> gitaly.RepositoryInfoResponse.ReferencesInfo - 87, // 4: gitaly.RepositoryInfoResponse.objects:type_name -> gitaly.RepositoryInfoResponse.ObjectsInfo - 89, // 5: gitaly.ObjectFormatRequest.repository:type_name -> gitaly.Repository - 90, // 6: gitaly.ObjectFormatResponse.format:type_name -> gitaly.ObjectFormat - 89, // 7: gitaly.ApplyGitattributesRequest.repository:type_name -> gitaly.Repository - 89, // 8: gitaly.FetchBundleRequest.repository:type_name -> gitaly.Repository - 89, // 9: gitaly.FetchRemoteRequest.repository:type_name -> gitaly.Repository + 92, // 0: gitaly.RepositoryExistsRequest.repository:type_name -> gitaly.Repository + 92, // 1: gitaly.RepositorySizeRequest.repository:type_name -> gitaly.Repository + 92, // 2: gitaly.RepositoryInfoRequest.repository:type_name -> gitaly.Repository + 88, // 3: gitaly.RepositoryInfoResponse.references:type_name -> gitaly.RepositoryInfoResponse.ReferencesInfo + 89, // 4: gitaly.RepositoryInfoResponse.objects:type_name -> gitaly.RepositoryInfoResponse.ObjectsInfo + 92, // 5: gitaly.ObjectFormatRequest.repository:type_name -> gitaly.Repository + 93, // 6: gitaly.ObjectFormatResponse.format:type_name -> gitaly.ObjectFormat + 92, // 7: gitaly.ApplyGitattributesRequest.repository:type_name -> gitaly.Repository + 92, // 8: gitaly.FetchBundleRequest.repository:type_name -> gitaly.Repository + 92, // 9: gitaly.FetchRemoteRequest.repository:type_name -> gitaly.Repository 67, // 10: gitaly.FetchRemoteRequest.remote_params:type_name -> gitaly.Remote - 89, // 11: gitaly.CreateRepositoryRequest.repository:type_name -> gitaly.Repository - 90, // 12: gitaly.CreateRepositoryRequest.object_format:type_name -> gitaly.ObjectFormat - 89, // 13: gitaly.GetArchiveRequest.repository:type_name -> gitaly.Repository + 92, // 11: gitaly.CreateRepositoryRequest.repository:type_name -> gitaly.Repository + 93, // 12: gitaly.CreateRepositoryRequest.object_format:type_name -> gitaly.ObjectFormat + 92, // 13: gitaly.GetArchiveRequest.repository:type_name -> gitaly.Repository 0, // 14: gitaly.GetArchiveRequest.format:type_name -> gitaly.GetArchiveRequest.Format - 89, // 15: gitaly.HasLocalBranchesRequest.repository:type_name -> gitaly.Repository - 89, // 16: gitaly.FetchSourceBranchRequest.repository:type_name -> gitaly.Repository - 89, // 17: gitaly.FetchSourceBranchRequest.source_repository:type_name -> gitaly.Repository - 89, // 18: gitaly.FsckRequest.repository:type_name -> gitaly.Repository - 89, // 19: gitaly.WriteRefRequest.repository:type_name -> gitaly.Repository - 89, // 20: gitaly.FindMergeBaseRequest.repository:type_name -> gitaly.Repository - 89, // 21: gitaly.CreateForkRequest.repository:type_name -> gitaly.Repository - 89, // 22: gitaly.CreateForkRequest.source_repository:type_name -> gitaly.Repository - 89, // 23: gitaly.CreateRepositoryFromURLRequest.repository:type_name -> gitaly.Repository - 89, // 24: gitaly.CreateBundleRequest.repository:type_name -> gitaly.Repository - 89, // 25: gitaly.CreateBundleFromRefListRequest.repository:type_name -> gitaly.Repository - 89, // 26: gitaly.GetConfigRequest.repository:type_name -> gitaly.Repository - 89, // 27: gitaly.RestoreCustomHooksRequest.repository:type_name -> gitaly.Repository - 89, // 28: gitaly.SetCustomHooksRequest.repository:type_name -> gitaly.Repository - 89, // 29: gitaly.BackupCustomHooksRequest.repository:type_name -> gitaly.Repository - 89, // 30: gitaly.GetCustomHooksRequest.repository:type_name -> gitaly.Repository - 89, // 31: gitaly.CreateRepositoryFromBundleRequest.repository:type_name -> gitaly.Repository - 89, // 32: gitaly.FindLicenseRequest.repository:type_name -> gitaly.Repository - 89, // 33: gitaly.GetInfoAttributesRequest.repository:type_name -> gitaly.Repository - 89, // 34: gitaly.CalculateChecksumRequest.repository:type_name -> gitaly.Repository - 89, // 35: gitaly.GetSnapshotRequest.repository:type_name -> gitaly.Repository - 89, // 36: gitaly.CreateRepositoryFromSnapshotRequest.repository:type_name -> gitaly.Repository - 89, // 37: gitaly.GetRawChangesRequest.repository:type_name -> gitaly.Repository - 88, // 38: gitaly.GetRawChangesResponse.raw_changes:type_name -> gitaly.GetRawChangesResponse.RawChange - 89, // 39: gitaly.SearchFilesByNameRequest.repository:type_name -> gitaly.Repository - 89, // 40: gitaly.SearchFilesByContentRequest.repository:type_name -> gitaly.Repository - 89, // 41: gitaly.GetObjectDirectorySizeRequest.repository:type_name -> gitaly.Repository - 89, // 42: gitaly.RemoveRepositoryRequest.repository:type_name -> gitaly.Repository - 89, // 43: gitaly.RenameRepositoryRequest.repository:type_name -> gitaly.Repository - 89, // 44: gitaly.ReplicateRepositoryRequest.repository:type_name -> gitaly.Repository - 89, // 45: gitaly.ReplicateRepositoryRequest.source:type_name -> gitaly.Repository - 89, // 46: gitaly.OptimizeRepositoryRequest.repository:type_name -> gitaly.Repository + 92, // 15: gitaly.HasLocalBranchesRequest.repository:type_name -> gitaly.Repository + 92, // 16: gitaly.FetchSourceBranchRequest.repository:type_name -> gitaly.Repository + 92, // 17: gitaly.FetchSourceBranchRequest.source_repository:type_name -> gitaly.Repository + 92, // 18: gitaly.FsckRequest.repository:type_name -> gitaly.Repository + 92, // 19: gitaly.WriteRefRequest.repository:type_name -> gitaly.Repository + 92, // 20: gitaly.FindMergeBaseRequest.repository:type_name -> gitaly.Repository + 92, // 21: gitaly.CreateForkRequest.repository:type_name -> gitaly.Repository + 92, // 22: gitaly.CreateForkRequest.source_repository:type_name -> gitaly.Repository + 92, // 23: gitaly.CreateRepositoryFromURLRequest.repository:type_name -> gitaly.Repository + 92, // 24: gitaly.CreateBundleRequest.repository:type_name -> gitaly.Repository + 92, // 25: gitaly.CreateBundleFromRefListRequest.repository:type_name -> gitaly.Repository + 92, // 26: gitaly.GetConfigRequest.repository:type_name -> gitaly.Repository + 92, // 27: gitaly.RestoreCustomHooksRequest.repository:type_name -> gitaly.Repository + 92, // 28: gitaly.SetCustomHooksRequest.repository:type_name -> gitaly.Repository + 92, // 29: gitaly.BackupCustomHooksRequest.repository:type_name -> gitaly.Repository + 92, // 30: gitaly.GetCustomHooksRequest.repository:type_name -> gitaly.Repository + 92, // 31: gitaly.CreateRepositoryFromBundleRequest.repository:type_name -> gitaly.Repository + 92, // 32: gitaly.FindLicenseRequest.repository:type_name -> gitaly.Repository + 92, // 33: gitaly.GetInfoAttributesRequest.repository:type_name -> gitaly.Repository + 92, // 34: gitaly.CalculateChecksumRequest.repository:type_name -> gitaly.Repository + 92, // 35: gitaly.GetSnapshotRequest.repository:type_name -> gitaly.Repository + 92, // 36: gitaly.CreateRepositoryFromSnapshotRequest.repository:type_name -> gitaly.Repository + 92, // 37: gitaly.GetRawChangesRequest.repository:type_name -> gitaly.Repository + 90, // 38: gitaly.GetRawChangesResponse.raw_changes:type_name -> gitaly.GetRawChangesResponse.RawChange + 92, // 39: gitaly.SearchFilesByNameRequest.repository:type_name -> gitaly.Repository + 92, // 40: gitaly.SearchFilesByContentRequest.repository:type_name -> gitaly.Repository + 92, // 41: gitaly.GetObjectDirectorySizeRequest.repository:type_name -> gitaly.Repository + 92, // 42: gitaly.RemoveRepositoryRequest.repository:type_name -> gitaly.Repository + 92, // 43: gitaly.RenameRepositoryRequest.repository:type_name -> gitaly.Repository + 92, // 44: gitaly.ReplicateRepositoryRequest.repository:type_name -> gitaly.Repository + 92, // 45: gitaly.ReplicateRepositoryRequest.source:type_name -> gitaly.Repository + 92, // 46: gitaly.OptimizeRepositoryRequest.repository:type_name -> gitaly.Repository 2, // 47: gitaly.OptimizeRepositoryRequest.strategy:type_name -> gitaly.OptimizeRepositoryRequest.Strategy - 89, // 48: gitaly.PruneUnreachableObjectsRequest.repository:type_name -> gitaly.Repository - 89, // 49: gitaly.SetFullPathRequest.repository:type_name -> gitaly.Repository - 89, // 50: gitaly.FullPathRequest.repository:type_name -> gitaly.Repository - 1, // 51: gitaly.GetRawChangesResponse.RawChange.operation:type_name -> gitaly.GetRawChangesResponse.RawChange.Operation - 3, // 52: gitaly.RepositoryService.RepositoryExists:input_type -> gitaly.RepositoryExistsRequest - 5, // 53: gitaly.RepositoryService.RepositorySize:input_type -> gitaly.RepositorySizeRequest - 7, // 54: gitaly.RepositoryService.RepositoryInfo:input_type -> gitaly.RepositoryInfoRequest - 9, // 55: gitaly.RepositoryService.ObjectFormat:input_type -> gitaly.ObjectFormatRequest - 11, // 56: gitaly.RepositoryService.ApplyGitattributes:input_type -> gitaly.ApplyGitattributesRequest - 15, // 57: gitaly.RepositoryService.FetchRemote:input_type -> gitaly.FetchRemoteRequest - 17, // 58: gitaly.RepositoryService.CreateRepository:input_type -> gitaly.CreateRepositoryRequest - 19, // 59: gitaly.RepositoryService.GetArchive:input_type -> gitaly.GetArchiveRequest - 21, // 60: gitaly.RepositoryService.HasLocalBranches:input_type -> gitaly.HasLocalBranchesRequest - 23, // 61: gitaly.RepositoryService.FetchSourceBranch:input_type -> gitaly.FetchSourceBranchRequest - 25, // 62: gitaly.RepositoryService.Fsck:input_type -> gitaly.FsckRequest - 27, // 63: gitaly.RepositoryService.WriteRef:input_type -> gitaly.WriteRefRequest - 29, // 64: gitaly.RepositoryService.FindMergeBase:input_type -> gitaly.FindMergeBaseRequest - 31, // 65: gitaly.RepositoryService.CreateFork:input_type -> gitaly.CreateForkRequest - 33, // 66: gitaly.RepositoryService.CreateRepositoryFromURL:input_type -> gitaly.CreateRepositoryFromURLRequest - 35, // 67: gitaly.RepositoryService.CreateBundle:input_type -> gitaly.CreateBundleRequest - 37, // 68: gitaly.RepositoryService.CreateBundleFromRefList:input_type -> gitaly.CreateBundleFromRefListRequest - 13, // 69: gitaly.RepositoryService.FetchBundle:input_type -> gitaly.FetchBundleRequest - 49, // 70: gitaly.RepositoryService.CreateRepositoryFromBundle:input_type -> gitaly.CreateRepositoryFromBundleRequest - 39, // 71: gitaly.RepositoryService.GetConfig:input_type -> gitaly.GetConfigRequest - 51, // 72: gitaly.RepositoryService.FindLicense:input_type -> gitaly.FindLicenseRequest - 53, // 73: gitaly.RepositoryService.GetInfoAttributes:input_type -> gitaly.GetInfoAttributesRequest - 55, // 74: gitaly.RepositoryService.CalculateChecksum:input_type -> gitaly.CalculateChecksumRequest - 57, // 75: gitaly.RepositoryService.GetSnapshot:input_type -> gitaly.GetSnapshotRequest - 59, // 76: gitaly.RepositoryService.CreateRepositoryFromSnapshot:input_type -> gitaly.CreateRepositoryFromSnapshotRequest - 61, // 77: gitaly.RepositoryService.GetRawChanges:input_type -> gitaly.GetRawChangesRequest - 65, // 78: gitaly.RepositoryService.SearchFilesByContent:input_type -> gitaly.SearchFilesByContentRequest - 63, // 79: gitaly.RepositoryService.SearchFilesByName:input_type -> gitaly.SearchFilesByNameRequest - 41, // 80: gitaly.RepositoryService.RestoreCustomHooks:input_type -> gitaly.RestoreCustomHooksRequest - 42, // 81: gitaly.RepositoryService.SetCustomHooks:input_type -> gitaly.SetCustomHooksRequest - 45, // 82: gitaly.RepositoryService.BackupCustomHooks:input_type -> gitaly.BackupCustomHooksRequest - 46, // 83: gitaly.RepositoryService.GetCustomHooks:input_type -> gitaly.GetCustomHooksRequest - 68, // 84: gitaly.RepositoryService.GetObjectDirectorySize:input_type -> gitaly.GetObjectDirectorySizeRequest - 70, // 85: gitaly.RepositoryService.RemoveRepository:input_type -> gitaly.RemoveRepositoryRequest - 72, // 86: gitaly.RepositoryService.RenameRepository:input_type -> gitaly.RenameRepositoryRequest - 74, // 87: gitaly.RepositoryService.ReplicateRepository:input_type -> gitaly.ReplicateRepositoryRequest - 76, // 88: gitaly.RepositoryService.OptimizeRepository:input_type -> gitaly.OptimizeRepositoryRequest - 78, // 89: gitaly.RepositoryService.PruneUnreachableObjects:input_type -> gitaly.PruneUnreachableObjectsRequest - 80, // 90: gitaly.RepositoryService.SetFullPath:input_type -> gitaly.SetFullPathRequest - 82, // 91: gitaly.RepositoryService.FullPath:input_type -> gitaly.FullPathRequest - 84, // 92: gitaly.RepositoryService.RemoveAll:input_type -> gitaly.RemoveAllRequest - 4, // 93: gitaly.RepositoryService.RepositoryExists:output_type -> gitaly.RepositoryExistsResponse - 6, // 94: gitaly.RepositoryService.RepositorySize:output_type -> gitaly.RepositorySizeResponse - 8, // 95: gitaly.RepositoryService.RepositoryInfo:output_type -> gitaly.RepositoryInfoResponse - 10, // 96: gitaly.RepositoryService.ObjectFormat:output_type -> gitaly.ObjectFormatResponse - 12, // 97: gitaly.RepositoryService.ApplyGitattributes:output_type -> gitaly.ApplyGitattributesResponse - 16, // 98: gitaly.RepositoryService.FetchRemote:output_type -> gitaly.FetchRemoteResponse - 18, // 99: gitaly.RepositoryService.CreateRepository:output_type -> gitaly.CreateRepositoryResponse - 20, // 100: gitaly.RepositoryService.GetArchive:output_type -> gitaly.GetArchiveResponse - 22, // 101: gitaly.RepositoryService.HasLocalBranches:output_type -> gitaly.HasLocalBranchesResponse - 24, // 102: gitaly.RepositoryService.FetchSourceBranch:output_type -> gitaly.FetchSourceBranchResponse - 26, // 103: gitaly.RepositoryService.Fsck:output_type -> gitaly.FsckResponse - 28, // 104: gitaly.RepositoryService.WriteRef:output_type -> gitaly.WriteRefResponse - 30, // 105: gitaly.RepositoryService.FindMergeBase:output_type -> gitaly.FindMergeBaseResponse - 32, // 106: gitaly.RepositoryService.CreateFork:output_type -> gitaly.CreateForkResponse - 34, // 107: gitaly.RepositoryService.CreateRepositoryFromURL:output_type -> gitaly.CreateRepositoryFromURLResponse - 36, // 108: gitaly.RepositoryService.CreateBundle:output_type -> gitaly.CreateBundleResponse - 38, // 109: gitaly.RepositoryService.CreateBundleFromRefList:output_type -> gitaly.CreateBundleFromRefListResponse - 14, // 110: gitaly.RepositoryService.FetchBundle:output_type -> gitaly.FetchBundleResponse - 50, // 111: gitaly.RepositoryService.CreateRepositoryFromBundle:output_type -> gitaly.CreateRepositoryFromBundleResponse - 40, // 112: gitaly.RepositoryService.GetConfig:output_type -> gitaly.GetConfigResponse - 52, // 113: gitaly.RepositoryService.FindLicense:output_type -> gitaly.FindLicenseResponse - 54, // 114: gitaly.RepositoryService.GetInfoAttributes:output_type -> gitaly.GetInfoAttributesResponse - 56, // 115: gitaly.RepositoryService.CalculateChecksum:output_type -> gitaly.CalculateChecksumResponse - 58, // 116: gitaly.RepositoryService.GetSnapshot:output_type -> gitaly.GetSnapshotResponse - 60, // 117: gitaly.RepositoryService.CreateRepositoryFromSnapshot:output_type -> gitaly.CreateRepositoryFromSnapshotResponse - 62, // 118: gitaly.RepositoryService.GetRawChanges:output_type -> gitaly.GetRawChangesResponse - 66, // 119: gitaly.RepositoryService.SearchFilesByContent:output_type -> gitaly.SearchFilesByContentResponse - 64, // 120: gitaly.RepositoryService.SearchFilesByName:output_type -> gitaly.SearchFilesByNameResponse - 43, // 121: gitaly.RepositoryService.RestoreCustomHooks:output_type -> gitaly.RestoreCustomHooksResponse - 44, // 122: gitaly.RepositoryService.SetCustomHooks:output_type -> gitaly.SetCustomHooksResponse - 47, // 123: gitaly.RepositoryService.BackupCustomHooks:output_type -> gitaly.BackupCustomHooksResponse - 48, // 124: gitaly.RepositoryService.GetCustomHooks:output_type -> gitaly.GetCustomHooksResponse - 69, // 125: gitaly.RepositoryService.GetObjectDirectorySize:output_type -> gitaly.GetObjectDirectorySizeResponse - 71, // 126: gitaly.RepositoryService.RemoveRepository:output_type -> gitaly.RemoveRepositoryResponse - 73, // 127: gitaly.RepositoryService.RenameRepository:output_type -> gitaly.RenameRepositoryResponse - 75, // 128: gitaly.RepositoryService.ReplicateRepository:output_type -> gitaly.ReplicateRepositoryResponse - 77, // 129: gitaly.RepositoryService.OptimizeRepository:output_type -> gitaly.OptimizeRepositoryResponse - 79, // 130: gitaly.RepositoryService.PruneUnreachableObjects:output_type -> gitaly.PruneUnreachableObjectsResponse - 81, // 131: gitaly.RepositoryService.SetFullPath:output_type -> gitaly.SetFullPathResponse - 83, // 132: gitaly.RepositoryService.FullPath:output_type -> gitaly.FullPathResponse - 85, // 133: gitaly.RepositoryService.RemoveAll:output_type -> gitaly.RemoveAllResponse - 93, // [93:134] is the sub-list for method output_type - 52, // [52:93] is the sub-list for method input_type - 52, // [52:52] is the sub-list for extension type_name - 52, // [52:52] is the sub-list for extension extendee - 0, // [0:52] is the sub-list for field type_name + 92, // 48: gitaly.PruneUnreachableObjectsRequest.repository:type_name -> gitaly.Repository + 92, // 49: gitaly.SetFullPathRequest.repository:type_name -> gitaly.Repository + 92, // 50: gitaly.FullPathRequest.repository:type_name -> gitaly.Repository + 92, // 51: gitaly.BackupRepositoryRequest.repository:type_name -> gitaly.Repository + 92, // 52: gitaly.BackupRepositoryRequest.vanity_repository:type_name -> gitaly.Repository + 1, // 53: gitaly.GetRawChangesResponse.RawChange.operation:type_name -> gitaly.GetRawChangesResponse.RawChange.Operation + 3, // 54: gitaly.RepositoryService.RepositoryExists:input_type -> gitaly.RepositoryExistsRequest + 5, // 55: gitaly.RepositoryService.RepositorySize:input_type -> gitaly.RepositorySizeRequest + 7, // 56: gitaly.RepositoryService.RepositoryInfo:input_type -> gitaly.RepositoryInfoRequest + 9, // 57: gitaly.RepositoryService.ObjectFormat:input_type -> gitaly.ObjectFormatRequest + 11, // 58: gitaly.RepositoryService.ApplyGitattributes:input_type -> gitaly.ApplyGitattributesRequest + 15, // 59: gitaly.RepositoryService.FetchRemote:input_type -> gitaly.FetchRemoteRequest + 17, // 60: gitaly.RepositoryService.CreateRepository:input_type -> gitaly.CreateRepositoryRequest + 19, // 61: gitaly.RepositoryService.GetArchive:input_type -> gitaly.GetArchiveRequest + 21, // 62: gitaly.RepositoryService.HasLocalBranches:input_type -> gitaly.HasLocalBranchesRequest + 23, // 63: gitaly.RepositoryService.FetchSourceBranch:input_type -> gitaly.FetchSourceBranchRequest + 25, // 64: gitaly.RepositoryService.Fsck:input_type -> gitaly.FsckRequest + 27, // 65: gitaly.RepositoryService.WriteRef:input_type -> gitaly.WriteRefRequest + 29, // 66: gitaly.RepositoryService.FindMergeBase:input_type -> gitaly.FindMergeBaseRequest + 31, // 67: gitaly.RepositoryService.CreateFork:input_type -> gitaly.CreateForkRequest + 33, // 68: gitaly.RepositoryService.CreateRepositoryFromURL:input_type -> gitaly.CreateRepositoryFromURLRequest + 35, // 69: gitaly.RepositoryService.CreateBundle:input_type -> gitaly.CreateBundleRequest + 37, // 70: gitaly.RepositoryService.CreateBundleFromRefList:input_type -> gitaly.CreateBundleFromRefListRequest + 13, // 71: gitaly.RepositoryService.FetchBundle:input_type -> gitaly.FetchBundleRequest + 49, // 72: gitaly.RepositoryService.CreateRepositoryFromBundle:input_type -> gitaly.CreateRepositoryFromBundleRequest + 39, // 73: gitaly.RepositoryService.GetConfig:input_type -> gitaly.GetConfigRequest + 51, // 74: gitaly.RepositoryService.FindLicense:input_type -> gitaly.FindLicenseRequest + 53, // 75: gitaly.RepositoryService.GetInfoAttributes:input_type -> gitaly.GetInfoAttributesRequest + 55, // 76: gitaly.RepositoryService.CalculateChecksum:input_type -> gitaly.CalculateChecksumRequest + 57, // 77: gitaly.RepositoryService.GetSnapshot:input_type -> gitaly.GetSnapshotRequest + 59, // 78: gitaly.RepositoryService.CreateRepositoryFromSnapshot:input_type -> gitaly.CreateRepositoryFromSnapshotRequest + 61, // 79: gitaly.RepositoryService.GetRawChanges:input_type -> gitaly.GetRawChangesRequest + 65, // 80: gitaly.RepositoryService.SearchFilesByContent:input_type -> gitaly.SearchFilesByContentRequest + 63, // 81: gitaly.RepositoryService.SearchFilesByName:input_type -> gitaly.SearchFilesByNameRequest + 41, // 82: gitaly.RepositoryService.RestoreCustomHooks:input_type -> gitaly.RestoreCustomHooksRequest + 42, // 83: gitaly.RepositoryService.SetCustomHooks:input_type -> gitaly.SetCustomHooksRequest + 45, // 84: gitaly.RepositoryService.BackupCustomHooks:input_type -> gitaly.BackupCustomHooksRequest + 46, // 85: gitaly.RepositoryService.GetCustomHooks:input_type -> gitaly.GetCustomHooksRequest + 68, // 86: gitaly.RepositoryService.GetObjectDirectorySize:input_type -> gitaly.GetObjectDirectorySizeRequest + 70, // 87: gitaly.RepositoryService.RemoveRepository:input_type -> gitaly.RemoveRepositoryRequest + 72, // 88: gitaly.RepositoryService.RenameRepository:input_type -> gitaly.RenameRepositoryRequest + 74, // 89: gitaly.RepositoryService.ReplicateRepository:input_type -> gitaly.ReplicateRepositoryRequest + 76, // 90: gitaly.RepositoryService.OptimizeRepository:input_type -> gitaly.OptimizeRepositoryRequest + 78, // 91: gitaly.RepositoryService.PruneUnreachableObjects:input_type -> gitaly.PruneUnreachableObjectsRequest + 80, // 92: gitaly.RepositoryService.SetFullPath:input_type -> gitaly.SetFullPathRequest + 82, // 93: gitaly.RepositoryService.FullPath:input_type -> gitaly.FullPathRequest + 84, // 94: gitaly.RepositoryService.RemoveAll:input_type -> gitaly.RemoveAllRequest + 86, // 95: gitaly.RepositoryService.BackupRepository:input_type -> gitaly.BackupRepositoryRequest + 4, // 96: gitaly.RepositoryService.RepositoryExists:output_type -> gitaly.RepositoryExistsResponse + 6, // 97: gitaly.RepositoryService.RepositorySize:output_type -> gitaly.RepositorySizeResponse + 8, // 98: gitaly.RepositoryService.RepositoryInfo:output_type -> gitaly.RepositoryInfoResponse + 10, // 99: gitaly.RepositoryService.ObjectFormat:output_type -> gitaly.ObjectFormatResponse + 12, // 100: gitaly.RepositoryService.ApplyGitattributes:output_type -> gitaly.ApplyGitattributesResponse + 16, // 101: gitaly.RepositoryService.FetchRemote:output_type -> gitaly.FetchRemoteResponse + 18, // 102: gitaly.RepositoryService.CreateRepository:output_type -> gitaly.CreateRepositoryResponse + 20, // 103: gitaly.RepositoryService.GetArchive:output_type -> gitaly.GetArchiveResponse + 22, // 104: gitaly.RepositoryService.HasLocalBranches:output_type -> gitaly.HasLocalBranchesResponse + 24, // 105: gitaly.RepositoryService.FetchSourceBranch:output_type -> gitaly.FetchSourceBranchResponse + 26, // 106: gitaly.RepositoryService.Fsck:output_type -> gitaly.FsckResponse + 28, // 107: gitaly.RepositoryService.WriteRef:output_type -> gitaly.WriteRefResponse + 30, // 108: gitaly.RepositoryService.FindMergeBase:output_type -> gitaly.FindMergeBaseResponse + 32, // 109: gitaly.RepositoryService.CreateFork:output_type -> gitaly.CreateForkResponse + 34, // 110: gitaly.RepositoryService.CreateRepositoryFromURL:output_type -> gitaly.CreateRepositoryFromURLResponse + 36, // 111: gitaly.RepositoryService.CreateBundle:output_type -> gitaly.CreateBundleResponse + 38, // 112: gitaly.RepositoryService.CreateBundleFromRefList:output_type -> gitaly.CreateBundleFromRefListResponse + 14, // 113: gitaly.RepositoryService.FetchBundle:output_type -> gitaly.FetchBundleResponse + 50, // 114: gitaly.RepositoryService.CreateRepositoryFromBundle:output_type -> gitaly.CreateRepositoryFromBundleResponse + 40, // 115: gitaly.RepositoryService.GetConfig:output_type -> gitaly.GetConfigResponse + 52, // 116: gitaly.RepositoryService.FindLicense:output_type -> gitaly.FindLicenseResponse + 54, // 117: gitaly.RepositoryService.GetInfoAttributes:output_type -> gitaly.GetInfoAttributesResponse + 56, // 118: gitaly.RepositoryService.CalculateChecksum:output_type -> gitaly.CalculateChecksumResponse + 58, // 119: gitaly.RepositoryService.GetSnapshot:output_type -> gitaly.GetSnapshotResponse + 60, // 120: gitaly.RepositoryService.CreateRepositoryFromSnapshot:output_type -> gitaly.CreateRepositoryFromSnapshotResponse + 62, // 121: gitaly.RepositoryService.GetRawChanges:output_type -> gitaly.GetRawChangesResponse + 66, // 122: gitaly.RepositoryService.SearchFilesByContent:output_type -> gitaly.SearchFilesByContentResponse + 64, // 123: gitaly.RepositoryService.SearchFilesByName:output_type -> gitaly.SearchFilesByNameResponse + 43, // 124: gitaly.RepositoryService.RestoreCustomHooks:output_type -> gitaly.RestoreCustomHooksResponse + 44, // 125: gitaly.RepositoryService.SetCustomHooks:output_type -> gitaly.SetCustomHooksResponse + 47, // 126: gitaly.RepositoryService.BackupCustomHooks:output_type -> gitaly.BackupCustomHooksResponse + 48, // 127: gitaly.RepositoryService.GetCustomHooks:output_type -> gitaly.GetCustomHooksResponse + 69, // 128: gitaly.RepositoryService.GetObjectDirectorySize:output_type -> gitaly.GetObjectDirectorySizeResponse + 71, // 129: gitaly.RepositoryService.RemoveRepository:output_type -> gitaly.RemoveRepositoryResponse + 73, // 130: gitaly.RepositoryService.RenameRepository:output_type -> gitaly.RenameRepositoryResponse + 75, // 131: gitaly.RepositoryService.ReplicateRepository:output_type -> gitaly.ReplicateRepositoryResponse + 77, // 132: gitaly.RepositoryService.OptimizeRepository:output_type -> gitaly.OptimizeRepositoryResponse + 79, // 133: gitaly.RepositoryService.PruneUnreachableObjects:output_type -> gitaly.PruneUnreachableObjectsResponse + 81, // 134: gitaly.RepositoryService.SetFullPath:output_type -> gitaly.SetFullPathResponse + 83, // 135: gitaly.RepositoryService.FullPath:output_type -> gitaly.FullPathResponse + 85, // 136: gitaly.RepositoryService.RemoveAll:output_type -> gitaly.RemoveAllResponse + 87, // 137: gitaly.RepositoryService.BackupRepository:output_type -> gitaly.BackupRepositoryResponse + 96, // [96:138] is the sub-list for method output_type + 54, // [54:96] is the sub-list for method input_type + 54, // [54:54] is the sub-list for extension type_name + 54, // [54:54] is the sub-list for extension extendee + 0, // [0:54] is the sub-list for field type_name } func init() { file_repository_proto_init() } @@ -7066,7 +7238,7 @@ func file_repository_proto_init() { } } file_repository_proto_msgTypes[83].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RepositoryInfoResponse_ReferencesInfo); i { + switch v := v.(*BackupRepositoryRequest); i { case 0: return &v.state case 1: @@ -7078,7 +7250,7 @@ func file_repository_proto_init() { } } file_repository_proto_msgTypes[84].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RepositoryInfoResponse_ObjectsInfo); i { + switch v := v.(*BackupRepositoryResponse); i { case 0: return &v.state case 1: @@ -7090,6 +7262,30 @@ func file_repository_proto_init() { } } file_repository_proto_msgTypes[85].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RepositoryInfoResponse_ReferencesInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_repository_proto_msgTypes[86].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RepositoryInfoResponse_ObjectsInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_repository_proto_msgTypes[87].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetRawChangesResponse_RawChange); i { case 0: return &v.state @@ -7101,6 +7297,18 @@ func file_repository_proto_init() { return nil } } + file_repository_proto_msgTypes[88].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BackupRepositoryResponse_SkippedError); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -7108,7 +7316,7 @@ func file_repository_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_repository_proto_rawDesc, NumEnums: 3, - NumMessages: 86, + NumMessages: 89, NumExtensions: 0, NumServices: 1, }, diff --git a/proto/go/gitalypb/repository_grpc.pb.go b/proto/go/gitalypb/repository_grpc.pb.go index db0fd8f15..8c3c77544 100644 --- a/proto/go/gitalypb/repository_grpc.pb.go +++ b/proto/go/gitalypb/repository_grpc.pb.go @@ -153,6 +153,10 @@ type RepositoryServiceClient interface { FullPath(ctx context.Context, in *FullPathRequest, opts ...grpc.CallOption) (*FullPathResponse, error) // RemoveAll deletes all repositories on a specified storage. RemoveAll(ctx context.Context, in *RemoveAllRequest, opts ...grpc.CallOption) (*RemoveAllResponse, error) + // BackupRepository creates a full backup streamed directly to + // object-storage. The backup is created synchronously. The destination must + // be configured in config.backup.go_cloud_url + BackupRepository(ctx context.Context, in *BackupRepositoryRequest, opts ...grpc.CallOption) (*BackupRepositoryResponse, error) } type repositoryServiceClient struct { @@ -888,6 +892,15 @@ func (c *repositoryServiceClient) RemoveAll(ctx context.Context, in *RemoveAllRe return out, nil } +func (c *repositoryServiceClient) BackupRepository(ctx context.Context, in *BackupRepositoryRequest, opts ...grpc.CallOption) (*BackupRepositoryResponse, error) { + out := new(BackupRepositoryResponse) + err := c.cc.Invoke(ctx, "/gitaly.RepositoryService/BackupRepository", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // RepositoryServiceServer is the server API for RepositoryService service. // All implementations must embed UnimplementedRepositoryServiceServer // for forward compatibility @@ -1023,6 +1036,10 @@ type RepositoryServiceServer interface { FullPath(context.Context, *FullPathRequest) (*FullPathResponse, error) // RemoveAll deletes all repositories on a specified storage. RemoveAll(context.Context, *RemoveAllRequest) (*RemoveAllResponse, error) + // BackupRepository creates a full backup streamed directly to + // object-storage. The backup is created synchronously. The destination must + // be configured in config.backup.go_cloud_url + BackupRepository(context.Context, *BackupRepositoryRequest) (*BackupRepositoryResponse, error) mustEmbedUnimplementedRepositoryServiceServer() } @@ -1153,6 +1170,9 @@ func (UnimplementedRepositoryServiceServer) FullPath(context.Context, *FullPathR func (UnimplementedRepositoryServiceServer) RemoveAll(context.Context, *RemoveAllRequest) (*RemoveAllResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method RemoveAll not implemented") } +func (UnimplementedRepositoryServiceServer) BackupRepository(context.Context, *BackupRepositoryRequest) (*BackupRepositoryResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method BackupRepository not implemented") +} func (UnimplementedRepositoryServiceServer) mustEmbedUnimplementedRepositoryServiceServer() {} // UnsafeRepositoryServiceServer may be embedded to opt out of forward compatibility for this service. @@ -1974,6 +1994,24 @@ func _RepositoryService_RemoveAll_Handler(srv interface{}, ctx context.Context, return interceptor(ctx, in, info, handler) } +func _RepositoryService_BackupRepository_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(BackupRepositoryRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RepositoryServiceServer).BackupRepository(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gitaly.RepositoryService/BackupRepository", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RepositoryServiceServer).BackupRepository(ctx, req.(*BackupRepositoryRequest)) + } + return interceptor(ctx, in, info, handler) +} + // RepositoryService_ServiceDesc is the grpc.ServiceDesc for RepositoryService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -2085,6 +2123,10 @@ var RepositoryService_ServiceDesc = grpc.ServiceDesc{ MethodName: "RemoveAll", Handler: _RepositoryService_RemoveAll_Handler, }, + { + MethodName: "BackupRepository", + Handler: _RepositoryService_BackupRepository_Handler, + }, }, Streams: []grpc.StreamDesc{ { diff --git a/proto/repository.proto b/proto/repository.proto index 93a96e19a..48f949bc0 100644 --- a/proto/repository.proto +++ b/proto/repository.proto @@ -343,6 +343,15 @@ service RepositoryService { rpc RemoveAll(RemoveAllRequest) returns (RemoveAllResponse) { option (intercepted_method) = true; } + + // BackupRepository creates a full backup streamed directly to + // object-storage. The backup is created synchronously. The destination must + // be configured in config.backup.go_cloud_url + rpc BackupRepository(BackupRepositoryRequest) returns (BackupRepositoryResponse) { + option (op_type) = { + op: ACCESSOR + }; + } } // This comment is left unintentionally blank. @@ -1167,3 +1176,20 @@ message RemoveAllRequest { // RemoveAllResponse is a response for the RemoveAll RPC. message RemoveAllResponse { } + +// BackupRepository is a request for the BackupRepository RPC. +message BackupRepositoryRequest { + // Repository is the repository to be backed up. + Repository repository = 1 [(target_repository)=true]; + // VanityRepository is used to determine the backup path. + Repository vanity_repository = 2; + // BackupId is the label used to identify this backup when restoring. + string backup_id = 3; +} + +// BackupRepositoryResponse is a response for the BackupRepository RPC. +message BackupRepositoryResponse { + // SkippedError is returned when the repository backup has been skipped. + message SkippedError { + } +} |