Commit 5e191cec authored by Zeger-Jan van de Weg's avatar Zeger-Jan van de Weg

Reimplement DeleteRefs in Go

By reusing the updater, the refs passed will be removed. This keeps the
delete operations on one Git process.

The listing and filtering for requests which delete everything expect
certain prefixes works the same as the Rugged code. About it looks more
verbose.

Part of: #1486
parent 7af55c31
Pipeline #47479598 passed with stages
in 9 minutes and 55 seconds
---
title: Reimplement DeleteRefs in Go
merge_request: 1069
author:
type: performance
package ref
import (
"bufio"
"context"
"fmt"
"strings"
"gitlab.com/gitlab-org/gitaly-proto/go/gitalypb"
"gitlab.com/gitlab-org/gitaly/internal/rubyserver"
"gitlab.com/gitlab-org/gitaly/internal/git"
"gitlab.com/gitlab-org/gitaly/internal/git/updateref"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
......@@ -15,17 +18,75 @@ func (s *server) DeleteRefs(ctx context.Context, in *gitalypb.DeleteRefsRequest)
return nil, status.Errorf(codes.InvalidArgument, "DeleteRefs: %v", err)
}
client, err := s.RefServiceClient(ctx)
updater, err := updateref.New(ctx, in.GetRepository())
if err != nil {
return nil, err
}
clientCtx, err := rubyserver.SetHeaders(ctx, in.GetRepository())
refnames, err := refsToRemove(ctx, in)
if err != nil {
// TODO decorate error
return nil, err
}
return client.DeleteRefs(clientCtx, in)
for _, ref := range refnames {
if err := updater.Delete(ref); err != nil {
return &gitalypb.DeleteRefsResponse{GitError: err.Error()}, nil
}
}
if err := updater.Wait(); err != nil {
return &gitalypb.DeleteRefsResponse{GitError: fmt.Sprintf("unable to delete refs: %s", err.Error())}, nil
}
return &gitalypb.DeleteRefsResponse{}, nil
}
func refsToRemove(ctx context.Context, req *gitalypb.DeleteRefsRequest) ([]string, error) {
var refs []string
if len(req.Refs) > 0 {
for _, ref := range req.Refs {
refs = append(refs, string(ref))
}
return refs, nil
}
cmd, err := git.Command(ctx, req.GetRepository(), "for-each-ref", "--format=%(refname)")
if err != nil {
return nil, err
}
var prefixes []string
for _, prefix := range req.ExceptWithPrefix {
prefixes = append(prefixes, string(prefix))
}
scanner := bufio.NewScanner(cmd)
for scanner.Scan() {
refName := scanner.Text()
if hasAnyPrefix(refName, prefixes) {
continue
}
refs = append(refs, string(refName))
}
if err != nil {
return nil, fmt.Errorf("error listing refs: %v", cmd.Wait())
}
return refs, nil
}
func hasAnyPrefix(s string, prefixes []string) bool {
for _, prefix := range prefixes {
if strings.HasPrefix(s, prefix) {
return true
}
}
return false
}
func validateDeleteRefRequest(req *gitalypb.DeleteRefsRequest) error {
......
......@@ -3,6 +3,7 @@ package ref
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gitlab.com/gitlab-org/gitaly-proto/go/gitalypb"
"gitlab.com/gitlab-org/gitaly/internal/testhelper"
......@@ -83,7 +84,7 @@ func TestFailedDeleteRefsRequestDueToGitError(t *testing.T) {
response, err := client.DeleteRefs(ctx, request)
require.NoError(t, err)
require.Contains(t, response.GitError, "Could not delete refs")
assert.Contains(t, response.GitError, "unable to delete refs")
}
func TestFailedDeleteRefsDueToValidation(t *testing.T) {
......
......@@ -76,6 +76,7 @@ module GitalyServer
end
end
# Post 11.10 this method can be removed
def delete_refs(request, call)
repo = Gitlab::Git::Repository.from_gitaly(request.repository, call)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment