diff options
author | Kim "BKC" Carlbäcker <kim.carlbacker@gmail.com> | 2017-09-22 11:25:50 +0300 |
---|---|---|
committer | Kim "BKC" Carlbäcker <kim.carlbacker@gmail.com> | 2017-09-29 23:35:04 +0300 |
commit | b3bcd7810a75dda671c26b2162bd13e8f0b4b090 (patch) | |
tree | 2423e1361b489c0dc165520b8e5a089abdeaaba4 | |
parent | 3cec5a47fdb56ccd83bc6829b36d8123b72ab797 (diff) |
Implement cmd/gitaly-ssh command
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | CHANGELOG.md | 2 | ||||
-rw-r--r-- | cmd/gitaly-ssh/main.go | 57 | ||||
-rw-r--r-- | cmd/gitaly-ssh/receive_pack.go | 33 | ||||
-rw-r--r-- | cmd/gitaly-ssh/upload_pack.go | 33 | ||||
-rw-r--r-- | internal/service/ssh/cmd/gitaly-receive-pack/main.go | 49 | ||||
-rw-r--r-- | internal/service/ssh/cmd/gitaly-upload-pack/main.go | 56 | ||||
-rw-r--r-- | internal/service/ssh/receive_pack_test.go | 22 | ||||
-rw-r--r-- | internal/service/ssh/testhelper_test.go | 16 | ||||
-rw-r--r-- | internal/service/ssh/upload_pack_test.go | 59 |
10 files changed, 185 insertions, 144 deletions
diff --git a/.gitignore b/.gitignore index ca6bf2d4c..5975a83ae 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,7 @@ /internal/service/smarthttp/testdata /internal/testhelper/testdata /config.toml -/internal/service/ssh/gitaly-*-pack +/internal/service/ssh/gitaly-ssh /.ruby-bundle /ruby/vendor/bundle /ruby/.bundle diff --git a/CHANGELOG.md b/CHANGELOG.md index 3eb65f4d8..bcc5e33d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ UNRELEASED https://gitlab.com/gitlab-org/gitaly/merge_requests/379 - Implement RepositoryService.GetArchive RPC https://gitlab.com/gitlab-org/gitaly/merge_requests/370 +- Add `gitaly-ssh` command + https://gitlab.com/gitlab-org/gitaly/merge_requests/368 v0.42.0 diff --git a/cmd/gitaly-ssh/main.go b/cmd/gitaly-ssh/main.go new file mode 100644 index 000000000..195d6370f --- /dev/null +++ b/cmd/gitaly-ssh/main.go @@ -0,0 +1,57 @@ +package main + +import ( + "log" + "os" + + "gitlab.com/gitlab-org/gitaly/auth" + "gitlab.com/gitlab-org/gitaly/client" + "google.golang.org/grpc" +) + +type packFn func(_, _ string) (int32, error) + +// GITALY_ADDRESS="tcp://1.2.3.4:9999" or "unix:/var/run/gitaly.sock" +// GITALY_TOKEN="foobar1234" +// GITALY_PAYLOAD="{repo...}" +// GITALY_WD="/path/to/working-directory" +// gitaly-ssh upload-pack <git-garbage-x2> +func main() { + // < 4 since git throws on 2x garbage here + if n := len(os.Args); n < 4 { + // TODO: Errors needs to be sent back some other way... pipes? + log.Fatalf("invalid number of arguments, expected at least 1, got %d", n-1) + } + + var packer packFn + switch os.Args[1] { + case "upload-pack": + packer = uploadPack + case "receive-pack": + packer = receivePack + default: + log.Fatalf("invalid pack command: %q", os.Args[1]) + } + + if wd := os.Getenv("GITALY_WD"); wd != "" { + if err := os.Chdir(wd); err != nil { + log.Fatalf("change to : %v", err) + } + } + + code, err := packer(os.Getenv("GITALY_ADDRESS"), os.Getenv("GITALY_PAYLOAD")) + if err != nil { + log.Fatalf("%s: %v", os.Args[1], err) + } + + os.Exit(int(code)) +} + +func dialOpts() []grpc.DialOption { + connOpts := client.DefaultDialOpts + if token := os.Getenv("GITALY_TOKEN"); token != "" { + connOpts = append(connOpts, grpc.WithPerRPCCredentials(gitalyauth.RPCCredentials(token))) + } + + return connOpts +} diff --git a/cmd/gitaly-ssh/receive_pack.go b/cmd/gitaly-ssh/receive_pack.go new file mode 100644 index 000000000..5b6489d67 --- /dev/null +++ b/cmd/gitaly-ssh/receive_pack.go @@ -0,0 +1,33 @@ +package main + +import ( + "context" + "fmt" + "os" + + "github.com/golang/protobuf/jsonpb" + + pb "gitlab.com/gitlab-org/gitaly-proto/go" + "gitlab.com/gitlab-org/gitaly/client" +) + +func receivePack(url, req string) (int32, error) { + var request pb.SSHReceivePackRequest + if err := jsonpb.UnmarshalString(req, &request); err != nil { + return 0, fmt.Errorf("json unmarshal: %v", err) + } + + if url == "" { + return 0, fmt.Errorf("gitaly address can not be empty") + } + conn, err := client.Dial(url, dialOpts()) + if err != nil { + return 0, fmt.Errorf("dial %q: %v", url, err) + } + defer conn.Close() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + return client.ReceivePack(ctx, conn, os.Stdin, os.Stdout, os.Stderr, &request) +} diff --git a/cmd/gitaly-ssh/upload_pack.go b/cmd/gitaly-ssh/upload_pack.go new file mode 100644 index 000000000..9526ea295 --- /dev/null +++ b/cmd/gitaly-ssh/upload_pack.go @@ -0,0 +1,33 @@ +package main + +import ( + "context" + "fmt" + "os" + + "github.com/golang/protobuf/jsonpb" + + pb "gitlab.com/gitlab-org/gitaly-proto/go" + "gitlab.com/gitlab-org/gitaly/client" +) + +func uploadPack(url, req string) (int32, error) { + var request pb.SSHUploadPackRequest + if err := jsonpb.UnmarshalString(req, &request); err != nil { + return 0, fmt.Errorf("json unmarshal: %v", err) + } + + if url == "" { + return 0, fmt.Errorf("gitaly address can not be empty") + } + conn, err := client.Dial(url, dialOpts()) + if err != nil { + return 0, fmt.Errorf("dial %q: %v", url, err) + } + defer conn.Close() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + return client.UploadPack(ctx, conn, os.Stdin, os.Stdout, os.Stderr, &request) +} diff --git a/internal/service/ssh/cmd/gitaly-receive-pack/main.go b/internal/service/ssh/cmd/gitaly-receive-pack/main.go deleted file mode 100644 index 90114b5d5..000000000 --- a/internal/service/ssh/cmd/gitaly-receive-pack/main.go +++ /dev/null @@ -1,49 +0,0 @@ -package main - -import ( - "log" - "os" - "strings" - "time" - - "golang.org/x/net/context" - - pb "gitlab.com/gitlab-org/gitaly-proto/go" - "gitlab.com/gitlab-org/gitaly/client" -) - -func main() { - if !(len(os.Args) >= 3 && strings.HasPrefix(os.Args[2], "git-receive-pack")) { - log.Fatalf("Not a valid command") - } - - addr := os.Getenv("GITALY_SOCKET") - if len(addr) == 0 { - log.Fatalf("GITALY_SOCKET not set") - } - - conn, err := client.Dial(addr, client.DefaultDialOpts) - if err != nil { - log.Fatalf("Error: %v", err) - } - defer conn.Close() - - req := &pb.SSHReceivePackRequest{ - Repository: &pb.Repository{ - RelativePath: os.Getenv("GL_RELATIVEPATH"), - StorageName: os.Getenv("GL_STORAGENAME"), - }, - GlRepository: os.Getenv("GL_REPOSITORY"), - GlId: os.Getenv("GL_ID"), - GlUsername: os.Getenv("GL_USERNAME"), - } - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - code, err := client.ReceivePack(ctx, conn, os.Stdin, os.Stdout, os.Stderr, req) - if err != nil { - log.Fatalf("Error: %v", err) - } - - os.Exit(int(code)) -} diff --git a/internal/service/ssh/cmd/gitaly-upload-pack/main.go b/internal/service/ssh/cmd/gitaly-upload-pack/main.go deleted file mode 100644 index 8df5dfe56..000000000 --- a/internal/service/ssh/cmd/gitaly-upload-pack/main.go +++ /dev/null @@ -1,56 +0,0 @@ -package main - -import ( - "log" - "os" - "strings" - "time" - - "golang.org/x/net/context" - - pb "gitlab.com/gitlab-org/gitaly-proto/go" - "gitlab.com/gitlab-org/gitaly/client" -) - -func main() { - if !(len(os.Args) >= 3 && strings.HasPrefix(os.Args[2], "git-upload-pack")) { - log.Fatalf("Not a valid command") - } - - addr := os.Getenv("GITALY_SOCKET") - if len(addr) == 0 { - log.Fatalf("GITALY_SOCKET not set") - } - - conn, err := client.Dial(addr, client.DefaultDialOpts) - if err != nil { - log.Fatalf("Error: %v", err) - } - defer conn.Close() - - req := &pb.SSHUploadPackRequest{ - Repository: &pb.Repository{ - RelativePath: os.Getenv("GL_RELATIVEPATH"), - StorageName: os.Getenv("GL_STORAGENAME"), - }, - } - - // Use GL_CONFIG_OPTIONS instead of GIT_CONFIG_PARAMETERS because the latter - // would be swallowed by the client git process - gitConfig := os.Getenv("GL_CONFIG_OPTIONS") - - if len(gitConfig) > 0 { - configs := strings.Split(gitConfig, " ") - - req.GitConfigOptions = append(req.GitConfigOptions, configs...) - } - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - code, err := client.UploadPack(ctx, conn, os.Stdin, os.Stdout, os.Stderr, req) - if err != nil { - log.Fatalf("Error: %v", err) - } - - os.Exit(int(code)) -} diff --git a/internal/service/ssh/receive_pack_test.go b/internal/service/ssh/receive_pack_test.go index 5cbaad0dc..f10948f39 100644 --- a/internal/service/ssh/receive_pack_test.go +++ b/internal/service/ssh/receive_pack_test.go @@ -10,6 +10,9 @@ import ( "testing" "time" + "github.com/golang/protobuf/jsonpb" + "github.com/stretchr/testify/require" + "gitlab.com/gitlab-org/gitaly/internal/testhelper" pb "gitlab.com/gitlab-org/gitaly-proto/go" @@ -121,16 +124,21 @@ func testCloneAndPush(t *testing.T, storageName, glID string) (string, string, e makeCommit(t, localRepoPath) + pbTempRepo := &pb.Repository{StorageName: storageName, RelativePath: tempRepo} + pbMarshaler := &jsonpb.Marshaler{} + payload, err := pbMarshaler.MarshalToString(&pb.SSHReceivePackRequest{ + Repository: pbTempRepo, + GlRepository: pbTempRepo.GetRelativePath(), + GlId: glID, + }) + require.NoError(t, err) + cmd := exec.Command("git", "-C", localRepoPath, "push", "-v", "git@localhost:test/test.git", "master") cmd.Env = []string{ - fmt.Sprintf("GITALY_SOCKET=unix://%s", serverSocketPath), - fmt.Sprintf("GL_STORAGENAME=%s", storageName), - fmt.Sprintf("GL_RELATIVEPATH=%s", tempRepo), - fmt.Sprintf("GL_REPOSITORY=%s", testRepo.GetRelativePath()), - fmt.Sprintf("GOPATH=%s", os.Getenv("GOPATH")), + fmt.Sprintf("GITALY_PAYLOAD=%s", payload), + fmt.Sprintf("GITALY_ADDRESS=unix:%s", serverSocketPath), fmt.Sprintf("PATH=%s", ".:"+os.Getenv("PATH")), - fmt.Sprintf("GIT_SSH_COMMAND=%s", receivePackPath), - fmt.Sprintf("GL_ID=%s", glID), + fmt.Sprintf(`GIT_SSH_COMMAND=%s receive-pack`, gitalySSHPath), } out, err := cmd.CombinedOutput() if err != nil { diff --git a/internal/service/ssh/testhelper_test.go b/internal/service/ssh/testhelper_test.go index 285c70382..87fbcf6b1 100644 --- a/internal/service/ssh/testhelper_test.go +++ b/internal/service/ssh/testhelper_test.go @@ -25,8 +25,7 @@ var ( serverSocketPath = testhelper.GetTemporaryGitalySocketFileName() testRepo *pb.Repository - uploadPackPath string - receivePackPath string + gitalySSHPath string cwd string ) @@ -48,15 +47,10 @@ func testMain(m *testing.M) int { testRepo = testhelper.TestRepository() // Build the test-binary that we need - os.Remove("gitaly-upload-pack") - testhelper.MustRunCommand(nil, nil, "go", "build", "gitlab.com/gitlab-org/gitaly/internal/service/ssh/cmd/gitaly-upload-pack") - defer os.Remove("gitaly-upload-pack") - uploadPackPath = path.Join(cwd, "gitaly-upload-pack") - - os.Remove("gitaly-receive-pack") - testhelper.MustRunCommand(nil, nil, "go", "build", "gitlab.com/gitlab-org/gitaly/internal/service/ssh/cmd/gitaly-receive-pack") - defer os.Remove("gitaly-receive-pack") - receivePackPath = path.Join(cwd, "gitaly-receive-pack") + os.Remove("gitaly-ssh") + testhelper.MustRunCommand(nil, nil, "go", "build", "gitlab.com/gitlab-org/gitaly/cmd/gitaly-ssh") + defer os.Remove("gitaly-ssh") + gitalySSHPath = path.Join(cwd, "gitaly-ssh") return m.Run() } diff --git a/internal/service/ssh/upload_pack_test.go b/internal/service/ssh/upload_pack_test.go index 8530c14d4..7f1ef2db0 100644 --- a/internal/service/ssh/upload_pack_test.go +++ b/internal/service/ssh/upload_pack_test.go @@ -9,6 +9,9 @@ import ( "strings" "testing" + "github.com/golang/protobuf/jsonpb" + "github.com/stretchr/testify/require" + "gitlab.com/gitlab-org/gitaly/internal/testhelper" pb "gitlab.com/gitlab-org/gitaly-proto/go" @@ -72,19 +75,26 @@ func TestUploadPackCloneSuccess(t *testing.T) { localRepoPath := path.Join(testRepoRoot, "gitlab-test-upload-pack-local") - tests := []*exec.Cmd{ - exec.Command("git", "clone", "--depth", "1", "git@localhost:test/test.git", localRepoPath), - exec.Command("git", "clone", "git@localhost:test/test.git", localRepoPath), + tests := []struct { + cmd *exec.Cmd + desc string + }{ + { + cmd: exec.Command("git", "clone", "git@localhost:test/test.git", localRepoPath), + desc: "full clone", + }, + { + cmd: exec.Command("git", "clone", "--depth", "1", "git@localhost:test/test.git", localRepoPath), + desc: "shallow clone", + }, } - for _, cmd := range tests { - lHead, rHead, _, _, err := testClone(t, testRepo.GetStorageName(), testRepo.GetRelativePath(), localRepoPath, "", cmd) - if err != nil { - t.Fatalf("clone failed: %v", err) - } - if strings.Compare(lHead, rHead) != 0 { - t.Fatalf("local and remote head not equal. clone failed: %q != %q", lHead, rHead) - } + for _, tc := range tests { + t.Run(tc.desc, func(t *testing.T) { + lHead, rHead, _, _, err := testClone(t, testRepo.GetStorageName(), testRepo.GetRelativePath(), localRepoPath, "", tc.cmd) + require.NoError(t, err, "clone failed") + require.Equal(t, lHead, rHead, "local and remote head not equal") + }) } } @@ -92,7 +102,7 @@ func TestUploadPackCloneHideTags(t *testing.T) { server := runSSHServer(t) defer server.Stop() - localRepoPath := path.Join(testRepoRoot, "gitlab-test-upload-pack-local") + localRepoPath := path.Join(testRepoRoot, "gitlab-test-upload-pack-local-hide-tags") cmd := exec.Command("git", "clone", "--mirror", "git@localhost:test/test.git", localRepoPath) @@ -113,7 +123,7 @@ func TestUploadPackCloneFailure(t *testing.T) { server := runSSHServer(t) defer server.Stop() - localRepoPath := path.Join(testRepoRoot, "gitlab-test-upload-pack-local") + localRepoPath := path.Join(testRepoRoot, "gitlab-test-upload-pack-local-failure") cmd := exec.Command("git", "clone", "git@localhost:test/test.git", localRepoPath) @@ -125,15 +135,24 @@ func TestUploadPackCloneFailure(t *testing.T) { func testClone(t *testing.T, storageName, relativePath, localRepoPath string, gitConfig string, cmd *exec.Cmd) (string, string, string, string, error) { defer os.RemoveAll(localRepoPath) + + pbTempRepo := &pb.Repository{StorageName: storageName, RelativePath: relativePath} + req := &pb.SSHUploadPackRequest{ + Repository: pbTempRepo, + } + if gitConfig != "" { + req.GitConfigOptions = strings.Split(gitConfig, " ") + } + pbMarshaler := &jsonpb.Marshaler{} + payload, err := pbMarshaler.MarshalToString(req) + + require.NoError(t, err) + cmd.Env = []string{ - fmt.Sprintf("GITALY_SOCKET=unix://%s", serverSocketPath), - fmt.Sprintf("GL_STORAGENAME=%s", storageName), - fmt.Sprintf("GL_RELATIVEPATH=%s", relativePath), - fmt.Sprintf("GL_REPOSITORY=%s", testRepo.GetRelativePath()), - fmt.Sprintf("GOPATH=%s", os.Getenv("GOPATH")), + fmt.Sprintf("GITALY_ADDRESS=unix:%s", serverSocketPath), + fmt.Sprintf("GITALY_PAYLOAD=%s", payload), fmt.Sprintf("PATH=%s", ".:"+os.Getenv("PATH")), - fmt.Sprintf("GL_CONFIG_OPTIONS=%s", gitConfig), - "GIT_SSH_COMMAND=gitaly-upload-pack", + fmt.Sprintf(`GIT_SSH_COMMAND=%s upload-pack`, gitalySSHPath), } out, err := cmd.CombinedOutput() |