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:
authorPaul Okstad <pokstad@gitlab.com>2019-11-01 18:34:57 +0300
committerPaul Okstad <pokstad@gitlab.com>2019-11-01 18:34:57 +0300
commit2d465eb4419d862f34f2720381ec6ec50b93842a (patch)
tree9a9dff30197376c4120f252cadcd079f05987277
parentc8d2fa42a571bec9ff225b9a65c98fa47ca0dba7 (diff)
parenta5e50b36a28e279adae2d5247fe985c9bb2a9945 (diff)
Merge branch 'jc-check-subcmd' into 'master'
Add check subcommand in gitaly-hooks Closes #2087 See merge request gitlab-org/gitaly!1587
-rw-r--r--changelogs/unreleased/jc-check-subcmd.yml5
-rw-r--r--cmd/gitaly-hooks/hooks.go77
-rw-r--r--cmd/gitaly-hooks/hooks_test.go106
3 files changed, 165 insertions, 23 deletions
diff --git a/changelogs/unreleased/jc-check-subcmd.yml b/changelogs/unreleased/jc-check-subcmd.yml
new file mode 100644
index 000000000..be33648e8
--- /dev/null
+++ b/changelogs/unreleased/jc-check-subcmd.yml
@@ -0,0 +1,5 @@
+---
+title: Add check subcommand in gitaly-hooks
+merge_request: 1587
+author:
+type: added
diff --git a/cmd/gitaly-hooks/hooks.go b/cmd/gitaly-hooks/hooks.go
index c699d0c6d..c526c1033 100644
--- a/cmd/gitaly-hooks/hooks.go
+++ b/cmd/gitaly-hooks/hooks.go
@@ -1,16 +1,18 @@
package main
import (
- "bytes"
"context"
"errors"
- "io"
+ "fmt"
+ "net/http"
"os"
"os/exec"
"path/filepath"
+ "strings"
"gitlab.com/gitlab-org/gitaly/internal/command"
"gitlab.com/gitlab-org/gitaly/internal/log"
+ "gopkg.in/yaml.v2"
)
func main() {
@@ -20,20 +22,33 @@ func main() {
logger.Fatal(errors.New("requires hook name"))
}
+ subCmd := os.Args[1]
+
+ if subCmd == "check" {
+ configPath := os.Args[2]
+
+ if err := checkGitlabAccess(configPath); err != nil {
+ os.Stderr.WriteString(err.Error())
+ os.Exit(1)
+ }
+
+ os.Stdout.WriteString("OK")
+ os.Exit(0)
+ }
+
gitlabRubyDir := os.Getenv("GITALY_RUBY_DIR")
if gitlabRubyDir == "" {
logger.Fatal(errors.New("GITALY_RUBY_DIR not set"))
}
- hookName := os.Args[1]
- rubyHookPath := filepath.Join(gitlabRubyDir, "gitlab-shell", "hooks", hookName)
+ rubyHookPath := filepath.Join(gitlabRubyDir, "gitlab-shell", "hooks", subCmd)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var hookCmd *exec.Cmd
- switch hookName {
+ switch subCmd {
case "update":
args := os.Args[2:]
if len(args) != 3 {
@@ -43,14 +58,12 @@ func main() {
hookCmd = exec.Command(rubyHookPath, args...)
case "pre-receive", "post-receive":
hookCmd = exec.Command(rubyHookPath)
+
default:
logger.Fatal(errors.New("hook name invalid"))
}
- var stderr bytes.Buffer
- mw := io.MultiWriter(&stderr, os.Stderr)
-
- cmd, err := command.New(ctx, hookCmd, os.Stdin, os.Stdout, mw, os.Environ()...)
+ cmd, err := command.New(ctx, hookCmd, os.Stdin, os.Stdout, os.Stderr, os.Environ()...)
if err != nil {
logger.Fatalf("error when starting command for %v: %v", rubyHookPath, err)
}
@@ -59,3 +72,49 @@ func main() {
os.Exit(1)
}
}
+
+// GitlabShellConfig contains a subset of gitlabshell's config.yml
+type GitlabShellConfig struct {
+ GitlabURL string `yaml:"gitlab_url"`
+ HTTPSettings HTTPSettings `yaml:"http_settings"`
+}
+
+// HTTPSettings contains fields for http settings
+type HTTPSettings struct {
+ User string `yaml:"user"`
+ Password string `yaml:"password"`
+}
+
+func checkGitlabAccess(configPath string) error {
+ cfgFile, err := os.Open(configPath)
+ if err != nil {
+ return fmt.Errorf("error when opening config file: %v", err)
+ }
+ defer cfgFile.Close()
+
+ config := GitlabShellConfig{}
+
+ if err := yaml.NewDecoder(cfgFile).Decode(&config); err != nil {
+ return fmt.Errorf("load toml: %v", err)
+ }
+
+ req, err := http.NewRequest("GET", fmt.Sprintf("%s/api/v4/internal/check", strings.TrimRight(config.GitlabURL, "/")), nil)
+ if err != nil {
+ return fmt.Errorf("could not create request for %s: %v", config.GitlabURL, err)
+ }
+
+ req.SetBasicAuth(config.HTTPSettings.User, config.HTTPSettings.Password)
+
+ client := &http.Client{}
+
+ resp, err := client.Do(req)
+ if err != nil {
+ return fmt.Errorf("error with request for %s: %v", config.GitlabURL, err)
+ }
+
+ if resp.StatusCode != 200 {
+ return fmt.Errorf("FAILED. code: %d", resp.StatusCode)
+ }
+
+ return nil
+}
diff --git a/cmd/gitaly-hooks/hooks_test.go b/cmd/gitaly-hooks/hooks_test.go
index cf8990c20..c7dbae128 100644
--- a/cmd/gitaly-hooks/hooks_test.go
+++ b/cmd/gitaly-hooks/hooks_test.go
@@ -2,6 +2,7 @@ package main
import (
"bytes"
+ "encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
@@ -13,6 +14,7 @@ import (
"path"
"path/filepath"
"strconv"
+ "strings"
"testing"
"github.com/stretchr/testify/require"
@@ -44,10 +46,10 @@ func TestHooksPrePostReceive(t *testing.T) {
changes := "abc"
- ts := gitlabTestServer(t, secretToken, key, glRepository, changes, true)
+ ts := gitlabTestServer(t, "", "", secretToken, key, glRepository, changes, true)
defer ts.Close()
- writeTemporaryConfigFile(t, tempGitlabShellDir, ts.URL)
+ writeTemporaryConfigFile(t, tempGitlabShellDir, GitlabShellConfig{GitlabURL: ts.URL})
writeShellSecretFile(t, tempGitlabShellDir, secretToken)
for _, hook := range []string{"pre-receive", "post-receive"} {
@@ -76,7 +78,7 @@ func TestHooksUpdate(t *testing.T) {
tempGitlabShellDir, cleanup := createTempGitlabShellDir(t)
defer cleanup()
- writeTemporaryConfigFile(t, tempGitlabShellDir, "http://www.example.com")
+ writeTemporaryConfigFile(t, tempGitlabShellDir, GitlabShellConfig{GitlabURL: "http://www.example.com"})
writeShellSecretFile(t, tempGitlabShellDir, "the wrong token")
require.NoError(t, os.MkdirAll(filepath.Join(tempGitlabShellDir, "hooks", "update.d"), 0755))
@@ -120,10 +122,10 @@ func TestHooksPostReceiveFailed(t *testing.T) {
// send back {"reference_counter_increased": false}, indicating something went wrong
// with the call
- ts := gitlabTestServer(t, secretToken, key, glRepository, "", false)
+ ts := gitlabTestServer(t, "", "", secretToken, key, glRepository, "", false)
defer ts.Close()
- writeTemporaryConfigFile(t, tempGitlabShellDir, ts.URL)
+ writeTemporaryConfigFile(t, tempGitlabShellDir, GitlabShellConfig{GitlabURL: ts.URL})
writeShellSecretFile(t, tempGitlabShellDir, secretToken)
for envName, env := range map[string][]string{"new": env(t, glRepository, tempGitlabShellDir, key), "old": oldEnv(t, glRepository, tempGitlabShellDir, key)} {
@@ -154,10 +156,10 @@ func TestHooksNotAllowed(t *testing.T) {
tempGitlabShellDir, cleanup := createTempGitlabShellDir(t)
defer cleanup()
- ts := gitlabTestServer(t, secretToken, key, glRepository, "", true)
+ ts := gitlabTestServer(t, "", "", secretToken, key, glRepository, "", true)
defer ts.Close()
- writeTemporaryConfigFile(t, tempGitlabShellDir, ts.URL)
+ writeTemporaryConfigFile(t, tempGitlabShellDir, GitlabShellConfig{GitlabURL: ts.URL})
writeShellSecretFile(t, tempGitlabShellDir, "the wrong token")
for envName, env := range map[string][]string{"new": env(t, glRepository, tempGitlabShellDir, key), "old": oldEnv(t, glRepository, tempGitlabShellDir, key)} {
@@ -176,8 +178,54 @@ func TestHooksNotAllowed(t *testing.T) {
}
}
-type GitlabShellConfig struct {
- GitlabURL string `yaml:"gitlab_url"`
+func TestCheckOK(t *testing.T) {
+ user, password := "user123", "password321"
+
+ ts := gitlabTestServer(t, user, password, "", 0, "", "", false)
+ defer ts.Close()
+
+ tempDir, err := ioutil.TempDir("", t.Name())
+ require.NoError(t, err)
+ defer func() {
+ os.RemoveAll(tempDir)
+ }()
+
+ configPath := writeTemporaryConfigFile(t, tempDir, GitlabShellConfig{GitlabURL: ts.URL, HTTPSettings: HTTPSettings{User: user, Password: password}})
+
+ cmd := exec.Command(fmt.Sprintf("%s/gitaly-hooks", config.Config.BinDir), "check", configPath)
+
+ var stderr, stdout bytes.Buffer
+ cmd.Stderr = &stderr
+ cmd.Stdout = &stdout
+
+ require.NoError(t, cmd.Run())
+ require.Empty(t, stderr.String())
+ require.Equal(t, "OK", stdout.String())
+}
+
+func TestCheckBadCreds(t *testing.T) {
+ user, password := "user123", "password321"
+
+ ts := gitlabTestServer(t, user, password, "", 0, "", "", false)
+ defer ts.Close()
+
+ tempDir, err := ioutil.TempDir("", t.Name())
+ require.NoError(t, err)
+ defer func() {
+ os.RemoveAll(tempDir)
+ }()
+
+ configPath := writeTemporaryConfigFile(t, tempDir, GitlabShellConfig{GitlabURL: ts.URL, HTTPSettings: HTTPSettings{User: user + "wrong", Password: password}})
+
+ cmd := exec.Command(fmt.Sprintf("%s/gitaly-hooks", config.Config.BinDir), "check", configPath)
+
+ var stderr, stdout bytes.Buffer
+ cmd.Stderr = &stderr
+ cmd.Stdout = &stdout
+
+ require.Error(t, cmd.Run())
+ require.Equal(t, "FAILED. code: 401", stderr.String())
+ require.Empty(t, stdout.String())
}
func handleAllowed(t *testing.T, secretToken string, key int, glRepository, changes string) func(w http.ResponseWriter, r *http.Request) {
@@ -230,11 +278,38 @@ func handlePostReceive(t *testing.T, secretToken string, key int, glRepository,
}
}
-func gitlabTestServer(t *testing.T, secretToken string, key int, glRepository, changes string, postReceiveCounterDecreased bool) *httptest.Server {
+func handleCheck(t *testing.T, user, password string) func(w http.ResponseWriter, r *http.Request) {
+ return func(w http.ResponseWriter, r *http.Request) {
+ auth := strings.SplitN(r.Header.Get("Authorization"), " ", 2)
+
+ if len(auth) != 2 || auth[0] != "Basic" {
+ http.Error(w, "authorization failed", http.StatusUnauthorized)
+ return
+ }
+
+ payload, _ := base64.StdEncoding.DecodeString(auth[1])
+ pair := strings.SplitN(string(payload), ":", 2)
+
+ if pair[0] != user || pair[1] != password {
+ w.WriteHeader(http.StatusUnauthorized)
+ return
+ }
+
+ w.WriteHeader(http.StatusOK)
+ }
+}
+
+func gitlabTestServer(t *testing.T,
+ user, password, secretToken string,
+ key int,
+ glRepository,
+ changes string,
+ postReceiveCounterDecreased bool) *httptest.Server {
mux := http.NewServeMux()
mux.Handle("/api/v4/internal/allowed", http.HandlerFunc(handleAllowed(t, secretToken, key, glRepository, changes)))
mux.Handle("/api/v4/internal/pre_receive", http.HandlerFunc(handlePreReceive(t, secretToken, glRepository)))
mux.Handle("/api/v4/internal/post_receive", http.HandlerFunc(handlePostReceive(t, secretToken, key, glRepository, changes, postReceiveCounterDecreased)))
+ mux.Handle("/api/v4/internal/check", http.HandlerFunc(handleCheck(t, user, password)))
return httptest.NewServer(mux)
}
@@ -247,11 +322,14 @@ func createTempGitlabShellDir(t *testing.T) (string, func()) {
}
}
-func writeTemporaryConfigFile(t *testing.T, dir, testServerURL string) {
- cfg := GitlabShellConfig{GitlabURL: testServerURL}
- out, err := yaml.Marshal(cfg)
+func writeTemporaryConfigFile(t *testing.T, dir string, config GitlabShellConfig) string {
+ out, err := yaml.Marshal(&config)
require.NoError(t, err)
- require.NoError(t, ioutil.WriteFile(filepath.Join(dir, "config.yml"), out, 0644))
+
+ path := filepath.Join(dir, "config.yml")
+ require.NoError(t, ioutil.WriteFile(path, out, 0644))
+
+ return path
}
func env(t *testing.T, glRepo, gitlabShellDir string, key int) []string {