From 39e07d33454ee459bd93ff5ef332edb74049051b Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Thu, 24 Mar 2022 16:22:22 +0000 Subject: Allow Commit.RawBlame to take a Range parameter Git supports a wide array of range options for this option, but for now we restrict the formats we support to the very simplest, which is of the form "1,100". We can use this to implement limits on how many lines of the blame we retrieve GitLab-side, which should help to reduce the resource usage of this particular RPC. Changelog: added --- internal/gitaly/service/commit/raw_blame.go | 16 ++++++++- internal/gitaly/service/commit/raw_blame_test.go | 43 +++++++++++++++++++----- 2 files changed, 49 insertions(+), 10 deletions(-) diff --git a/internal/gitaly/service/commit/raw_blame.go b/internal/gitaly/service/commit/raw_blame.go index 860e511b7..0d83546bf 100644 --- a/internal/gitaly/service/commit/raw_blame.go +++ b/internal/gitaly/service/commit/raw_blame.go @@ -3,6 +3,7 @@ package commit import ( "fmt" "io" + "regexp" "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus" "gitlab.com/gitlab-org/gitaly/v14/internal/git" @@ -12,6 +13,8 @@ import ( "google.golang.org/grpc/status" ) +var validBlameRange = regexp.MustCompile(`\A\d+,\d+\z`) + func (s *server) RawBlame(in *gitalypb.RawBlameRequest, stream gitalypb.CommitService_RawBlameServer) error { if err := validateRawBlameRequest(in); err != nil { return status.Errorf(codes.InvalidArgument, "RawBlame: %v", err) @@ -20,10 +23,16 @@ func (s *server) RawBlame(in *gitalypb.RawBlameRequest, stream gitalypb.CommitSe ctx := stream.Context() revision := string(in.GetRevision()) path := string(in.GetPath()) + blameRange := in.GetRange() + + flags := []git.Option{git.Flag{Name: "-p"}} + if blameRange != "" { + flags = append(flags, git.ValueFlag{Name: "-L", Value: blameRange}) + } cmd, err := s.gitCmdFactory.New(ctx, in.Repository, git.SubCmd{ Name: "blame", - Flags: []git.Option{git.Flag{Name: "-p"}}, + Flags: flags, Args: []string{revision}, PostSepArgs: []string{path}, }) @@ -59,5 +68,10 @@ func validateRawBlameRequest(in *gitalypb.RawBlameRequest) error { return fmt.Errorf("empty Path") } + blameRange := in.GetRange() + if blameRange != "" && !validBlameRange.MatchString(blameRange) { + return fmt.Errorf("invalid Range") + } + return nil } diff --git a/internal/gitaly/service/commit/raw_blame_test.go b/internal/gitaly/service/commit/raw_blame_test.go index 5de17dd91..d2a07fe0b 100644 --- a/internal/gitaly/service/commit/raw_blame_test.go +++ b/internal/gitaly/service/commit/raw_blame_test.go @@ -20,21 +20,31 @@ func TestSuccessfulRawBlameRequest(t *testing.T) { testCases := []struct { revision, path, data []byte + blameRange string }{ { - revision: []byte("e63f41fe459e62e1228fcef60d7189127aeba95a"), - path: []byte("files/ruby/popen.rb"), - data: testhelper.MustReadFile(t, "testdata/files-ruby-popen-e63f41f-blame.txt"), + revision: []byte("e63f41fe459e62e1228fcef60d7189127aeba95a"), + path: []byte("files/ruby/popen.rb"), + data: testhelper.MustReadFile(t, "testdata/files-ruby-popen-e63f41f-blame.txt"), + blameRange: "", }, { - revision: []byte("e63f41fe459e62e1228fcef60d7189127aeba95a"), - path: []byte("files/ruby/../ruby/popen.rb"), - data: testhelper.MustReadFile(t, "testdata/files-ruby-popen-e63f41f-blame.txt"), + revision: []byte("e63f41fe459e62e1228fcef60d7189127aeba95a"), + path: []byte("files/ruby/popen.rb"), + data: testhelper.MustReadFile(t, "testdata/files-ruby-popen-e63f41f-blame.txt")[0:956], + blameRange: "1,5", }, { - revision: []byte("93dcf076a236c837dd47d61f86d95a6b3d71b586"), - path: []byte("gitaly/empty-file"), - data: []byte{}, + revision: []byte("e63f41fe459e62e1228fcef60d7189127aeba95a"), + path: []byte("files/ruby/../ruby/popen.rb"), + data: testhelper.MustReadFile(t, "testdata/files-ruby-popen-e63f41f-blame.txt"), + blameRange: "", + }, + { + revision: []byte("93dcf076a236c837dd47d61f86d95a6b3d71b586"), + path: []byte("gitaly/empty-file"), + data: []byte{}, + blameRange: "", }, } @@ -44,6 +54,7 @@ func TestSuccessfulRawBlameRequest(t *testing.T) { Repository: repo, Revision: testCase.revision, Path: testCase.path, + Range: testCase.blameRange, } c, err := client.RawBlame(ctx, request) require.NoError(t, err) @@ -73,6 +84,7 @@ func TestFailedRawBlameRequest(t *testing.T) { description string repo *gitalypb.Repository revision, path []byte + blameRange string code codes.Code }{ { @@ -80,6 +92,7 @@ func TestFailedRawBlameRequest(t *testing.T) { repo: invalidRepo, revision: []byte("master"), path: []byte("a/b/c"), + blameRange: "", code: codes.InvalidArgument, }, { @@ -87,6 +100,7 @@ func TestFailedRawBlameRequest(t *testing.T) { repo: repo, revision: []byte(""), path: []byte("a/b/c"), + blameRange: "", code: codes.InvalidArgument, }, { @@ -94,6 +108,7 @@ func TestFailedRawBlameRequest(t *testing.T) { repo: repo, revision: []byte("abcdef"), path: []byte(""), + blameRange: "", code: codes.InvalidArgument, }, { @@ -101,6 +116,15 @@ func TestFailedRawBlameRequest(t *testing.T) { repo: repo, revision: []byte("--output=/meow"), path: []byte("a/b/c"), + blameRange: "", + code: codes.InvalidArgument, + }, + { + description: "Invalid range", + repo: repo, + revision: []byte("abcdef"), + path: []byte("a/b/c"), + blameRange: "foo", code: codes.InvalidArgument, }, } @@ -111,6 +135,7 @@ func TestFailedRawBlameRequest(t *testing.T) { Repository: testCase.repo, Revision: testCase.revision, Path: testCase.path, + Range: testCase.blameRange, } c, err := client.RawBlame(ctx, &request) require.NoError(t, err) -- cgit v1.2.3