diff options
author | Jacob Vosmaer (GitLab) <jacob@gitlab.com> | 2017-07-03 14:32:11 +0300 |
---|---|---|
committer | Jacob Vosmaer (GitLab) <jacob@gitlab.com> | 2017-07-03 14:32:11 +0300 |
commit | 57f976e4e7f92c39869af37ec5fbd1c2dbf2d3fb (patch) | |
tree | 0ccf1d71f5f307782600fa888ec449c4dffdf7b0 | |
parent | 2a702cf905656e7fd2a0a02f04af1ea7eecb2bcf (diff) | |
parent | fd220a3c953f767f0ce4b6d2eed2f3f0e7366c9f (diff) |
Merge branch 'zj-repo-exists' into 'master'
Repo exists implemenation
Closes #335
See merge request !200
-rw-r--r-- | CHANGELOG.md | 2 | ||||
-rw-r--r-- | internal/helper/repo.go | 40 | ||||
-rw-r--r-- | internal/service/repository/repository.go | 21 | ||||
-rw-r--r-- | internal/service/repository/repository_test.go | 91 | ||||
-rw-r--r-- | internal/service/repository/server.go | 12 | ||||
-rw-r--r-- | internal/service/repository/testhelper_test.go | 64 |
6 files changed, 224 insertions, 6 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 873fff428..7eded313c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ v0.14.0 - Override gRPC code to Canceled/DeadlineExceeded on requests with canceled contexts https://gitlab.com/gitlab-org/gitaly/merge_requests/199 +- Add RepositoryExists Implementation + https://gitlab.com/gitlab-org/gitaly/merge_requests/200 v0.13.0 diff --git a/internal/helper/repo.go b/internal/helper/repo.go index a3cd3363c..2c3a1f51c 100644 --- a/internal/helper/repo.go +++ b/internal/helper/repo.go @@ -18,6 +18,26 @@ import ( // relevant error codes and should be passed back to gRPC without further // decoration. func GetRepoPath(repo *pb.Repository) (string, error) { + repoPath, err := GetPath(repo) + if err != nil { + return "", err + } + + if repoPath == "" { + return "", grpc.Errorf(codes.InvalidArgument, "GetRepoPath: empty repo") + } + + if IsGitDirectory(repoPath) { + return repoPath, nil + } + + return "", grpc.Errorf(codes.NotFound, "GetRepoPath: not a git repository '%s'", repoPath) +} + +// GetPath returns the path of the repo passed as first argument. An error is +// returned when either the storage can't be found or the path includes +// constructs trying to perform directory traversal. +func GetPath(repo *pb.Repository) (string, error) { storagePath, ok := config.StoragePath(repo.GetStorageName()) if !ok { return "", grpc.Errorf(codes.InvalidArgument, "GetRepoPath: invalid storage name '%s'", repo.GetStorageName()) @@ -33,15 +53,23 @@ func GetRepoPath(repo *pb.Repository) (string, error) { return "", grpc.Errorf(codes.InvalidArgument, "GetRepoPath: relative path can't contain directory traversal") } - repoPath := path.Join(storagePath, relativePath) + return path.Join(storagePath, relativePath), nil +} - if repoPath == "" { - return "", grpc.Errorf(codes.InvalidArgument, "GetRepoPath: empty repo") +// IsGitDirectory checks if the directory passed as first argument looks like +// a valid git directory. +func IsGitDirectory(dir string) bool { + if dir == "" { + return false + } + + if _, err := os.Stat(path.Join(dir, "objects")); err != nil { + return false } - if _, err := os.Stat(path.Join(repoPath, "objects")); err != nil { - return "", grpc.Errorf(codes.NotFound, "GetRepoPath: not a git repository '%s'", repoPath) + if _, err := os.Stat(path.Join(dir, "HEAD")); err != nil { + return false } - return repoPath, nil + return true } diff --git a/internal/service/repository/repository.go b/internal/service/repository/repository.go new file mode 100644 index 000000000..61ff2040d --- /dev/null +++ b/internal/service/repository/repository.go @@ -0,0 +1,21 @@ +package repository + +import ( + "golang.org/x/net/context" + + pb "gitlab.com/gitlab-org/gitaly-proto/go" + "gitlab.com/gitlab-org/gitaly/internal/helper" +) + +func (s *server) Exists(ctx context.Context, in *pb.RepositoryExistsRequest) (*pb.RepositoryExistsResponse, error) { + path, err := helper.GetPath(in.Repository) + if err != nil { + return nil, err + } + + if helper.IsGitDirectory(path) { + return &pb.RepositoryExistsResponse{Exists: true}, nil + } + + return &pb.RepositoryExistsResponse{Exists: false}, nil +} diff --git a/internal/service/repository/repository_test.go b/internal/service/repository/repository_test.go new file mode 100644 index 000000000..01ef5cbf5 --- /dev/null +++ b/internal/service/repository/repository_test.go @@ -0,0 +1,91 @@ +package repository + +import ( + "testing" + + pb "gitlab.com/gitlab-org/gitaly-proto/go" + "gitlab.com/gitlab-org/gitaly/internal/config" + "gitlab.com/gitlab-org/gitaly/internal/testhelper" + + "github.com/stretchr/testify/require" + "golang.org/x/net/context" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" +) + +func TestRepositoryExists(t *testing.T) { + server := runRepoServer(t) + defer server.Stop() + + client := newRepositoryClient(t) + + // Setup storage paths + testStorages := []config.Storage{ + {Name: "default", Path: testhelper.GitlabTestStoragePath()}, + {Name: "other", Path: "/home/git/repositories2"}, + } + + defer func(oldStorages []config.Storage) { + config.Config.Storages = oldStorages + }(config.Config.Storages) + config.Config.Storages = testStorages + + queries := []struct { + Request *pb.RepositoryExistsRequest + ErrorCode codes.Code + Exists bool + }{ + { + Request: &pb.RepositoryExistsRequest{ + Repository: nil, + }, + ErrorCode: codes.InvalidArgument, + }, + { + Request: &pb.RepositoryExistsRequest{ + Repository: &pb.Repository{ + StorageName: "", + RelativePath: testhelper.TestRelativePath, + }, + }, + ErrorCode: codes.InvalidArgument, + }, + { + Request: &pb.RepositoryExistsRequest{ + Repository: &pb.Repository{ + StorageName: "default", + RelativePath: "", + }, + }, + ErrorCode: codes.InvalidArgument, + }, + { + Request: &pb.RepositoryExistsRequest{ + Repository: &pb.Repository{ + StorageName: "default", + RelativePath: testhelper.TestRelativePath, + }, + }, + Exists: true, + }, + { + Request: &pb.RepositoryExistsRequest{ + Repository: &pb.Repository{ + StorageName: "other", + RelativePath: testhelper.TestRelativePath, + }, + }, + Exists: false, + }, + } + + for _, tc := range queries { + response, err := client.Exists(context.Background(), tc.Request) + if err != nil { + require.Equal(t, tc.ErrorCode, grpc.Code(err)) + continue + } + + require.Equal(t, tc.Exists, response.Exists) + } +} diff --git a/internal/service/repository/server.go b/internal/service/repository/server.go new file mode 100644 index 000000000..d47c1a9b8 --- /dev/null +++ b/internal/service/repository/server.go @@ -0,0 +1,12 @@ +package repository + +import ( + pb "gitlab.com/gitlab-org/gitaly-proto/go" +) + +type server struct{} + +// NewServer creates a new instance of a gRPC repo server +func NewServer() pb.RepositoryServiceServer { + return &server{} +} diff --git a/internal/service/repository/testhelper_test.go b/internal/service/repository/testhelper_test.go new file mode 100644 index 000000000..0972baa75 --- /dev/null +++ b/internal/service/repository/testhelper_test.go @@ -0,0 +1,64 @@ +package repository + +import ( + "net" + "os" + "path" + "testing" + "time" + + log "github.com/Sirupsen/logrus" + pb "gitlab.com/gitlab-org/gitaly-proto/go" + "gitlab.com/gitlab-org/gitaly/internal/testhelper" + "google.golang.org/grpc" + "google.golang.org/grpc/reflection" +) + +const scratchDir = "testdata/scratch" + +var ( + serverSocketPath = path.Join(scratchDir, "gitaly.sock") + testRepo *pb.Repository +) + +func TestMain(m *testing.M) { + testRepo = testhelper.TestRepository() + + if err := os.MkdirAll(scratchDir, 0755); err != nil { + log.WithError(err).Fatal("mkdirall failed") + } + + os.Exit(func() int { + return m.Run() + }()) +} + +func newRepositoryClient(t *testing.T) pb.RepositoryServiceClient { + connOpts := []grpc.DialOption{ + grpc.WithInsecure(), + grpc.WithDialer(func(addr string, _ time.Duration) (net.Conn, error) { + return net.Dial("unix", addr) + }), + } + conn, err := grpc.Dial(serverSocketPath, connOpts...) + if err != nil { + t.Fatal(err) + } + + return pb.NewRepositoryServiceClient(conn) +} + +func runRepoServer(t *testing.T) *grpc.Server { + server := grpc.NewServer() + listener, err := net.Listen("unix", serverSocketPath) + if err != nil { + t.Fatal(err) + } + + pb.RegisterRepositoryServiceServer(server, NewServer()) + reflection.Register(server) + + go server.Serve(listener) + + return server +} |