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

gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Steinhardt <psteinhardt@gitlab.com>2022-03-01 17:09:26 +0300
committerPatrick Steinhardt <psteinhardt@gitlab.com>2022-03-01 17:09:26 +0300
commitd88d29185b7c9f233551fcb4e42a56a1d68d48a4 (patch)
treef05503dec239cc270747a94df1af4c05027c6764
parent94770e72f7f79310bc88e79760d71ae0ffa17ca5 (diff)
operations: Return error from UserSquash when resolving revisions fails
We do not return an error in case resolving either the start or end revision of a UserSquash call fails. This makes us blind for the error conditions in this RPC, which is in turn creating problems in Praefect setups where we have to rely on errors in order to decide whether we have to create replication jobs or not. Return structured errors in case the error condition triggers such that callers can tell what kind of error they have been hitting. Changelog: changed
-rw-r--r--internal/gitaly/service/operations/squash.go37
-rw-r--r--internal/gitaly/service/operations/squash_test.go63
-rw-r--r--internal/metadata/featureflag/ff_user_squash_improved_error_handling.go7
3 files changed, 100 insertions, 7 deletions
diff --git a/internal/gitaly/service/operations/squash.go b/internal/gitaly/service/operations/squash.go
index 9e778f55d..270fdd5d3 100644
--- a/internal/gitaly/service/operations/squash.go
+++ b/internal/gitaly/service/operations/squash.go
@@ -11,6 +11,7 @@ import (
"gitlab.com/gitlab-org/gitaly/v14/internal/git2go"
"gitlab.com/gitlab-org/gitaly/v14/internal/helper"
"gitlab.com/gitlab-org/gitaly/v14/internal/helper/text"
+ "gitlab.com/gitlab-org/gitaly/v14/internal/metadata/featureflag"
"gitlab.com/gitlab-org/gitaly/v14/proto/go/gitalypb"
)
@@ -110,6 +111,24 @@ func (s *Server) userSquash(ctx context.Context, req *gitalypb.UserSquashRequest
// all parents of the start commit.
startCommit, err := repo.ResolveRevision(ctx, git.Revision(req.GetStartSha()+"^{commit}"))
if err != nil {
+ if featureflag.UserSquashImprovedErrorHandling.IsEnabled(ctx) {
+ detailedErr, err := helper.ErrWithDetails(
+ helper.ErrInvalidArgumentf("resolving start revision: %w", err),
+ &gitalypb.UserSquashError{
+ Error: &gitalypb.UserSquashError_ResolveRevision{
+ ResolveRevision: &gitalypb.ResolveRevisionError{
+ Revision: []byte(req.GetStartSha()),
+ },
+ },
+ },
+ )
+ if err != nil {
+ return "", helper.ErrInternalf("error details: %w", err)
+ }
+
+ return "", detailedErr
+ }
+
return "", fmt.Errorf("cannot resolve start commit: %w", gitError{
// This error is simply for backwards compatibility. We should just
// return the plain error eventually.
@@ -121,6 +140,24 @@ func (s *Server) userSquash(ctx context.Context, req *gitalypb.UserSquashRequest
// And we need to take the tree of the end commit. This tree already is the result
endCommit, err := repo.ResolveRevision(ctx, git.Revision(req.GetEndSha()+"^{commit}"))
if err != nil {
+ if featureflag.UserSquashImprovedErrorHandling.IsEnabled(ctx) {
+ detailedErr, err := helper.ErrWithDetails(
+ helper.ErrInvalidArgumentf("resolving end revision: %w", err),
+ &gitalypb.UserSquashError{
+ Error: &gitalypb.UserSquashError_ResolveRevision{
+ ResolveRevision: &gitalypb.ResolveRevisionError{
+ Revision: []byte(req.GetEndSha()),
+ },
+ },
+ },
+ )
+ if err != nil {
+ return "", helper.ErrInternalf("error details: %w", err)
+ }
+
+ return "", detailedErr
+ }
+
return "", fmt.Errorf("cannot resolve end commit's tree: %w", gitError{
// This error is simply for backwards compatibility. We should just
// return the plain error eventually.
diff --git a/internal/gitaly/service/operations/squash_test.go b/internal/gitaly/service/operations/squash_test.go
index b758ba4aa..fae48985a 100644
--- a/internal/gitaly/service/operations/squash_test.go
+++ b/internal/gitaly/service/operations/squash_test.go
@@ -1,6 +1,7 @@
package operations
import (
+ "context"
"fmt"
"os"
"path/filepath"
@@ -14,6 +15,7 @@ import (
"gitlab.com/gitlab-org/gitaly/v14/internal/gitaly/config"
"gitlab.com/gitlab-org/gitaly/v14/internal/helper"
"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/proto/go/gitalypb"
"google.golang.org/grpc/codes"
@@ -554,8 +556,11 @@ func TestUserSquash_ancestry(t *testing.T) {
}
func TestUserSquash_gitError(t *testing.T) {
+ testhelper.NewFeatureSets(featureflag.UserSquashImprovedErrorHandling).Run(t, testUserSquashGitError)
+}
+
+func testUserSquashGitError(t *testing.T, ctx context.Context) {
t.Parallel()
- ctx := testhelper.Context(t)
ctx, _, repo, _, client := setupOperationsService(t, ctx)
@@ -575,9 +580,31 @@ func TestUserSquash_gitError(t *testing.T) {
StartSha: "doesntexisting",
EndSha: endSha,
},
- expectedResponse: &gitalypb.UserSquashResponse{
- GitError: "fatal: ambiguous argument 'doesntexisting...54cec5282aa9f21856362fe321c800c236a61615': unknown revision or path not in the working tree.\nUse '--' to separate paths from revisions, like this:\n'git <command> [<revision>...] -- [<file>...]'\n",
- },
+ expectedErr: func() error {
+ if featureflag.UserSquashImprovedErrorHandling.IsEnabled(ctx) {
+ return errWithDetails(t,
+ helper.ErrInvalidArgumentf("resolving start revision: reference not found"),
+ &gitalypb.UserSquashError{
+ Error: &gitalypb.UserSquashError_ResolveRevision{
+ ResolveRevision: &gitalypb.ResolveRevisionError{
+ Revision: []byte("doesntexisting"),
+ },
+ },
+ },
+ )
+ }
+
+ return nil
+ }(),
+ expectedResponse: func() *gitalypb.UserSquashResponse {
+ if featureflag.UserSquashImprovedErrorHandling.IsEnabled(ctx) {
+ return nil
+ }
+
+ return &gitalypb.UserSquashResponse{
+ GitError: "fatal: ambiguous argument 'doesntexisting...54cec5282aa9f21856362fe321c800c236a61615': unknown revision or path not in the working tree.\nUse '--' to separate paths from revisions, like this:\n'git <command> [<revision>...] -- [<file>...]'\n",
+ }
+ }(),
},
{
desc: "not existing end SHA",
@@ -589,9 +616,31 @@ func TestUserSquash_gitError(t *testing.T) {
StartSha: startSha,
EndSha: "doesntexisting",
},
- expectedResponse: &gitalypb.UserSquashResponse{
- GitError: "fatal: ambiguous argument 'b83d6e391c22777fca1ed3012fce84f633d7fed0...doesntexisting': unknown revision or path not in the working tree.\nUse '--' to separate paths from revisions, like this:\n'git <command> [<revision>...] -- [<file>...]'\n",
- },
+ expectedErr: func() error {
+ if featureflag.UserSquashImprovedErrorHandling.IsEnabled(ctx) {
+ return errWithDetails(t,
+ helper.ErrInvalidArgumentf("resolving end revision: reference not found"),
+ &gitalypb.UserSquashError{
+ Error: &gitalypb.UserSquashError_ResolveRevision{
+ ResolveRevision: &gitalypb.ResolveRevisionError{
+ Revision: []byte("doesntexisting"),
+ },
+ },
+ },
+ )
+ }
+
+ return nil
+ }(),
+ expectedResponse: func() *gitalypb.UserSquashResponse {
+ if featureflag.UserSquashImprovedErrorHandling.IsEnabled(ctx) {
+ return nil
+ }
+
+ return &gitalypb.UserSquashResponse{
+ GitError: "fatal: ambiguous argument 'b83d6e391c22777fca1ed3012fce84f633d7fed0...doesntexisting': unknown revision or path not in the working tree.\nUse '--' to separate paths from revisions, like this:\n'git <command> [<revision>...] -- [<file>...]'\n",
+ }
+ }(),
},
{
desc: "user has no name set",
diff --git a/internal/metadata/featureflag/ff_user_squash_improved_error_handling.go b/internal/metadata/featureflag/ff_user_squash_improved_error_handling.go
new file mode 100644
index 000000000..e749e6f84
--- /dev/null
+++ b/internal/metadata/featureflag/ff_user_squash_improved_error_handling.go
@@ -0,0 +1,7 @@
+package featureflag
+
+// UserSquashImprovedErrorHandling enables proper error handling in the UserSquash RPC. When this
+// flag is disabled many error cases were returning successfully with an error message embedded in
+// the response. With this flag enabled, this is converted to return real gRPC errors with
+// structured errors.
+var UserSquashImprovedErrorHandling = NewFeatureFlag("user_squash_improved_error_handling", false)