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:
authorÆvar Arnfjörð Bjarmason <avar@gitlab.com>2020-12-15 01:44:46 +0300
committerÆvar Arnfjörð Bjarmason <avar@gitlab.com>2020-12-15 01:44:46 +0300
commit546f3976c32661c59246cc4b8eb73cfe4eba5b24 (patch)
treecf7fd1b38e72483e6fcf2a41af22b74a79dbf6cc
parent61c631b3e50b2a4db5c6c821d1d22906260f2f28 (diff)
parent4674e1f8ddecf47846327a670a7c6bcc4676fcc4 (diff)
Merge branch 'pks-hooks-payload-gl-values' into 'master'
hooks: Convert GL_ values to be injected via the hooks payload Closes #3201 See merge request gitlab-org/gitaly!2913
-rw-r--r--cmd/gitaly-hooks/hooks_test.go46
-rw-r--r--internal/git/hooks_options.go33
-rw-r--r--internal/git/hooks_payload.go79
-rw-r--r--internal/git/hooks_payload_test.go102
-rw-r--r--internal/gitaly/hook/custom.go11
-rw-r--r--internal/gitaly/hook/postreceive.go24
-rw-r--r--internal/gitaly/hook/postreceive_test.go66
-rw-r--r--internal/gitaly/hook/prereceive.go30
-rw-r--r--internal/gitaly/hook/prereceive_test.go62
-rw-r--r--internal/gitaly/hook/update.go5
-rw-r--r--internal/gitaly/hook/update_test.go37
-rw-r--r--internal/gitaly/service/hook/post_receive_test.go23
-rw-r--r--internal/gitaly/service/hook/pre_receive_test.go43
-rw-r--r--internal/gitaly/service/hook/reference_transaction_test.go1
-rw-r--r--internal/gitaly/service/hook/update_test.go10
-rw-r--r--internal/gitaly/service/operations/update_with_hooks.go11
-rw-r--r--internal/gitaly/service/operations/update_with_hooks_test.go19
-rw-r--r--internal/gitaly/service/smarthttp/receive_pack_test.go34
-rw-r--r--internal/gitaly/service/ssh/receive_pack_test.go54
-rw-r--r--ruby/lib/gitlab/git/hook.rb16
-rw-r--r--ruby/spec/lib/gitlab/git/hook_spec.rb17
21 files changed, 473 insertions, 250 deletions
diff --git a/cmd/gitaly-hooks/hooks_test.go b/cmd/gitaly-hooks/hooks_test.go
index c0efd9cfa..ab650b7ff 100644
--- a/cmd/gitaly-hooks/hooks_test.go
+++ b/cmd/gitaly-hooks/hooks_test.go
@@ -29,8 +29,8 @@ import (
)
type glHookValues struct {
- GLID, GLUsername, GLRepo, GLProtocol, GitObjectDir string
- GitAlternateObjectDirs []string
+ GLID, GLUsername, GLProtocol, GitObjectDir string
+ GitAlternateObjectDirs []string
}
type proxyValues struct {
@@ -39,16 +39,16 @@ type proxyValues struct {
// envForHooks generates a set of environment variables for gitaly hooks
func envForHooks(t testing.TB, gitlabShellDir string, repo *gitalypb.Repository, glHookValues glHookValues, proxyValues proxyValues, gitPushOptions ...string) []string {
- payload, err := git.NewHooksPayload(config.Config, repo, nil, nil).Env()
+ payload, err := git.NewHooksPayload(config.Config, repo, nil, nil, &git.ReceiveHooksPayload{
+ UserID: glHookValues.GLID,
+ Username: glHookValues.GLUsername,
+ Protocol: glHookValues.GLProtocol,
+ }).Env()
require.NoError(t, err)
env := append(os.Environ(), []string{
payload,
"GITALY_BIN_DIR=" + config.Config.BinDir,
- fmt.Sprintf("GL_ID=%s", glHookValues.GLID),
- fmt.Sprintf("GL_REPOSITORY=%s", glHookValues.GLRepo),
- fmt.Sprintf("GL_PROTOCOL=%s", glHookValues.GLProtocol),
- fmt.Sprintf("GL_USERNAME=%s", glHookValues.GLUsername),
fmt.Sprintf("%s=%s", gitalylog.GitalyLogDirEnvKey, gitlabShellDir),
}...)
env = append(env, hooks.GitPushOptions(gitPushOptions)...)
@@ -140,7 +140,6 @@ func testHooksPrePostReceive(t *testing.T) {
glID := "key-1234"
glUsername := "iamgitlab"
glProtocol := "ssh"
- glRepository := "some_repo"
tempGitlabShellDir, cleanup := testhelper.CreateTemporaryGitlabShellDir(t)
defer cleanup()
@@ -159,7 +158,7 @@ func testHooksPrePostReceive(t *testing.T) {
Password: gitlabPassword,
SecretToken: secretToken,
GLID: glID,
- GLRepository: glRepository,
+ GLRepository: testRepo.GetGlRepository(),
Changes: changes,
PostReceiveCounterDecreased: true,
Protocol: "ssh",
@@ -228,7 +227,6 @@ func testHooksPrePostReceive(t *testing.T) {
glHookValues{
GLID: glID,
GLUsername: glUsername,
- GLRepo: glRepository,
GLProtocol: glProtocol,
GitObjectDir: c.GitObjectDir,
GitAlternateObjectDirs: c.GitAlternateObjectDirs,
@@ -250,7 +248,7 @@ func testHooksPrePostReceive(t *testing.T) {
output := string(testhelper.MustReadFile(t, customHookOutputPath))
requireContainsOnce(t, output, "GL_USERNAME="+glUsername)
requireContainsOnce(t, output, "GL_ID="+glID)
- requireContainsOnce(t, output, "GL_REPOSITORY="+glRepository)
+ requireContainsOnce(t, output, "GL_REPOSITORY="+testRepo.GetGlRepository())
requireContainsOnce(t, output, "HTTP_PROXY="+httpProxy)
requireContainsOnce(t, output, "http_proxy="+httpProxy)
requireContainsOnce(t, output, "HTTPS_PROXY="+httpsProxy)
@@ -281,7 +279,6 @@ func TestHooksUpdate(t *testing.T) {
glID := "key-1234"
glUsername := "iamgitlab"
glProtocol := "ssh"
- glRepository := "some_repo"
tempGitlabShellDir, cleanup := testhelper.CreateTemporaryGitlabShellDir(t)
defer cleanup()
@@ -306,7 +303,6 @@ func TestHooksUpdate(t *testing.T) {
testHooksUpdate(t, tempGitlabShellDir, token, glHookValues{
GLID: glID,
GLUsername: glUsername,
- GLRepo: glRepository,
GLProtocol: glProtocol,
})
}
@@ -366,7 +362,7 @@ open('%s', 'w') { |f| f.puts(JSON.dump(ARGV)) }
output := string(testhelper.MustReadFile(t, customHookOutputPath))
require.Contains(t, output, "GL_USERNAME="+glValues.GLUsername)
require.Contains(t, output, "GL_ID="+glValues.GLID)
- require.Contains(t, output, "GL_REPOSITORY="+glValues.GLRepo)
+ require.Contains(t, output, "GL_REPOSITORY="+testRepo.GetGlRepository())
require.Contains(t, output, "GL_PROTOCOL="+glValues.GLProtocol)
}
@@ -378,7 +374,6 @@ func TestHooksPostReceiveFailed(t *testing.T) {
glID := "key-1234"
glUsername := "iamgitlab"
glProtocol := "ssh"
- glRepository := "some_repo"
changes := "oldhead newhead"
tempGitlabShellDir, cleanup := testhelper.CreateTemporaryGitlabShellDir(t)
@@ -397,7 +392,7 @@ func TestHooksPostReceiveFailed(t *testing.T) {
SecretToken: secretToken,
Changes: changes,
GLID: glID,
- GLRepository: glRepository,
+ GLRepository: testRepo.GetGlRepository(),
PostReceiveCounterDecreased: false,
Protocol: "ssh",
}
@@ -476,18 +471,15 @@ func TestHooksPostReceiveFailed(t *testing.T) {
SocketPath: "/path/to/socket",
Token: "secret",
},
+ &git.ReceiveHooksPayload{
+ UserID: glID,
+ Username: glUsername,
+ Protocol: glProtocol,
+ },
).Env()
require.NoError(t, err)
- env := envForHooks(t, tempGitlabShellDir, testRepo,
- glHookValues{
- GLID: glID,
- GLUsername: glUsername,
- GLRepo: glRepository,
- GLProtocol: glProtocol,
- },
- proxyValues{},
- )
+ env := envForHooks(t, tempGitlabShellDir, testRepo, glHookValues{}, proxyValues{})
env = append(env, hooksPayload)
cmd := exec.Command(postReceiveHookPath)
@@ -511,7 +503,6 @@ func TestHooksNotAllowed(t *testing.T) {
glID := "key-1234"
glUsername := "iamgitlab"
glProtocol := "ssh"
- glRepository := "some_repo"
changes := "oldhead newhead"
tempGitlabShellDir, cleanup := testhelper.CreateTemporaryGitlabShellDir(t)
@@ -525,7 +516,7 @@ func TestHooksNotAllowed(t *testing.T) {
Password: "",
SecretToken: secretToken,
GLID: glID,
- GLRepository: glRepository,
+ GLRepository: testRepo.GetGlRepository(),
Changes: changes,
PostReceiveCounterDecreased: true,
Protocol: "ssh",
@@ -562,7 +553,6 @@ func TestHooksNotAllowed(t *testing.T) {
glHookValues{
GLID: glID,
GLUsername: glUsername,
- GLRepo: glRepository,
GLProtocol: glProtocol,
},
proxyValues{})
diff --git a/internal/git/hooks_options.go b/internal/git/hooks_options.go
index 2475dbbe0..642aa7f51 100644
--- a/internal/git/hooks_options.go
+++ b/internal/git/hooks_options.go
@@ -30,7 +30,7 @@ func WithRefTxHook(ctx context.Context, repo *gitalypb.Repository, cfg config.Cf
return fmt.Errorf("missing repo: %w", ErrInvalidArg)
}
- if err := cc.configureHooks(ctx, repo, cfg); err != nil {
+ if err := cc.configureHooks(ctx, repo, cfg, nil); err != nil {
return fmt.Errorf("ref hook env var: %w", err)
}
@@ -41,7 +41,12 @@ func WithRefTxHook(ctx context.Context, repo *gitalypb.Repository, cfg config.Cf
// configureHooks updates the command configuration to include all environment
// variables required by the reference transaction hook and any other needed
// options to successfully execute hooks.
-func (cc *cmdCfg) configureHooks(ctx context.Context, repo *gitalypb.Repository, cfg config.Cfg) error {
+func (cc *cmdCfg) configureHooks(
+ ctx context.Context,
+ repo *gitalypb.Repository,
+ cfg config.Cfg,
+ receiveHooksPayload *ReceiveHooksPayload,
+) error {
if cc.hooksConfigured {
return errors.New("hooks already configured")
}
@@ -51,7 +56,7 @@ func (cc *cmdCfg) configureHooks(ctx context.Context, repo *gitalypb.Repository,
return err
}
- payload, err := NewHooksPayload(cfg, repo, transaction, praefect).Env()
+ payload, err := NewHooksPayload(cfg, repo, transaction, praefect, receiveHooksPayload).Env()
if err != nil {
return err
}
@@ -83,26 +88,14 @@ type ReceivePackRequest interface {
// git-receive-pack(1).
func WithReceivePackHooks(ctx context.Context, cfg config.Cfg, req ReceivePackRequest, protocol string) CmdOpt {
return func(cc *cmdCfg) error {
- if err := cc.configureHooks(ctx, req.GetRepository(), config.Config); err != nil {
+ if err := cc.configureHooks(ctx, req.GetRepository(), config.Config, &ReceiveHooksPayload{
+ UserID: req.GetGlId(),
+ Username: req.GetGlUsername(),
+ Protocol: protocol,
+ }); err != nil {
return err
}
- env, err := receivePackHookEnv(req, protocol)
- if err != nil {
- return fmt.Errorf("receive-pack hook envvars: %w", err)
- }
-
- cc.env = append(cc.env, env...)
return nil
}
}
-
-func receivePackHookEnv(req ReceivePackRequest, protocol string) ([]string, error) {
- return []string{
- fmt.Sprintf("GL_ID=%s", req.GetGlId()),
- fmt.Sprintf("GL_USERNAME=%s", req.GetGlUsername()),
- fmt.Sprintf("GL_REPOSITORY=%s", req.GetGlRepository()),
- fmt.Sprintf("GL_PROJECT_PATH=%s", req.GetRepository().GetGlProjectPath()),
- fmt.Sprintf("GL_PROTOCOL=%s", protocol),
- }, nil
-}
diff --git a/internal/git/hooks_payload.go b/internal/git/hooks_payload.go
index 5bc1b193b..8852a34f3 100644
--- a/internal/git/hooks_payload.go
+++ b/internal/git/hooks_payload.go
@@ -40,8 +40,29 @@ type HooksPayload struct {
// Gitaly's internal socket.
InternalSocketToken string `json:"internal_socket_token"`
- Transaction *metadata.Transaction `json:"transaction"`
- Praefect *metadata.PraefectServer `json:"praefect"`
+ // Transaction is used to identify a reference transaction. This is an optional field -- if
+ // it's not set, no transactional voting will happen.
+ Transaction *metadata.Transaction `json:"transaction"`
+ // Praefect is used to identify the Praefect server which is hosting the transaction. This
+ // field must be set if and only if `Transaction` is.
+ Praefect *metadata.PraefectServer `json:"praefect"`
+
+ // ReceiveHooksPayload contains information required when executing
+ // git-receive-pack.
+ ReceiveHooksPayload *ReceiveHooksPayload `json:"receive_hooks_payload"`
+}
+
+// ReceiveHooksPayload contains all information which is required for hooks
+// executed by git-receive-pack, namely the pre-receive, update or post-receive
+// hook.
+type ReceiveHooksPayload struct {
+ // Username contains the name of the user who has caused the hook to be executed.
+ Username string `json:"username"`
+ // UserID contains the ID of the user who has caused the hook to be executed.
+ UserID string `json:"userid"`
+ // Protocol contains the protocol via which the hook was executed. This
+ // can be one of "web", "ssh" or "smarthttp".
+ Protocol string `json:"protocol"`
}
// jsonHooksPayload wraps the HooksPayload such that we can manually encode the
@@ -53,7 +74,13 @@ type jsonHooksPayload struct {
// NewHooksPayload creates a new hooks payload which can then be encoded and
// passed to Git hooks.
-func NewHooksPayload(cfg config.Cfg, repo *gitalypb.Repository, tx *metadata.Transaction, praefect *metadata.PraefectServer) HooksPayload {
+func NewHooksPayload(
+ cfg config.Cfg,
+ repo *gitalypb.Repository,
+ tx *metadata.Transaction,
+ praefect *metadata.PraefectServer,
+ receiveHooksPayload *ReceiveHooksPayload,
+) HooksPayload {
return HooksPayload{
Repo: repo,
BinDir: cfg.BinDir,
@@ -61,6 +88,7 @@ func NewHooksPayload(cfg config.Cfg, repo *gitalypb.Repository, tx *metadata.Tra
InternalSocketToken: cfg.Auth.Token,
Transaction: tx,
Praefect: praefect,
+ ReceiveHooksPayload: receiveHooksPayload,
}
}
@@ -131,6 +159,17 @@ func HooksPayloadFromEnv(envs []string) (HooksPayload, error) {
}
}
+ // If we didn't find a ReceiveHooksPayload, then we need to fallback to
+ // the GL_ environment values with the same reasoning as for
+ // `fallbackPayloadFromEnv()`.
+ if payload.ReceiveHooksPayload == nil {
+ receiveHooksPayload, err := fallbackReceiveHooksPayloadFromEnv(envs)
+ if err != nil {
+ return HooksPayload{}, err
+ }
+ payload.ReceiveHooksPayload = receiveHooksPayload
+ }
+
return payload, nil
}
@@ -173,6 +212,40 @@ func fallbackPayloadFromEnv(envs []string) (HooksPayload, error) {
return payload, nil
}
+// fallbackReceiveHooksPayloadFromEnv is a compatibility layer for upgrades
+// where it may happen that the new gitaly-hooks binary has already been
+// installed while the old server is still running.
+//
+// We need to keep in mind that it's perfectly fine for hooks to not have
+// the GL_ values in case we only run the reference-transaction hook. We cannot
+// distinguish those cases, so the best we can do is check for the first value
+// to exist: if it exists, we assume all the others must exist as well.
+// Otherwise, we assume none exist. This should be fine as all three hooks
+// expect those values to be set, while the reference-transaction hook doesn't
+// care at all for them.
+func fallbackReceiveHooksPayloadFromEnv(envs []string) (*ReceiveHooksPayload, error) {
+ protocol, ok := lookupEnv(envs, "GL_PROTOCOL")
+ if !ok {
+ return nil, nil
+ }
+
+ userID, ok := lookupEnv(envs, "GL_ID")
+ if !ok {
+ return nil, errors.New("no user ID found in hooks environment")
+ }
+
+ username, ok := lookupEnv(envs, "GL_USERNAME")
+ if !ok {
+ return nil, errors.New("no user ID found in hooks environment")
+ }
+
+ return &ReceiveHooksPayload{
+ Protocol: protocol,
+ UserID: userID,
+ Username: username,
+ }, nil
+}
+
// Env encodes the given HooksPayload into an environment variable.
func (p HooksPayload) Env() (string, error) {
repo, err := jsonpbMarshaller.MarshalToString(p.Repo)
diff --git a/internal/git/hooks_payload_test.go b/internal/git/hooks_payload_test.go
index aa91e25ce..609b7d149 100644
--- a/internal/git/hooks_payload_test.go
+++ b/internal/git/hooks_payload_test.go
@@ -20,7 +20,7 @@ func TestHooksPayload(t *testing.T) {
require.NoError(t, err)
t.Run("envvar has proper name", func(t *testing.T) {
- env, err := NewHooksPayload(config.Config, repo, nil, nil).Env()
+ env, err := NewHooksPayload(config.Config, repo, nil, nil, nil).Env()
require.NoError(t, err)
require.True(t, strings.HasPrefix(env, ErrHooksPayloadNotFound+"="))
})
@@ -43,7 +43,7 @@ func TestHooksPayload(t *testing.T) {
require.NoError(t, err)
t.Run("roundtrip succeeds", func(t *testing.T) {
- env, err := NewHooksPayload(config.Config, repo, nil, nil).Env()
+ env, err := NewHooksPayload(config.Config, repo, nil, nil, nil).Env()
require.NoError(t, err)
payload, err := HooksPayloadFromEnv([]string{
@@ -62,7 +62,7 @@ func TestHooksPayload(t *testing.T) {
})
t.Run("roundtrip with transaction succeeds", func(t *testing.T) {
- env, err := NewHooksPayload(config.Config, repo, &tx, &praefect).Env()
+ env, err := NewHooksPayload(config.Config, repo, &tx, &praefect, nil).Env()
require.NoError(t, err)
payload, err := HooksPayloadFromEnv([]string{env})
@@ -195,7 +195,7 @@ func TestHooksPayload(t *testing.T) {
})
t.Run("payload with missing Praefect", func(t *testing.T) {
- env, err := NewHooksPayload(config.Config, repo, nil, nil).Env()
+ env, err := NewHooksPayload(config.Config, repo, nil, nil, nil).Env()
require.NoError(t, err)
_, err = HooksPayloadFromEnv([]string{env, txEnv})
@@ -203,7 +203,7 @@ func TestHooksPayload(t *testing.T) {
})
t.Run("payload with fallback transaction", func(t *testing.T) {
- env, err := NewHooksPayload(config.Config, repo, nil, nil).Env()
+ env, err := NewHooksPayload(config.Config, repo, nil, nil, nil).Env()
require.NoError(t, err)
payload, err := HooksPayloadFromEnv([]string{env, txEnv, praefectEnv})
@@ -218,4 +218,96 @@ func TestHooksPayload(t *testing.T) {
Praefect: &praefect,
}, payload)
})
+
+ t.Run("fallback with GL_ values", func(t *testing.T) {
+ payload, err := HooksPayloadFromEnv([]string{
+ "GITALY_BIN_DIR=/bin/dir",
+ "GITALY_SOCKET=/path/to/socket",
+ "GITALY_TOKEN=secret",
+ "GITALY_REPOSITORY=" + marshalledRepo,
+ "GL_ID=1234",
+ "GL_USERNAME=user",
+ "GL_PROTOCOL=ssh",
+ })
+ require.NoError(t, err)
+
+ require.Equal(t, HooksPayload{
+ Repo: repo,
+ BinDir: "/bin/dir",
+ InternalSocket: "/path/to/socket",
+ InternalSocketToken: "secret",
+ ReceiveHooksPayload: &ReceiveHooksPayload{
+ UserID: "1234",
+ Username: "user",
+ Protocol: "ssh",
+ },
+ }, payload)
+ })
+
+ t.Run("payload with GL_ values", func(t *testing.T) {
+ env, err := NewHooksPayload(config.Config, repo, nil, nil, nil).Env()
+ require.NoError(t, err)
+
+ payload, err := HooksPayloadFromEnv([]string{
+ env,
+ "GL_ID=1234",
+ "GL_USERNAME=user",
+ "GL_PROTOCOL=ssh",
+ })
+ require.NoError(t, err)
+
+ require.Equal(t, HooksPayload{
+ Repo: repo,
+ BinDir: config.Config.BinDir,
+ InternalSocket: config.Config.GitalyInternalSocketPath(),
+ InternalSocketToken: config.Config.Auth.Token,
+ ReceiveHooksPayload: &ReceiveHooksPayload{
+ UserID: "1234",
+ Username: "user",
+ Protocol: "ssh",
+ },
+ }, payload)
+ })
+
+ t.Run("payload with missing GL_ID", func(t *testing.T) {
+ env, err := NewHooksPayload(config.Config, repo, nil, nil, nil).Env()
+ require.NoError(t, err)
+
+ _, err = HooksPayloadFromEnv([]string{
+ env,
+ "GL_USERNAME=user",
+ "GL_PROTOCOL=ssh",
+ })
+ require.Error(t, err)
+ require.Contains(t, "no user ID found in hooks environment", err.Error())
+ })
+
+ t.Run("receive hooks payload", func(t *testing.T) {
+ env, err := NewHooksPayload(config.Config, repo, nil, nil, &ReceiveHooksPayload{
+ UserID: "1234",
+ Username: "user",
+ Protocol: "ssh",
+ }).Env()
+ require.NoError(t, err)
+
+ payload, err := HooksPayloadFromEnv([]string{
+ env,
+ "GL_ID=wrong",
+ "GL_USERNAME=wrong",
+ "GL_PROTOCOL=wrong",
+ })
+ require.NoError(t, err)
+
+ require.Equal(t, HooksPayload{
+ Repo: repo,
+ BinDir: config.Config.BinDir,
+ InternalSocket: config.Config.GitalyInternalSocketPath(),
+ InternalSocketToken: config.Config.Auth.Token,
+ ReceiveHooksPayload: &ReceiveHooksPayload{
+ UserID: "1234",
+ Username: "user",
+ Protocol: "ssh",
+ },
+ }, payload)
+ })
}
diff --git a/internal/gitaly/hook/custom.go b/internal/gitaly/hook/custom.go
index 1ed289ea3..2687f926c 100644
--- a/internal/gitaly/hook/custom.go
+++ b/internal/gitaly/hook/custom.go
@@ -12,6 +12,7 @@ import (
"strings"
"gitlab.com/gitlab-org/gitaly/internal/command"
+ "gitlab.com/gitlab-org/gitaly/internal/git"
"gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
"golang.org/x/sys/unix"
)
@@ -133,3 +134,13 @@ func validHook(fi os.FileInfo, filename string) bool {
return true
}
+
+func customHooksEnv(payload git.HooksPayload) []string {
+ return []string{
+ "GL_REPOSITORY=" + payload.Repo.GetGlRepository(),
+ "GL_PROJECT_PATH=" + payload.Repo.GetGlProjectPath(),
+ "GL_ID=" + payload.ReceiveHooksPayload.UserID,
+ "GL_USERNAME=" + payload.ReceiveHooksPayload.Username,
+ "GL_PROTOCOL=" + payload.ReceiveHooksPayload.Protocol,
+ }
+}
diff --git a/internal/gitaly/hook/postreceive.go b/internal/gitaly/hook/postreceive.go
index d6b1a1da9..da763cd32 100644
--- a/internal/gitaly/hook/postreceive.go
+++ b/internal/gitaly/hook/postreceive.go
@@ -135,15 +135,22 @@ func (m *GitLabHookManager) PostReceiveHook(ctx context.Context, repo *gitalypb.
return helper.ErrInternalf("hook got no reference updates")
}
- glID, glRepo := getEnvVar("GL_ID", env), getEnvVar("GL_REPOSITORY", env)
- if glID == "" {
- return helper.ErrInternalf("GL_ID not set")
+ if payload.ReceiveHooksPayload == nil {
+ return helper.ErrInternalf("payload has no receive hooks info")
}
- if glRepo == "" {
- return helper.ErrInternalf("GL_REPOSITORY not set")
+ if payload.ReceiveHooksPayload.UserID == "" {
+ return helper.ErrInternalf("user ID not set")
+ }
+ if repo.GetGlRepository() == "" {
+ return helper.ErrInternalf("repository not set")
}
- ok, messages, err := m.gitlabAPI.PostReceive(ctx, glRepo, glID, string(changes), pushOptions...)
+ ok, messages, err := m.gitlabAPI.PostReceive(
+ ctx, repo.GetGlRepository(),
+ payload.ReceiveHooksPayload.UserID,
+ string(changes),
+ pushOptions...,
+ )
if err != nil {
return fmt.Errorf("GitLab: %v", err)
}
@@ -161,10 +168,13 @@ func (m *GitLabHookManager) PostReceiveHook(ctx context.Context, repo *gitalypb.
return helper.ErrInternalf("creating custom hooks executor: %v", err)
}
+ customHooksEnv := append(env, customHooksEnv(payload)...)
+ customHooksEnv = append(customHooksEnv, hooks.GitPushOptions(pushOptions)...)
+
if err = executor(
ctx,
nil,
- append(env, hooks.GitPushOptions(pushOptions)...),
+ customHooksEnv,
bytes.NewReader(changes),
stdout,
stderr,
diff --git a/internal/gitaly/hook/postreceive_test.go b/internal/gitaly/hook/postreceive_test.go
index 8cdd98735..113d06833 100644
--- a/internal/gitaly/hook/postreceive_test.go
+++ b/internal/gitaly/hook/postreceive_test.go
@@ -57,16 +57,6 @@ func TestPrintAlert(t *testing.T) {
}
}
-func envWithout(envVars []string, value string) []string {
- result := make([]string, 0, len(envVars))
- for _, envVar := range envVars {
- if !strings.HasPrefix(envVar, fmt.Sprintf("%s=", value)) {
- result = append(result, envVar)
- }
- }
- return result
-}
-
func TestPostReceive_customHook(t *testing.T) {
repo, repoPath, cleanup := testhelper.NewTestRepo(t)
defer cleanup()
@@ -74,15 +64,16 @@ func TestPostReceive_customHook(t *testing.T) {
hookManager := NewManager(config.NewLocator(config.Config), GitlabAPIStub, config.Config)
standardEnv := []string{
- "GL_ID=1234",
- fmt.Sprintf("GL_PROJECT_PATH=%s", repo.GetGlProjectPath()),
- "GL_PROTOCOL=web",
fmt.Sprintf("GL_REPO=%s", repo),
- fmt.Sprintf("GL_REPOSITORY=%s", repo.GetGlRepository()),
- "GL_USERNAME=user",
}
- payload, err := git.NewHooksPayload(config.Config, repo, nil, nil).Env()
+ receiveHooksPayload := &git.ReceiveHooksPayload{
+ UserID: "1234",
+ Username: "user",
+ Protocol: "web",
+ }
+
+ payload, err := git.NewHooksPayload(config.Config, repo, nil, nil, receiveHooksPayload).Env()
require.NoError(t, err)
primaryPayload, err := git.NewHooksPayload(
@@ -95,6 +86,7 @@ func TestPostReceive_customHook(t *testing.T) {
SocketPath: "/path/to/socket",
Token: "secret",
},
+ receiveHooksPayload,
).Env()
require.NoError(t, err)
@@ -108,6 +100,7 @@ func TestPostReceive_customHook(t *testing.T) {
SocketPath: "/path/to/socket",
Token: "secret",
},
+ receiveHooksPayload,
).Env()
require.NoError(t, err)
@@ -122,11 +115,19 @@ func TestPostReceive_customHook(t *testing.T) {
expectedStderr string
}{
{
- desc: "hook receives environment variables",
- env: append(standardEnv, payload),
- stdin: "changes\n",
- hook: "#!/bin/sh\nenv | grep -e '^GL_' -e '^GITALY_' | sort\n",
- expectedStdout: strings.Join(append([]string{payload}, standardEnv...), "\n") + "\n",
+ desc: "hook receives environment variables",
+ env: append(standardEnv, payload),
+ stdin: "changes\n",
+ hook: "#!/bin/sh\nenv | grep -e '^GL_' -e '^GITALY_' | sort\n",
+ expectedStdout: strings.Join([]string{
+ payload,
+ "GL_ID=1234",
+ fmt.Sprintf("GL_PROJECT_PATH=%s", repo.GetGlProjectPath()),
+ "GL_PROTOCOL=web",
+ fmt.Sprintf("GL_REPO=%s", repo),
+ fmt.Sprintf("GL_REPOSITORY=%s", repo.GetGlRepository()),
+ "GL_USERNAME=user",
+ }, "\n") + "\n",
},
{
desc: "push options are passed through",
@@ -193,18 +194,6 @@ func TestPostReceive_customHook(t *testing.T) {
hook: "#!/bin/sh\necho foo\n",
},
{
- desc: "missing GL_ID causes error",
- env: envWithout(append(standardEnv, payload), "GL_ID"),
- stdin: "changes\n",
- expectedErr: "GL_ID not set",
- },
- {
- desc: "missing GL_REPOSITORY causes error",
- env: envWithout(append(standardEnv, payload), "GL_REPOSITORY"),
- stdin: "changes\n",
- expectedErr: "GL_REPOSITORY not set",
- },
- {
desc: "missing changes cause error",
env: append(standardEnv, payload),
expectedErr: "hook got no reference updates",
@@ -259,17 +248,16 @@ func TestPostReceive_gitlab(t *testing.T) {
testRepo, testRepoPath, cleanup := testhelper.NewTestRepo(t)
defer cleanup()
- payload, err := git.NewHooksPayload(config.Config, testRepo, nil, nil).Env()
+ payload, err := git.NewHooksPayload(config.Config, testRepo, nil, nil, &git.ReceiveHooksPayload{
+ UserID: "1234",
+ Username: "user",
+ Protocol: "web",
+ }).Env()
require.NoError(t, err)
standardEnv := []string{
payload,
- "GL_ID=1234",
- fmt.Sprintf("GL_PROJECT_PATH=%s", testRepo.GetGlProjectPath()),
- "GL_PROTOCOL=web",
fmt.Sprintf("GL_REPO=%s", testRepo),
- fmt.Sprintf("GL_REPOSITORY=%s", testRepo.GetGlRepository()),
- "GL_USERNAME=user",
}
testCases := []struct {
diff --git a/internal/gitaly/hook/prereceive.go b/internal/gitaly/hook/prereceive.go
index a67fb59f8..9bd54f7c7 100644
--- a/internal/gitaly/hook/prereceive.go
+++ b/internal/gitaly/hook/prereceive.go
@@ -53,7 +53,7 @@ func (m *GitLabHookManager) PreReceiveHook(ctx context.Context, repo *gitalypb.R
// Only the primary should execute hooks and increment reference counters.
if isPrimary(payload) {
- if err := m.preReceiveHook(ctx, repo, env, changes, stdout, stderr); err != nil {
+ if err := m.preReceiveHook(ctx, payload, repo, env, changes, stdout, stderr); err != nil {
// If the pre-receive hook declines the push, then we need to stop any
// secondaries voting on the transaction.
m.stopTransaction(ctx, payload)
@@ -64,7 +64,7 @@ func (m *GitLabHookManager) PreReceiveHook(ctx context.Context, repo *gitalypb.R
return nil
}
-func (m *GitLabHookManager) preReceiveHook(ctx context.Context, repo *gitalypb.Repository, env []string, changes []byte, stdout, stderr io.Writer) error {
+func (m *GitLabHookManager) preReceiveHook(ctx context.Context, payload git.HooksPayload, repo *gitalypb.Repository, env []string, changes []byte, stdout, stderr io.Writer) error {
repoPath, err := m.locator.GetRepoPath(repo)
if err != nil {
return helper.ErrInternalf("getting repo path: %v", err)
@@ -84,24 +84,26 @@ func (m *GitLabHookManager) preReceiveHook(ctx context.Context, repo *gitalypb.R
return helper.ErrInternalf("hook got no reference updates")
}
- glID, glRepo, glProtocol := getEnvVar("GL_ID", env), getEnvVar("GL_REPOSITORY", env), getEnvVar("GL_PROTOCOL", env)
- if glID == "" {
- return helper.ErrInternalf("GL_ID not set")
+ if repo.GetGlRepository() == "" {
+ return helper.ErrInternalf("repository not set")
}
- if glRepo == "" {
- return helper.ErrInternalf("GL_REPOSITORY not set")
+ if payload.ReceiveHooksPayload == nil {
+ return helper.ErrInternalf("payload has no receive hooks info")
}
- if glProtocol == "" {
- return helper.ErrInternalf("GL_PROTOCOL not set")
+ if payload.ReceiveHooksPayload.UserID == "" {
+ return helper.ErrInternalf("user ID not set")
+ }
+ if payload.ReceiveHooksPayload.Protocol == "" {
+ return helper.ErrInternalf("protocol not set")
}
params := AllowedParams{
RepoPath: repoPath,
GitObjectDirectory: repo.GitObjectDirectory,
GitAlternateObjectDirectories: repo.GitAlternateObjectDirectories,
- GLRepository: glRepo,
- GLID: glID,
- GLProtocol: glProtocol,
+ GLRepository: repo.GetGlRepository(),
+ GLID: payload.ReceiveHooksPayload.UserID,
+ GLProtocol: payload.ReceiveHooksPayload.Protocol,
Changes: string(changes),
}
@@ -122,7 +124,7 @@ func (m *GitLabHookManager) preReceiveHook(ctx context.Context, repo *gitalypb.R
if err = executor(
ctx,
nil,
- env,
+ append(env, customHooksEnv(payload)...),
bytes.NewReader(changes),
stdout,
stderr,
@@ -131,7 +133,7 @@ func (m *GitLabHookManager) preReceiveHook(ctx context.Context, repo *gitalypb.R
}
// reference counter
- ok, err := m.gitlabAPI.PreReceive(ctx, glRepo)
+ ok, err := m.gitlabAPI.PreReceive(ctx, repo.GetGlRepository())
if err != nil {
return helper.ErrInternalf("calling pre_receive endpoint: %v", err)
}
diff --git a/internal/gitaly/hook/prereceive_test.go b/internal/gitaly/hook/prereceive_test.go
index 7690a220d..de38e0f5c 100644
--- a/internal/gitaly/hook/prereceive_test.go
+++ b/internal/gitaly/hook/prereceive_test.go
@@ -23,15 +23,16 @@ func TestPrereceive_customHooks(t *testing.T) {
hookManager := NewManager(config.NewLocator(config.Config), GitlabAPIStub, config.Config)
standardEnv := []string{
- "GL_ID=1234",
- fmt.Sprintf("GL_PROJECT_PATH=%s", repo.GetGlProjectPath()),
- "GL_PROTOCOL=web",
fmt.Sprintf("GL_REPO=%s", repo),
- fmt.Sprintf("GL_REPOSITORY=%s", repo.GetGlRepository()),
- "GL_USERNAME=user",
}
- payload, err := git.NewHooksPayload(config.Config, repo, nil, nil).Env()
+ receiveHooksPayload := &git.ReceiveHooksPayload{
+ UserID: "1234",
+ Username: "user",
+ Protocol: "web",
+ }
+
+ payload, err := git.NewHooksPayload(config.Config, repo, nil, nil, receiveHooksPayload).Env()
require.NoError(t, err)
primaryPayload, err := git.NewHooksPayload(
@@ -44,6 +45,7 @@ func TestPrereceive_customHooks(t *testing.T) {
SocketPath: "/path/to/socket",
Token: "secret",
},
+ receiveHooksPayload,
).Env()
require.NoError(t, err)
@@ -57,6 +59,7 @@ func TestPrereceive_customHooks(t *testing.T) {
SocketPath: "/path/to/socket",
Token: "secret",
},
+ receiveHooksPayload,
).Env()
require.NoError(t, err)
@@ -70,11 +73,19 @@ func TestPrereceive_customHooks(t *testing.T) {
expectedStderr string
}{
{
- desc: "hook receives environment variables",
- env: append(standardEnv, payload),
- hook: "#!/bin/sh\nenv | grep -e '^GL_' -e '^GITALY_' | sort\n",
- stdin: "change\n",
- expectedStdout: strings.Join(append([]string{payload}, standardEnv...), "\n") + "\n",
+ desc: "hook receives environment variables",
+ env: append(standardEnv, payload),
+ hook: "#!/bin/sh\nenv | grep -e '^GL_' -e '^GITALY_' | sort\n",
+ stdin: "change\n",
+ expectedStdout: strings.Join([]string{
+ payload,
+ "GL_ID=1234",
+ fmt.Sprintf("GL_PROJECT_PATH=%s", repo.GetGlProjectPath()),
+ "GL_PROTOCOL=web",
+ fmt.Sprintf("GL_REPO=%s", repo),
+ fmt.Sprintf("GL_REPOSITORY=%s", repo.GetGlRepository()),
+ "GL_USERNAME=user",
+ }, "\n") + "\n",
},
{
desc: "hook can write to stderr and stdout",
@@ -126,24 +137,6 @@ func TestPrereceive_customHooks(t *testing.T) {
stdin: "change\n",
},
{
- desc: "missing GL_ID causes error",
- env: envWithout(append(standardEnv, payload), "GL_ID"),
- stdin: "change\n",
- expectedErr: "GL_ID not set",
- },
- {
- desc: "missing GL_REPOSITORY causes error",
- env: envWithout(append(standardEnv, payload), "GL_REPOSITORY"),
- stdin: "change\n",
- expectedErr: "GL_REPOSITORY not set",
- },
- {
- desc: "missing GL_PROTOCOL causes error",
- env: envWithout(append(standardEnv, payload), "GL_PROTOCOL"),
- stdin: "change\n",
- expectedErr: "GL_PROTOCOL not set",
- },
- {
desc: "missing changes cause error",
env: append(standardEnv, payload),
expectedErr: "hook got no reference updates",
@@ -199,17 +192,16 @@ func TestPrereceive_gitlab(t *testing.T) {
testRepo, testRepoPath, cleanup := testhelper.NewTestRepo(t)
defer cleanup()
- payload, err := git.NewHooksPayload(config.Config, testRepo, nil, nil).Env()
+ payload, err := git.NewHooksPayload(config.Config, testRepo, nil, nil, &git.ReceiveHooksPayload{
+ UserID: "1234",
+ Username: "user",
+ Protocol: "web",
+ }).Env()
require.NoError(t, err)
standardEnv := []string{
payload,
- "GL_ID=1234",
- fmt.Sprintf("GL_PROJECT_PATH=%s", testRepo.GetGlProjectPath()),
- "GL_PROTOCOL=web",
fmt.Sprintf("GL_REPO=%s", testRepo),
- fmt.Sprintf("GL_REPOSITORY=%s", testRepo.GetGlRepository()),
- "GL_USERNAME=user",
}
testCases := []struct {
diff --git a/internal/gitaly/hook/update.go b/internal/gitaly/hook/update.go
index d887f2eca..75dec09dd 100644
--- a/internal/gitaly/hook/update.go
+++ b/internal/gitaly/hook/update.go
@@ -28,6 +28,9 @@ func (m *GitLabHookManager) UpdateHook(ctx context.Context, repo *gitalypb.Repos
if err := git.ValidateCommitID(newValue); err != nil {
return helper.ErrInternalf("hook got invalid new value: %w", err)
}
+ if payload.ReceiveHooksPayload == nil {
+ return helper.ErrInternalf("payload has no receive hooks info")
+ }
executor, err := m.newCustomHooksExecutor(repo, "update")
if err != nil {
@@ -37,7 +40,7 @@ func (m *GitLabHookManager) UpdateHook(ctx context.Context, repo *gitalypb.Repos
if err = executor(
ctx,
[]string{ref, oldValue, newValue},
- env,
+ append(env, customHooksEnv(payload)...),
nil,
stdout,
stderr,
diff --git a/internal/gitaly/hook/update_test.go b/internal/gitaly/hook/update_test.go
index 046d66500..acae50b01 100644
--- a/internal/gitaly/hook/update_test.go
+++ b/internal/gitaly/hook/update_test.go
@@ -20,15 +20,16 @@ func TestUpdate_customHooks(t *testing.T) {
hookManager := NewManager(config.NewLocator(config.Config), GitlabAPIStub, config.Config)
standardEnv := []string{
- "GL_ID=1234",
- fmt.Sprintf("GL_PROJECT_PATH=%s", repo.GetGlProjectPath()),
- "GL_PROTOCOL=web",
fmt.Sprintf("GL_REPO=%s", repo),
- fmt.Sprintf("GL_REPOSITORY=%s", repo.GetGlRepository()),
- "GL_USERNAME=user",
}
- payload, err := git.NewHooksPayload(config.Config, repo, nil, nil).Env()
+ receiveHooksPayload := &git.ReceiveHooksPayload{
+ UserID: "1234",
+ Username: "user",
+ Protocol: "web",
+ }
+
+ payload, err := git.NewHooksPayload(config.Config, repo, nil, nil, receiveHooksPayload).Env()
require.NoError(t, err)
primaryPayload, err := git.NewHooksPayload(
@@ -41,6 +42,7 @@ func TestUpdate_customHooks(t *testing.T) {
SocketPath: "/path/to/socket",
Token: "secret",
},
+ receiveHooksPayload,
).Env()
require.NoError(t, err)
@@ -54,6 +56,7 @@ func TestUpdate_customHooks(t *testing.T) {
SocketPath: "/path/to/socket",
Token: "secret",
},
+ receiveHooksPayload,
).Env()
require.NoError(t, err)
@@ -72,13 +75,21 @@ func TestUpdate_customHooks(t *testing.T) {
expectedStderr string
}{
{
- desc: "hook receives environment variables",
- env: append(standardEnv, payload),
- reference: "refs/heads/master",
- oldHash: hash1,
- newHash: hash2,
- hook: "#!/bin/sh\nenv | grep -e '^GL_' -e '^GITALY_' | sort\n",
- expectedStdout: strings.Join(append([]string{payload}, standardEnv...), "\n") + "\n",
+ desc: "hook receives environment variables",
+ env: append(standardEnv, payload),
+ reference: "refs/heads/master",
+ oldHash: hash1,
+ newHash: hash2,
+ hook: "#!/bin/sh\nenv | grep -e '^GL_' -e '^GITALY_' | sort\n",
+ expectedStdout: strings.Join([]string{
+ payload,
+ "GL_ID=1234",
+ fmt.Sprintf("GL_PROJECT_PATH=%s", repo.GetGlProjectPath()),
+ "GL_PROTOCOL=web",
+ fmt.Sprintf("GL_REPO=%s", repo),
+ fmt.Sprintf("GL_REPOSITORY=%s", repo.GetGlRepository()),
+ "GL_USERNAME=user",
+ }, "\n") + "\n",
},
{
desc: "hook receives arguments",
diff --git a/internal/gitaly/service/hook/post_receive_test.go b/internal/gitaly/service/hook/post_receive_test.go
index 566db311b..75426a98a 100644
--- a/internal/gitaly/service/hook/post_receive_test.go
+++ b/internal/gitaly/service/hook/post_receive_test.go
@@ -51,7 +51,7 @@ func TestHooksMissingStdin(t *testing.T) {
Password: password,
SecretToken: secretToken,
GLID: "key_id",
- GLRepository: "repository",
+ GLRepository: testRepo.GetGlRepository(),
Changes: "changes",
PostReceiveCounterDecreased: true,
Protocol: "protocol",
@@ -118,6 +118,11 @@ func TestHooksMissingStdin(t *testing.T) {
SocketPath: "/path/to/socket",
Token: "secret",
},
+ &git.ReceiveHooksPayload{
+ UserID: "key_id",
+ Username: "username",
+ Protocol: "protocol",
+ },
).Env()
require.NoError(t, err)
@@ -127,10 +132,6 @@ func TestHooksMissingStdin(t *testing.T) {
Repository: testRepo,
EnvironmentVariables: []string{
hooksPayload,
- "GL_ID=key_id",
- "GL_USERNAME=username",
- "GL_PROTOCOL=protocol",
- "GL_REPOSITORY=repository",
},
}))
@@ -206,7 +207,7 @@ To create a merge request for okay, visit:
Password: password,
SecretToken: secretToken,
GLID: "key_id",
- GLRepository: "repository",
+ GLRepository: testRepo.GetGlRepository(),
Changes: "changes",
PostReceiveCounterDecreased: true,
PostReceiveMessages: tc.basicMessages,
@@ -248,15 +249,15 @@ To create a merge request for okay, visit:
stream, err := client.PostReceiveHook(ctx)
require.NoError(t, err)
- hooksPayload, err := git.NewHooksPayload(config.Config, testRepo, nil, nil).Env()
+ hooksPayload, err := git.NewHooksPayload(config.Config, testRepo, nil, nil, &git.ReceiveHooksPayload{
+ UserID: "key_id",
+ Username: "username",
+ Protocol: "protocol",
+ }).Env()
require.NoError(t, err)
envVars := []string{
hooksPayload,
- "GL_ID=key_id",
- "GL_USERNAME=username",
- "GL_PROTOCOL=protocol",
- "GL_REPOSITORY=repository",
}
require.NoError(t, stream.Send(&gitalypb.PostReceiveHookRequest{
diff --git a/internal/gitaly/service/hook/pre_receive_test.go b/internal/gitaly/service/hook/pre_receive_test.go
index 4f95915ad..eb9514dce 100644
--- a/internal/gitaly/service/hook/pre_receive_test.go
+++ b/internal/gitaly/service/hook/pre_receive_test.go
@@ -83,7 +83,7 @@ func receivePreReceive(t *testing.T, stream gitalypb.HookService_PreReceiveHookC
func TestPreReceiveHook_GitlabAPIAccess(t *testing.T) {
user, password := "user", "password"
secretToken := "secret123"
- glID, glRepository := "key-123", "repository"
+ glID := "key-123"
changes := "changes123"
protocol := "http"
testRepo, testRepoPath, cleanupFn := testhelper.NewTestRepo(t)
@@ -112,7 +112,7 @@ func TestPreReceiveHook_GitlabAPIAccess(t *testing.T) {
Password: password,
SecretToken: secretToken,
GLID: glID,
- GLRepository: glRepository,
+ GLRepository: testRepo.GetGlRepository(),
Changes: changes,
PostReceiveCounterDecreased: true,
Protocol: protocol,
@@ -145,7 +145,11 @@ func TestPreReceiveHook_GitlabAPIAccess(t *testing.T) {
ctx, cancel := testhelper.Context()
defer cancel()
- hooksPayload, err := git.NewHooksPayload(config.Config, testRepo, nil, nil).Env()
+ hooksPayload, err := git.NewHooksPayload(config.Config, testRepo, nil, nil, &git.ReceiveHooksPayload{
+ UserID: glID,
+ Username: "username",
+ Protocol: protocol,
+ }).Env()
require.NoError(t, err)
stdin := bytes.NewBufferString(changes)
@@ -153,10 +157,6 @@ func TestPreReceiveHook_GitlabAPIAccess(t *testing.T) {
Repository: testRepo,
EnvironmentVariables: []string{
hooksPayload,
- "GL_ID=" + glID,
- "GL_PROTOCOL=" + protocol,
- "GL_USERNAME=username",
- "GL_REPOSITORY=" + glRepository,
},
}
@@ -252,7 +252,11 @@ func TestPreReceive_APIErrors(t *testing.T) {
ctx, cancel := testhelper.Context()
defer cancel()
- hooksPayload, err := git.NewHooksPayload(config.Config, testRepo, nil, nil).Env()
+ hooksPayload, err := git.NewHooksPayload(config.Config, testRepo, nil, nil, &git.ReceiveHooksPayload{
+ UserID: "key-123",
+ Username: "username",
+ Protocol: "web",
+ }).Env()
require.NoError(t, err)
stream, err := client.PreReceiveHook(ctx)
@@ -261,10 +265,6 @@ func TestPreReceive_APIErrors(t *testing.T) {
Repository: testRepo,
EnvironmentVariables: []string{
hooksPayload,
- "GL_ID=key-123",
- "GL_PROTOCOL=web",
- "GL_USERNAME=username",
- "GL_REPOSITORY=repository",
},
}))
require.NoError(t, stream.Send(&gitalypb.PreReceiveHookRequest{
@@ -319,7 +319,11 @@ exit %d
ctx, cancel := testhelper.Context()
defer cancel()
- hooksPayload, err := git.NewHooksPayload(config.Config, testRepo, nil, nil).Env()
+ hooksPayload, err := git.NewHooksPayload(config.Config, testRepo, nil, nil, &git.ReceiveHooksPayload{
+ UserID: "key-123",
+ Username: "username",
+ Protocol: "web",
+ }).Env()
require.NoError(t, err)
stream, err := client.PreReceiveHook(ctx)
@@ -328,10 +332,6 @@ exit %d
Repository: testRepo,
EnvironmentVariables: []string{
hooksPayload,
- "GL_ID=key-123",
- "GL_PROTOCOL=web",
- "GL_USERNAME=username",
- "GL_REPOSITORY=repository",
},
}))
require.NoError(t, stream.Send(&gitalypb.PreReceiveHookRequest{
@@ -455,15 +455,16 @@ func TestPreReceiveHook_Primary(t *testing.T) {
SocketPath: "/path/to/socket",
Token: "secret",
},
+ &git.ReceiveHooksPayload{
+ UserID: "key-123",
+ Username: "username",
+ Protocol: "web",
+ },
).Env()
require.NoError(t, err)
environment := []string{
hooksPayload,
- "GL_ID=key-123",
- "GL_PROTOCOL=web",
- "GL_USERNAME=username",
- "GL_REPOSITORY=repository",
}
stream, err := client.PreReceiveHook(ctx)
diff --git a/internal/gitaly/service/hook/reference_transaction_test.go b/internal/gitaly/service/hook/reference_transaction_test.go
index 3e08d6a32..e7c4b660b 100644
--- a/internal/gitaly/service/hook/reference_transaction_test.go
+++ b/internal/gitaly/service/hook/reference_transaction_test.go
@@ -134,6 +134,7 @@ func TestReferenceTransactionHook(t *testing.T) {
&metadata.PraefectServer{
ListenAddr: "tcp://" + listener.Addr().String(),
},
+ nil,
).Env()
require.NoError(t, err)
diff --git a/internal/gitaly/service/hook/update_test.go b/internal/gitaly/service/hook/update_test.go
index d8ceb9b7d..e7c266982 100644
--- a/internal/gitaly/service/hook/update_test.go
+++ b/internal/gitaly/service/hook/update_test.go
@@ -44,15 +44,15 @@ func TestUpdate_CustomHooks(t *testing.T) {
client, conn := newHooksClient(t, serverSocketPath)
defer conn.Close()
- hooksPayload, err := git.NewHooksPayload(config.Config, testRepo, nil, nil).Env()
+ hooksPayload, err := git.NewHooksPayload(config.Config, testRepo, nil, nil, &git.ReceiveHooksPayload{
+ UserID: "key-123",
+ Username: "username",
+ Protocol: "web",
+ }).Env()
require.NoError(t, err)
envVars := []string{
hooksPayload,
- "GL_ID=key-123",
- "GL_USERNAME=username",
- "GL_PROTOCOL=protocol",
- "GL_REPOSITORY=repository",
}
ctx, cancel := testhelper.Context()
diff --git a/internal/gitaly/service/operations/update_with_hooks.go b/internal/gitaly/service/operations/update_with_hooks.go
index 134466236..ba90634bc 100644
--- a/internal/gitaly/service/operations/update_with_hooks.go
+++ b/internal/gitaly/service/operations/update_with_hooks.go
@@ -36,7 +36,11 @@ func (s *Server) updateReferenceWithHooks(ctx context.Context, repo *gitalypb.Re
return err
}
- payload, err := git.NewHooksPayload(s.cfg, repo, transaction, praefect).Env()
+ payload, err := git.NewHooksPayload(s.cfg, repo, transaction, praefect, &git.ReceiveHooksPayload{
+ UserID: user.GetGlId(),
+ Username: user.GetGlUsername(),
+ Protocol: "web",
+ }).Env()
if err != nil {
return err
}
@@ -53,11 +57,6 @@ func (s *Server) updateReferenceWithHooks(ctx context.Context, repo *gitalypb.Re
env := []string{
payload,
- "GL_PROTOCOL=web",
- fmt.Sprintf("GL_ID=%s", user.GetGlId()),
- fmt.Sprintf("GL_USERNAME=%s", user.GetGlUsername()),
- fmt.Sprintf("GL_REPOSITORY=%s", repo.GetGlRepository()),
- fmt.Sprintf("GL_PROJECT_PATH=%s", repo.GetGlProjectPath()),
}
changes := fmt.Sprintf("%s %s %s\n", oldrev, newrev, reference)
diff --git a/internal/gitaly/service/operations/update_with_hooks_test.go b/internal/gitaly/service/operations/update_with_hooks_test.go
index ecd480e90..87ad136fd 100644
--- a/internal/gitaly/service/operations/update_with_hooks_test.go
+++ b/internal/gitaly/service/operations/update_with_hooks_test.go
@@ -131,16 +131,15 @@ func TestUpdateReferenceWithHooks(t *testing.T) {
repo, repoPath, cleanup := testhelper.NewTestRepo(t)
defer cleanup()
- payload, err := git.NewHooksPayload(config.Config, repo, nil, nil).Env()
+ payload, err := git.NewHooksPayload(config.Config, repo, nil, nil, &git.ReceiveHooksPayload{
+ UserID: "1234",
+ Username: "Username",
+ Protocol: "web",
+ }).Env()
require.NoError(t, err)
expectedEnv := []string{
payload,
- "GL_ID=1234",
- "GL_PROJECT_PATH=gitlab-org/gitlab-test",
- "GL_PROTOCOL=web",
- "GL_REPOSITORY=project-1",
- "GL_USERNAME=Username",
}
testCases := []struct {
@@ -158,21 +157,21 @@ func TestUpdateReferenceWithHooks(t *testing.T) {
changes, err := ioutil.ReadAll(stdin)
require.NoError(t, err)
require.Equal(t, fmt.Sprintf("%s %s refs/heads/master\n", oldRev, git.NullSHA), string(changes))
- require.Subset(t, env, expectedEnv)
+ require.Equal(t, env, expectedEnv)
return nil
},
update: func(t *testing.T, ctx context.Context, repo *gitalypb.Repository, ref, oldValue, newValue string, env []string, stdout, stderr io.Writer) error {
require.Equal(t, "refs/heads/master", ref)
require.Equal(t, oldRev, oldValue)
require.Equal(t, newValue, git.NullSHA)
- require.Subset(t, env, expectedEnv)
+ require.Equal(t, env, expectedEnv)
return nil
},
postReceive: func(t *testing.T, ctx context.Context, repo *gitalypb.Repository, pushOptions, env []string, stdin io.Reader, stdout, stderr io.Writer) error {
changes, err := ioutil.ReadAll(stdin)
require.NoError(t, err)
require.Equal(t, fmt.Sprintf("%s %s refs/heads/master\n", oldRev, git.NullSHA), string(changes))
- require.Subset(t, env, expectedEnv)
+ require.Equal(t, env, expectedEnv)
require.Empty(t, pushOptions)
return nil
},
@@ -181,7 +180,7 @@ func TestUpdateReferenceWithHooks(t *testing.T) {
require.NoError(t, err)
require.Equal(t, fmt.Sprintf("%s %s refs/heads/master\n", oldRev, git.NullSHA), string(changes))
require.Equal(t, state, hook.ReferenceTransactionPrepared)
- require.Subset(t, env, expectedEnv)
+ require.Equal(t, env, expectedEnv)
return nil
},
expectedRefDeletion: true,
diff --git a/internal/gitaly/service/smarthttp/receive_pack_test.go b/internal/gitaly/service/smarthttp/receive_pack_test.go
index 36acf19f7..888911d45 100644
--- a/internal/gitaly/service/smarthttp/receive_pack_test.go
+++ b/internal/gitaly/service/smarthttp/receive_pack_test.go
@@ -61,7 +61,7 @@ func TestSuccessfulReceivePackRequest(t *testing.T) {
projectPath := "project/path"
repo.GlProjectPath = projectPath
- firstRequest := &gitalypb.PostReceivePackRequest{Repository: repo, GlId: "user-123", GlRepository: "project-456"}
+ firstRequest := &gitalypb.PostReceivePackRequest{Repository: repo, GlUsername: "user", GlId: "123", GlRepository: "project-456"}
response := doPush(t, stream, firstRequest, push.body)
expectedResponse := "0049\x01000eunpack ok\n0019ok refs/heads/master\n0019ok refs/heads/branch\n00000000"
@@ -73,14 +73,30 @@ func TestSuccessfulReceivePackRequest(t *testing.T) {
envData, err := ioutil.ReadFile(hookOutputFile)
require.NoError(t, err, "get git env data")
- for _, env := range []string{
- "GL_ID=user-123",
- "GL_REPOSITORY=project-456",
- "GL_PROTOCOL=http",
- "GL_PROJECT_PATH=" + projectPath,
- } {
- require.Contains(t, strings.Split(string(envData), "\n"), env)
- }
+ payload, err := git.HooksPayloadFromEnv(strings.Split(string(envData), "\n"))
+ require.NoError(t, err)
+
+ // Compare the repository up front so that we can use require.Equal for
+ // the remaining values.
+ testhelper.ProtoEqual(t, repo, payload.Repo)
+ payload.Repo = nil
+
+ // If running tests with Praefect, then these would be set, but we have
+ // no way of figuring out their actual contents. So let's just remove
+ // that data, too.
+ payload.Transaction = nil
+ payload.Praefect = nil
+
+ require.Equal(t, git.HooksPayload{
+ BinDir: config.Config.BinDir,
+ InternalSocket: config.Config.GitalyInternalSocketPath(),
+ InternalSocketToken: config.Config.Auth.Token,
+ ReceiveHooksPayload: &git.ReceiveHooksPayload{
+ UserID: "123",
+ Username: "user",
+ Protocol: "http",
+ },
+ }, payload)
}
func TestSuccessfulReceivePackRequestWithGitProtocol(t *testing.T) {
diff --git a/internal/gitaly/service/ssh/receive_pack_test.go b/internal/gitaly/service/ssh/receive_pack_test.go
index c7c66266d..7f66ab142 100644
--- a/internal/gitaly/service/ssh/receive_pack_test.go
+++ b/internal/gitaly/service/ssh/receive_pack_test.go
@@ -91,7 +91,13 @@ func TestReceivePackPushSuccess(t *testing.T) {
glRepository := "project-456"
glProjectPath := "project/path"
- lHead, rHead, err := testCloneAndPush(t, serverSocketPath, pushParams{storageName: testhelper.DefaultStorageName, glID: "user-123", glRepository: glRepository, glProjectPath: glProjectPath})
+ lHead, rHead, err := testCloneAndPush(t, serverSocketPath, pushParams{
+ storageName: testhelper.DefaultStorageName,
+ glID: "123",
+ glUsername: "user",
+ glRepository: glRepository,
+ glProjectPath: glProjectPath,
+ })
if err != nil {
t.Fatal(err)
}
@@ -100,14 +106,35 @@ func TestReceivePackPushSuccess(t *testing.T) {
envData, err := ioutil.ReadFile(hookOutputFile)
require.NoError(t, err, "get git env data")
- for _, env := range []string{
- "GL_ID=user-123",
- fmt.Sprintf("GL_REPOSITORY=%s", glRepository),
- fmt.Sprintf("GL_PROJECT_PATH=%s", glProjectPath),
- "GL_PROTOCOL=ssh",
- } {
- require.Contains(t, strings.Split(string(envData), "\n"), env)
- }
+ payload, err := git.HooksPayloadFromEnv(strings.Split(string(envData), "\n"))
+ require.NoError(t, err)
+
+ // Compare the repository up front so that we can use require.Equal for
+ // the remaining values.
+ testhelper.ProtoEqual(t, &gitalypb.Repository{
+ StorageName: testhelper.DefaultStorageName,
+ RelativePath: "gitlab-test-ssh-receive-pack.git",
+ GlProjectPath: glProjectPath,
+ GlRepository: glRepository,
+ }, payload.Repo)
+ payload.Repo = nil
+
+ // If running tests with Praefect, then these would be set, but we have
+ // no way of figuring out their actual contents. So let's just remove
+ // that data, too.
+ payload.Transaction = nil
+ payload.Praefect = nil
+
+ require.Equal(t, git.HooksPayload{
+ BinDir: config.Config.BinDir,
+ InternalSocket: config.Config.GitalyInternalSocketPath(),
+ InternalSocketToken: config.Config.Auth.Token,
+ ReceiveHooksPayload: &git.ReceiveHooksPayload{
+ UserID: "123",
+ Username: "user",
+ Protocol: "ssh",
+ },
+ }, payload)
}
func TestReceivePackPushSuccessWithGitProtocol(t *testing.T) {
@@ -313,12 +340,18 @@ func setupSSHClone(t *testing.T) (SSHCloneDetails, func()) {
}
func sshPush(t *testing.T, cloneDetails SSHCloneDetails, serverSocketPath string, params pushParams) (string, string, error) {
- pbTempRepo := &gitalypb.Repository{StorageName: params.storageName, RelativePath: cloneDetails.TempRepo, GlProjectPath: params.glProjectPath}
+ pbTempRepo := &gitalypb.Repository{
+ StorageName: params.storageName,
+ RelativePath: cloneDetails.TempRepo,
+ GlProjectPath: params.glProjectPath,
+ GlRepository: params.glRepository,
+ }
pbMarshaler := &jsonpb.Marshaler{}
payload, err := pbMarshaler.MarshalToString(&gitalypb.SSHReceivePackRequest{
Repository: pbTempRepo,
GlRepository: params.glRepository,
GlId: params.glID,
+ GlUsername: params.glUsername,
GitConfigOptions: params.gitConfigOptions,
GitProtocol: params.gitProtocol,
})
@@ -392,6 +425,7 @@ func drainPostReceivePackResponse(stream gitalypb.SSHService_SSHReceivePackClien
type pushParams struct {
storageName string
glID string
+ glUsername string
glRepository string
glProjectPath string
gitConfigOptions []string
diff --git a/ruby/lib/gitlab/git/hook.rb b/ruby/lib/gitlab/git/hook.rb
index d45342974..07dc0c8a4 100644
--- a/ruby/lib/gitlab/git/hook.rb
+++ b/ruby/lib/gitlab/git/hook.rb
@@ -109,12 +109,17 @@ module Gitlab
err_message
end
- def hooks_payload(transaction)
+ def hooks_payload(gl_id, gl_username, transaction)
payload = {
repository: repository.gitaly_repository.to_json,
binary_directory: Gitlab.config.gitaly.bin_dir,
internal_socket: Gitlab.config.gitaly.internal_socket,
- internal_socket_token: ENV['GITALY_TOKEN']
+ internal_socket_token: ENV['GITALY_TOKEN'],
+ receive_hooks_payload: {
+ userid: gl_id,
+ username: gl_username,
+ protocol: GL_PROTOCOL
+ }
}
payload.merge!(transaction.payload) if transaction
@@ -124,14 +129,9 @@ module Gitlab
def env_base_vars(gl_id, gl_username, transaction = nil)
{
- 'GITALY_HOOKS_PAYLOAD' => hooks_payload(transaction),
+ 'GITALY_HOOKS_PAYLOAD' => hooks_payload(gl_id, gl_username, transaction),
'GITALY_LOG_DIR' => Gitlab.config.logging.dir,
'GITALY_BIN_DIR' => Gitlab.config.gitaly.bin_dir,
- 'GL_ID' => gl_id,
- 'GL_USERNAME' => gl_username,
- 'GL_REPOSITORY' => repository.gl_repository,
- 'GL_PROJECT_PATH' => repository.gl_project_path,
- 'GL_PROTOCOL' => GL_PROTOCOL,
'PWD' => repo_path,
'GIT_DIR' => repo_path
}
diff --git a/ruby/spec/lib/gitlab/git/hook_spec.rb b/ruby/spec/lib/gitlab/git/hook_spec.rb
index 1abfd1755..133051b5e 100644
--- a/ruby/spec/lib/gitlab/git/hook_spec.rb
+++ b/ruby/spec/lib/gitlab/git/hook_spec.rb
@@ -38,10 +38,17 @@ describe Gitlab::Git::Hook do
context 'when the hooks require environment variables' do
let(:vars) do
{
- 'GL_ID' => 'user-123',
- 'GL_USERNAME' => 'janedoe',
- 'GL_REPOSITORY' => repo.gl_repository,
- 'GL_PROTOCOL' => 'web',
+ 'GITALY_HOOKS_PAYLOAD' => Base64.strict_encode64({
+ repository: repo.gitaly_repository.to_json,
+ binary_directory: Gitlab.config.gitaly.bin_dir,
+ internal_socket: Gitlab.config.gitaly.internal_socket,
+ internal_socket_token: nil,
+ receive_hooks_payload: {
+ userid: 'user-123',
+ username: 'janedoe',
+ protocol: 'web'
+ }
+ }.to_json),
'PWD' => repo.path,
'GIT_DIR' => repo.path
}
@@ -65,7 +72,7 @@ describe Gitlab::Git::Hook do
it 'returns true' do
hook_names.each do |hook|
trigger_result = described_class.new(hook, repo)
- .trigger(vars['GL_ID'], vars['GL_USERNAME'], '0' * 40, 'a' * 40, 'master', push_options: push_options)
+ .trigger('user-123', 'janedoe', '0' * 40, 'a' * 40, 'master', push_options: push_options)
expect(trigger_result.first).to be(true), "#{hook} failed: #{trigger_result.last}"
end