diff options
author | Nick Thomas <nick@gitlab.com> | 2019-10-03 18:11:56 +0100 |
---|---|---|
committer | Nick Thomas <nick@gitlab.com> | 2019-10-08 13:13:59 +0100 |
commit | ba9b7c0e8acd7b3acb03086a5c1e132256fbd36e (patch) | |
tree | c51ad0a6abc344cdb51b3681bd429b612f553ed0 | |
parent | 542b26139243c8fbed2af669d081f10c4ebbae40 (diff) | |
download | gitlab-shell-ba9b7c0e8acd7b3acb03086a5c1e132256fbd36e.tar.gz |
Rewrite bin/check in Go
-rw-r--r-- | .gitignore | 1 | ||||
-rwxr-xr-x | bin/check | 32 | ||||
-rw-r--r-- | go/cmd/check/main.go | 42 | ||||
-rw-r--r-- | go/internal/command/command.go | 7 | ||||
-rw-r--r-- | go/internal/command/command_test.go | 14 | ||||
-rw-r--r-- | go/internal/command/healthcheck/healthcheck.go | 49 | ||||
-rw-r--r-- | go/internal/command/healthcheck/healthcheck_test.go | 90 | ||||
-rw-r--r-- | go/internal/executable/executable.go | 1 | ||||
-rw-r--r-- | go/internal/gitlabnet/healthcheck/client.go | 54 | ||||
-rw-r--r-- | go/internal/gitlabnet/healthcheck/client_test.go | 48 |
10 files changed, 306 insertions, 32 deletions
@@ -18,3 +18,4 @@ hooks/*.d /bin/gitaly-upload-pack /bin/gitaly-receive-pack /bin/gitaly-upload-archive +/bin/check diff --git a/bin/check b/bin/check deleted file mode 100755 index 6d42f9b..0000000 --- a/bin/check +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env ruby - -require_relative '../lib/gitlab_init' -require_relative '../lib/gitlab_net' - -# -# GitLab shell check task -# - -print "Check GitLab API access: " -begin - resp = GitlabNet.new.check - - if resp.code != "200" - abort "FAILED. code: #{resp.code}" - end - - puts 'OK' - - check_values = JSON.parse(resp.body) - - print 'Redis available via internal API: ' - if check_values['redis'] - puts 'OK' - else - abort 'FAILED' - end -rescue GitlabNet::ApiUnreachableError - abort "FAILED: Failed to connect to internal API" -end - -puts "\n" diff --git a/go/cmd/check/main.go b/go/cmd/check/main.go new file mode 100644 index 0000000..1d32f77 --- /dev/null +++ b/go/cmd/check/main.go @@ -0,0 +1,42 @@ +package main + +import ( + "fmt" + "os" + + "gitlab.com/gitlab-org/gitlab-shell/go/internal/command" + "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/readwriter" + "gitlab.com/gitlab-org/gitlab-shell/go/internal/config" + "gitlab.com/gitlab-org/gitlab-shell/go/internal/executable" +) + +func main() { + readWriter := &readwriter.ReadWriter{ + Out: os.Stdout, + In: os.Stdin, + ErrOut: os.Stderr, + } + + executable, err := executable.New(executable.Healthcheck) + if err != nil { + fmt.Fprintln(readWriter.ErrOut, "Failed to determine executable, exiting") + os.Exit(1) + } + + config, err := config.NewFromDir(executable.RootDir) + if err != nil { + fmt.Fprintln(readWriter.ErrOut, "Failed to read config, exiting") + os.Exit(1) + } + + cmd, err := command.New(executable, os.Args[1:], config, readWriter) + if err != nil { + fmt.Fprintf(readWriter.ErrOut, "%v\n", err) + os.Exit(1) + } + + if err = cmd.Execute(); err != nil { + fmt.Fprintf(readWriter.ErrOut, "%v\n", err) + os.Exit(1) + } +} diff --git a/go/internal/command/command.go b/go/internal/command/command.go index 40d92e0..52393df 100644 --- a/go/internal/command/command.go +++ b/go/internal/command/command.go @@ -5,6 +5,7 @@ import ( "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/authorizedprincipals" "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/commandargs" "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/discover" + "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/healthcheck" "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/lfsauthenticate" "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/readwriter" "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/receivepack" @@ -41,6 +42,8 @@ func buildCommand(e *executable.Executable, args commandargs.CommandArgs, config return buildAuthorizedKeysCommand(args.(*commandargs.AuthorizedKeys), config, readWriter) case executable.AuthorizedPrincipalsCheck: return buildAuthorizedPrincipalsCommand(args.(*commandargs.AuthorizedPrincipals), config, readWriter) + case executable.Healthcheck: + return buildHealthcheckCommand(config, readWriter) } return nil @@ -72,3 +75,7 @@ func buildAuthorizedKeysCommand(args *commandargs.AuthorizedKeys, config *config func buildAuthorizedPrincipalsCommand(args *commandargs.AuthorizedPrincipals, config *config.Config, readWriter *readwriter.ReadWriter) Command { return &authorizedprincipals.Command{Config: config, Args: args, ReadWriter: readWriter} } + +func buildHealthcheckCommand(config *config.Config, readWriter *readwriter.ReadWriter) Command { + return &healthcheck.Command{Config: config, ReadWriter: readWriter} +} diff --git a/go/internal/command/command_test.go b/go/internal/command/command_test.go index 59f22e2..8f14db3 100644 --- a/go/internal/command/command_test.go +++ b/go/internal/command/command_test.go @@ -9,6 +9,7 @@ import ( "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/authorizedkeys" "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/authorizedprincipals" "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/discover" + "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/healthcheck" "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/lfsauthenticate" "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/receivepack" "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/shared/disallowedcommand" @@ -23,6 +24,7 @@ import ( var ( authorizedKeysExec = &executable.Executable{Name: executable.AuthorizedKeysCheck} authorizedPrincipalsExec = &executable.Executable{Name: executable.AuthorizedPrincipalsCheck} + checkExec = &executable.Executable{Name: executable.Healthcheck} gitlabShellExec = &executable.Executable{Name: executable.GitlabShell} basicConfig = &config.Config{GitlabUrl: "http+unix://gitlab.socket"} @@ -35,6 +37,13 @@ func buildEnv(command string) map[string]string { } } +func buildCheckAllowedEnv(command string) map[string]string { + out := buildEnv(command) + out["GITLAB_SHELL_ALLOW_CHECK_COMMAND"] = "1" + + return out +} + func TestNew(t *testing.T) { testCases := []struct { desc string @@ -80,6 +89,11 @@ func TestNew(t *testing.T) { expectedType: &uploadarchive.Command{}, }, { + desc: "it returns a Healthcheck command", + executable: checkExec, + expectedType: &healthcheck.Command{}, + }, + { desc: "it returns a AuthorizedKeys command", executable: authorizedKeysExec, arguments: []string{"git", "git", "key"}, diff --git a/go/internal/command/healthcheck/healthcheck.go b/go/internal/command/healthcheck/healthcheck.go new file mode 100644 index 0000000..fef981c --- /dev/null +++ b/go/internal/command/healthcheck/healthcheck.go @@ -0,0 +1,49 @@ +package healthcheck + +import ( + "fmt" + + "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/readwriter" + "gitlab.com/gitlab-org/gitlab-shell/go/internal/config" + "gitlab.com/gitlab-org/gitlab-shell/go/internal/gitlabnet/healthcheck" +) + +var ( + apiMessage = "Internal API available" + redisMessage = "Redis available via internal API" +) + +type Command struct { + Config *config.Config + ReadWriter *readwriter.ReadWriter +} + +func (c *Command) Execute() error { + response, err := c.runCheck() + if err != nil { + return fmt.Errorf("%v: FAILED - %v", apiMessage, err) + } + + fmt.Fprintf(c.ReadWriter.Out, "%v: OK\n", apiMessage) + + if !response.Redis { + return fmt.Errorf("%v: FAILED", redisMessage) + } + + fmt.Fprintf(c.ReadWriter.Out, "%v: OK\n", redisMessage) + return nil +} + +func (c *Command) runCheck() (*healthcheck.Response, error) { + client, err := healthcheck.NewClient(c.Config) + if err != nil { + return nil, err + } + + response, err := client.Check() + if err != nil { + return nil, err + } + + return response, nil +} diff --git a/go/internal/command/healthcheck/healthcheck_test.go b/go/internal/command/healthcheck/healthcheck_test.go new file mode 100644 index 0000000..6c92ebc --- /dev/null +++ b/go/internal/command/healthcheck/healthcheck_test.go @@ -0,0 +1,90 @@ +package healthcheck + +import ( + "bytes" + "encoding/json" + "net/http" + "testing" + + "github.com/stretchr/testify/require" + + "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/readwriter" + "gitlab.com/gitlab-org/gitlab-shell/go/internal/config" + "gitlab.com/gitlab-org/gitlab-shell/go/internal/gitlabnet/healthcheck" + "gitlab.com/gitlab-org/gitlab-shell/go/internal/gitlabnet/testserver" +) + +var ( + okResponse = &healthcheck.Response{ + APIVersion: "v4", + GitlabVersion: "v12.0.0-ee", + GitlabRevision: "3b13818e8330f68625d80d9bf5d8049c41fbe197", + Redis: true, + } + + badRedisResponse = &healthcheck.Response{Redis: false} + + okHandlers = buildTestHandlers(200, okResponse) + badRedisHandlers = buildTestHandlers(200, badRedisResponse) + brokenHandlers = buildTestHandlers(500, nil) +) + +func buildTestHandlers(code int, rsp *healthcheck.Response) []testserver.TestRequestHandler { + return []testserver.TestRequestHandler{ + { + Path: "/api/v4/internal/check", + Handler: func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(code) + if rsp != nil { + json.NewEncoder(w).Encode(rsp) + } + }, + }, + } +} + +func TestExecute(t *testing.T) { + url, cleanup := testserver.StartSocketHttpServer(t, okHandlers) + defer cleanup() + + buffer := &bytes.Buffer{} + cmd := &Command{ + Config: &config.Config{GitlabUrl: url}, + ReadWriter: &readwriter.ReadWriter{Out: buffer}, + } + + err := cmd.Execute() + + require.NoError(t, err) + require.Equal(t, "Internal API available: OK\nRedis available via internal API: OK\n", buffer.String()) +} + +func TestFailingRedisExecute(t *testing.T) { + url, cleanup := testserver.StartSocketHttpServer(t, badRedisHandlers) + defer cleanup() + + buffer := &bytes.Buffer{} + cmd := &Command{ + Config: &config.Config{GitlabUrl: url}, + ReadWriter: &readwriter.ReadWriter{Out: buffer}, + } + + err := cmd.Execute() + require.Error(t, err, "Redis available via internal API: FAILED") + require.Equal(t, "Internal API available: OK\n", buffer.String()) +} + +func TestFailingAPIExecute(t *testing.T) { + url, cleanup := testserver.StartSocketHttpServer(t, brokenHandlers) + defer cleanup() + + buffer := &bytes.Buffer{} + cmd := &Command{ + Config: &config.Config{GitlabUrl: url}, + ReadWriter: &readwriter.ReadWriter{Out: buffer}, + } + + err := cmd.Execute() + require.Empty(t, buffer.String()) + require.EqualError(t, err, "Internal API available: FAILED - Internal API error (500)") +} diff --git a/go/internal/executable/executable.go b/go/internal/executable/executable.go index 2e7d26e..c6355b9 100644 --- a/go/internal/executable/executable.go +++ b/go/internal/executable/executable.go @@ -7,6 +7,7 @@ import ( const ( BinDir = "bin" + Healthcheck = "check" GitlabShell = "gitlab-shell" AuthorizedKeysCheck = "gitlab-shell-authorized-keys-check" AuthorizedPrincipalsCheck = "gitlab-shell-authorized-principals-check" diff --git a/go/internal/gitlabnet/healthcheck/client.go b/go/internal/gitlabnet/healthcheck/client.go new file mode 100644 index 0000000..288b150 --- /dev/null +++ b/go/internal/gitlabnet/healthcheck/client.go @@ -0,0 +1,54 @@ +package healthcheck + +import ( + "fmt" + "net/http" + + "gitlab.com/gitlab-org/gitlab-shell/go/internal/config" + "gitlab.com/gitlab-org/gitlab-shell/go/internal/gitlabnet" +) + +const ( + checkPath = "/check" +) + +type Client struct { + config *config.Config + client *gitlabnet.GitlabClient +} + +type Response struct { + APIVersion string `json:"api_version"` + GitlabVersion string `json:"gitlab_version"` + GitlabRevision string `json:"gitlab_rev"` + Redis bool `json:"redis"` +} + +func NewClient(config *config.Config) (*Client, error) { + client, err := gitlabnet.GetClient(config) + if err != nil { + return nil, fmt.Errorf("Error creating http client: %v", err) + } + + return &Client{config: config, client: client}, nil +} + +func (c *Client) Check() (*Response, error) { + resp, err := c.client.Get(checkPath) + if err != nil { + return nil, err + } + + defer resp.Body.Close() + + return parse(resp) +} + +func parse(hr *http.Response) (*Response, error) { + response := &Response{} + if err := gitlabnet.ParseJSON(hr, response); err != nil { + return nil, err + } + + return response, nil +} diff --git a/go/internal/gitlabnet/healthcheck/client_test.go b/go/internal/gitlabnet/healthcheck/client_test.go new file mode 100644 index 0000000..d32e6f4 --- /dev/null +++ b/go/internal/gitlabnet/healthcheck/client_test.go @@ -0,0 +1,48 @@ +package healthcheck + +import ( + "encoding/json" + "net/http" + "testing" + + "gitlab.com/gitlab-org/gitlab-shell/go/internal/config" + "gitlab.com/gitlab-org/gitlab-shell/go/internal/gitlabnet/testserver" + + "github.com/stretchr/testify/require" +) + +var ( + requests = []testserver.TestRequestHandler{ + { + Path: "/api/v4/internal/check", + Handler: func(w http.ResponseWriter, r *http.Request) { + json.NewEncoder(w).Encode(testResponse) + }, + }, + } + + testResponse = &Response{ + APIVersion: "v4", + GitlabVersion: "v12.0.0-ee", + GitlabRevision: "3b13818e8330f68625d80d9bf5d8049c41fbe197", + Redis: true, + } +) + +func TestCheck(t *testing.T) { + client, cleanup := setup(t) + defer cleanup() + + result, err := client.Check() + require.NoError(t, err) + require.Equal(t, testResponse, result) +} + +func setup(t *testing.T) (*Client, func()) { + url, cleanup := testserver.StartSocketHttpServer(t, requests) + + client, err := NewClient(&config.Config{GitlabUrl: url}) + require.NoError(t, err) + + return client, cleanup +} |