diff options
author | Patrick Steinhardt <psteinhardt@gitlab.com> | 2022-03-31 08:03:59 +0300 |
---|---|---|
committer | Patrick Steinhardt <psteinhardt@gitlab.com> | 2022-03-31 08:03:59 +0300 |
commit | a32498721df03d6a16731f7aa4ef5143f3fb60d7 (patch) | |
tree | dc234e526d18ea27665356f15dfdee8742766861 | |
parent | 438801e8fdf288fe1629365c455941467776bab2 (diff) | |
parent | a650f42c86d5a1f9e78afffe200b39bb95a89859 (diff) |
Merge branch 'pks-proto-introduce-maintenance-operations' into 'master'
proto: Introduce new "maintenance" RPC type
Closes #4079
See merge request gitlab-org/gitaly!4399
45 files changed, 1927 insertions, 229 deletions
diff --git a/internal/git/gittest/delta_islands.go b/internal/git/gittest/delta_islands.go index 286e40389..23f2e8884 100644 --- a/internal/git/gittest/delta_islands.go +++ b/internal/git/gittest/delta_islands.go @@ -15,6 +15,8 @@ import ( // TestDeltaIslands is based on the tests in // https://github.com/git/git/blob/master/t/t5320-delta-islands.sh . func TestDeltaIslands(t *testing.T, cfg config.Cfg, repoPath string, repack func() error) { + t.Helper() + // Create blobs that we expect Git to use delta compression on. blob1, err := io.ReadAll(io.LimitReader(rand.Reader, 100000)) require.NoError(t, err) diff --git a/internal/gitaly/service/ref/pack_refs_test.go b/internal/gitaly/service/ref/pack_refs_test.go index f16aa8175..e6da2b733 100644 --- a/internal/gitaly/service/ref/pack_refs_test.go +++ b/internal/gitaly/service/ref/pack_refs_test.go @@ -2,6 +2,7 @@ package ref import ( "bufio" + "context" "fmt" "os" "path/filepath" @@ -13,6 +14,7 @@ import ( "gitlab.com/gitlab-org/gitaly/v14/internal/git" "gitlab.com/gitlab-org/gitaly/v14/internal/git/gittest" "gitlab.com/gitlab-org/gitaly/v14/internal/git/localrepo" + "gitlab.com/gitlab-org/gitaly/v14/internal/metadata/featureflag" "gitlab.com/gitlab-org/gitaly/v14/internal/testhelper" "gitlab.com/gitlab-org/gitaly/v14/proto/go/gitalypb" "google.golang.org/grpc/codes" @@ -20,7 +22,12 @@ import ( ) func TestPackRefsSuccessfulRequest(t *testing.T) { - ctx := testhelper.Context(t) + t.Parallel() + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testPackRefsSuccessfulRequest) +} + +func testPackRefsSuccessfulRequest(t *testing.T, ctx context.Context) { + t.Parallel() cfg, repoProto, repoPath, client := setupRefService(ctx, t) @@ -67,9 +74,19 @@ func linesInPackfile(t *testing.T, repoPath string) int { func TestPackRefs_invalidRequest(t *testing.T) { t.Parallel() + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testPackRefsInvalidRequest) +} + +func testPackRefsInvalidRequest(t *testing.T, ctx context.Context) { + t.Parallel() cfg, client := setupRefServiceWithoutRepo(t) + praefectErr := `mutator call: route repository mutator: get repository id: repository "default"/"bar" not found` + if featureflag.MaintenanceOperationRouting.IsEnabled(ctx) { + praefectErr = `routing repository maintenance: getting repository metadata: repository not found` + } + tests := []struct { repo *gitalypb.Repository err error @@ -92,7 +109,7 @@ func TestPackRefs_invalidRequest(t *testing.T) { codes.NotFound, gitalyOrPraefect( fmt.Sprintf(`GetRepoPath: not a git repository: "%s/bar"`, cfg.Storages[0].Path), - `mutator call: route repository mutator: get repository id: repository "default"/"bar" not found`, + praefectErr, ), ), }, @@ -100,7 +117,6 @@ func TestPackRefs_invalidRequest(t *testing.T) { for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { - ctx := testhelper.Context(t) //nolint:staticcheck _, err := client.PackRefs(ctx, &gitalypb.PackRefsRequest{Repository: tc.repo}) testhelper.RequireGrpcError(t, err, tc.err) diff --git a/internal/gitaly/service/repository/cleanup_test.go b/internal/gitaly/service/repository/cleanup_test.go index 1802fee12..50fc670ce 100644 --- a/internal/gitaly/service/repository/cleanup_test.go +++ b/internal/gitaly/service/repository/cleanup_test.go @@ -1,6 +1,7 @@ package repository import ( + "context" "fmt" "os" "path/filepath" @@ -10,6 +11,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gitlab.com/gitlab-org/gitaly/v14/internal/git/gittest" + "gitlab.com/gitlab-org/gitaly/v14/internal/metadata/featureflag" "gitlab.com/gitlab-org/gitaly/v14/internal/testhelper" "gitlab.com/gitlab-org/gitaly/v14/proto/go/gitalypb" "google.golang.org/grpc/codes" @@ -24,6 +26,11 @@ const ( // https://gitlab.com/gitlab-org/gitaly/issues/1750 func TestCleanupDeletesStaleWorktrees(t *testing.T) { t.Parallel() + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testCleanupDeletesStaleWorktrees) +} + +func testCleanupDeletesStaleWorktrees(t *testing.T, ctx context.Context) { + t.Parallel() cfg, client := setupRepositoryServiceWithoutRepo(t) testCases := []struct { @@ -50,7 +57,6 @@ func TestCleanupDeletesStaleWorktrees(t *testing.T) { for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { - ctx := testhelper.Context(t) repo, repoPath := gittest.CreateRepository(ctx, t, cfg, gittest.CreateRepositoryConfig{ Seed: gittest.SeedGitLabTest, }) @@ -86,8 +92,12 @@ func TestCleanupDeletesStaleWorktrees(t *testing.T) { func TestCleanupDeletesOrphanedWorktrees(t *testing.T) { t.Parallel() + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testCleanupDeletesOrphanedWorktrees) +} + +func testCleanupDeletesOrphanedWorktrees(t *testing.T, ctx context.Context) { + t.Parallel() - ctx := testhelper.Context(t) _, repo, repoPath, client := setupRepositoryService(ctx, t) worktreeCheckoutPath := filepath.Join(repoPath, worktreePrefix, "test-worktree") @@ -110,12 +120,16 @@ func TestCleanupDeletesOrphanedWorktrees(t *testing.T) { // https://gitlab.com/gitlab-org/gitaly/issues/1750 func TestCleanupDisconnectedWorktrees(t *testing.T) { t.Parallel() + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testCleanupDisconnectedWorktrees) +} + +func testCleanupDisconnectedWorktrees(t *testing.T, ctx context.Context) { + t.Parallel() const ( worktreeName = "test-worktree" worktreeAdminDir = "worktrees" ) - ctx := testhelper.Context(t) cfg, repo, repoPath, client := setupRepositoryService(ctx, t) worktreePath := filepath.Join(repoPath, worktreePrefix, worktreeName) @@ -151,10 +165,19 @@ func TestCleanupDisconnectedWorktrees(t *testing.T) { func TestCleanup_invalidRequest(t *testing.T) { t.Parallel() + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testCleanupInvalidRequest) +} + +func testCleanupInvalidRequest(t *testing.T, ctx context.Context) { + t.Parallel() - ctx := testhelper.Context(t) cfg, client := setupRepositoryServiceWithoutRepo(t) + praefectErr := `mutator call: route repository mutator: get repository id: repository "default"/"so/me/some.git" not found` + if featureflag.MaintenanceOperationRouting.IsEnabled(ctx) { + praefectErr = `routing repository maintenance: getting repository metadata: repository not found` + } + for _, tc := range []struct { desc string in *gitalypb.Repository @@ -176,7 +199,7 @@ func TestCleanup_invalidRequest(t *testing.T) { codes.NotFound, gitalyOrPraefect( fmt.Sprintf(`GetRepoPath: not a git repository: %q`, filepath.Join(cfg.Storages[0].Path, "so/me/some.git")), - `mutator call: route repository mutator: get repository id: repository "default"/"so/me/some.git" not found`, + praefectErr, ), ), }, diff --git a/internal/gitaly/service/repository/commit_graph_test.go b/internal/gitaly/service/repository/commit_graph_test.go index c55c64665..b50f520dd 100644 --- a/internal/gitaly/service/repository/commit_graph_test.go +++ b/internal/gitaly/service/repository/commit_graph_test.go @@ -2,6 +2,7 @@ package repository import ( "bytes" + "context" "fmt" "os" "path/filepath" @@ -11,6 +12,7 @@ import ( "github.com/stretchr/testify/require" "gitlab.com/gitlab-org/gitaly/v14/internal/git/gittest" "gitlab.com/gitlab-org/gitaly/v14/internal/git/stats" + "gitlab.com/gitlab-org/gitaly/v14/internal/metadata/featureflag" "gitlab.com/gitlab-org/gitaly/v14/internal/testhelper" "gitlab.com/gitlab-org/gitaly/v14/internal/testhelper/testserver" "gitlab.com/gitlab-org/gitaly/v14/proto/go/gitalypb" @@ -20,8 +22,12 @@ import ( func TestWriteCommitGraph_withExistingCommitGraphCreatedWithDefaults(t *testing.T) { t.Parallel() + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testWriteCommitGraphWithExistingCommitGraphCreatedWithDefaults) +} + +func testWriteCommitGraphWithExistingCommitGraphCreatedWithDefaults(t *testing.T, ctx context.Context) { + t.Parallel() - ctx := testhelper.Context(t) cfg, repo, repoPath, client := setupRepositoryService(ctx, t) commitGraphPath := filepath.Join(repoPath, stats.CommitGraphRelPath) @@ -58,8 +64,12 @@ func TestWriteCommitGraph_withExistingCommitGraphCreatedWithDefaults(t *testing. func TestWriteCommitGraph_withExistingCommitGraphCreatedWithSplit(t *testing.T) { t.Parallel() + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testWriteCommitGraphWithExistingCommitGraphCreatedWithSplit) +} + +func testWriteCommitGraphWithExistingCommitGraphCreatedWithSplit(t *testing.T, ctx context.Context) { + t.Parallel() - ctx := testhelper.Context(t) cfg, repo, repoPath, client := setupRepositoryService(ctx, t) commitGraphPath := filepath.Join(repoPath, stats.CommitGraphRelPath) @@ -96,8 +106,12 @@ func TestWriteCommitGraph_withExistingCommitGraphCreatedWithSplit(t *testing.T) func TestWriteCommitGraph(t *testing.T) { t.Parallel() + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testWriteCommitGraph) +} + +func testWriteCommitGraph(t *testing.T, ctx context.Context) { + t.Parallel() - ctx := testhelper.Context(t) _, repo, repoPath, client := setupRepositoryService(ctx, t) chainPath := filepath.Join(repoPath, stats.CommitGraphChainRelPath) @@ -160,8 +174,12 @@ func TestWriteCommitGraph_validationChecks(t *testing.T) { func TestUpdateCommitGraph(t *testing.T) { t.Parallel() + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testUpdateCommitGraph) +} + +func testUpdateCommitGraph(t *testing.T, ctx context.Context) { + t.Parallel() - ctx := testhelper.Context(t) cfg, repo, repoPath, client := setupRepositoryService(ctx, t) chainPath := filepath.Join(repoPath, stats.CommitGraphChainRelPath) diff --git a/internal/gitaly/service/repository/gc_test.go b/internal/gitaly/service/repository/gc_test.go index 2849a12fb..3308110bd 100644 --- a/internal/gitaly/service/repository/gc_test.go +++ b/internal/gitaly/service/repository/gc_test.go @@ -1,6 +1,7 @@ package repository import ( + "context" "fmt" "os" "path/filepath" @@ -15,6 +16,7 @@ import ( "gitlab.com/gitlab-org/gitaly/v14/internal/git/gittest" "gitlab.com/gitlab-org/gitaly/v14/internal/git/stats" "gitlab.com/gitlab-org/gitaly/v14/internal/helper/text" + "gitlab.com/gitlab-org/gitaly/v14/internal/metadata/featureflag" "gitlab.com/gitlab-org/gitaly/v14/internal/testhelper" "gitlab.com/gitlab-org/gitaly/v14/internal/testhelper/testserver" "gitlab.com/gitlab-org/gitaly/v14/proto/go/gitalypb" @@ -30,8 +32,12 @@ var ( func TestGarbageCollectCommitGraph(t *testing.T) { t.Parallel() + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testGarbageCollectCommitGraph) +} + +func testGarbageCollectCommitGraph(t *testing.T, ctx context.Context) { + t.Parallel() - ctx := testhelper.Context(t) _, repo, repoPath, client := setupRepositoryService(ctx, t) //nolint:staticcheck @@ -45,8 +51,12 @@ func TestGarbageCollectCommitGraph(t *testing.T) { func TestGarbageCollectSuccess(t *testing.T) { t.Parallel() + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testGarbageCollectSuccess) +} + +func testGarbageCollectSuccess(t *testing.T, ctx context.Context) { + t.Parallel() - ctx := testhelper.Context(t) _, repo, repoPath, client := setupRepositoryService(ctx, t) tests := []struct { @@ -98,7 +108,11 @@ func TestGarbageCollectSuccess(t *testing.T) { func TestGarbageCollectWithPrune(t *testing.T) { t.Parallel() - ctx := testhelper.Context(t) + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testGarbageCollectWithPrune) +} + +func testGarbageCollectWithPrune(t *testing.T, ctx context.Context) { + t.Parallel() cfg, repo, repoPath, client := setupRepositoryService(ctx, t) @@ -110,7 +124,7 @@ func TestGarbageCollectWithPrune(t *testing.T) { // create a reference to the blob, so it should not be removed by gc gittest.WriteCommit(t, cfg, repoPath, gittest.WithTreeEntries(gittest.TreeEntry{ - OID: git.ObjectID(blobHashes[2]), Path: t.Name(), Mode: "100644", + OID: git.ObjectID(blobHashes[2]), Path: "blob-name", Mode: "100644", }), ) @@ -141,7 +155,11 @@ func TestGarbageCollectWithPrune(t *testing.T) { func TestGarbageCollectLogStatistics(t *testing.T) { t.Parallel() - ctx := testhelper.Context(t) + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testGarbageCollectLogStatistics) +} + +func testGarbageCollectLogStatistics(t *testing.T, ctx context.Context) { + t.Parallel() logger, hook := test.NewNullLogger() _, repo, _, client := setupRepositoryService(ctx, t, testserver.WithLogger(logger)) @@ -155,8 +173,12 @@ func TestGarbageCollectLogStatistics(t *testing.T) { func TestGarbageCollectDeletesRefsLocks(t *testing.T) { t.Parallel() + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testGarbageCollectDeletesRefsLocks) +} + +func testGarbageCollectDeletesRefsLocks(t *testing.T, ctx context.Context) { + t.Parallel() - ctx := testhelper.Context(t) _, repo, repoPath, client := setupRepositoryService(ctx, t) req := &gitalypb.GarbageCollectRequest{Repository: repo} @@ -197,6 +219,11 @@ func TestGarbageCollectDeletesRefsLocks(t *testing.T) { func TestGarbageCollectDeletesPackedRefsLock(t *testing.T) { t.Parallel() + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testGarbageCollectDeletesPackedRefsLock) +} + +func testGarbageCollectDeletesPackedRefsLock(t *testing.T, ctx context.Context) { + t.Parallel() cfg, client := setupRepositoryServiceWithoutRepo(t) testCases := []struct { @@ -223,7 +250,6 @@ func TestGarbageCollectDeletesPackedRefsLock(t *testing.T) { for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { - ctx := testhelper.Context(t) repo, repoPath := gittest.CreateRepository(ctx, t, cfg, gittest.CreateRepositoryConfig{ Seed: gittest.SeedGitLabTest, }) @@ -264,8 +290,12 @@ func TestGarbageCollectDeletesPackedRefsLock(t *testing.T) { func TestGarbageCollectDeletesFileLocks(t *testing.T) { t.Parallel() + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testGarbageCollectDeletesFileLocks) +} + +func testGarbageCollectDeletesFileLocks(t *testing.T, ctx context.Context) { + t.Parallel() - ctx := testhelper.Context(t) _, repo, repoPath, client := setupRepositoryService(ctx, t) req := &gitalypb.GarbageCollectRequest{Repository: repo} @@ -301,6 +331,11 @@ func TestGarbageCollectDeletesFileLocks(t *testing.T) { func TestGarbageCollectDeletesPackedRefsNew(t *testing.T) { t.Parallel() + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testGarbageCollectDeletesPackedRefsNew) +} + +func testGarbageCollectDeletesPackedRefsNew(t *testing.T, ctx context.Context) { + t.Parallel() cfg, client := setupRepositoryServiceWithoutRepo(t) testCases := []struct { @@ -326,7 +361,6 @@ func TestGarbageCollectDeletesPackedRefsNew(t *testing.T) { for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { - ctx := testhelper.Context(t) repo, repoPath := gittest.CreateRepository(ctx, t, cfg) req := &gitalypb.GarbageCollectRequest{Repository: repo} @@ -356,11 +390,20 @@ func TestGarbageCollectDeletesPackedRefsNew(t *testing.T) { func TestGarbageCollectFailure(t *testing.T) { t.Parallel() + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testGarbageCollectFailure) +} + +func testGarbageCollectFailure(t *testing.T, ctx context.Context) { + t.Parallel() - ctx := testhelper.Context(t) _, repo, repoPath, client := setupRepositoryService(ctx, t) storagePath := strings.TrimSuffix(repoPath, "/"+repo.RelativePath) + praefectErr := `mutator call: route repository mutator: get repository id: repository "default"/"bar" not found` + if featureflag.MaintenanceOperationRouting.IsEnabled(ctx) { + praefectErr = `routing repository maintenance: getting repository metadata: repository not found` + } + tests := []struct { repo *gitalypb.Repository err error @@ -379,7 +422,7 @@ func TestGarbageCollectFailure(t *testing.T) { codes.NotFound, gitalyOrPraefect( fmt.Sprintf(`GetRepoPath: not a git repository: "%s/bar"`, storagePath), - `mutator call: route repository mutator: get repository id: repository "default"/"bar" not found`, + praefectErr, ), ), }, @@ -396,8 +439,12 @@ func TestGarbageCollectFailure(t *testing.T) { func TestCleanupInvalidKeepAroundRefs(t *testing.T) { t.Parallel() + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testCleanupInvalidKeepAroundRefs) +} + +func testCleanupInvalidKeepAroundRefs(t *testing.T, ctx context.Context) { + t.Parallel() - ctx := testhelper.Context(t) cfg, repo, repoPath, client := setupRepositoryService(ctx, t) // Make the directory, so we can create random reflike things in it @@ -491,8 +538,12 @@ func mustCreateFileWithTimes(t testing.TB, path string, mTime time.Time) { func TestGarbageCollectDeltaIslands(t *testing.T) { t.Parallel() + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testGarbageCollectDeltaIslands) +} + +func testGarbageCollectDeltaIslands(t *testing.T, ctx context.Context) { + t.Parallel() - ctx := testhelper.Context(t) cfg, repo, repoPath, client := setupRepositoryService(ctx, t) gittest.TestDeltaIslands(t, cfg, repoPath, func() error { diff --git a/internal/gitaly/service/repository/midx_test.go b/internal/gitaly/service/repository/midx_test.go index 984f4ee44..d0bea2ca0 100644 --- a/internal/gitaly/service/repository/midx_test.go +++ b/internal/gitaly/service/repository/midx_test.go @@ -17,6 +17,7 @@ import ( "gitlab.com/gitlab-org/gitaly/v14/internal/gitaly/transaction" "gitlab.com/gitlab-org/gitaly/v14/internal/helper/text" "gitlab.com/gitlab-org/gitaly/v14/internal/metadata" + "gitlab.com/gitlab-org/gitaly/v14/internal/metadata/featureflag" "gitlab.com/gitlab-org/gitaly/v14/internal/testhelper" "gitlab.com/gitlab-org/gitaly/v14/internal/testhelper/testserver" "gitlab.com/gitlab-org/gitaly/v14/internal/transaction/txinfo" @@ -28,8 +29,12 @@ import ( func TestMidxWrite(t *testing.T) { t.Parallel() + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testMidxWrite) +} + +func testMidxWrite(t *testing.T, ctx context.Context) { + t.Parallel() - ctx := testhelper.Context(t) cfg, repo, repoPath, client := setupRepositoryService(ctx, t) //nolint:staticcheck @@ -47,8 +52,12 @@ func TestMidxWrite(t *testing.T) { func TestMidxRewrite(t *testing.T) { t.Parallel() + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testMidxRewrite) +} + +func testMidxRewrite(t *testing.T, ctx context.Context) { + t.Parallel() - ctx := testhelper.Context(t) _, repo, repoPath, client := setupRepositoryService(ctx, t) midxPath := filepath.Join(repoPath, MidxRelPath) @@ -75,8 +84,12 @@ func TestMidxRewrite(t *testing.T) { func TestMidxRepack(t *testing.T) { t.Parallel() + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testMidxRepack) +} + +func testMidxRepack(t *testing.T, ctx context.Context) { + t.Parallel() - ctx := testhelper.Context(t) cfg, repo, repoPath, client := setupRepositoryService(ctx, t) // add some pack files with different sizes @@ -115,7 +128,11 @@ func TestMidxRepack(t *testing.T) { func TestMidxRepack_transactional(t *testing.T) { t.Parallel() - ctx := testhelper.Context(t) + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testMidxRepackTransactional) +} + +func testMidxRepackTransactional(t *testing.T, ctx context.Context) { + t.Parallel() txManager := transaction.NewTrackingManager() @@ -145,12 +162,16 @@ func TestMidxRepack_transactional(t *testing.T) { func TestMidxRepackExpire(t *testing.T) { t.Parallel() + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testMidxRepackExpire) +} + +func testMidxRepackExpire(t *testing.T, ctx context.Context) { + t.Parallel() cfg, client := setupRepositoryServiceWithoutRepo(t) for _, packsAdded := range []int{3, 5, 11, 20} { t.Run(fmt.Sprintf("Test repack expire with %d added packs", packsAdded), func(t *testing.T) { - ctx := testhelper.Context(t) repo, repoPath := gittest.CreateRepository(ctx, t, cfg, gittest.CreateRepositoryConfig{ Seed: gittest.SeedGitLabTest, }) diff --git a/internal/gitaly/service/repository/optimize_test.go b/internal/gitaly/service/repository/optimize_test.go index a843b5668..4c63c3972 100644 --- a/internal/gitaly/service/repository/optimize_test.go +++ b/internal/gitaly/service/repository/optimize_test.go @@ -2,6 +2,7 @@ package repository import ( "bytes" + "context" "fmt" "os" "path/filepath" @@ -13,6 +14,7 @@ import ( "gitlab.com/gitlab-org/gitaly/v14/internal/git" "gitlab.com/gitlab-org/gitaly/v14/internal/git/gittest" "gitlab.com/gitlab-org/gitaly/v14/internal/git/stats" + "gitlab.com/gitlab-org/gitaly/v14/internal/metadata/featureflag" "gitlab.com/gitlab-org/gitaly/v14/internal/testhelper" "gitlab.com/gitlab-org/gitaly/v14/proto/go/gitalypb" "google.golang.org/grpc/codes" @@ -43,8 +45,12 @@ func getNewestPackfileModtime(t *testing.T, repoPath string) time.Time { func TestOptimizeRepository(t *testing.T) { t.Parallel() + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testOptimizeRepository) +} + +func testOptimizeRepository(t *testing.T, ctx context.Context) { + t.Parallel() - ctx := testhelper.Context(t) cfg, repoProto, repoPath, client := setupRepositoryService(ctx, t) gittest.Exec(t, cfg, "-C", repoPath, "repack", "-A", "-b") @@ -157,10 +163,19 @@ func TestOptimizeRepository(t *testing.T) { func TestOptimizeRepositoryValidation(t *testing.T) { t.Parallel() + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testOptimizeRepositoryValidation) +} + +func testOptimizeRepositoryValidation(t *testing.T, ctx context.Context) { + t.Parallel() - ctx := testhelper.Context(t) cfg, repo, _, client := setupRepositoryService(ctx, t) + praefectErr := `mutator call: route repository mutator: get repository id: repository "default"/"path/not/exist" not found` + if featureflag.MaintenanceOperationRouting.IsEnabled(ctx) { + praefectErr = `routing repository maintenance: getting repository metadata: repository not found` + } + testCases := []struct { desc string repo *gitalypb.Repository @@ -183,7 +198,7 @@ func TestOptimizeRepositoryValidation(t *testing.T) { codes.NotFound, gitalyOrPraefect( fmt.Sprintf(`GetRepoPath: not a git repository: "%s/path/not/exist"`, cfg.Storages[0].Path), - `mutator call: route repository mutator: get repository id: repository "default"/"path/not/exist" not found`, + praefectErr, ), ), }, @@ -193,7 +208,7 @@ func TestOptimizeRepositoryValidation(t *testing.T) { t.Run(tc.desc, func(t *testing.T) { _, err := client.OptimizeRepository(ctx, &gitalypb.OptimizeRepositoryRequest{Repository: tc.repo}) require.Error(t, err) - testhelper.RequireGrpcError(t, err, tc.exp) + testhelper.RequireGrpcError(t, tc.exp, err) }) } diff --git a/internal/gitaly/service/repository/prune_unreachable_objects_test.go b/internal/gitaly/service/repository/prune_unreachable_objects_test.go index 3d1d6c1f8..b02aa224e 100644 --- a/internal/gitaly/service/repository/prune_unreachable_objects_test.go +++ b/internal/gitaly/service/repository/prune_unreachable_objects_test.go @@ -1,6 +1,7 @@ package repository import ( + "context" "os" "path/filepath" "testing" @@ -10,12 +11,18 @@ import ( "gitlab.com/gitlab-org/gitaly/v14/internal/git" "gitlab.com/gitlab-org/gitaly/v14/internal/git/gittest" "gitlab.com/gitlab-org/gitaly/v14/internal/helper" + "gitlab.com/gitlab-org/gitaly/v14/internal/metadata/featureflag" "gitlab.com/gitlab-org/gitaly/v14/internal/testhelper" "gitlab.com/gitlab-org/gitaly/v14/proto/go/gitalypb" ) func TestPruneUnreachableObjects(t *testing.T) { - ctx := testhelper.Context(t) + t.Parallel() + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testPruneUnreachableObjects) +} + +func testPruneUnreachableObjects(t *testing.T, ctx context.Context) { + t.Parallel() cfg, client := setupRepositoryServiceWithoutRepo(t) diff --git a/internal/gitaly/service/repository/repack_test.go b/internal/gitaly/service/repository/repack_test.go index d7efb78dc..244e23077 100644 --- a/internal/gitaly/service/repository/repack_test.go +++ b/internal/gitaly/service/repository/repack_test.go @@ -1,6 +1,7 @@ package repository import ( + "context" "fmt" "path/filepath" "testing" @@ -12,6 +13,7 @@ import ( "github.com/stretchr/testify/require" "gitlab.com/gitlab-org/gitaly/v14/internal/git/gittest" "gitlab.com/gitlab-org/gitaly/v14/internal/git/stats" + "gitlab.com/gitlab-org/gitaly/v14/internal/metadata/featureflag" "gitlab.com/gitlab-org/gitaly/v14/internal/testhelper" "gitlab.com/gitlab-org/gitaly/v14/internal/testhelper/testserver" "gitlab.com/gitlab-org/gitaly/v14/proto/go/gitalypb" @@ -21,8 +23,12 @@ import ( func TestRepackIncrementalSuccess(t *testing.T) { t.Parallel() + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testRepackIncrementalSuccess) +} + +func testRepackIncrementalSuccess(t *testing.T, ctx context.Context) { + t.Parallel() - ctx := testhelper.Context(t) _, repo, repoPath, client := setupRepositoryService(ctx, t) packPath := filepath.Join(repoPath, "objects", "pack") @@ -48,7 +54,11 @@ func TestRepackIncrementalSuccess(t *testing.T) { func TestRepackIncrementalCollectLogStatistics(t *testing.T) { t.Parallel() - ctx := testhelper.Context(t) + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testRepackIncrementalCollectLogStatistics) +} + +func testRepackIncrementalCollectLogStatistics(t *testing.T, ctx context.Context) { + t.Parallel() logger, hook := test.NewNullLogger() _, repo, _, client := setupRepositoryService(ctx, t, testserver.WithLogger(logger)) @@ -62,8 +72,12 @@ func TestRepackIncrementalCollectLogStatistics(t *testing.T) { func TestRepackLocal(t *testing.T) { t.Parallel() + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testRepackLocal) +} + +func testRepackLocal(t *testing.T, ctx context.Context) { + t.Parallel() - ctx := testhelper.Context(t) cfg, repo, repoPath, client := setupRepositoryService(ctx, t) altObjectsDir := "./alt-objects" @@ -99,8 +113,18 @@ func TestRepackLocal(t *testing.T) { func TestRepackIncrementalFailure(t *testing.T) { t.Parallel() + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testRepackIncrementalFailure) +} + +func testRepackIncrementalFailure(t *testing.T, ctx context.Context) { + t.Parallel() cfg, client := setupRepositoryServiceWithoutRepo(t) + praefectErr := `mutator call: route repository mutator: get repository id: repository "default"/"bar" not found` + if featureflag.MaintenanceOperationRouting.IsEnabled(ctx) { + praefectErr = `routing repository maintenance: getting repository metadata: repository not found` + } + tests := []struct { repo *gitalypb.Repository err error @@ -128,7 +152,7 @@ func TestRepackIncrementalFailure(t *testing.T) { codes.NotFound, gitalyOrPraefect( fmt.Sprintf(`GetRepoPath: not a git repository: "%s/bar"`, cfg.Storages[0].Path), - `mutator call: route repository mutator: get repository id: repository "default"/"bar" not found`, + praefectErr, ), ), }, @@ -136,7 +160,6 @@ func TestRepackIncrementalFailure(t *testing.T) { for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { - ctx := testhelper.Context(t) //nolint:staticcheck _, err := client.RepackIncremental(ctx, &gitalypb.RepackIncrementalRequest{Repository: tc.repo}) testhelper.RequireGrpcError(t, err, tc.err) @@ -146,6 +169,11 @@ func TestRepackIncrementalFailure(t *testing.T) { func TestRepackFullSuccess(t *testing.T) { t.Parallel() + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testRepackFullSuccess) +} + +func testRepackFullSuccess(t *testing.T, ctx context.Context) { + t.Parallel() cfg, client := setupRepositoryServiceWithoutRepo(t) tests := []struct { @@ -158,8 +186,6 @@ func TestRepackFullSuccess(t *testing.T) { for _, test := range tests { t.Run(test.desc, func(t *testing.T) { - ctx := testhelper.Context(t) - var repoPath string test.req.Repository, repoPath = gittest.CreateRepository(ctx, t, cfg, gittest.CreateRepositoryConfig{ Seed: gittest.SeedGitLabTest, @@ -202,7 +228,11 @@ func TestRepackFullSuccess(t *testing.T) { func TestRepackFullCollectLogStatistics(t *testing.T) { t.Parallel() - ctx := testhelper.Context(t) + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testRepackFullCollectLogStatistics) +} + +func testRepackFullCollectLogStatistics(t *testing.T, ctx context.Context) { + t.Parallel() logger, hook := test.NewNullLogger() _, repo, _, client := setupRepositoryService(ctx, t, testserver.WithLogger(logger)) @@ -243,8 +273,18 @@ func doBitmapsContainHashCache(t *testing.T, bitmapPaths []string) { func TestRepackFullFailure(t *testing.T) { t.Parallel() + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testRepackFullFailure) +} + +func testRepackFullFailure(t *testing.T, ctx context.Context) { + t.Parallel() cfg, client := setupRepositoryServiceWithoutRepo(t) + praefectErr := `mutator call: route repository mutator: get repository id: repository "default"/"bar" not found` + if featureflag.MaintenanceOperationRouting.IsEnabled(ctx) { + praefectErr = `routing repository maintenance: getting repository metadata: repository not found` + } + tests := []struct { desc string repo *gitalypb.Repository @@ -272,7 +312,7 @@ func TestRepackFullFailure(t *testing.T) { codes.NotFound, gitalyOrPraefect( fmt.Sprintf(`GetRepoPath: not a git repository: "%s/bar"`, cfg.Storages[0].Path), - `mutator call: route repository mutator: get repository id: repository "default"/"bar" not found`, + praefectErr, ), ), }, @@ -280,7 +320,6 @@ func TestRepackFullFailure(t *testing.T) { for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { - ctx := testhelper.Context(t) //nolint:staticcheck _, err := client.RepackFull(ctx, &gitalypb.RepackFullRequest{Repository: tc.repo}) testhelper.RequireGrpcError(t, err, tc.err) @@ -290,8 +329,12 @@ func TestRepackFullFailure(t *testing.T) { func TestRepackFullDeltaIslands(t *testing.T) { t.Parallel() + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testRepackFullDeltaIslands) +} + +func testRepackFullDeltaIslands(t *testing.T, ctx context.Context) { + t.Parallel() - ctx := testhelper.Context(t) cfg, repo, repoPath, client := setupRepositoryService(ctx, t) gittest.TestDeltaIslands(t, cfg, repoPath, func() error { diff --git a/internal/metadata/featureflag/ff_maintenance_operation_routing.go b/internal/metadata/featureflag/ff_maintenance_operation_routing.go new file mode 100644 index 000000000..9f108af9c --- /dev/null +++ b/internal/metadata/featureflag/ff_maintenance_operation_routing.go @@ -0,0 +1,4 @@ +package featureflag + +// MaintenanceOperationRouting enables routing logic that is specific to maintenance operations. +var MaintenanceOperationRouting = NewFeatureFlag("maintenance_operation_routing", false) diff --git a/internal/middleware/cache/cache.go b/internal/middleware/cache/cache.go index 6634fcbfa..aed1c74e1 100644 --- a/internal/middleware/cache/cache.go +++ b/internal/middleware/cache/cache.go @@ -24,6 +24,22 @@ func shouldIgnore(reg *protoregistry.Registry, fullMethod string) bool { return strings.HasPrefix(fullMethod, "/grpc.health") || reg.IsInterceptedMethod(fullMethod) } +func shouldInvalidate(mi protoregistry.MethodInfo) bool { + if mi.Scope != protoregistry.ScopeRepository { + return false + } + + if mi.Operation == protoregistry.OpAccessor { + return false + } + + if mi.Operation == protoregistry.OpMaintenance { + return false + } + + return true +} + // StreamInvalidator will invalidate any mutating RPC that targets a // repository in a gRPC stream based RPC func StreamInvalidator(ci diskcache.Invalidator, reg *protoregistry.Registry) grpc.StreamServerInterceptor { @@ -41,7 +57,7 @@ func StreamInvalidator(ci diskcache.Invalidator, reg *protoregistry.Registry) gr return handler(srv, ss) } - if mInfo.Scope != protoregistry.ScopeRepository || mInfo.Operation == protoregistry.OpAccessor { + if !shouldInvalidate(mInfo) { return handler(srv, ss) } @@ -68,7 +84,7 @@ func UnaryInvalidator(ci diskcache.Invalidator, reg *protoregistry.Registry) grp return handler(ctx, req) } - if mInfo.Scope != protoregistry.ScopeRepository || mInfo.Operation == protoregistry.OpAccessor { + if !shouldInvalidate(mInfo) { return handler(ctx, req) } diff --git a/internal/middleware/cache/cache_test.go b/internal/middleware/cache/cache_test.go index 9a271efb1..b23a5b2be 100644 --- a/internal/middleware/cache/cache_test.go +++ b/internal/middleware/cache/cache_test.go @@ -70,7 +70,7 @@ func TestInvalidators(t *testing.T) { StorageName: "3", } - expectedSvcRequests := []*gitalypb.Repository{repo1, repo2, repo3, repo1, repo2} + expectedSvcRequests := []*gitalypb.Repository{repo1, repo1, repo2, repo3, repo1, repo2, repo2} expectedInvalidations := []*gitalypb.Repository{repo2, repo3, repo1} // Should NOT trigger cache invalidation @@ -81,6 +81,14 @@ func TestInvalidators(t *testing.T) { _, err = c.Recv() // make client call synchronous by waiting for close assert.Equal(t, err, io.EOF) + // Should NOT trigger cache invalidation + c, err = cli.ClientStreamRepoMaintainer(ctx, &testdata.Request{ + Destination: repo1, + }) + assert.NoError(t, err) + _, err = c.Recv() // make client call synchronous by waiting for close + assert.Equal(t, err, io.EOF) + // Should trigger cache invalidation c, err = cli.ClientStreamRepoMutator(ctx, &testdata.Request{ Destination: repo2, @@ -109,6 +117,12 @@ func TestInvalidators(t *testing.T) { }) require.NoError(t, err) + // Should NOT trigger cache invalidation + _, err = cli.ClientUnaryRepoMaintainer(ctx, &testdata.Request{ + Destination: repo2, + }) + require.NoError(t, err) + // Health checks should NOT trigger cache invalidation hcr := &grpc_health_v1.HealthCheckRequest{Service: "TestService"} _, err = grpc_health_v1.NewHealthClient(cc).Check(ctx, hcr) @@ -203,6 +217,11 @@ func (ts *testSvc) ClientStreamRepoAccessor(req *testdata.Request, _ testdata.Te return nil } +func (ts *testSvc) ClientStreamRepoMaintainer(req *testdata.Request, _ testdata.TestService_ClientStreamRepoMaintainerServer) error { + ts.repoRequests = append(ts.repoRequests, req.GetDestination()) + return nil +} + func (ts *testSvc) ClientUnaryRepoMutator(_ context.Context, req *testdata.Request) (*testdata.Response, error) { ts.repoRequests = append(ts.repoRequests, req.GetDestination()) return &testdata.Response{}, nil @@ -212,3 +231,8 @@ func (ts *testSvc) ClientUnaryRepoAccessor(_ context.Context, req *testdata.Requ ts.repoRequests = append(ts.repoRequests, req.GetDestination()) return &testdata.Response{}, nil } + +func (ts *testSvc) ClientUnaryRepoMaintainer(_ context.Context, req *testdata.Request) (*testdata.Response, error) { + ts.repoRequests = append(ts.repoRequests, req.GetDestination()) + return &testdata.Response{}, nil +} diff --git a/internal/middleware/cache/prometheus.go b/internal/middleware/cache/prometheus.go index 195bab26b..073402d5e 100644 --- a/internal/middleware/cache/prometheus.go +++ b/internal/middleware/cache/prometheus.go @@ -40,6 +40,8 @@ var ( rpcOpTypes.WithLabelValues("accessor").Inc() case protoregistry.OpMutator: rpcOpTypes.WithLabelValues("mutator").Inc() + case protoregistry.OpMaintenance: + rpcOpTypes.WithLabelValues("maintenance").Inc() default: rpcOpTypes.WithLabelValues("unknown").Inc() } diff --git a/internal/middleware/cache/testdata/stream.pb.go b/internal/middleware/cache/testdata/stream.pb.go index dc2cfdc5d..ffaebf70a 100644 --- a/internal/middleware/cache/testdata/stream.pb.go +++ b/internal/middleware/cache/testdata/stream.pb.go @@ -124,7 +124,7 @@ var file_middleware_cache_testdata_stream_proto_rawDesc = []byte{ 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x11, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x04, 0xf0, - 0x97, 0x28, 0x01, 0x32, 0xb9, 0x02, 0x0a, 0x0b, 0x54, 0x65, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, + 0x97, 0x28, 0x01, 0x32, 0xd4, 0x03, 0x0a, 0x0b, 0x54, 0x65, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4a, 0x0a, 0x17, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x70, 0x6f, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x11, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, @@ -134,21 +134,31 @@ var file_middleware_cache_testdata_stream_proto_rawDesc = []byte{ 0x65, 0x70, 0x6f, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x12, 0x11, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x02, 0x30, 0x01, 0x12, 0x47, 0x0a, 0x16, - 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x55, 0x6e, 0x61, 0x72, 0x79, 0x52, 0x65, 0x70, 0x6f, 0x4d, - 0x75, 0x74, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x11, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x64, 0x61, 0x74, - 0x61, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x74, 0x65, 0x73, 0x74, - 0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, - 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x48, 0x0a, 0x17, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x55, - 0x6e, 0x61, 0x72, 0x79, 0x52, 0x65, 0x70, 0x6f, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, - 0x12, 0x11, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x02, 0x42, - 0x45, 0x5a, 0x43, 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, 0x34, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x6d, 0x69, 0x64, - 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x2f, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2f, 0x74, 0x65, - 0x73, 0x74, 0x64, 0x61, 0x74, 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x02, 0x30, 0x01, 0x12, 0x4d, 0x0a, 0x1a, + 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x70, 0x6f, + 0x4d, 0x61, 0x69, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x11, 0x2e, 0x74, 0x65, 0x73, + 0x74, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, + 0x74, 0x65, 0x73, 0x74, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x03, 0x30, 0x01, 0x12, 0x47, 0x0a, 0x16, 0x43, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x55, 0x6e, 0x61, 0x72, 0x79, 0x52, 0x65, 0x70, 0x6f, 0x4d, 0x75, + 0x74, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x11, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x64, 0x61, 0x74, 0x61, + 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x64, + 0x61, 0x74, 0x61, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, + 0x28, 0x02, 0x08, 0x01, 0x12, 0x48, 0x0a, 0x17, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x55, 0x6e, + 0x61, 0x72, 0x79, 0x52, 0x65, 0x70, 0x6f, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x12, + 0x11, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x02, 0x12, 0x4a, + 0x0a, 0x19, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x55, 0x6e, 0x61, 0x72, 0x79, 0x52, 0x65, 0x70, + 0x6f, 0x4d, 0x61, 0x69, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x11, 0x2e, 0x74, 0x65, + 0x73, 0x74, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, + 0x2e, 0x74, 0x65, 0x73, 0x74, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x03, 0x42, 0x45, 0x5a, 0x43, 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, 0x34, 0x2f, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, + 0x72, 0x65, 0x2f, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x64, 0x61, 0x74, + 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -174,15 +184,19 @@ var file_middleware_cache_testdata_stream_proto_depIdxs = []int32{ 0, // 1: testdata.InterceptedService.IgnoredMethod:input_type -> testdata.Request 0, // 2: testdata.TestService.ClientStreamRepoMutator:input_type -> testdata.Request 0, // 3: testdata.TestService.ClientStreamRepoAccessor:input_type -> testdata.Request - 0, // 4: testdata.TestService.ClientUnaryRepoMutator:input_type -> testdata.Request - 0, // 5: testdata.TestService.ClientUnaryRepoAccessor:input_type -> testdata.Request - 1, // 6: testdata.InterceptedService.IgnoredMethod:output_type -> testdata.Response - 1, // 7: testdata.TestService.ClientStreamRepoMutator:output_type -> testdata.Response - 1, // 8: testdata.TestService.ClientStreamRepoAccessor:output_type -> testdata.Response - 1, // 9: testdata.TestService.ClientUnaryRepoMutator:output_type -> testdata.Response - 1, // 10: testdata.TestService.ClientUnaryRepoAccessor:output_type -> testdata.Response - 6, // [6:11] is the sub-list for method output_type - 1, // [1:6] is the sub-list for method input_type + 0, // 4: testdata.TestService.ClientStreamRepoMaintainer:input_type -> testdata.Request + 0, // 5: testdata.TestService.ClientUnaryRepoMutator:input_type -> testdata.Request + 0, // 6: testdata.TestService.ClientUnaryRepoAccessor:input_type -> testdata.Request + 0, // 7: testdata.TestService.ClientUnaryRepoMaintainer:input_type -> testdata.Request + 1, // 8: testdata.InterceptedService.IgnoredMethod:output_type -> testdata.Response + 1, // 9: testdata.TestService.ClientStreamRepoMutator:output_type -> testdata.Response + 1, // 10: testdata.TestService.ClientStreamRepoAccessor:output_type -> testdata.Response + 1, // 11: testdata.TestService.ClientStreamRepoMaintainer:output_type -> testdata.Response + 1, // 12: testdata.TestService.ClientUnaryRepoMutator:output_type -> testdata.Response + 1, // 13: testdata.TestService.ClientUnaryRepoAccessor:output_type -> testdata.Response + 1, // 14: testdata.TestService.ClientUnaryRepoMaintainer:output_type -> testdata.Response + 8, // [8:15] is the sub-list for method output_type + 1, // [1:8] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name diff --git a/internal/middleware/cache/testdata/stream.proto b/internal/middleware/cache/testdata/stream.proto index aed6ab7dd..c977b0d69 100644 --- a/internal/middleware/cache/testdata/stream.proto +++ b/internal/middleware/cache/testdata/stream.proto @@ -32,6 +32,12 @@ service TestService { }; } + rpc ClientStreamRepoMaintainer(Request) returns (stream Response) { + option (gitaly.op_type) = { + op: MAINTENANCE + }; + } + rpc ClientUnaryRepoMutator(Request) returns (Response) { option (gitaly.op_type) = { op: MUTATOR @@ -43,4 +49,10 @@ service TestService { op: ACCESSOR }; } + + rpc ClientUnaryRepoMaintainer(Request) returns (Response) { + option (gitaly.op_type) = { + op: MAINTENANCE + }; + } } diff --git a/internal/middleware/cache/testdata/stream_grpc.pb.go b/internal/middleware/cache/testdata/stream_grpc.pb.go index ec9aaa71e..066eddc51 100644 --- a/internal/middleware/cache/testdata/stream_grpc.pb.go +++ b/internal/middleware/cache/testdata/stream_grpc.pb.go @@ -106,8 +106,10 @@ var InterceptedService_ServiceDesc = grpc.ServiceDesc{ type TestServiceClient interface { ClientStreamRepoMutator(ctx context.Context, in *Request, opts ...grpc.CallOption) (TestService_ClientStreamRepoMutatorClient, error) ClientStreamRepoAccessor(ctx context.Context, in *Request, opts ...grpc.CallOption) (TestService_ClientStreamRepoAccessorClient, error) + ClientStreamRepoMaintainer(ctx context.Context, in *Request, opts ...grpc.CallOption) (TestService_ClientStreamRepoMaintainerClient, error) ClientUnaryRepoMutator(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) ClientUnaryRepoAccessor(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) + ClientUnaryRepoMaintainer(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) } type testServiceClient struct { @@ -182,6 +184,38 @@ func (x *testServiceClientStreamRepoAccessorClient) Recv() (*Response, error) { return m, nil } +func (c *testServiceClient) ClientStreamRepoMaintainer(ctx context.Context, in *Request, opts ...grpc.CallOption) (TestService_ClientStreamRepoMaintainerClient, error) { + stream, err := c.cc.NewStream(ctx, &TestService_ServiceDesc.Streams[2], "/testdata.TestService/ClientStreamRepoMaintainer", opts...) + if err != nil { + return nil, err + } + x := &testServiceClientStreamRepoMaintainerClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type TestService_ClientStreamRepoMaintainerClient interface { + Recv() (*Response, error) + grpc.ClientStream +} + +type testServiceClientStreamRepoMaintainerClient struct { + grpc.ClientStream +} + +func (x *testServiceClientStreamRepoMaintainerClient) Recv() (*Response, error) { + m := new(Response) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + func (c *testServiceClient) ClientUnaryRepoMutator(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) { out := new(Response) err := c.cc.Invoke(ctx, "/testdata.TestService/ClientUnaryRepoMutator", in, out, opts...) @@ -200,14 +234,25 @@ func (c *testServiceClient) ClientUnaryRepoAccessor(ctx context.Context, in *Req return out, nil } +func (c *testServiceClient) ClientUnaryRepoMaintainer(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) { + out := new(Response) + err := c.cc.Invoke(ctx, "/testdata.TestService/ClientUnaryRepoMaintainer", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // TestServiceServer is the server API for TestService service. // All implementations must embed UnimplementedTestServiceServer // for forward compatibility type TestServiceServer interface { ClientStreamRepoMutator(*Request, TestService_ClientStreamRepoMutatorServer) error ClientStreamRepoAccessor(*Request, TestService_ClientStreamRepoAccessorServer) error + ClientStreamRepoMaintainer(*Request, TestService_ClientStreamRepoMaintainerServer) error ClientUnaryRepoMutator(context.Context, *Request) (*Response, error) ClientUnaryRepoAccessor(context.Context, *Request) (*Response, error) + ClientUnaryRepoMaintainer(context.Context, *Request) (*Response, error) mustEmbedUnimplementedTestServiceServer() } @@ -221,12 +266,18 @@ func (UnimplementedTestServiceServer) ClientStreamRepoMutator(*Request, TestServ func (UnimplementedTestServiceServer) ClientStreamRepoAccessor(*Request, TestService_ClientStreamRepoAccessorServer) error { return status.Errorf(codes.Unimplemented, "method ClientStreamRepoAccessor not implemented") } +func (UnimplementedTestServiceServer) ClientStreamRepoMaintainer(*Request, TestService_ClientStreamRepoMaintainerServer) error { + return status.Errorf(codes.Unimplemented, "method ClientStreamRepoMaintainer not implemented") +} func (UnimplementedTestServiceServer) ClientUnaryRepoMutator(context.Context, *Request) (*Response, error) { return nil, status.Errorf(codes.Unimplemented, "method ClientUnaryRepoMutator not implemented") } func (UnimplementedTestServiceServer) ClientUnaryRepoAccessor(context.Context, *Request) (*Response, error) { return nil, status.Errorf(codes.Unimplemented, "method ClientUnaryRepoAccessor not implemented") } +func (UnimplementedTestServiceServer) ClientUnaryRepoMaintainer(context.Context, *Request) (*Response, error) { + return nil, status.Errorf(codes.Unimplemented, "method ClientUnaryRepoMaintainer not implemented") +} func (UnimplementedTestServiceServer) mustEmbedUnimplementedTestServiceServer() {} // UnsafeTestServiceServer may be embedded to opt out of forward compatibility for this service. @@ -282,6 +333,27 @@ func (x *testServiceClientStreamRepoAccessorServer) Send(m *Response) error { return x.ServerStream.SendMsg(m) } +func _TestService_ClientStreamRepoMaintainer_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(Request) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(TestServiceServer).ClientStreamRepoMaintainer(m, &testServiceClientStreamRepoMaintainerServer{stream}) +} + +type TestService_ClientStreamRepoMaintainerServer interface { + Send(*Response) error + grpc.ServerStream +} + +type testServiceClientStreamRepoMaintainerServer struct { + grpc.ServerStream +} + +func (x *testServiceClientStreamRepoMaintainerServer) Send(m *Response) error { + return x.ServerStream.SendMsg(m) +} + func _TestService_ClientUnaryRepoMutator_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(Request) if err := dec(in); err != nil { @@ -318,6 +390,24 @@ func _TestService_ClientUnaryRepoAccessor_Handler(srv interface{}, ctx context.C return interceptor(ctx, in, info, handler) } +func _TestService_ClientUnaryRepoMaintainer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Request) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TestServiceServer).ClientUnaryRepoMaintainer(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/testdata.TestService/ClientUnaryRepoMaintainer", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TestServiceServer).ClientUnaryRepoMaintainer(ctx, req.(*Request)) + } + return interceptor(ctx, in, info, handler) +} + // TestService_ServiceDesc is the grpc.ServiceDesc for TestService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -333,6 +423,10 @@ var TestService_ServiceDesc = grpc.ServiceDesc{ MethodName: "ClientUnaryRepoAccessor", Handler: _TestService_ClientUnaryRepoAccessor_Handler, }, + { + MethodName: "ClientUnaryRepoMaintainer", + Handler: _TestService_ClientUnaryRepoMaintainer_Handler, + }, }, Streams: []grpc.StreamDesc{ { @@ -345,6 +439,11 @@ var TestService_ServiceDesc = grpc.ServiceDesc{ Handler: _TestService_ClientStreamRepoAccessor_Handler, ServerStreams: true, }, + { + StreamName: "ClientStreamRepoMaintainer", + Handler: _TestService_ClientStreamRepoMaintainer_Handler, + ServerStreams: true, + }, }, Metadata: "middleware/cache/testdata/stream.proto", } diff --git a/internal/praefect/coordinator.go b/internal/praefect/coordinator.go index 41621914d..7e3c4b60c 100644 --- a/internal/praefect/coordinator.go +++ b/internal/praefect/coordinator.go @@ -92,20 +92,9 @@ var transactionRPCs = map[string]transactionsCondition{ "/gitaly.ObjectPoolService/ReduplicateRepository": transactionsDisabled, "/gitaly.RepositoryService/RenameRepository": transactionsDisabled, - // The following list of RPCs are considered idempotent RPCs: while they write into the - // target repository, this shouldn't ever have any user-visible impact given that they're - // purely optimizations of the on-disk state. These RPCs are thus treated specially and - // shouldn't ever cause a repository generation bump. - "/gitaly.RefService/PackRefs": transactionsDisabled, - "/gitaly.RepositoryService/Cleanup": transactionsDisabled, - "/gitaly.RepositoryService/GarbageCollect": transactionsDisabled, - "/gitaly.RepositoryService/MidxRepack": transactionsDisabled, - "/gitaly.RepositoryService/OptimizeRepository": transactionsDisabled, - "/gitaly.RepositoryService/PruneUnreachableObjects": transactionsDisabled, - "/gitaly.RepositoryService/RepackFull": transactionsDisabled, - "/gitaly.RepositoryService/RepackIncremental": transactionsDisabled, - "/gitaly.RepositoryService/RestoreCustomHooks": transactionsDisabled, - "/gitaly.RepositoryService/WriteCommitGraph": transactionsDisabled, + // This RPC call should be made transactional. Furthermore, we should consider whether we + // have to replicate custom hooks. + "/gitaly.RepositoryService/RestoreCustomHooks": transactionsDisabled, } // forcePrimaryRoutingRPCs tracks RPCs which need to always get routed to the primary. This should @@ -331,6 +320,12 @@ func (c *Coordinator) directRepositoryScopedMessage(ctx context.Context, call gr ps, err = c.accessorStreamParameters(ctx, call) case protoregistry.OpMutator: ps, err = c.mutatorStreamParameters(ctx, call) + case protoregistry.OpMaintenance: + if featureflag.MaintenanceOperationRouting.IsEnabled(ctx) { + ps, err = c.maintenanceStreamParameters(ctx, call) + } else { + ps, err = c.mutatorStreamParameters(ctx, call) + } default: err = fmt.Errorf("unknown operation type: %v", call.methodInfo.Operation) } @@ -340,6 +335,10 @@ func (c *Coordinator) directRepositoryScopedMessage(ctx context.Context, call gr return nil, helper.ErrNotFound(err) } + if errors.Is(err, commonerr.ErrRepositoryNotFound) { + return nil, helper.ErrNotFound(err) + } + return nil, err } @@ -580,6 +579,69 @@ func (c *Coordinator) mutatorStreamParameters(ctx context.Context, call grpcCall return proxy.NewStreamParameters(primaryDest, secondaryDests, reqFinalizer, nil), nil } +// maintenanceStreamParameters returns stream parameters for a maintenance-style RPC. The RPC call +// is proxied to all nodes. Because it shouldn't matter whether a node is the primary or not in this +// context, we just pick the first node returned by the router to be the primary. Returns an error +// in case any of the nodes has failed to perform the maintenance RPC. +func (c *Coordinator) maintenanceStreamParameters(ctx context.Context, call grpcCall) (*proxy.StreamParameters, error) { + route, err := c.router.RouteRepositoryMaintenance(ctx, call.targetRepo.StorageName, call.targetRepo.RelativePath) + if err != nil { + return nil, fmt.Errorf("routing repository maintenance: %w", err) + } + + peerCtx := streamParametersContext(ctx) + + nodeDests := make([]proxy.Destination, 0, len(route.Nodes)) + nodeErrors := &nodeErrors{ + errByNode: make(map[string]error), + } + + for _, node := range route.Nodes { + node := node + + nodeMsg, err := rewrittenRepositoryMessage(call.methodInfo, call.msg, node.Storage, route.ReplicaPath, "") + if err != nil { + return nil, err + } + + nodeDests = append(nodeDests, proxy.Destination{ + Ctx: peerCtx, + Conn: node.Connection, + Msg: nodeMsg, + ErrHandler: func(err error) error { + nodeErrors.Lock() + defer nodeErrors.Unlock() + nodeErrors.errByNode[node.Storage] = err + + ctxlogrus.Extract(ctx).WithField("gitaly_storage", node.Storage).WithError(err).Error("proxying maintenance RPC to node failed") + + // We ignore any errors returned by nodes such that they all have a + // chance to finish their maintenance RPC in a best-effort strategy. + // In case any node fails though, the RPC call will return with an + // error after all nodes have finished. + return nil + }, + }) + } + + return proxy.NewStreamParameters(nodeDests[0], nodeDests[1:], func() error { + nodeErrors.Lock() + defer nodeErrors.Unlock() + + // In case any of the nodes has recorded an error we will return it. It shouldn't + // matter which error we return exactly, so we just return errors in the order we've + // got from the router. This also has the nice property that any error returned by + // the primary node would be prioritized over all the others. + for _, node := range route.Nodes { + if nodeErr, ok := nodeErrors.errByNode[node.Storage]; ok && nodeErr != nil { + return nodeErr + } + } + + return nil + }, nil), nil +} + // streamParametersContexts converts the contexts with incoming metadata into a context that is // usable by peer Gitaly nodes. func streamParametersContext(ctx context.Context) context.Context { diff --git a/internal/praefect/coordinator_test.go b/internal/praefect/coordinator_test.go index 3fc515da0..fd28f29d6 100644 --- a/internal/praefect/coordinator_test.go +++ b/internal/praefect/coordinator_test.go @@ -115,13 +115,18 @@ func TestStreamDirectorReadOnlyEnforcement(t *testing.T) { protoregistry.GitalyProtoPreregistered, ) - frame, err := proto.Marshal(&gitalypb.CleanupRequest{Repository: &gitalypb.Repository{ - StorageName: virtualStorage, - RelativePath: relativePath, - }}) + frame, err := proto.Marshal(&gitalypb.DeleteRefsRequest{ + Repository: &gitalypb.Repository{ + StorageName: virtualStorage, + RelativePath: relativePath, + }, + Refs: [][]byte{ + []byte("refs/heads/does-not-exist"), + }, + }) require.NoError(t, err) - _, err = coordinator.StreamDirector(ctx, "/gitaly.RepositoryService/Cleanup", &mockPeeker{frame: frame}) + _, err = coordinator.StreamDirector(ctx, "/gitaly.RefService/DeleteRefs", &mockPeeker{frame: frame}) if tc.readOnly { require.Equal(t, ErrRepositoryReadOnly, err) testhelper.RequireGrpcCode(t, err, codes.FailedPrecondition) @@ -386,6 +391,196 @@ func TestStreamDirectorMutator_StopTransaction(t *testing.T) { require.NoError(t, err) } +func TestStreamDirector_maintenance(t *testing.T) { + t.Parallel() + + node1 := &config.Node{ + Address: "unix://" + testhelper.GetTemporaryGitalySocketFileName(t), + Storage: "praefect-internal-1", + } + + node2 := &config.Node{ + Address: "unix://" + testhelper.GetTemporaryGitalySocketFileName(t), + Storage: "praefect-internal-2", + } + + cfg := config.Config{ + VirtualStorages: []*config.VirtualStorage{ + { + Name: "praefect", + Nodes: []*config.Node{node1, node2}, + }, + }, + } + + db := testdb.New(t) + + repo := gitalypb.Repository{ + StorageName: "praefect", + RelativePath: "/path/to/hashed/storage", + } + + ctx := testhelper.Context(t) + + nodeSet, err := DialNodes(ctx, cfg.VirtualStorages, protoregistry.GitalyProtoPreregistered, nil, nil, nil) + require.NoError(t, err) + defer nodeSet.Close() + + tx := db.Begin(t) + defer tx.Rollback(t) + + rs := datastore.NewPostgresRepositoryStore(tx, cfg.StorageNames()) + require.NoError(t, rs.CreateRepository(ctx, 1, repo.StorageName, repo.RelativePath, + repo.RelativePath, node1.Storage, []string{node2.Storage}, nil, true, true)) + + testdb.SetHealthyNodes(t, ctx, tx, map[string]map[string][]string{"praefect": cfg.StorageNames()}) + + registry, err := protoregistry.NewFromPaths("praefect/mock/mock.proto") + require.NoError(t, err) + + queueInterceptor := datastore.NewReplicationEventQueueInterceptor(datastore.NewPostgresReplicationEventQueue(tx)) + + coordinator := NewCoordinator( + queueInterceptor, + rs, + NewPerRepositoryRouter( + nodeSet.Connections(), + nodes.NewPerRepositoryElector(tx), + StaticHealthChecker(cfg.StorageNames()), + NewLockedRandom(rand.New(rand.NewSource(0))), + rs, + datastore.NewAssignmentStore(tx, cfg.StorageNames()), + rs, + nil, + ), + nil, + cfg, + registry, + ) + + message, err := proto.Marshal(&mock.RepoRequest{ + Repo: &repo, + }) + require.NoError(t, err) + + methodInfo, err := registry.LookupMethod("/mock.SimpleService/RepoMaintenanceUnary") + require.NoError(t, err) + + for _, tc := range []struct { + desc string + primaryErr error + secondaryErr error + expectedErr error + }{ + { + desc: "successful", + }, + { + desc: "primary returns an error", + primaryErr: helper.ErrNotFoundf("primary error"), + expectedErr: helper.ErrNotFoundf("primary error"), + }, + { + desc: "secondary returns an error", + secondaryErr: helper.ErrNotFoundf("secondary error"), + expectedErr: helper.ErrNotFoundf("secondary error"), + }, + { + desc: "primary error preferred", + primaryErr: helper.ErrNotFoundf("primary error"), + secondaryErr: helper.ErrNotFoundf("secondary error"), + expectedErr: helper.ErrNotFoundf("primary error"), + }, + } { + t.Run(tc.desc, func(t *testing.T) { + // Behaviour between the new and old routing are sufficiently different that + // it doesn't make sense to try and cram both tests in here. + ctx := featureflag.ContextWithFeatureFlag(ctx, featureflag.MaintenanceOperationRouting, true) + + queueInterceptor.OnEnqueue(func(context.Context, datastore.ReplicationEvent, datastore.ReplicationEventQueue) (datastore.ReplicationEvent, error) { + require.FailNow(t, "no replication jobs should have been created") + return datastore.ReplicationEvent{}, fmt.Errorf("unexpected call") + }) + + streamParams, err := coordinator.StreamDirector(ctx, methodInfo.FullMethodName(), &mockPeeker{message}) + require.NoError(t, err) + + var targetNodes []string + destinationByAddress := make(map[string]proxy.Destination) + for _, node := range append(streamParams.Secondaries(), streamParams.Primary()) { + targetNodes = append(targetNodes, node.Conn.Target()) + destinationByAddress[node.Conn.Target()] = node + } + + // Assert that both nodes are part of the stream parameters. Because the + // order is not deterministic (we randomly shuffle primary and secondary + // nodes) we only assert that elements match, not that any of both nodes has + // a specific role. + require.ElementsMatch(t, []string{ + node1.Address, + node2.Address, + }, targetNodes) + + // Assert that the target repositories were rewritten as expected for all of + // the nodes. + for _, nodeCfg := range []*config.Node{node1, node2} { + destination, ok := destinationByAddress[nodeCfg.Address] + require.True(t, ok) + + request, err := methodInfo.UnmarshalRequestProto(destination.Msg) + require.NoError(t, err) + + rewrittenTargetRepo, err := methodInfo.TargetRepo(request) + require.NoError(t, err) + require.Equal(t, nodeCfg.Storage, rewrittenTargetRepo.GetStorageName(), "stream director should have rewritten the storage name") + } + + // We now inject errors into the streams by manually executing the error + // handlers. This simulates that the RPCs have been proxied and may or may + // not have returned an error. + // + // Note that these functions should not return an error: this is a strict + // requirement because otherwise failures returned by the primary would + // abort the operation on secondaries. + require.Nil(t, streamParams.Primary().ErrHandler(tc.primaryErr)) + require.Nil(t, streamParams.Secondaries()[0].ErrHandler(tc.secondaryErr)) + + // The request finalizer should then see the errors as injected above. + require.Equal(t, tc.expectedErr, streamParams.RequestFinalizer()) + }) + } + + t.Run("disabled maintenance routing", func(t *testing.T) { + ctx := featureflag.ContextWithFeatureFlag(ctx, featureflag.MaintenanceOperationRouting, false) + + replicationEventCounter := 0 + queueInterceptor.OnEnqueue(func(context.Context, datastore.ReplicationEvent, datastore.ReplicationEventQueue) (datastore.ReplicationEvent, error) { + replicationEventCounter++ + return datastore.ReplicationEvent{}, nil + }) + + streamParams, err := coordinator.StreamDirector(ctx, methodInfo.FullMethodName(), &mockPeeker{message}) + require.NoError(t, err) + + // We're cheating a bit: the method we use here is not something that the + // coordinator would know, and thus it wouldn't ever set up transactions for that + // call either. This is accidentally the same behaviour like for any of the other + // preexisting maintenance-style RPCs though, so it's good enough. Furthermore, we + // really only want to ensure that the feature flag does its thing. The actual logic + // of mutator stream parameters is tested elsewhere already. + require.Equal(t, node1.Address, streamParams.Primary().Conn.Target()) + require.Empty(t, streamParams.Secondaries()) + + // There shouldn't be any replication event yet. + require.Zero(t, replicationEventCounter) + + require.NoError(t, streamParams.RequestFinalizer()) + + // But there should be one after having called the request finalizer. + require.Equal(t, 1, replicationEventCounter) + }) +} + type mockRouter struct { Router routeRepositoryAccessorFunc func(ctx context.Context, virtualStorage, relativePath string, forcePrimary bool) (RepositoryAccessorRoute, error) diff --git a/internal/praefect/middleware/methodtype.go b/internal/praefect/middleware/methodtype.go index 2478b0470..a3a0667b3 100644 --- a/internal/praefect/middleware/methodtype.go +++ b/internal/praefect/middleware/methodtype.go @@ -47,6 +47,8 @@ func observeMethodType(registry *protoregistry.Registry, fullMethod string) { opType = "accessor" case protoregistry.OpMutator: opType = "mutator" + case protoregistry.OpMaintenance: + opType = "maintenance" default: return } diff --git a/internal/praefect/mock/mock.pb.go b/internal/praefect/mock/mock.pb.go index 76ba4ff21..ffdd2df38 100644 --- a/internal/praefect/mock/mock.pb.go +++ b/internal/praefect/mock/mock.pb.go @@ -86,7 +86,7 @@ var file_praefect_mock_mock_proto_rawDesc = []byte{ 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x04, 0x72, 0x65, 0x70, 0x6f, 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, 0x04, - 0x72, 0x65, 0x70, 0x6f, 0x32, 0x9e, 0x01, 0x0a, 0x0d, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x53, + 0x72, 0x65, 0x70, 0x6f, 0x32, 0xe9, 0x01, 0x0a, 0x0d, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x46, 0x0a, 0x11, 0x52, 0x65, 0x70, 0x6f, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x55, 0x6e, 0x61, 0x72, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x6f, 0x63, 0x6b, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, @@ -96,11 +96,16 @@ var file_praefect_mock_mock_proto_rawDesc = []byte{ 0x72, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x6f, 0x63, 0x6b, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x06, 0xfa, - 0x97, 0x28, 0x02, 0x08, 0x01, 0x42, 0x39, 0x5a, 0x37, 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, 0x34, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x2f, 0x70, 0x72, 0x61, 0x65, 0x66, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x6f, 0x63, 0x6b, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x49, 0x0a, 0x14, 0x52, 0x65, 0x70, 0x6f, 0x4d, 0x61, 0x69, + 0x6e, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x55, 0x6e, 0x61, 0x72, 0x79, 0x12, 0x11, 0x2e, + 0x6d, 0x6f, 0x63, 0x6b, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x03, + 0x42, 0x39, 0x5a, 0x37, 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, 0x34, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x72, + 0x61, 0x65, 0x66, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x6f, 0x63, 0x6b, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( @@ -125,10 +130,12 @@ var file_praefect_mock_mock_proto_depIdxs = []int32{ 1, // 0: mock.RepoRequest.repo:type_name -> gitaly.Repository 0, // 1: mock.SimpleService.RepoAccessorUnary:input_type -> mock.RepoRequest 0, // 2: mock.SimpleService.RepoMutatorUnary:input_type -> mock.RepoRequest - 2, // 3: mock.SimpleService.RepoAccessorUnary:output_type -> google.protobuf.Empty - 2, // 4: mock.SimpleService.RepoMutatorUnary:output_type -> google.protobuf.Empty - 3, // [3:5] is the sub-list for method output_type - 1, // [1:3] is the sub-list for method input_type + 0, // 3: mock.SimpleService.RepoMaintenanceUnary:input_type -> mock.RepoRequest + 2, // 4: mock.SimpleService.RepoAccessorUnary:output_type -> google.protobuf.Empty + 2, // 5: mock.SimpleService.RepoMutatorUnary:output_type -> google.protobuf.Empty + 2, // 6: mock.SimpleService.RepoMaintenanceUnary:output_type -> google.protobuf.Empty + 4, // [4:7] is the sub-list for method output_type + 1, // [1:4] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name diff --git a/internal/praefect/mock/mock.proto b/internal/praefect/mock/mock.proto index 694ac2e4f..748befbf6 100644 --- a/internal/praefect/mock/mock.proto +++ b/internal/praefect/mock/mock.proto @@ -33,4 +33,12 @@ service SimpleService { scope_level: REPOSITORY }; } + + // RepoMaintenanceUnary is a unary RPC that maintains a repo + rpc RepoMaintenanceUnary(RepoRequest) returns (google.protobuf.Empty) { + option (gitaly.op_type) = { + op: MAINTENANCE + scope_level: REPOSITORY + }; + } } diff --git a/internal/praefect/mock/mock_grpc.pb.go b/internal/praefect/mock/mock_grpc.pb.go index 60203340b..0f3f1522a 100644 --- a/internal/praefect/mock/mock_grpc.pb.go +++ b/internal/praefect/mock/mock_grpc.pb.go @@ -23,6 +23,8 @@ type SimpleServiceClient interface { RepoAccessorUnary(ctx context.Context, in *RepoRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) // RepoMutatorUnary is a unary RPC that mutates a repo RepoMutatorUnary(ctx context.Context, in *RepoRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) + // RepoMaintenanceUnary is a unary RPC that maintains a repo + RepoMaintenanceUnary(ctx context.Context, in *RepoRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) } type simpleServiceClient struct { @@ -51,6 +53,15 @@ func (c *simpleServiceClient) RepoMutatorUnary(ctx context.Context, in *RepoRequ return out, nil } +func (c *simpleServiceClient) RepoMaintenanceUnary(ctx context.Context, in *RepoRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, "/mock.SimpleService/RepoMaintenanceUnary", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // SimpleServiceServer is the server API for SimpleService service. // All implementations must embed UnimplementedSimpleServiceServer // for forward compatibility @@ -59,6 +70,8 @@ type SimpleServiceServer interface { RepoAccessorUnary(context.Context, *RepoRequest) (*emptypb.Empty, error) // RepoMutatorUnary is a unary RPC that mutates a repo RepoMutatorUnary(context.Context, *RepoRequest) (*emptypb.Empty, error) + // RepoMaintenanceUnary is a unary RPC that maintains a repo + RepoMaintenanceUnary(context.Context, *RepoRequest) (*emptypb.Empty, error) mustEmbedUnimplementedSimpleServiceServer() } @@ -72,6 +85,9 @@ func (UnimplementedSimpleServiceServer) RepoAccessorUnary(context.Context, *Repo func (UnimplementedSimpleServiceServer) RepoMutatorUnary(context.Context, *RepoRequest) (*emptypb.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method RepoMutatorUnary not implemented") } +func (UnimplementedSimpleServiceServer) RepoMaintenanceUnary(context.Context, *RepoRequest) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method RepoMaintenanceUnary not implemented") +} func (UnimplementedSimpleServiceServer) mustEmbedUnimplementedSimpleServiceServer() {} // UnsafeSimpleServiceServer may be embedded to opt out of forward compatibility for this service. @@ -121,6 +137,24 @@ func _SimpleService_RepoMutatorUnary_Handler(srv interface{}, ctx context.Contex return interceptor(ctx, in, info, handler) } +func _SimpleService_RepoMaintenanceUnary_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RepoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SimpleServiceServer).RepoMaintenanceUnary(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/mock.SimpleService/RepoMaintenanceUnary", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SimpleServiceServer).RepoMaintenanceUnary(ctx, req.(*RepoRequest)) + } + return interceptor(ctx, in, info, handler) +} + // SimpleService_ServiceDesc is the grpc.ServiceDesc for SimpleService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -136,6 +170,10 @@ var SimpleService_ServiceDesc = grpc.ServiceDesc{ MethodName: "RepoMutatorUnary", Handler: _SimpleService_RepoMutatorUnary_Handler, }, + { + MethodName: "RepoMaintenanceUnary", + Handler: _SimpleService_RepoMaintenanceUnary_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "praefect/mock/mock.proto", diff --git a/internal/praefect/protoregistry/protoregistry.go b/internal/praefect/protoregistry/protoregistry.go index bc82bbf05..d2b900b1e 100644 --- a/internal/praefect/protoregistry/protoregistry.go +++ b/internal/praefect/protoregistry/protoregistry.go @@ -35,6 +35,10 @@ const ( OpAccessor // OpMutator = mutator operation type (modifies a repository) OpMutator + // OpMaintenance is an operation which performs maintenance-tasks on the repository. It + // shouldn't ever result in a user-visible change in behaviour, except that it may repair + // corrupt data. + OpMaintenance ) // Scope represents the intended scope of an RPC method @@ -250,6 +254,8 @@ func parseMethodInfo( opCode = OpAccessor case gitalypb.OperationMsg_MUTATOR: opCode = OpMutator + case gitalypb.OperationMsg_MAINTENANCE: + opCode = OpMaintenance default: opCode = OpUnknown } diff --git a/internal/praefect/protoregistry/protoregistry_test.go b/internal/praefect/protoregistry/protoregistry_test.go index 4ce683c07..89976b7c5 100644 --- a/internal/praefect/protoregistry/protoregistry_test.go +++ b/internal/praefect/protoregistry/protoregistry_test.go @@ -92,7 +92,7 @@ func TestNewProtoRegistry(t *testing.T) { "GetTagMessages": protoregistry.OpAccessor, "ListBranchNamesContainingCommit": protoregistry.OpAccessor, "ListTagNamesContainingCommit": protoregistry.OpAccessor, - "PackRefs": protoregistry.OpMutator, + "PackRefs": protoregistry.OpMaintenance, "RefExists": protoregistry.OpAccessor, }, "RemoteService": { @@ -104,7 +104,7 @@ func TestNewProtoRegistry(t *testing.T) { "ApplyGitattributes": protoregistry.OpMutator, "BackupCustomHooks": protoregistry.OpAccessor, "CalculateChecksum": protoregistry.OpAccessor, - "Cleanup": protoregistry.OpMutator, + "Cleanup": protoregistry.OpMaintenance, "CreateBundle": protoregistry.OpAccessor, "CreateFork": protoregistry.OpMutator, "CreateRepository": protoregistry.OpMutator, @@ -117,16 +117,16 @@ func TestNewProtoRegistry(t *testing.T) { "FindLicense": protoregistry.OpAccessor, "FindMergeBase": protoregistry.OpAccessor, "Fsck": protoregistry.OpAccessor, - "GarbageCollect": protoregistry.OpMutator, + "GarbageCollect": protoregistry.OpMaintenance, "GetArchive": protoregistry.OpAccessor, "GetInfoAttributes": protoregistry.OpAccessor, "GetRawChanges": protoregistry.OpAccessor, "GetSnapshot": protoregistry.OpAccessor, "HasLocalBranches": protoregistry.OpAccessor, - "OptimizeRepository": protoregistry.OpMutator, - "PruneUnreachableObjects": protoregistry.OpMutator, - "RepackFull": protoregistry.OpMutator, - "RepackIncremental": protoregistry.OpMutator, + "OptimizeRepository": protoregistry.OpMaintenance, + "PruneUnreachableObjects": protoregistry.OpMaintenance, + "RepackFull": protoregistry.OpMaintenance, + "RepackIncremental": protoregistry.OpMaintenance, "RepositoryExists": protoregistry.OpAccessor, "RepositorySize": protoregistry.OpAccessor, "RestoreCustomHooks": protoregistry.OpMutator, diff --git a/internal/praefect/replicator_test.go b/internal/praefect/replicator_test.go index 79e4135d7..6862b2f3f 100644 --- a/internal/praefect/replicator_test.go +++ b/internal/praefect/replicator_test.go @@ -2,6 +2,7 @@ package praefect import ( "context" + "fmt" "path/filepath" "strings" "sync" @@ -283,6 +284,10 @@ func TestReplicatorDowngradeAttempt(t *testing.T) { } func TestReplicator_PropagateReplicationJob(t *testing.T) { + testhelper.NewFeatureSets(featureflag.MaintenanceOperationRouting).Run(t, testReplicatorPropagateReplicationJob) +} + +func testReplicatorPropagateReplicationJob(t *testing.T, ctx context.Context) { t.Parallel() primaryStorage, secondaryStorage := "internal-gitaly-0", "internal-gitaly-1" @@ -318,16 +323,29 @@ func TestReplicator_PropagateReplicationJob(t *testing.T) { // By using WaitGroup we are sure the test cleanup will be started after all replication // requests are completed, so no running cache IO operations happen. queue := datastore.NewReplicationEventQueueInterceptor(datastore.NewPostgresReplicationEventQueue(testdb.New(t))) + var wg sync.WaitGroup - queue.OnEnqueue(func(ctx context.Context, event datastore.ReplicationEvent, queue datastore.ReplicationEventQueue) (datastore.ReplicationEvent, error) { - wg.Add(1) - return queue.Enqueue(ctx, event) - }) - queue.OnAcknowledge(func(ctx context.Context, state datastore.JobState, eventIDs []uint64, queue datastore.ReplicationEventQueue) ([]uint64, error) { - acknowledged, err := queue.Acknowledge(ctx, state, eventIDs) - wg.Add(-len(eventIDs)) - return acknowledged, err - }) + if featureflag.MaintenanceOperationRouting.IsEnabled(ctx) { + // When maintenance operation routing is enabled we don't expect to see any + // replication events. The observed behaviour should still be the same though: we + // expect to observe the RPC calls on both the primary and secondary node because we + // route them to both at the same time. + queue.OnEnqueue(func(ctx context.Context, event datastore.ReplicationEvent, queue datastore.ReplicationEventQueue) (datastore.ReplicationEvent, error) { + require.FailNow(t, "no replication jobs should have been created") + return datastore.ReplicationEvent{}, fmt.Errorf("unexpected enqueue") + }) + } else { + queue.OnEnqueue(func(ctx context.Context, event datastore.ReplicationEvent, queue datastore.ReplicationEventQueue) (datastore.ReplicationEvent, error) { + wg.Add(1) + return queue.Enqueue(ctx, event) + }) + queue.OnAcknowledge(func(ctx context.Context, state datastore.JobState, eventIDs []uint64, queue datastore.ReplicationEventQueue) ([]uint64, error) { + acknowledged, err := queue.Acknowledge(ctx, state, eventIDs) + wg.Add(-len(eventIDs)) + return acknowledged, err + }) + } + logEntry := testhelper.NewDiscardingLogEntry(t) nodeMgr, err := nodes.NewManager(logEntry, conf, nil, nil, promtest.NewMockHistogramVec(), protoregistry.GitalyProtoPreregistered, nil, nil, nil) @@ -362,7 +380,7 @@ func TestReplicator_PropagateReplicationJob(t *testing.T) { prf := NewGRPCServer(conf, logEntry, protoregistry.GitalyProtoPreregistered, coordinator.StreamDirector, txMgr, rs, nil, nil, nil, nil) listener, port := listenAvailPort(t) - ctx, cancel := context.WithCancel(testhelper.Context(t)) + ctx, cancel := context.WithCancel(ctx) go prf.Serve(listener) defer prf.Stop() diff --git a/internal/praefect/router.go b/internal/praefect/router.go index 1b6f19387..da9dbd36f 100644 --- a/internal/praefect/router.go +++ b/internal/praefect/router.go @@ -49,6 +49,16 @@ type RepositoryMutatorRoute struct { ReplicationTargets []string } +// RepositoryMaintenanceRoute describes how to route a repository scoped maintenance call. +type RepositoryMaintenanceRoute struct { + // RepositoryID is the repository's ID as Praefect identifies it. + RepositoryID int64 + // ReplicaPath is the disk path where the replicas are stored. + ReplicaPath string + // Nodes contains all the nodes the call should be routed to. + Nodes []RouterNode +} + // Router decides which nodes to direct accessor and mutator RPCs to. type Router interface { // RouteStorageAccessor returns the node which should serve the storage accessor request. @@ -66,4 +76,9 @@ type Router interface { // RouteRepositoryCreation decides returns the primary and secondaries that should handle the repository creation // request. It is up to the caller to store the assignments and primary information after finishing the RPC. RouteRepositoryCreation(ctx context.Context, virtualStorage, relativePath, additionalRepoRelativePath string) (RepositoryMutatorRoute, error) + // RouteRepositoryMaintenance routes the given maintenance-style RPC to all nodes which + // should perform maintenance. This would typically include all online nodes, regardless of + // whether the repository hosted by them is up-to-date or not. Maintenance tasks should + // never be replicated. + RouteRepositoryMaintenance(ctx context.Context, virtualStorage, relativePath string) (RepositoryMaintenanceRoute, error) } diff --git a/internal/praefect/router_node_manager.go b/internal/praefect/router_node_manager.go index 9cceaea0a..f1f3c474a 100644 --- a/internal/praefect/router_node_manager.go +++ b/internal/praefect/router_node_manager.go @@ -151,3 +151,34 @@ func (r *nodeManagerRouter) RouteRepositoryCreation(ctx context.Context, virtual ReplicationTargets: replicationTargets, }, nil } + +// RouteRepositoryMaintenance includes all healthy nodes regardless of whether they're consistent or +// not. +func (r *nodeManagerRouter) RouteRepositoryMaintenance(ctx context.Context, virtualStorage, relativePath string) (RepositoryMaintenanceRoute, error) { + shard, err := r.mgr.GetShard(ctx, virtualStorage) + if err != nil { + return RepositoryMaintenanceRoute{}, fmt.Errorf("get shard: %w", err) + } + + nodes := make([]RouterNode, 0, 1+len(shard.Secondaries)) + + if shard.Primary.IsHealthy() { + nodes = append(nodes, toRouterNode(shard.Primary)) + } + + for _, secondary := range shard.Secondaries { + if secondary.IsHealthy() { + nodes = append(nodes, toRouterNode(secondary)) + continue + } + } + + if len(nodes) == 0 { + return RepositoryMaintenanceRoute{}, ErrNoHealthyNodes + } + + return RepositoryMaintenanceRoute{ + ReplicaPath: relativePath, + Nodes: nodes, + }, nil +} diff --git a/internal/praefect/router_per_repository.go b/internal/praefect/router_per_repository.go index 2685829f8..6f1b4e7bd 100644 --- a/internal/praefect/router_per_repository.go +++ b/internal/praefect/router_per_repository.go @@ -367,3 +367,56 @@ func (r *PerRepositoryRouter) RouteRepositoryCreation(ctx context.Context, virtu ReplicationTargets: replicationTargets, }, nil } + +// RouteRepositoryMaintenance will route the maintenance call to all healthy nodes in a best-effort +// strategy. We do not raise an error in case the primary node is unhealthy, but will in case all +// nodes are unhealthy. +func (r *PerRepositoryRouter) RouteRepositoryMaintenance(ctx context.Context, virtualStorage, relativePath string) (RepositoryMaintenanceRoute, error) { + healthyNodes, err := r.healthyNodes(virtualStorage) + if err != nil { + return RepositoryMaintenanceRoute{}, err + } + + healthyNodesByStorage := map[string]RouterNode{} + for _, healthyNode := range healthyNodes { + healthyNodesByStorage[healthyNode.Storage] = healthyNode + } + + metadata, err := r.rs.GetRepositoryMetadataByPath(ctx, virtualStorage, relativePath) + if err != nil { + return RepositoryMaintenanceRoute{}, fmt.Errorf("getting repository metadata: %w", err) + } + + nodes := make([]RouterNode, 0, len(metadata.Replicas)) + for _, replica := range metadata.Replicas { + node, ok := healthyNodesByStorage[replica.Storage] + if !ok { + continue + } + + // If the is not assigned to the replica it either hasn't yet been created + // or it will eventually get deleted. In neither case does it make sense to + // maintain it, so we skip such nodes. + if !replica.Assigned { + continue + } + + // If the repository doesn't exist on the replica there is no need to perform any + // maintenance tasks at all. + if replica.Generation < 0 { + continue + } + + nodes = append(nodes, node) + } + + if len(nodes) == 0 { + return RepositoryMaintenanceRoute{}, ErrNoHealthyNodes + } + + return RepositoryMaintenanceRoute{ + RepositoryID: metadata.RepositoryID, + ReplicaPath: metadata.ReplicaPath, + Nodes: nodes, + }, nil +} diff --git a/internal/praefect/router_per_repository_test.go b/internal/praefect/router_per_repository_test.go index dfc68552d..44d8b10ce 100644 --- a/internal/praefect/router_per_repository_test.go +++ b/internal/praefect/router_per_repository_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/stretchr/testify/require" + "gitlab.com/gitlab-org/gitaly/v14/internal/git/gittest" "gitlab.com/gitlab-org/gitaly/v14/internal/praefect/commonerr" "gitlab.com/gitlab-org/gitaly/v14/internal/praefect/datastore" "gitlab.com/gitlab-org/gitaly/v14/internal/praefect/nodes" @@ -448,6 +449,218 @@ func TestPerRepositoryRouter_RouteRepositoryMutator(t *testing.T) { } } +func TestPerRepositoryRouter_RouteRepositoryMaintenance(t *testing.T) { + t.Parallel() + + db := testdb.New(t) + + var ( + virtualStorage = "virtual-storage-1" + relativePath = gittest.NewRepositoryName(t, true) + ) + + configuredStorages := []string{"primary", "secondary-1", "secondary-2"} + + for _, tc := range []struct { + desc string + virtualStorage string + healthyStorages []string + assignedStorages []string + storageGenerations map[string]int + expectedStorages []string + expectedErr error + }{ + { + desc: "unknown virtual storage", + virtualStorage: "unknown", + expectedErr: nodes.ErrVirtualStorageNotExist, + }, + { + desc: "all nodes unhealthy", + virtualStorage: virtualStorage, + storageGenerations: map[string]int{ + "primary": 1, + "secondary-1": 1, + "secondary-2": 1, + }, + expectedErr: ErrNoHealthyNodes, + }, + { + desc: "unhealthy primary", + virtualStorage: virtualStorage, + healthyStorages: []string{"secondary-1", "secondary-2"}, + storageGenerations: map[string]int{ + "primary": 1, + "secondary-1": 1, + "secondary-2": 1, + }, + expectedStorages: []string{"secondary-1", "secondary-2"}, + }, + { + desc: "unhealthy secondaries", + virtualStorage: virtualStorage, + healthyStorages: []string{"primary"}, + storageGenerations: map[string]int{ + "primary": 1, + "secondary-1": 1, + "secondary-2": 1, + }, + expectedStorages: []string{"primary"}, + }, + { + desc: "all nodes healthy", + virtualStorage: virtualStorage, + healthyStorages: configuredStorages, + storageGenerations: map[string]int{ + "primary": 1, + "secondary-1": 1, + "secondary-2": 1, + }, + expectedStorages: []string{"primary", "secondary-1", "secondary-2"}, + }, + { + desc: "unassigned primary is ignored", + virtualStorage: virtualStorage, + healthyStorages: configuredStorages, + assignedStorages: []string{"secondary-1", "secondary-2"}, + storageGenerations: map[string]int{ + "primary": 1, + "secondary-1": 1, + "secondary-2": 1, + }, + expectedStorages: []string{"secondary-1", "secondary-2"}, + }, + { + desc: "unassigned secondary is ignored", + virtualStorage: virtualStorage, + healthyStorages: configuredStorages, + assignedStorages: []string{"primary", "secondary-1"}, + storageGenerations: map[string]int{ + "primary": 1, + "secondary-1": 1, + "secondary-2": 1, + }, + expectedStorages: []string{"primary", "secondary-1"}, + }, + { + desc: "no assigned nodes", + virtualStorage: virtualStorage, + healthyStorages: configuredStorages, + storageGenerations: map[string]int{ + "primary": 1, + "secondary-1": 1, + "secondary-2": 1, + }, + expectedStorages: []string{"primary", "secondary-1", "secondary-2"}, + }, + { + desc: "unhealthy and unassigned nodes", + virtualStorage: virtualStorage, + healthyStorages: []string{"primary", "secondary-1"}, + assignedStorages: []string{"primary", "secondary-2"}, + storageGenerations: map[string]int{ + "primary": 1, + "secondary-1": 1, + "secondary-2": 1, + }, + expectedStorages: []string{"primary"}, + }, + { + desc: "missing repo on primary", + virtualStorage: virtualStorage, + healthyStorages: configuredStorages, + assignedStorages: configuredStorages, + storageGenerations: map[string]int{ + "primary": -1, + "secondary-1": 9000, + "secondary-2": 9000, + }, + expectedStorages: []string{"secondary-1", "secondary-2"}, + }, + { + desc: "missing repo on secondary", + virtualStorage: virtualStorage, + healthyStorages: configuredStorages, + assignedStorages: configuredStorages, + storageGenerations: map[string]int{ + "primary": 9000, + "secondary-1": 9000, + "secondary-2": -1, + }, + expectedStorages: []string{"primary", "secondary-1"}, + }, + { + desc: "mixed generations", + virtualStorage: virtualStorage, + healthyStorages: configuredStorages, + assignedStorages: configuredStorages, + storageGenerations: map[string]int{ + "primary": 0, + "secondary-1": 1, + "secondary-2": 2, + }, + expectedStorages: []string{"primary", "secondary-1", "secondary-2"}, + }, + } { + t.Run(tc.desc, func(t *testing.T) { + ctx := testhelper.Context(t) + + tx := db.Begin(t) + defer tx.Rollback(t) + + conns := Connections{ + virtualStorage: { + "primary": &grpc.ClientConn{}, + "secondary-1": &grpc.ClientConn{}, + "secondary-2": &grpc.ClientConn{}, + }, + } + + rs := datastore.NewPostgresRepositoryStore(tx, map[string][]string{ + virtualStorage: configuredStorages, + }) + + repositoryID, err := rs.ReserveRepositoryID(ctx, virtualStorage, relativePath) + require.NoError(t, err) + require.NoError(t, rs.CreateRepository(ctx, repositoryID, virtualStorage, relativePath, relativePath, "primary", []string{"secondary-1", "secondary-2"}, nil, true, false)) + + for _, storage := range tc.assignedStorages { + _, err := tx.ExecContext(ctx, ` + INSERT INTO repository_assignments + VALUES ($1, $2, $3, $4) + `, virtualStorage, relativePath, storage, repositoryID) + require.NoError(t, err) + } + + for storage, generation := range tc.storageGenerations { + require.NoError(t, rs.SetGeneration(ctx, repositoryID, storage, relativePath, generation)) + } + + router := NewPerRepositoryRouter(conns, nil, StaticHealthChecker{ + virtualStorage: tc.healthyStorages, + }, nil, nil, nil, rs, nil) + + route, err := router.RouteRepositoryMaintenance(ctx, tc.virtualStorage, relativePath) + require.Equal(t, tc.expectedErr, err) + if err == nil { + var expectedStorages []RouterNode + for _, expectedNode := range tc.expectedStorages { + expectedStorages = append(expectedStorages, RouterNode{ + Storage: expectedNode, + Connection: conns[tc.virtualStorage][expectedNode], + }) + } + + require.Equal(t, RepositoryMaintenanceRoute{ + RepositoryID: repositoryID, + ReplicaPath: relativePath, + Nodes: expectedStorages, + }, route) + } + }) + } +} + func TestPerRepositoryRouter_RouteRepositoryCreation(t *testing.T) { t.Parallel() diff --git a/proto/go/gitalypb/lint.pb.go b/proto/go/gitalypb/lint.pb.go index cb57cecac..13c98757d 100644 --- a/proto/go/gitalypb/lint.pb.go +++ b/proto/go/gitalypb/lint.pb.go @@ -24,9 +24,10 @@ const ( type OperationMsg_Operation int32 const ( - OperationMsg_UNKNOWN OperationMsg_Operation = 0 - OperationMsg_MUTATOR OperationMsg_Operation = 1 - OperationMsg_ACCESSOR OperationMsg_Operation = 2 + OperationMsg_UNKNOWN OperationMsg_Operation = 0 + OperationMsg_MUTATOR OperationMsg_Operation = 1 + OperationMsg_ACCESSOR OperationMsg_Operation = 2 + OperationMsg_MAINTENANCE OperationMsg_Operation = 3 ) // Enum value maps for OperationMsg_Operation. @@ -35,11 +36,13 @@ var ( 0: "UNKNOWN", 1: "MUTATOR", 2: "ACCESSOR", + 3: "MAINTENANCE", } OperationMsg_Operation_value = map[string]int32{ - "UNKNOWN": 0, - "MUTATOR": 1, - "ACCESSOR": 2, + "UNKNOWN": 0, + "MUTATOR": 1, + "ACCESSOR": 2, + "MAINTENANCE": 3, } ) @@ -288,7 +291,7 @@ var file_lint_proto_rawDesc = []byte{ 0x0a, 0x0a, 0x6c, 0x69, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe4, 0x01, 0x0a, 0x0c, 0x4f, 0x70, 0x65, 0x72, 0x61, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf5, 0x01, 0x0a, 0x0c, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x73, 0x67, 0x12, 0x2e, 0x0a, 0x02, 0x6f, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x73, 0x67, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, @@ -296,50 +299,51 @@ var file_lint_proto_rawDesc = []byte{ 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x73, 0x67, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x52, 0x0a, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x4c, - 0x65, 0x76, 0x65, 0x6c, 0x22, 0x33, 0x0a, 0x09, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x65, 0x76, 0x65, 0x6c, 0x22, 0x44, 0x0a, 0x09, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x4d, 0x55, 0x54, 0x41, 0x54, 0x4f, 0x52, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x41, - 0x43, 0x43, 0x45, 0x53, 0x53, 0x4f, 0x52, 0x10, 0x02, 0x22, 0x32, 0x0a, 0x05, 0x53, 0x63, 0x6f, - 0x70, 0x65, 0x12, 0x0e, 0x0a, 0x0a, 0x52, 0x45, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x4f, 0x52, 0x59, - 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x54, 0x4f, 0x52, 0x41, 0x47, 0x45, 0x10, 0x02, 0x22, - 0x04, 0x08, 0x01, 0x10, 0x01, 0x2a, 0x06, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x3a, 0x43, 0x0a, - 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x12, 0x1f, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xfe, 0x82, - 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, - 0x65, 0x64, 0x3a, 0x4f, 0x0a, 0x07, 0x6f, 0x70, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1e, 0x2e, + 0x43, 0x43, 0x45, 0x53, 0x53, 0x4f, 0x52, 0x10, 0x02, 0x12, 0x0f, 0x0a, 0x0b, 0x4d, 0x41, 0x49, + 0x4e, 0x54, 0x45, 0x4e, 0x41, 0x4e, 0x43, 0x45, 0x10, 0x03, 0x22, 0x32, 0x0a, 0x05, 0x53, 0x63, + 0x6f, 0x70, 0x65, 0x12, 0x0e, 0x0a, 0x0a, 0x52, 0x45, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x4f, 0x52, + 0x59, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x54, 0x4f, 0x52, 0x41, 0x47, 0x45, 0x10, 0x02, + 0x22, 0x04, 0x08, 0x01, 0x10, 0x01, 0x2a, 0x06, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x3a, 0x43, + 0x0a, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x12, 0x1f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xff, 0x82, - 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x4f, - 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x73, 0x67, 0x52, 0x06, 0x6f, 0x70, 0x54, - 0x79, 0x70, 0x65, 0x3a, 0x4f, 0x0a, 0x12, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, - 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68, - 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x80, 0x83, 0x05, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x11, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, - 0x74, 0x68, 0x6f, 0x64, 0x3a, 0x39, 0x0a, 0x07, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, - 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xe1, - 0xc8, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x3a, - 0x3f, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x1d, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xe2, 0xc8, 0x05, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, - 0x3a, 0x4c, 0x0a, 0x11, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, - 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xe3, 0xc8, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x74, 0x61, - 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x3a, 0x54, - 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x70, - 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xe4, 0xc8, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, - 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, - 0x74, 0x6f, 0x72, 0x79, 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, 0x34, 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, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xfe, + 0x82, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, + 0x74, 0x65, 0x64, 0x3a, 0x4f, 0x0a, 0x07, 0x6f, 0x70, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1e, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xff, + 0x82, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, + 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x73, 0x67, 0x52, 0x06, 0x6f, 0x70, + 0x54, 0x79, 0x70, 0x65, 0x3a, 0x4f, 0x0a, 0x12, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, + 0x74, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74, + 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x80, 0x83, 0x05, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x11, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x4d, + 0x65, 0x74, 0x68, 0x6f, 0x64, 0x3a, 0x39, 0x0a, 0x07, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, + 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0xe1, 0xc8, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, + 0x3a, 0x3f, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x1d, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xe2, 0xc8, + 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, + 0x79, 0x3a, 0x4c, 0x0a, 0x11, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x72, 0x65, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xe3, 0xc8, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x74, + 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x3a, + 0x54, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x65, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, + 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xe4, 0xc8, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x52, 0x65, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x6f, 0x72, 0x79, 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, 0x34, 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 ( diff --git a/proto/go/gitalypb/ref.pb.go b/proto/go/gitalypb/ref.pb.go index e2a4e4b97..116e1ac18 100644 --- a/proto/go/gitalypb/ref.pb.go +++ b/proto/go/gitalypb/ref.pb.go @@ -3255,7 +3255,7 @@ var file_ref_proto_rawDesc = []byte{ 0x17, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x66, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x66, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x09, 0x88, 0x02, 0x01, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x47, 0x0a, + 0x73, 0x65, 0x22, 0x09, 0x88, 0x02, 0x01, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x03, 0x12, 0x47, 0x0a, 0x08, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x66, 0x73, 0x12, 0x17, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x66, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x4c, 0x69, 0x73, 0x74, diff --git a/proto/go/gitalypb/repository-service.pb.go b/proto/go/gitalypb/repository-service.pb.go index 4e346ae7e..54e49033c 100644 --- a/proto/go/gitalypb/repository-service.pb.go +++ b/proto/go/gitalypb/repository-service.pb.go @@ -5057,28 +5057,28 @@ var file_repository_service_proto_rawDesc = []byte{ 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x70, 0x61, 0x63, 0x6b, 0x49, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x09, 0x88, 0x02, 0x01, 0xfa, 0x97, 0x28, 0x02, - 0x08, 0x01, 0x12, 0x4e, 0x0a, 0x0a, 0x52, 0x65, 0x70, 0x61, 0x63, 0x6b, 0x46, 0x75, 0x6c, 0x6c, + 0x08, 0x03, 0x12, 0x4e, 0x0a, 0x0a, 0x52, 0x65, 0x70, 0x61, 0x63, 0x6b, 0x46, 0x75, 0x6c, 0x6c, 0x12, 0x19, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x70, 0x61, 0x63, 0x6b, 0x46, 0x75, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x70, 0x61, 0x63, 0x6b, 0x46, 0x75, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x09, 0x88, 0x02, 0x01, 0xfa, 0x97, 0x28, 0x02, - 0x08, 0x01, 0x12, 0x4e, 0x0a, 0x0a, 0x4d, 0x69, 0x64, 0x78, 0x52, 0x65, 0x70, 0x61, 0x63, 0x6b, + 0x08, 0x03, 0x12, 0x4e, 0x0a, 0x0a, 0x4d, 0x69, 0x64, 0x78, 0x52, 0x65, 0x70, 0x61, 0x63, 0x6b, 0x12, 0x19, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x4d, 0x69, 0x64, 0x78, 0x52, 0x65, 0x70, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x4d, 0x69, 0x64, 0x78, 0x52, 0x65, 0x70, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x09, 0x88, 0x02, 0x01, 0xfa, 0x97, 0x28, 0x02, - 0x08, 0x01, 0x12, 0x5a, 0x0a, 0x0e, 0x47, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6c, + 0x08, 0x03, 0x12, 0x5a, 0x0a, 0x0e, 0x47, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x12, 0x1d, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x47, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x47, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x09, 0x88, 0x02, 0x01, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x60, + 0x6e, 0x73, 0x65, 0x22, 0x09, 0x88, 0x02, 0x01, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x03, 0x12, 0x60, 0x0a, 0x10, 0x57, 0x72, 0x69, 0x74, 0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x1f, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x09, 0x88, 0x02, 0x01, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x09, 0x88, 0x02, 0x01, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x03, 0x12, 0x57, 0x0a, 0x0e, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1d, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x69, 0x7a, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, @@ -5196,7 +5196,7 @@ var file_repository_service_proto_rawDesc = []byte{ 0x70, 0x12, 0x16, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x09, 0x88, 0x02, 0x01, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x50, 0x0a, + 0x73, 0x65, 0x22, 0x09, 0x88, 0x02, 0x01, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x03, 0x12, 0x50, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x12, 0x1a, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, @@ -5273,14 +5273,14 @@ var file_repository_service_proto_rawDesc = []byte{ 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 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, 0x01, 0x12, 0x72, 0x0a, 0x17, 0x50, 0x72, 0x75, 0x6e, 0x65, 0x55, 0x6e, 0x72, 0x65, + 0x02, 0x08, 0x03, 0x12, 0x72, 0x0a, 0x17, 0x50, 0x72, 0x75, 0x6e, 0x65, 0x55, 0x6e, 0x72, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x26, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x50, 0x72, 0x75, 0x6e, 0x65, 0x55, 0x6e, 0x72, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x50, 0x72, 0x75, 0x6e, 0x65, 0x55, 0x6e, 0x72, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x4e, 0x0a, 0x0b, 0x53, 0x65, 0x74, 0x46, 0x75, + 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x03, 0x12, 0x4e, 0x0a, 0x0b, 0x53, 0x65, 0x74, 0x46, 0x75, 0x6c, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x12, 0x1a, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x53, 0x65, 0x74, 0x46, 0x75, 0x6c, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x53, 0x65, 0x74, 0x46, diff --git a/proto/go/internal/linter/lint.go b/proto/go/internal/linter/lint.go index 6d8ed34b6..48eb0f252 100644 --- a/proto/go/internal/linter/lint.go +++ b/proto/go/internal/linter/lint.go @@ -43,6 +43,9 @@ func ensureMethodOpType(fileDesc *descriptorpb.FileDescriptorProto, m *descripto // if mutator, we need to make sure we specify scope or target repo return ml.validateMutator() + case gitalypb.OperationMsg_MAINTENANCE: + return ml.validateMaintenance() + case gitalypb.OperationMsg_UNKNOWN: return errors.New("op set to UNKNOWN") diff --git a/proto/go/internal/linter/lint_test.go b/proto/go/internal/linter/lint_test.go index bbf868d20..5747b3893 100644 --- a/proto/go/internal/linter/lint_test.go +++ b/proto/go/internal/linter/lint_test.go @@ -39,6 +39,14 @@ func TestLintFile(t *testing.T) { formatError("go/internal/linter/testdata/invalid.proto", "InvalidService", "InvalidMethod13", errors.New("unexpected count of storage field 0, expected 1, found storage label at: []")), formatError("go/internal/linter/testdata/invalid.proto", "InvalidService", "InvalidMethod14", errors.New("unexpected count of storage field 2, expected 1, found storage label at: [RequestWithMultipleNestedStorage.inner_message.storage_name RequestWithMultipleNestedStorage.storage_name]")), formatError("go/internal/linter/testdata/invalid.proto", "InvalidService", "InvalidMethod15", errors.New("operation type defined on an intercepted method")), + formatError("go/internal/linter/testdata/invalid.proto", "InvalidService", "MaintenanceWithMissingRepository", errors.New("unexpected count of target_repository fields 0, expected 1, found target_repository label at: []")), + formatError("go/internal/linter/testdata/invalid.proto", "InvalidService", "MaintenanceWithUnflaggedRepository", errors.New("unexpected count of target_repository fields 0, expected 1, found target_repository label at: []")), + formatError("go/internal/linter/testdata/invalid.proto", "InvalidService", "MaintenanceWithWrongNestedRepositoryType", errors.New("wrong type of field RequestWithWrongTypeRepository.header.repository, expected .gitaly.Repository, got .test.InvalidMethodResponse")), + formatError("go/internal/linter/testdata/invalid.proto", "InvalidService", "MaintenanceWithInvalidTargetType", errors.New("unexpected count of target_repository fields 0, expected 1, found target_repository label at: []")), + formatError("go/internal/linter/testdata/invalid.proto", "InvalidService", "MaintenanceWithInvalidNestedRequest", errors.New("unexpected count of target_repository fields 0, expected 1, found target_repository label at: []")), + formatError("go/internal/linter/testdata/invalid.proto", "InvalidService", "MaintenanceWithStorageAndRepository", errors.New("unexpected count of storage field 1, expected 0, found storage label at: [RequestWithStorageAndRepo.storage_name]")), + formatError("go/internal/linter/testdata/invalid.proto", "InvalidService", "MaintenanceWithNestedStorageAndRepository", errors.New("unexpected count of storage field 1, expected 0, found storage label at: [RequestWithNestedStorageAndRepo.inner_message.storage_name]")), + formatError("go/internal/linter/testdata/invalid.proto", "InvalidService", "MaintenanceWithStorageScope", errors.New("unknown operation scope level 2")), }, }, } { diff --git a/proto/go/internal/linter/method.go b/proto/go/internal/linter/method.go index 11ad03c36..b336f7f4f 100644 --- a/proto/go/internal/linter/method.go +++ b/proto/go/internal/linter/method.go @@ -49,6 +49,17 @@ func (ml methodLinter) validateMutator() error { } } +// validateMaintenance ensures that the message is repository-scoped and that it's got a target +// repository. +func (ml methodLinter) validateMaintenance() error { + switch scope := ml.opMsg.GetScopeLevel(); scope { + case gitalypb.OperationMsg_REPOSITORY: + return ml.ensureValidRepoScope() + default: + return fmt.Errorf("unknown operation scope level %d", scope) + } +} + func (ml methodLinter) ensureValidStorageScope() error { if err := ml.ensureValidTargetRepository(0); err != nil { return err diff --git a/proto/go/internal/linter/testdata/invalid.pb.go b/proto/go/internal/linter/testdata/invalid.pb.go index cc379716f..1252ad32f 100644 --- a/proto/go/internal/linter/testdata/invalid.pb.go +++ b/proto/go/internal/linter/testdata/invalid.pb.go @@ -828,7 +828,7 @@ var file_go_internal_linter_testdata_invalid_proto_rawDesc = []byte{ 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x02, 0x1a, 0x04, 0xf0, 0x97, 0x28, 0x01, - 0x32, 0xd9, 0x09, 0x0a, 0x0e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x53, 0x65, 0x72, 0x76, + 0x32, 0xc1, 0x10, 0x0a, 0x0e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4b, 0x0a, 0x0e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x30, 0x12, 0x1a, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, @@ -905,12 +905,67 @@ var file_go_internal_linter_testdata_invalid_proto_rawDesc = []byte{ 0x65, 0x73, 0x74, 0x57, 0x69, 0x74, 0x68, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x41, 0x6e, 0x64, 0x52, 0x65, 0x70, 0x6f, 0x1a, 0x1b, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x08, 0x80, 0x98, 0x28, 0x01, 0xfa, 0x97, 0x28, 0x00, 0x42, 0x44, 0x5a, 0x42, - 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, 0x34, - 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x2f, 0x6c, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x64, 0x61, - 0x74, 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x73, 0x65, 0x22, 0x08, 0x80, 0x98, 0x28, 0x01, 0xfa, 0x97, 0x28, 0x00, 0x12, 0x63, 0x0a, 0x20, + 0x4d, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x57, 0x69, 0x74, 0x68, 0x4d, + 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, + 0x12, 0x1a, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x4d, + 0x65, 0x74, 0x68, 0x6f, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x74, + 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x4d, 0x65, 0x74, 0x68, 0x6f, + 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, + 0x03, 0x12, 0x70, 0x0a, 0x22, 0x4d, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, + 0x57, 0x69, 0x74, 0x68, 0x55, 0x6e, 0x66, 0x6c, 0x61, 0x67, 0x67, 0x65, 0x64, 0x52, 0x65, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x25, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x57, 0x69, 0x74, 0x68, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, + 0x52, 0x65, 0x70, 0x6f, 0x4e, 0x6f, 0x74, 0x46, 0x6c, 0x61, 0x67, 0x67, 0x65, 0x64, 0x1a, 0x1b, + 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x4d, 0x65, 0x74, + 0x68, 0x6f, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, + 0x02, 0x08, 0x03, 0x12, 0x75, 0x0a, 0x28, 0x4d, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x61, 0x6e, + 0x63, 0x65, 0x57, 0x69, 0x74, 0x68, 0x57, 0x72, 0x6f, 0x6e, 0x67, 0x4e, 0x65, 0x73, 0x74, 0x65, + 0x64, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, + 0x24, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x57, 0x69, + 0x74, 0x68, 0x57, 0x72, 0x6f, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x6f, 0x72, 0x79, 0x1a, 0x1b, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x76, + 0x61, 0x6c, 0x69, 0x64, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x03, 0x12, 0x60, 0x0a, 0x20, 0x4d, 0x61, + 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x57, 0x69, 0x74, 0x68, 0x49, 0x6e, 0x76, + 0x61, 0x6c, 0x69, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x17, + 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x54, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x1a, 0x1b, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x49, + 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x03, 0x12, 0x66, 0x0a, 0x23, + 0x4d, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x57, 0x69, 0x74, 0x68, 0x49, + 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x1a, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x76, 0x61, 0x6c, + 0x69, 0x64, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1b, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x4d, 0x65, + 0x74, 0x68, 0x6f, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, + 0x28, 0x02, 0x08, 0x03, 0x12, 0x6b, 0x0a, 0x23, 0x4d, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x61, + 0x6e, 0x63, 0x65, 0x57, 0x69, 0x74, 0x68, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x41, 0x6e, + 0x64, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x1f, 0x2e, 0x74, 0x65, + 0x73, 0x74, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x57, 0x69, 0x74, 0x68, 0x53, 0x74, + 0x6f, 0x72, 0x61, 0x67, 0x65, 0x41, 0x6e, 0x64, 0x52, 0x65, 0x70, 0x6f, 0x1a, 0x1b, 0x2e, 0x74, + 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x4d, 0x65, 0x74, 0x68, 0x6f, + 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, + 0x03, 0x12, 0x77, 0x0a, 0x29, 0x4d, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, + 0x57, 0x69, 0x74, 0x68, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, + 0x65, 0x41, 0x6e, 0x64, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x25, + 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x57, 0x69, 0x74, + 0x68, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x41, 0x6e, + 0x64, 0x52, 0x65, 0x70, 0x6f, 0x1a, 0x1b, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x76, + 0x61, 0x6c, 0x69, 0x64, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x03, 0x12, 0x68, 0x0a, 0x1b, 0x4d, 0x61, + 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x57, 0x69, 0x74, 0x68, 0x53, 0x74, 0x6f, + 0x72, 0x61, 0x67, 0x65, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x22, 0x2e, 0x74, 0x65, 0x73, 0x74, + 0x2e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x57, 0x69, 0x74, 0x68, 0x52, 0x65, 0x70, 0x6f, 0x1a, 0x1b, 0x2e, + 0x74, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x4d, 0x65, 0x74, 0x68, + 0x6f, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x08, 0xfa, 0x97, 0x28, 0x04, + 0x08, 0x03, 0x10, 0x02, 0x42, 0x44, 0x5a, 0x42, 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, 0x34, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, + 0x6f, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x6c, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x64, 0x61, 0x74, 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -971,23 +1026,39 @@ var file_go_internal_linter_testdata_invalid_proto_depIdxs = []int32{ 2, // 23: test.InvalidService.InvalidMethod13:input_type -> test.InvalidTargetType 8, // 24: test.InvalidService.InvalidMethod14:input_type -> test.RequestWithMultipleNestedStorage 6, // 25: test.InvalidService.InvalidMethod15:input_type -> test.RequestWithStorageAndRepo - 3, // 26: test.InterceptedWithOperationType.InvalidMethod:output_type -> test.InvalidMethodResponse - 3, // 27: test.InvalidService.InvalidMethod0:output_type -> test.InvalidMethodResponse - 3, // 28: test.InvalidService.InvalidMethod1:output_type -> test.InvalidMethodResponse - 3, // 29: test.InvalidService.InvalidMethod2:output_type -> test.InvalidMethodResponse - 3, // 30: test.InvalidService.InvalidMethod4:output_type -> test.InvalidMethodResponse - 3, // 31: test.InvalidService.InvalidMethod5:output_type -> test.InvalidMethodResponse - 3, // 32: test.InvalidService.InvalidMethod6:output_type -> test.InvalidMethodResponse - 3, // 33: test.InvalidService.InvalidMethod7:output_type -> test.InvalidMethodResponse - 3, // 34: test.InvalidService.InvalidMethod8:output_type -> test.InvalidMethodResponse - 3, // 35: test.InvalidService.InvalidMethod9:output_type -> test.InvalidMethodResponse - 3, // 36: test.InvalidService.InvalidMethod10:output_type -> test.InvalidMethodResponse - 3, // 37: test.InvalidService.InvalidMethod11:output_type -> test.InvalidMethodResponse - 3, // 38: test.InvalidService.InvalidMethod13:output_type -> test.InvalidMethodResponse - 3, // 39: test.InvalidService.InvalidMethod14:output_type -> test.InvalidMethodResponse - 3, // 40: test.InvalidService.InvalidMethod15:output_type -> test.InvalidMethodResponse - 26, // [26:41] is the sub-list for method output_type - 11, // [11:26] is the sub-list for method input_type + 0, // 26: test.InvalidService.MaintenanceWithMissingRepository:input_type -> test.InvalidMethodRequest + 11, // 27: test.InvalidService.MaintenanceWithUnflaggedRepository:input_type -> test.RequestWithNestedRepoNotFlagged + 10, // 28: test.InvalidService.MaintenanceWithWrongNestedRepositoryType:input_type -> test.RequestWithWrongTypeRepository + 2, // 29: test.InvalidService.MaintenanceWithInvalidTargetType:input_type -> test.InvalidTargetType + 4, // 30: test.InvalidService.MaintenanceWithInvalidNestedRequest:input_type -> test.InvalidNestedRequest + 6, // 31: test.InvalidService.MaintenanceWithStorageAndRepository:input_type -> test.RequestWithStorageAndRepo + 7, // 32: test.InvalidService.MaintenanceWithNestedStorageAndRepository:input_type -> test.RequestWithNestedStorageAndRepo + 1, // 33: test.InvalidService.MaintenanceWithStorageScope:input_type -> test.InvalidMethodRequestWithRepo + 3, // 34: test.InterceptedWithOperationType.InvalidMethod:output_type -> test.InvalidMethodResponse + 3, // 35: test.InvalidService.InvalidMethod0:output_type -> test.InvalidMethodResponse + 3, // 36: test.InvalidService.InvalidMethod1:output_type -> test.InvalidMethodResponse + 3, // 37: test.InvalidService.InvalidMethod2:output_type -> test.InvalidMethodResponse + 3, // 38: test.InvalidService.InvalidMethod4:output_type -> test.InvalidMethodResponse + 3, // 39: test.InvalidService.InvalidMethod5:output_type -> test.InvalidMethodResponse + 3, // 40: test.InvalidService.InvalidMethod6:output_type -> test.InvalidMethodResponse + 3, // 41: test.InvalidService.InvalidMethod7:output_type -> test.InvalidMethodResponse + 3, // 42: test.InvalidService.InvalidMethod8:output_type -> test.InvalidMethodResponse + 3, // 43: test.InvalidService.InvalidMethod9:output_type -> test.InvalidMethodResponse + 3, // 44: test.InvalidService.InvalidMethod10:output_type -> test.InvalidMethodResponse + 3, // 45: test.InvalidService.InvalidMethod11:output_type -> test.InvalidMethodResponse + 3, // 46: test.InvalidService.InvalidMethod13:output_type -> test.InvalidMethodResponse + 3, // 47: test.InvalidService.InvalidMethod14:output_type -> test.InvalidMethodResponse + 3, // 48: test.InvalidService.InvalidMethod15:output_type -> test.InvalidMethodResponse + 3, // 49: test.InvalidService.MaintenanceWithMissingRepository:output_type -> test.InvalidMethodResponse + 3, // 50: test.InvalidService.MaintenanceWithUnflaggedRepository:output_type -> test.InvalidMethodResponse + 3, // 51: test.InvalidService.MaintenanceWithWrongNestedRepositoryType:output_type -> test.InvalidMethodResponse + 3, // 52: test.InvalidService.MaintenanceWithInvalidTargetType:output_type -> test.InvalidMethodResponse + 3, // 53: test.InvalidService.MaintenanceWithInvalidNestedRequest:output_type -> test.InvalidMethodResponse + 3, // 54: test.InvalidService.MaintenanceWithStorageAndRepository:output_type -> test.InvalidMethodResponse + 3, // 55: test.InvalidService.MaintenanceWithNestedStorageAndRepository:output_type -> test.InvalidMethodResponse + 3, // 56: test.InvalidService.MaintenanceWithStorageScope:output_type -> test.InvalidMethodResponse + 34, // [34:57] is the sub-list for method output_type + 11, // [11:34] is the sub-list for method input_type 11, // [11:11] is the sub-list for extension type_name 11, // [11:11] is the sub-list for extension extendee 0, // [0:11] is the sub-list for field type_name diff --git a/proto/go/internal/linter/testdata/invalid.proto b/proto/go/internal/linter/testdata/invalid.proto index a8c3ce54a..badafa4da 100644 --- a/proto/go/internal/linter/testdata/invalid.proto +++ b/proto/go/internal/linter/testdata/invalid.proto @@ -162,4 +162,50 @@ service InvalidService { option (gitaly.intercepted_method) = true; option (gitaly.op_type) = {}; }; + + rpc MaintenanceWithMissingRepository(InvalidMethodRequest) returns (InvalidMethodResponse) { + option (gitaly.op_type).op = MAINTENANCE; + } + + rpc MaintenanceWithUnflaggedRepository(RequestWithNestedRepoNotFlagged) returns (InvalidMethodResponse) { + option (gitaly.op_type).op = MAINTENANCE; + } + + rpc MaintenanceWithWrongNestedRepositoryType(RequestWithWrongTypeRepository) returns (InvalidMethodResponse) { + option (gitaly.op_type).op = MAINTENANCE; + } + + rpc MaintenanceWithInvalidTargetType(InvalidTargetType) returns (InvalidMethodResponse) { + option (gitaly.op_type) = { + op: MAINTENANCE + }; + } + + rpc MaintenanceWithInvalidNestedRequest(InvalidNestedRequest) returns (InvalidMethodResponse) { + option (gitaly.op_type) = { + op: MAINTENANCE + }; + } + + rpc MaintenanceWithStorageAndRepository(RequestWithStorageAndRepo) returns (InvalidMethodResponse) { + option (gitaly.op_type) = { + op: MAINTENANCE + scope_level: REPOSITORY + }; + } + + rpc MaintenanceWithNestedStorageAndRepository(RequestWithNestedStorageAndRepo) returns (InvalidMethodResponse) { + option (gitaly.op_type) = { + op: MAINTENANCE + scope_level: REPOSITORY + }; + } + + rpc MaintenanceWithStorageScope(InvalidMethodRequestWithRepo) returns (InvalidMethodResponse) { + option (gitaly.op_type) = { + op: MAINTENANCE + scope_level: STORAGE + }; + } + } diff --git a/proto/go/internal/linter/testdata/invalid_grpc.pb.go b/proto/go/internal/linter/testdata/invalid_grpc.pb.go index 2f109e568..02211b2f9 100644 --- a/proto/go/internal/linter/testdata/invalid_grpc.pb.go +++ b/proto/go/internal/linter/testdata/invalid_grpc.pb.go @@ -135,6 +135,14 @@ type InvalidServiceClient interface { InvalidMethod14(ctx context.Context, in *RequestWithMultipleNestedStorage, opts ...grpc.CallOption) (*InvalidMethodResponse, error) // Intercepted methods must not have operation type annotations. InvalidMethod15(ctx context.Context, in *RequestWithStorageAndRepo, opts ...grpc.CallOption) (*InvalidMethodResponse, error) + MaintenanceWithMissingRepository(ctx context.Context, in *InvalidMethodRequest, opts ...grpc.CallOption) (*InvalidMethodResponse, error) + MaintenanceWithUnflaggedRepository(ctx context.Context, in *RequestWithNestedRepoNotFlagged, opts ...grpc.CallOption) (*InvalidMethodResponse, error) + MaintenanceWithWrongNestedRepositoryType(ctx context.Context, in *RequestWithWrongTypeRepository, opts ...grpc.CallOption) (*InvalidMethodResponse, error) + MaintenanceWithInvalidTargetType(ctx context.Context, in *InvalidTargetType, opts ...grpc.CallOption) (*InvalidMethodResponse, error) + MaintenanceWithInvalidNestedRequest(ctx context.Context, in *InvalidNestedRequest, opts ...grpc.CallOption) (*InvalidMethodResponse, error) + MaintenanceWithStorageAndRepository(ctx context.Context, in *RequestWithStorageAndRepo, opts ...grpc.CallOption) (*InvalidMethodResponse, error) + MaintenanceWithNestedStorageAndRepository(ctx context.Context, in *RequestWithNestedStorageAndRepo, opts ...grpc.CallOption) (*InvalidMethodResponse, error) + MaintenanceWithStorageScope(ctx context.Context, in *InvalidMethodRequestWithRepo, opts ...grpc.CallOption) (*InvalidMethodResponse, error) } type invalidServiceClient struct { @@ -271,6 +279,78 @@ func (c *invalidServiceClient) InvalidMethod15(ctx context.Context, in *RequestW return out, nil } +func (c *invalidServiceClient) MaintenanceWithMissingRepository(ctx context.Context, in *InvalidMethodRequest, opts ...grpc.CallOption) (*InvalidMethodResponse, error) { + out := new(InvalidMethodResponse) + err := c.cc.Invoke(ctx, "/test.InvalidService/MaintenanceWithMissingRepository", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *invalidServiceClient) MaintenanceWithUnflaggedRepository(ctx context.Context, in *RequestWithNestedRepoNotFlagged, opts ...grpc.CallOption) (*InvalidMethodResponse, error) { + out := new(InvalidMethodResponse) + err := c.cc.Invoke(ctx, "/test.InvalidService/MaintenanceWithUnflaggedRepository", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *invalidServiceClient) MaintenanceWithWrongNestedRepositoryType(ctx context.Context, in *RequestWithWrongTypeRepository, opts ...grpc.CallOption) (*InvalidMethodResponse, error) { + out := new(InvalidMethodResponse) + err := c.cc.Invoke(ctx, "/test.InvalidService/MaintenanceWithWrongNestedRepositoryType", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *invalidServiceClient) MaintenanceWithInvalidTargetType(ctx context.Context, in *InvalidTargetType, opts ...grpc.CallOption) (*InvalidMethodResponse, error) { + out := new(InvalidMethodResponse) + err := c.cc.Invoke(ctx, "/test.InvalidService/MaintenanceWithInvalidTargetType", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *invalidServiceClient) MaintenanceWithInvalidNestedRequest(ctx context.Context, in *InvalidNestedRequest, opts ...grpc.CallOption) (*InvalidMethodResponse, error) { + out := new(InvalidMethodResponse) + err := c.cc.Invoke(ctx, "/test.InvalidService/MaintenanceWithInvalidNestedRequest", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *invalidServiceClient) MaintenanceWithStorageAndRepository(ctx context.Context, in *RequestWithStorageAndRepo, opts ...grpc.CallOption) (*InvalidMethodResponse, error) { + out := new(InvalidMethodResponse) + err := c.cc.Invoke(ctx, "/test.InvalidService/MaintenanceWithStorageAndRepository", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *invalidServiceClient) MaintenanceWithNestedStorageAndRepository(ctx context.Context, in *RequestWithNestedStorageAndRepo, opts ...grpc.CallOption) (*InvalidMethodResponse, error) { + out := new(InvalidMethodResponse) + err := c.cc.Invoke(ctx, "/test.InvalidService/MaintenanceWithNestedStorageAndRepository", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *invalidServiceClient) MaintenanceWithStorageScope(ctx context.Context, in *InvalidMethodRequestWithRepo, opts ...grpc.CallOption) (*InvalidMethodResponse, error) { + out := new(InvalidMethodResponse) + err := c.cc.Invoke(ctx, "/test.InvalidService/MaintenanceWithStorageScope", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // InvalidServiceServer is the server API for InvalidService service. // All implementations must embed UnimplementedInvalidServiceServer // for forward compatibility @@ -303,6 +383,14 @@ type InvalidServiceServer interface { InvalidMethod14(context.Context, *RequestWithMultipleNestedStorage) (*InvalidMethodResponse, error) // Intercepted methods must not have operation type annotations. InvalidMethod15(context.Context, *RequestWithStorageAndRepo) (*InvalidMethodResponse, error) + MaintenanceWithMissingRepository(context.Context, *InvalidMethodRequest) (*InvalidMethodResponse, error) + MaintenanceWithUnflaggedRepository(context.Context, *RequestWithNestedRepoNotFlagged) (*InvalidMethodResponse, error) + MaintenanceWithWrongNestedRepositoryType(context.Context, *RequestWithWrongTypeRepository) (*InvalidMethodResponse, error) + MaintenanceWithInvalidTargetType(context.Context, *InvalidTargetType) (*InvalidMethodResponse, error) + MaintenanceWithInvalidNestedRequest(context.Context, *InvalidNestedRequest) (*InvalidMethodResponse, error) + MaintenanceWithStorageAndRepository(context.Context, *RequestWithStorageAndRepo) (*InvalidMethodResponse, error) + MaintenanceWithNestedStorageAndRepository(context.Context, *RequestWithNestedStorageAndRepo) (*InvalidMethodResponse, error) + MaintenanceWithStorageScope(context.Context, *InvalidMethodRequestWithRepo) (*InvalidMethodResponse, error) mustEmbedUnimplementedInvalidServiceServer() } @@ -352,6 +440,30 @@ func (UnimplementedInvalidServiceServer) InvalidMethod14(context.Context, *Reque func (UnimplementedInvalidServiceServer) InvalidMethod15(context.Context, *RequestWithStorageAndRepo) (*InvalidMethodResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method InvalidMethod15 not implemented") } +func (UnimplementedInvalidServiceServer) MaintenanceWithMissingRepository(context.Context, *InvalidMethodRequest) (*InvalidMethodResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method MaintenanceWithMissingRepository not implemented") +} +func (UnimplementedInvalidServiceServer) MaintenanceWithUnflaggedRepository(context.Context, *RequestWithNestedRepoNotFlagged) (*InvalidMethodResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method MaintenanceWithUnflaggedRepository not implemented") +} +func (UnimplementedInvalidServiceServer) MaintenanceWithWrongNestedRepositoryType(context.Context, *RequestWithWrongTypeRepository) (*InvalidMethodResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method MaintenanceWithWrongNestedRepositoryType not implemented") +} +func (UnimplementedInvalidServiceServer) MaintenanceWithInvalidTargetType(context.Context, *InvalidTargetType) (*InvalidMethodResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method MaintenanceWithInvalidTargetType not implemented") +} +func (UnimplementedInvalidServiceServer) MaintenanceWithInvalidNestedRequest(context.Context, *InvalidNestedRequest) (*InvalidMethodResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method MaintenanceWithInvalidNestedRequest not implemented") +} +func (UnimplementedInvalidServiceServer) MaintenanceWithStorageAndRepository(context.Context, *RequestWithStorageAndRepo) (*InvalidMethodResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method MaintenanceWithStorageAndRepository not implemented") +} +func (UnimplementedInvalidServiceServer) MaintenanceWithNestedStorageAndRepository(context.Context, *RequestWithNestedStorageAndRepo) (*InvalidMethodResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method MaintenanceWithNestedStorageAndRepository not implemented") +} +func (UnimplementedInvalidServiceServer) MaintenanceWithStorageScope(context.Context, *InvalidMethodRequestWithRepo) (*InvalidMethodResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method MaintenanceWithStorageScope not implemented") +} func (UnimplementedInvalidServiceServer) mustEmbedUnimplementedInvalidServiceServer() {} // UnsafeInvalidServiceServer may be embedded to opt out of forward compatibility for this service. @@ -617,6 +729,150 @@ func _InvalidService_InvalidMethod15_Handler(srv interface{}, ctx context.Contex return interceptor(ctx, in, info, handler) } +func _InvalidService_MaintenanceWithMissingRepository_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InvalidMethodRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(InvalidServiceServer).MaintenanceWithMissingRepository(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/test.InvalidService/MaintenanceWithMissingRepository", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(InvalidServiceServer).MaintenanceWithMissingRepository(ctx, req.(*InvalidMethodRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _InvalidService_MaintenanceWithUnflaggedRepository_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RequestWithNestedRepoNotFlagged) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(InvalidServiceServer).MaintenanceWithUnflaggedRepository(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/test.InvalidService/MaintenanceWithUnflaggedRepository", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(InvalidServiceServer).MaintenanceWithUnflaggedRepository(ctx, req.(*RequestWithNestedRepoNotFlagged)) + } + return interceptor(ctx, in, info, handler) +} + +func _InvalidService_MaintenanceWithWrongNestedRepositoryType_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RequestWithWrongTypeRepository) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(InvalidServiceServer).MaintenanceWithWrongNestedRepositoryType(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/test.InvalidService/MaintenanceWithWrongNestedRepositoryType", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(InvalidServiceServer).MaintenanceWithWrongNestedRepositoryType(ctx, req.(*RequestWithWrongTypeRepository)) + } + return interceptor(ctx, in, info, handler) +} + +func _InvalidService_MaintenanceWithInvalidTargetType_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InvalidTargetType) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(InvalidServiceServer).MaintenanceWithInvalidTargetType(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/test.InvalidService/MaintenanceWithInvalidTargetType", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(InvalidServiceServer).MaintenanceWithInvalidTargetType(ctx, req.(*InvalidTargetType)) + } + return interceptor(ctx, in, info, handler) +} + +func _InvalidService_MaintenanceWithInvalidNestedRequest_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InvalidNestedRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(InvalidServiceServer).MaintenanceWithInvalidNestedRequest(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/test.InvalidService/MaintenanceWithInvalidNestedRequest", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(InvalidServiceServer).MaintenanceWithInvalidNestedRequest(ctx, req.(*InvalidNestedRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _InvalidService_MaintenanceWithStorageAndRepository_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RequestWithStorageAndRepo) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(InvalidServiceServer).MaintenanceWithStorageAndRepository(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/test.InvalidService/MaintenanceWithStorageAndRepository", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(InvalidServiceServer).MaintenanceWithStorageAndRepository(ctx, req.(*RequestWithStorageAndRepo)) + } + return interceptor(ctx, in, info, handler) +} + +func _InvalidService_MaintenanceWithNestedStorageAndRepository_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RequestWithNestedStorageAndRepo) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(InvalidServiceServer).MaintenanceWithNestedStorageAndRepository(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/test.InvalidService/MaintenanceWithNestedStorageAndRepository", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(InvalidServiceServer).MaintenanceWithNestedStorageAndRepository(ctx, req.(*RequestWithNestedStorageAndRepo)) + } + return interceptor(ctx, in, info, handler) +} + +func _InvalidService_MaintenanceWithStorageScope_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InvalidMethodRequestWithRepo) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(InvalidServiceServer).MaintenanceWithStorageScope(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/test.InvalidService/MaintenanceWithStorageScope", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(InvalidServiceServer).MaintenanceWithStorageScope(ctx, req.(*InvalidMethodRequestWithRepo)) + } + return interceptor(ctx, in, info, handler) +} + // InvalidService_ServiceDesc is the grpc.ServiceDesc for InvalidService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -680,6 +936,38 @@ var InvalidService_ServiceDesc = grpc.ServiceDesc{ MethodName: "InvalidMethod15", Handler: _InvalidService_InvalidMethod15_Handler, }, + { + MethodName: "MaintenanceWithMissingRepository", + Handler: _InvalidService_MaintenanceWithMissingRepository_Handler, + }, + { + MethodName: "MaintenanceWithUnflaggedRepository", + Handler: _InvalidService_MaintenanceWithUnflaggedRepository_Handler, + }, + { + MethodName: "MaintenanceWithWrongNestedRepositoryType", + Handler: _InvalidService_MaintenanceWithWrongNestedRepositoryType_Handler, + }, + { + MethodName: "MaintenanceWithInvalidTargetType", + Handler: _InvalidService_MaintenanceWithInvalidTargetType_Handler, + }, + { + MethodName: "MaintenanceWithInvalidNestedRequest", + Handler: _InvalidService_MaintenanceWithInvalidNestedRequest_Handler, + }, + { + MethodName: "MaintenanceWithStorageAndRepository", + Handler: _InvalidService_MaintenanceWithStorageAndRepository_Handler, + }, + { + MethodName: "MaintenanceWithNestedStorageAndRepository", + Handler: _InvalidService_MaintenanceWithNestedStorageAndRepository_Handler, + }, + { + MethodName: "MaintenanceWithStorageScope", + Handler: _InvalidService_MaintenanceWithStorageScope_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "go/internal/linter/testdata/invalid.proto", diff --git a/proto/go/internal/linter/testdata/valid.pb.go b/proto/go/internal/linter/testdata/valid.pb.go index b136201be..2885e8430 100644 --- a/proto/go/internal/linter/testdata/valid.pb.go +++ b/proto/go/internal/linter/testdata/valid.pb.go @@ -581,7 +581,7 @@ var file_go_internal_linter_testdata_valid_proto_rawDesc = []byte{ 0x12, 0x12, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x04, 0xf0, 0x97, 0x28, 0x01, 0x32, - 0x88, 0x05, 0x0a, 0x0c, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0xc4, 0x08, 0x0a, 0x0c, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x3d, 0x0a, 0x0a, 0x54, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x12, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x52, @@ -621,12 +621,40 @@ var file_go_internal_linter_testdata_valid_proto_rawDesc = []byte{ 0x6f, 0x64, 0x31, 0x30, 0x12, 0x19, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x04, 0x80, 0x98, 0x28, 0x01, 0x42, 0x44, 0x5a, 0x42, 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, 0x34, 0x2f, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, - 0x2f, 0x6c, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x64, 0x61, 0x74, 0x61, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x04, 0x80, 0x98, 0x28, 0x01, 0x12, 0x42, 0x0a, 0x0f, 0x54, 0x65, + 0x73, 0x74, 0x4d, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x2e, + 0x74, 0x65, 0x73, 0x74, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x13, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x03, 0x12, 0x53, + 0x0a, 0x20, 0x54, 0x65, 0x73, 0x74, 0x4d, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x63, + 0x65, 0x57, 0x69, 0x74, 0x68, 0x45, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x53, 0x63, 0x6f, + 0x70, 0x65, 0x12, 0x12, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x56, 0x61, + 0x6c, 0x69, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, + 0x02, 0x08, 0x03, 0x12, 0x59, 0x0a, 0x20, 0x54, 0x65, 0x73, 0x74, 0x4d, 0x61, 0x69, 0x6e, 0x74, + 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x57, 0x69, 0x74, 0x68, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x56, + 0x61, 0x6c, 0x69, 0x64, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x13, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x03, 0x12, 0x65, + 0x0a, 0x26, 0x54, 0x65, 0x73, 0x74, 0x4d, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x63, + 0x65, 0x57, 0x69, 0x74, 0x68, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x53, 0x68, 0x61, 0x72, 0x65, + 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, + 0x56, 0x61, 0x6c, 0x69, 0x64, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x53, 0x68, 0x61, 0x72, 0x65, + 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, + 0x56, 0x61, 0x6c, 0x69, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, + 0x97, 0x28, 0x02, 0x08, 0x03, 0x12, 0x5f, 0x0a, 0x21, 0x54, 0x65, 0x73, 0x74, 0x4d, 0x75, 0x74, + 0x61, 0x74, 0x6f, 0x72, 0x57, 0x69, 0x74, 0x68, 0x49, 0x6e, 0x6e, 0x65, 0x72, 0x4e, 0x65, 0x73, + 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x2e, 0x74, 0x65, 0x73, + 0x74, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x49, 0x6e, 0x6e, 0x65, 0x72, 0x4e, 0x65, 0x73, 0x74, + 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x74, 0x65, 0x73, 0x74, + 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, + 0xfa, 0x97, 0x28, 0x02, 0x08, 0x03, 0x42, 0x44, 0x5a, 0x42, 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, 0x34, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2f, 0x67, 0x6f, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x6c, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x64, 0x61, 0x74, 0x61, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -675,18 +703,28 @@ var file_go_internal_linter_testdata_valid_proto_depIdxs = []int32{ 2, // 14: test.ValidService.TestMethod8:input_type -> test.ValidStorageRequest 5, // 15: test.ValidService.TestMethod9:input_type -> test.ValidStorageNestedRequest 2, // 16: test.ValidService.TestMethod10:input_type -> test.ValidStorageRequest - 3, // 17: test.InterceptedService.TestMethod:output_type -> test.ValidResponse - 3, // 18: test.ValidService.TestMethod:output_type -> test.ValidResponse - 3, // 19: test.ValidService.TestMethod2:output_type -> test.ValidResponse - 3, // 20: test.ValidService.TestMethod3:output_type -> test.ValidResponse - 3, // 21: test.ValidService.TestMethod5:output_type -> test.ValidResponse - 3, // 22: test.ValidService.TestMethod6:output_type -> test.ValidResponse - 3, // 23: test.ValidService.TestMethod7:output_type -> test.ValidResponse - 3, // 24: test.ValidService.TestMethod8:output_type -> test.ValidResponse - 3, // 25: test.ValidService.TestMethod9:output_type -> test.ValidResponse - 3, // 26: test.ValidService.TestMethod10:output_type -> test.ValidResponse - 17, // [17:27] is the sub-list for method output_type - 7, // [7:17] is the sub-list for method input_type + 0, // 17: test.ValidService.TestMaintenance:input_type -> test.ValidRequest + 0, // 18: test.ValidService.TestMaintenanceWithExplicitScope:input_type -> test.ValidRequest + 4, // 19: test.ValidService.TestMaintenanceWithNestedRequest:input_type -> test.ValidNestedRequest + 6, // 20: test.ValidService.TestMaintenanceWithNestedSharedRequest:input_type -> test.ValidNestedSharedRequest + 7, // 21: test.ValidService.TestMutatorWithInnerNestedRequest:input_type -> test.ValidInnerNestedRequest + 3, // 22: test.InterceptedService.TestMethod:output_type -> test.ValidResponse + 3, // 23: test.ValidService.TestMethod:output_type -> test.ValidResponse + 3, // 24: test.ValidService.TestMethod2:output_type -> test.ValidResponse + 3, // 25: test.ValidService.TestMethod3:output_type -> test.ValidResponse + 3, // 26: test.ValidService.TestMethod5:output_type -> test.ValidResponse + 3, // 27: test.ValidService.TestMethod6:output_type -> test.ValidResponse + 3, // 28: test.ValidService.TestMethod7:output_type -> test.ValidResponse + 3, // 29: test.ValidService.TestMethod8:output_type -> test.ValidResponse + 3, // 30: test.ValidService.TestMethod9:output_type -> test.ValidResponse + 3, // 31: test.ValidService.TestMethod10:output_type -> test.ValidResponse + 3, // 32: test.ValidService.TestMaintenance:output_type -> test.ValidResponse + 3, // 33: test.ValidService.TestMaintenanceWithExplicitScope:output_type -> test.ValidResponse + 3, // 34: test.ValidService.TestMaintenanceWithNestedRequest:output_type -> test.ValidResponse + 3, // 35: test.ValidService.TestMaintenanceWithNestedSharedRequest:output_type -> test.ValidResponse + 3, // 36: test.ValidService.TestMutatorWithInnerNestedRequest:output_type -> test.ValidResponse + 22, // [22:37] is the sub-list for method output_type + 7, // [7:22] is the sub-list for method input_type 7, // [7:7] is the sub-list for extension type_name 7, // [7:7] is the sub-list for extension extendee 0, // [0:7] is the sub-list for field type_name diff --git a/proto/go/internal/linter/testdata/valid.proto b/proto/go/internal/linter/testdata/valid.proto index 0e3614792..9d1c9532e 100644 --- a/proto/go/internal/linter/testdata/valid.proto +++ b/proto/go/internal/linter/testdata/valid.proto @@ -112,4 +112,36 @@ service ValidService { rpc TestMethod10(ValidStorageRequest) returns (ValidResponse) { option (gitaly.intercepted_method) = true; } + + rpc TestMaintenance(ValidRequest) returns (ValidResponse) { + option (gitaly.op_type) = { + op: MAINTENANCE + }; + } + + rpc TestMaintenanceWithExplicitScope(ValidRequest) returns (ValidResponse) { + option (gitaly.op_type) = { + op: MAINTENANCE + scope_level: REPOSITORY // repo can be explicitly included + }; + } + + rpc TestMaintenanceWithNestedRequest(ValidNestedRequest) returns (ValidResponse) { + option (gitaly.op_type) = { + op: MAINTENANCE + }; + } + + rpc TestMaintenanceWithNestedSharedRequest(ValidNestedSharedRequest) returns (ValidResponse) { + option (gitaly.op_type) = { + op: MAINTENANCE + }; + } + + rpc TestMutatorWithInnerNestedRequest(ValidInnerNestedRequest) returns (ValidResponse) { + option (gitaly.op_type) = { + op: MAINTENANCE + }; + } + } diff --git a/proto/go/internal/linter/testdata/valid_grpc.pb.go b/proto/go/internal/linter/testdata/valid_grpc.pb.go index d545af891..c9d929b1f 100644 --- a/proto/go/internal/linter/testdata/valid_grpc.pb.go +++ b/proto/go/internal/linter/testdata/valid_grpc.pb.go @@ -114,6 +114,11 @@ type ValidServiceClient interface { TestMethod9(ctx context.Context, in *ValidStorageNestedRequest, opts ...grpc.CallOption) (*ValidResponse, error) // Intercepted methods do not need operation type annotations. TestMethod10(ctx context.Context, in *ValidStorageRequest, opts ...grpc.CallOption) (*ValidResponse, error) + TestMaintenance(ctx context.Context, in *ValidRequest, opts ...grpc.CallOption) (*ValidResponse, error) + TestMaintenanceWithExplicitScope(ctx context.Context, in *ValidRequest, opts ...grpc.CallOption) (*ValidResponse, error) + TestMaintenanceWithNestedRequest(ctx context.Context, in *ValidNestedRequest, opts ...grpc.CallOption) (*ValidResponse, error) + TestMaintenanceWithNestedSharedRequest(ctx context.Context, in *ValidNestedSharedRequest, opts ...grpc.CallOption) (*ValidResponse, error) + TestMutatorWithInnerNestedRequest(ctx context.Context, in *ValidInnerNestedRequest, opts ...grpc.CallOption) (*ValidResponse, error) } type validServiceClient struct { @@ -205,6 +210,51 @@ func (c *validServiceClient) TestMethod10(ctx context.Context, in *ValidStorageR return out, nil } +func (c *validServiceClient) TestMaintenance(ctx context.Context, in *ValidRequest, opts ...grpc.CallOption) (*ValidResponse, error) { + out := new(ValidResponse) + err := c.cc.Invoke(ctx, "/test.ValidService/TestMaintenance", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *validServiceClient) TestMaintenanceWithExplicitScope(ctx context.Context, in *ValidRequest, opts ...grpc.CallOption) (*ValidResponse, error) { + out := new(ValidResponse) + err := c.cc.Invoke(ctx, "/test.ValidService/TestMaintenanceWithExplicitScope", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *validServiceClient) TestMaintenanceWithNestedRequest(ctx context.Context, in *ValidNestedRequest, opts ...grpc.CallOption) (*ValidResponse, error) { + out := new(ValidResponse) + err := c.cc.Invoke(ctx, "/test.ValidService/TestMaintenanceWithNestedRequest", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *validServiceClient) TestMaintenanceWithNestedSharedRequest(ctx context.Context, in *ValidNestedSharedRequest, opts ...grpc.CallOption) (*ValidResponse, error) { + out := new(ValidResponse) + err := c.cc.Invoke(ctx, "/test.ValidService/TestMaintenanceWithNestedSharedRequest", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *validServiceClient) TestMutatorWithInnerNestedRequest(ctx context.Context, in *ValidInnerNestedRequest, opts ...grpc.CallOption) (*ValidResponse, error) { + out := new(ValidResponse) + err := c.cc.Invoke(ctx, "/test.ValidService/TestMutatorWithInnerNestedRequest", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // ValidServiceServer is the server API for ValidService service. // All implementations must embed UnimplementedValidServiceServer // for forward compatibility @@ -219,6 +269,11 @@ type ValidServiceServer interface { TestMethod9(context.Context, *ValidStorageNestedRequest) (*ValidResponse, error) // Intercepted methods do not need operation type annotations. TestMethod10(context.Context, *ValidStorageRequest) (*ValidResponse, error) + TestMaintenance(context.Context, *ValidRequest) (*ValidResponse, error) + TestMaintenanceWithExplicitScope(context.Context, *ValidRequest) (*ValidResponse, error) + TestMaintenanceWithNestedRequest(context.Context, *ValidNestedRequest) (*ValidResponse, error) + TestMaintenanceWithNestedSharedRequest(context.Context, *ValidNestedSharedRequest) (*ValidResponse, error) + TestMutatorWithInnerNestedRequest(context.Context, *ValidInnerNestedRequest) (*ValidResponse, error) mustEmbedUnimplementedValidServiceServer() } @@ -253,6 +308,21 @@ func (UnimplementedValidServiceServer) TestMethod9(context.Context, *ValidStorag func (UnimplementedValidServiceServer) TestMethod10(context.Context, *ValidStorageRequest) (*ValidResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method TestMethod10 not implemented") } +func (UnimplementedValidServiceServer) TestMaintenance(context.Context, *ValidRequest) (*ValidResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method TestMaintenance not implemented") +} +func (UnimplementedValidServiceServer) TestMaintenanceWithExplicitScope(context.Context, *ValidRequest) (*ValidResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method TestMaintenanceWithExplicitScope not implemented") +} +func (UnimplementedValidServiceServer) TestMaintenanceWithNestedRequest(context.Context, *ValidNestedRequest) (*ValidResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method TestMaintenanceWithNestedRequest not implemented") +} +func (UnimplementedValidServiceServer) TestMaintenanceWithNestedSharedRequest(context.Context, *ValidNestedSharedRequest) (*ValidResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method TestMaintenanceWithNestedSharedRequest not implemented") +} +func (UnimplementedValidServiceServer) TestMutatorWithInnerNestedRequest(context.Context, *ValidInnerNestedRequest) (*ValidResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method TestMutatorWithInnerNestedRequest not implemented") +} func (UnimplementedValidServiceServer) mustEmbedUnimplementedValidServiceServer() {} // UnsafeValidServiceServer may be embedded to opt out of forward compatibility for this service. @@ -428,6 +498,96 @@ func _ValidService_TestMethod10_Handler(srv interface{}, ctx context.Context, de return interceptor(ctx, in, info, handler) } +func _ValidService_TestMaintenance_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ValidRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ValidServiceServer).TestMaintenance(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/test.ValidService/TestMaintenance", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ValidServiceServer).TestMaintenance(ctx, req.(*ValidRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ValidService_TestMaintenanceWithExplicitScope_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ValidRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ValidServiceServer).TestMaintenanceWithExplicitScope(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/test.ValidService/TestMaintenanceWithExplicitScope", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ValidServiceServer).TestMaintenanceWithExplicitScope(ctx, req.(*ValidRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ValidService_TestMaintenanceWithNestedRequest_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ValidNestedRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ValidServiceServer).TestMaintenanceWithNestedRequest(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/test.ValidService/TestMaintenanceWithNestedRequest", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ValidServiceServer).TestMaintenanceWithNestedRequest(ctx, req.(*ValidNestedRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ValidService_TestMaintenanceWithNestedSharedRequest_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ValidNestedSharedRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ValidServiceServer).TestMaintenanceWithNestedSharedRequest(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/test.ValidService/TestMaintenanceWithNestedSharedRequest", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ValidServiceServer).TestMaintenanceWithNestedSharedRequest(ctx, req.(*ValidNestedSharedRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ValidService_TestMutatorWithInnerNestedRequest_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ValidInnerNestedRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ValidServiceServer).TestMutatorWithInnerNestedRequest(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/test.ValidService/TestMutatorWithInnerNestedRequest", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ValidServiceServer).TestMutatorWithInnerNestedRequest(ctx, req.(*ValidInnerNestedRequest)) + } + return interceptor(ctx, in, info, handler) +} + // ValidService_ServiceDesc is the grpc.ServiceDesc for ValidService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -471,6 +631,26 @@ var ValidService_ServiceDesc = grpc.ServiceDesc{ MethodName: "TestMethod10", Handler: _ValidService_TestMethod10_Handler, }, + { + MethodName: "TestMaintenance", + Handler: _ValidService_TestMaintenance_Handler, + }, + { + MethodName: "TestMaintenanceWithExplicitScope", + Handler: _ValidService_TestMaintenanceWithExplicitScope_Handler, + }, + { + MethodName: "TestMaintenanceWithNestedRequest", + Handler: _ValidService_TestMaintenanceWithNestedRequest_Handler, + }, + { + MethodName: "TestMaintenanceWithNestedSharedRequest", + Handler: _ValidService_TestMaintenanceWithNestedSharedRequest_Handler, + }, + { + MethodName: "TestMutatorWithInnerNestedRequest", + Handler: _ValidService_TestMutatorWithInnerNestedRequest_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "go/internal/linter/testdata/valid.proto", diff --git a/proto/lint.proto b/proto/lint.proto index 572cc6c7f..e656e1710 100644 --- a/proto/lint.proto +++ b/proto/lint.proto @@ -11,6 +11,7 @@ message OperationMsg { UNKNOWN = 0; MUTATOR = 1; ACCESSOR = 2; + MAINTENANCE = 3; } Operation op = 1; diff --git a/proto/ref.proto b/proto/ref.proto index 21cdfa9dd..72eb62c35 100644 --- a/proto/ref.proto +++ b/proto/ref.proto @@ -102,7 +102,7 @@ service RefService { rpc PackRefs(PackRefsRequest) returns (PackRefsResponse) { option deprecated = true; option (op_type) = { - op: MUTATOR + op: MAINTENANCE }; } diff --git a/proto/repository-service.proto b/proto/repository-service.proto index bb8660ceb..3fda65d1c 100644 --- a/proto/repository-service.proto +++ b/proto/repository-service.proto @@ -18,7 +18,7 @@ service RepositoryService { rpc RepackIncremental(RepackIncrementalRequest) returns (RepackIncrementalResponse) { option deprecated = true; option (op_type) = { - op: MUTATOR + op: MAINTENANCE }; } @@ -26,7 +26,7 @@ service RepositoryService { rpc RepackFull(RepackFullRequest) returns (RepackFullResponse) { option deprecated = true; option (op_type) = { - op: MUTATOR + op: MAINTENANCE }; } @@ -34,7 +34,7 @@ service RepositoryService { rpc MidxRepack(MidxRepackRequest) returns (MidxRepackResponse) { option deprecated = true; option (op_type) = { - op: MUTATOR + op: MAINTENANCE }; } @@ -42,7 +42,7 @@ service RepositoryService { rpc GarbageCollect(GarbageCollectRequest) returns (GarbageCollectResponse) { option deprecated = true; option (op_type) = { - op: MUTATOR + op: MAINTENANCE }; } @@ -50,7 +50,7 @@ service RepositoryService { rpc WriteCommitGraph(WriteCommitGraphRequest) returns (WriteCommitGraphResponse) { option deprecated = true; option (op_type) = { - op: MUTATOR + op: MAINTENANCE }; } @@ -180,7 +180,7 @@ service RepositoryService { rpc Cleanup(CleanupRequest) returns (CleanupResponse) { option deprecated = true; option (op_type) = { - op: MUTATOR + op: MAINTENANCE }; } @@ -251,7 +251,7 @@ service RepositoryService { // Gitaly has complete control of the on-disk state of repositories. rpc OptimizeRepository(OptimizeRepositoryRequest) returns (OptimizeRepositoryResponse) { option (op_type) = { - op: MUTATOR + op: MAINTENANCE }; } @@ -268,7 +268,7 @@ service RepositoryService { // wait 30 minutes, and then call PruneUnreachableObjects. rpc PruneUnreachableObjects(PruneUnreachableObjectsRequest) returns (PruneUnreachableObjectsResponse) { option (op_type) = { - op: MUTATOR + op: MAINTENANCE }; } diff --git a/ruby/proto/gitaly/lint_pb.rb b/ruby/proto/gitaly/lint_pb.rb index 6ab97305d..2cfe612e0 100644 --- a/ruby/proto/gitaly/lint_pb.rb +++ b/ruby/proto/gitaly/lint_pb.rb @@ -14,6 +14,7 @@ Google::Protobuf::DescriptorPool.generated_pool.build do value :UNKNOWN, 0 value :MUTATOR, 1 value :ACCESSOR, 2 + value :MAINTENANCE, 3 end add_enum "gitaly.OperationMsg.Scope" do value :REPOSITORY, 0 |