diff options
author | Jacob Vosmaer (GitLab) <jacob@gitlab.com> | 2017-09-22 15:56:31 +0300 |
---|---|---|
committer | Ahmad Sherif <ahmad.m.sherif@gmail.com> | 2017-09-22 15:56:31 +0300 |
commit | d53cbe09cad0ee227e24f2af36b7caac1dccc0d6 (patch) | |
tree | eb60824bd9b2cfa6b9921a105c7ffdd8a7cd108d | |
parent | f220038f1f46291d2eea829665ae205111573fff (diff) |
Implement CreateRepository
-rw-r--r-- | CHANGELOG.md | 2 | ||||
-rw-r--r-- | internal/rubyserver/rubyserver.go | 22 | ||||
-rw-r--r-- | internal/rubyserver/rubyserver_test.go | 4 | ||||
-rw-r--r-- | internal/service/commit/stats.go | 8 | ||||
-rw-r--r-- | internal/service/register.go | 3 | ||||
-rw-r--r-- | internal/service/repository/create.go | 22 | ||||
-rw-r--r-- | internal/service/repository/create_test.go | 84 | ||||
-rw-r--r-- | internal/service/repository/server.go | 17 | ||||
-rw-r--r-- | internal/service/repository/testdata/gitlab-shell/hooks/.gitkeep | 0 | ||||
-rw-r--r-- | internal/service/repository/testhelper_test.go | 31 | ||||
-rw-r--r-- | ruby/lib/gitaly_server.rb | 2 | ||||
-rw-r--r-- | ruby/lib/gitaly_server/repository_service.rb | 12 | ||||
-rw-r--r-- | ruby/lib/gitlab/git.rb | 14 |
13 files changed, 199 insertions, 22 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index da4fd7b1f..2da2d8dd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ UNRELEASED https://gitlab.com/gitlab-org/gitaly/merge_requests/360 - Implement UserDeleteTag RPC https://gitlab.com/gitlab-org/gitaly/merge_requests/366 +- Implement RepositoryService::CreateRepository + https://gitlab.com/gitlab-org/gitaly/merge_requests/361 v0.40.0 - Use context cancellation instead of command.Close diff --git a/internal/rubyserver/rubyserver.go b/internal/rubyserver/rubyserver.go index 3db51ea92..0470019b2 100644 --- a/internal/rubyserver/rubyserver.go +++ b/internal/rubyserver/rubyserver.go @@ -97,10 +97,16 @@ func (s *Server) Stop() { func Start() (*Server, error) { lazyInit.Do(prepareSocketPath) + cfg := config.Config + env := []string{ + "GITALY_RUBY_GIT_BIN_PATH=" + command.GitPath(), + fmt.Sprintf("GITALY_RUBY_WRITE_BUFFER_SIZE=%d", streamio.WriteBufferSize), + "GITALY_RUBY_GITLAB_SHELL_PATH=" + cfg.GitlabShell.Dir, + } + args := []string{"bundle", "exec", "bin/gitaly-ruby", fmt.Sprintf("%d", os.Getpid()), socketPath()} - env := append(os.Environ(), "GITALY_RUBY_GIT_BIN_PATH="+command.GitPath(), - fmt.Sprintf("GITALY_RUBY_WRITE_BUFFER_SIZE=%d", streamio.WriteBufferSize)) - p, err := supervisor.New("gitaly-ruby", env, args, config.Config.Ruby.Dir) + + p, err := supervisor.New("gitaly-ruby", append(os.Environ(), env...), args, cfg.Ruby.Dir) return &Server{Process: p}, err } @@ -136,6 +142,14 @@ func (s *Server) OperationServiceClient(ctx context.Context) (pb.OperationServic return pb.NewOperationServiceClient(conn), err } +// RepositoryServiceClient returns a RefServiceClient instance that is +// configured to connect to the running Ruby server. This assumes Start() +// has been called already. +func (s *Server) RepositoryServiceClient(ctx context.Context) (pb.RepositoryServiceClient, error) { + conn, err := s.getConnection(ctx) + return pb.NewRepositoryServiceClient(conn), err +} + func (s *Server) getConnection(ctx context.Context) (*grpc.ClientConn, error) { s.clientConnMu.RLock() conn := s.clientConn @@ -180,7 +194,7 @@ func dialOptions() []grpc.DialOption { // SetHeaders adds headers that tell gitaly-ruby the full path to the repository. func SetHeaders(ctx context.Context, repo *pb.Repository) (context.Context, error) { - repoPath, err := helper.GetRepoPath(repo) + repoPath, err := helper.GetPath(repo) if err != nil { return nil, err } diff --git a/internal/rubyserver/rubyserver_test.go b/internal/rubyserver/rubyserver_test.go index cb3dfd1e2..c39782d3b 100644 --- a/internal/rubyserver/rubyserver_test.go +++ b/internal/rubyserver/rubyserver_test.go @@ -34,10 +34,6 @@ func TestSetHeaders(t *testing.T) { errType: codes.InvalidArgument, }, { - repo: &pb.Repository{StorageName: testRepo.GetStorageName(), RelativePath: "bar.git"}, - errType: codes.NotFound, - }, - { repo: testRepo, errType: codes.OK, }, diff --git a/internal/service/commit/stats.go b/internal/service/commit/stats.go index 6c5efd335..9b647f15b 100644 --- a/internal/service/commit/stats.go +++ b/internal/service/commit/stats.go @@ -4,6 +4,7 @@ import ( "golang.org/x/net/context" pb "gitlab.com/gitlab-org/gitaly-proto/go" + "gitlab.com/gitlab-org/gitaly/internal/helper" "gitlab.com/gitlab-org/gitaly/internal/rubyserver" ) @@ -13,7 +14,12 @@ func (s *server) CommitStats(ctx context.Context, in *pb.CommitStatsRequest) (*p return nil, err } - clientCtx, err := rubyserver.SetHeaders(ctx, in.GetRepository()) + repo := in.GetRepository() + if _, err := helper.GetRepoPath(repo); err != nil { + return nil, err + } + + clientCtx, err := rubyserver.SetHeaders(ctx, repo) if err != nil { return nil, err } diff --git a/internal/service/register.go b/internal/service/register.go index fe2636c6b..cfdd93584 100644 --- a/internal/service/register.go +++ b/internal/service/register.go @@ -44,8 +44,7 @@ func RegisterAll(grpcServer *grpc.Server, rubyServer *rubyserver.Server) { blobService := blob.NewServer() pb.RegisterBlobServiceServer(grpcServer, blobService) - repositoryService := repository.NewServer() - pb.RegisterRepositoryServiceServer(grpcServer, repositoryService) + pb.RegisterRepositoryServiceServer(grpcServer, repository.NewServer(rubyServer)) namespaceService := namespace.NewServer() pb.RegisterNamespaceServiceServer(grpcServer, namespaceService) diff --git a/internal/service/repository/create.go b/internal/service/repository/create.go new file mode 100644 index 000000000..65ecd63b2 --- /dev/null +++ b/internal/service/repository/create.go @@ -0,0 +1,22 @@ +package repository + +import ( + pb "gitlab.com/gitlab-org/gitaly-proto/go" + "gitlab.com/gitlab-org/gitaly/internal/rubyserver" + + "golang.org/x/net/context" +) + +func (s *server) CreateRepository(ctx context.Context, req *pb.CreateRepositoryRequest) (*pb.CreateRepositoryResponse, error) { + client, err := s.RepositoryServiceClient(ctx) + if err != nil { + return nil, err + } + + clientCtx, err := rubyserver.SetHeaders(ctx, req.GetRepository()) + if err != nil { + return nil, err + } + + return client.CreateRepository(clientCtx, req) +} diff --git a/internal/service/repository/create_test.go b/internal/service/repository/create_test.go new file mode 100644 index 000000000..81fa71274 --- /dev/null +++ b/internal/service/repository/create_test.go @@ -0,0 +1,84 @@ +package repository + +import ( + "fmt" + "os" + "path" + "testing" + + pb "gitlab.com/gitlab-org/gitaly-proto/go" + "gitlab.com/gitlab-org/gitaly/internal/config" + "gitlab.com/gitlab-org/gitaly/internal/helper" + "gitlab.com/gitlab-org/gitaly/internal/testhelper" + + "github.com/stretchr/testify/require" + "google.golang.org/grpc/codes" +) + +func TestCreateRepositorySuccess(t *testing.T) { + server := runRepoServer(t) + defer server.Stop() + + client, conn := newRepositoryClient(t) + defer conn.Close() + + ctx, cancel := testhelper.Context() + defer cancel() + + storageDir, err := helper.GetStorageByName("default") + require.NoError(t, err) + relativePath := "create-repository-test.git" + repoDir := path.Join(storageDir, relativePath) + require.NoError(t, os.RemoveAll(repoDir)) + + repo := &pb.Repository{StorageName: "default", RelativePath: relativePath} + req := &pb.CreateRepositoryRequest{Repository: repo} + _, err = client.CreateRepository(ctx, req) + require.NoError(t, err) + + for _, dir := range []string{repoDir, path.Join(repoDir, "refs")} { + fi, err := os.Stat(dir) + require.NoError(t, err) + require.True(t, fi.IsDir(), "%q must be a directory", fi.Name()) + } + + hooksDir := path.Join(repoDir, "hooks") + + fi, err := os.Lstat(hooksDir) + require.NoError(t, err) + require.True(t, fi.Mode()&os.ModeSymlink > 0, "expected %q to be a symlink, got mode %v", hooksDir, fi.Mode()) + + hooksTarget, err := os.Readlink(hooksDir) + require.NoError(t, err) + require.Equal(t, path.Join(config.Config.GitlabShell.Dir, "hooks"), hooksTarget) +} + +func TestCreateRepositoryFailure(t *testing.T) { + server := runRepoServer(t) + defer server.Stop() + + client, conn := newRepositoryClient(t) + defer conn.Close() + + ctx, cancel := testhelper.Context() + defer cancel() + + testCases := []struct { + repo *pb.Repository + code codes.Code + }{ + { + repo: &pb.Repository{StorageName: "does not exist", RelativePath: "foobar.git"}, + code: codes.InvalidArgument, + }, + } + + for _, tc := range testCases { + t.Run(fmt.Sprintf("%+v", tc.repo), func(t *testing.T) { + _, err := client.CreateRepository(ctx, &pb.CreateRepositoryRequest{Repository: tc.repo}) + + require.Error(t, err) + testhelper.AssertGrpcError(t, err, tc.code, "") + }) + } +} diff --git a/internal/service/repository/server.go b/internal/service/repository/server.go index 0ad70bc7e..13d859902 100644 --- a/internal/service/repository/server.go +++ b/internal/service/repository/server.go @@ -1,19 +1,16 @@ package repository import ( - pb "gitlab.com/gitlab-org/gitaly-proto/go" - "gitlab.com/gitlab-org/gitaly/internal/helper" + "gitlab.com/gitlab-org/gitaly/internal/rubyserver" - "golang.org/x/net/context" + 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{} +type server struct { + *rubyserver.Server } -func (*server) CreateRepository(context.Context, *pb.CreateRepositoryRequest) (*pb.CreateRepositoryResponse, error) { - return nil, helper.Unimplemented +// NewServer creates a new instance of a gRPC repo server +func NewServer(rs *rubyserver.Server) pb.RepositoryServiceServer { + return &server{rs} } diff --git a/internal/service/repository/testdata/gitlab-shell/hooks/.gitkeep b/internal/service/repository/testdata/gitlab-shell/hooks/.gitkeep new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/internal/service/repository/testdata/gitlab-shell/hooks/.gitkeep diff --git a/internal/service/repository/testhelper_test.go b/internal/service/repository/testhelper_test.go index 536645eae..824f5c083 100644 --- a/internal/service/repository/testhelper_test.go +++ b/internal/service/repository/testhelper_test.go @@ -1,13 +1,17 @@ package repository import ( + "log" "net" "os" + "path/filepath" "testing" "time" "github.com/stretchr/testify/assert" pb "gitlab.com/gitlab-org/gitaly-proto/go" + "gitlab.com/gitlab-org/gitaly/internal/config" + "gitlab.com/gitlab-org/gitaly/internal/rubyserver" "gitlab.com/gitlab-org/gitaly/internal/testhelper" "google.golang.org/grpc" "google.golang.org/grpc/reflection" @@ -20,6 +24,7 @@ var ( testTime = time.Date(2006, 1, 2, 15, 4, 5, 0, time.UTC) serverSocketPath = testhelper.GetTemporaryGitalySocketFileName() testRepo = testhelper.TestRepository() + rubyServer *rubyserver.Server ) func newRepositoryClient(t *testing.T) (pb.RepositoryServiceClient, *grpc.ClientConn) { @@ -44,7 +49,7 @@ func runRepoServer(t *testing.T) *grpc.Server { t.Fatal(err) } - pb.RegisterRepositoryServiceServer(server, NewServer()) + pb.RegisterRepositoryServiceServer(server, NewServer(rubyServer)) reflection.Register(server) go server.Serve(listener) @@ -66,3 +71,27 @@ func assertModTimeAfter(t *testing.T, afterTime time.Time, paths ...string) bool } return t.Failed() } + +func TestMain(m *testing.M) { + os.Exit(testMain(m)) +} + +func testMain(m *testing.M) int { + defer testhelper.MustHaveNoChildProcess() + + testhelper.ConfigureRuby() + + var err error + config.Config.GitlabShell.Dir, err = filepath.Abs("testdata/gitlab-shell") + if err != nil { + log.Fatal(err) + } + + rubyServer, err = rubyserver.Start() + if err != nil { + log.Fatal(err) + } + defer rubyServer.Stop() + + return m.Run() +} diff --git a/ruby/lib/gitaly_server.rb b/ruby/lib/gitaly_server.rb index deaea16c4..52e744bfd 100644 --- a/ruby/lib/gitaly_server.rb +++ b/ruby/lib/gitaly_server.rb @@ -7,6 +7,7 @@ require_relative 'gitaly_server/commit_service.rb' require_relative 'gitaly_server/diff_service.rb' require_relative 'gitaly_server/ref_service.rb' require_relative 'gitaly_server/operations_service.rb' +require_relative 'gitaly_server/repository_service.rb' module GitalyServer REPO_PATH_HEADER = 'gitaly-repo-path'.freeze @@ -20,5 +21,6 @@ module GitalyServer server.handle(DiffService.new) server.handle(RefService.new) server.handle(OperationsService.new) + server.handle(RepositoryService.new) end end diff --git a/ruby/lib/gitaly_server/repository_service.rb b/ruby/lib/gitaly_server/repository_service.rb new file mode 100644 index 000000000..8006ddd6a --- /dev/null +++ b/ruby/lib/gitaly_server/repository_service.rb @@ -0,0 +1,12 @@ +module GitalyServer + class RepositoryService < Gitaly::RepositoryService::Service + def create_repository(request, _call) + repo_path = GitalyServer.repo_path(_call) + + # TODO refactor Repository.create to eliminate bogus '/' argument + Gitlab::Git::Repository.create('/', repo_path, bare: true, symlink_hooks_to: Gitlab.config.gitlab_shell.hooks_path) + + Gitaly::CreateRepositoryResponse.new + end + end +end diff --git a/ruby/lib/gitlab/git.rb b/ruby/lib/gitlab/git.rb index de57cd81b..fca45d316 100644 --- a/ruby/lib/gitlab/git.rb +++ b/ruby/lib/gitlab/git.rb @@ -38,9 +38,23 @@ module Gitlab end end + class GitlabShell + def path + ENV['GITALY_RUBY_GITLAB_SHELL_PATH'] + end + + def hooks_path + File.join(path, 'hooks') + end + end + def git Git.new end + + def gitlab_shell + GitlabShell.new + end end def self.config |