diff options
author | Igor Drozdov <idrozdov@gitlab.com> | 2020-04-07 07:00:04 +0300 |
---|---|---|
committer | Ash McKenzie <amckenzie@gitlab.com> | 2020-04-14 23:38:52 +1000 |
commit | 40b3ddc142b906a79f22c726a0a61dd113c39485 (patch) | |
tree | e0b6d0a37d049dbd01e164a95dc0f8728d51d7e2 /internal/command/shared | |
parent | 74bef786d44b270167c7f9dbb029d197fe32ac4f (diff) | |
download | gitlab-shell-40b3ddc142b906a79f22c726a0a61dd113c39485.tar.gz |
Extract customaction into a separate moduleid-extract-custom-action-in-separate-module
We'll reuse this module for uploadpack in the future
Diffstat (limited to 'internal/command/shared')
-rw-r--r-- | internal/command/shared/customaction/customaction.go | 95 | ||||
-rw-r--r-- | internal/command/shared/customaction/customaction_test.go | 85 |
2 files changed, 180 insertions, 0 deletions
diff --git a/internal/command/shared/customaction/customaction.go b/internal/command/shared/customaction/customaction.go new file mode 100644 index 0000000..c4b6647 --- /dev/null +++ b/internal/command/shared/customaction/customaction.go @@ -0,0 +1,95 @@ +package customaction + +import ( + "bytes" + "errors" + + "io" + "io/ioutil" + "net/http" + + "gitlab.com/gitlab-org/gitlab-shell/internal/command/readwriter" + "gitlab.com/gitlab-org/gitlab-shell/internal/config" + "gitlab.com/gitlab-org/gitlab-shell/internal/gitlabnet" + "gitlab.com/gitlab-org/gitlab-shell/internal/gitlabnet/accessverifier" +) + +type Request struct { + SecretToken []byte `json:"secret_token"` + Data accessverifier.CustomPayloadData `json:"data"` + Output []byte `json:"output"` +} + +type Response struct { + Result []byte `json:"result"` + Message string `json:"message"` +} + +type Command struct { + Config *config.Config + ReadWriter *readwriter.ReadWriter +} + +func (c *Command) Execute(response *accessverifier.Response) error { + data := response.Payload.Data + apiEndpoints := data.ApiEndpoints + + if len(apiEndpoints) == 0 { + return errors.New("Custom action error: Empty API endpoints") + } + + return c.processApiEndpoints(response) +} + +func (c *Command) processApiEndpoints(response *accessverifier.Response) error { + client, err := gitlabnet.GetClient(c.Config) + + if err != nil { + return err + } + + data := response.Payload.Data + request := &Request{Data: data} + request.Data.UserId = response.Who + + for _, endpoint := range data.ApiEndpoints { + response, err := c.performRequest(client, endpoint, request) + if err != nil { + return err + } + + if err = c.displayResult(response.Result); err != nil { + return err + } + + // In the context of the git push sequence of events, it's necessary to read + // stdin in order to capture output to pass onto subsequent commands + output, err := ioutil.ReadAll(c.ReadWriter.In) + if err != nil { + return err + } + request.Output = output + } + + return nil +} + +func (c *Command) performRequest(client *gitlabnet.GitlabClient, endpoint string, request *Request) (*Response, error) { + response, err := client.DoRequest(http.MethodPost, endpoint, request) + if err != nil { + return nil, err + } + defer response.Body.Close() + + cr := &Response{} + if err := gitlabnet.ParseJSON(response, cr); err != nil { + return nil, err + } + + return cr, nil +} + +func (c *Command) displayResult(result []byte) error { + _, err := io.Copy(c.ReadWriter.Out, bytes.NewReader(result)) + return err +} diff --git a/internal/command/shared/customaction/customaction_test.go b/internal/command/shared/customaction/customaction_test.go new file mode 100644 index 0000000..3dfe288 --- /dev/null +++ b/internal/command/shared/customaction/customaction_test.go @@ -0,0 +1,85 @@ +package customaction + +import ( + "bytes" + "encoding/json" + "io/ioutil" + "net/http" + "testing" + + "github.com/stretchr/testify/require" + + "gitlab.com/gitlab-org/gitlab-shell/internal/command/readwriter" + "gitlab.com/gitlab-org/gitlab-shell/internal/config" + "gitlab.com/gitlab-org/gitlab-shell/internal/gitlabnet/accessverifier" + "gitlab.com/gitlab-org/gitlab-shell/internal/gitlabnet/testserver" +) + +func TestExecute(t *testing.T) { + who := "key-1" + + requests := []testserver.TestRequestHandler{ + { + Path: "/geo/proxy/info_refs", + Handler: func(w http.ResponseWriter, r *http.Request) { + b, err := ioutil.ReadAll(r.Body) + require.NoError(t, err) + + var request *Request + require.NoError(t, json.Unmarshal(b, &request)) + + require.Equal(t, request.Data.UserId, who) + require.Empty(t, request.Output) + + err = json.NewEncoder(w).Encode(Response{Result: []byte("custom")}) + require.NoError(t, err) + }, + }, + { + Path: "/geo/proxy/push", + Handler: func(w http.ResponseWriter, r *http.Request) { + b, err := ioutil.ReadAll(r.Body) + require.NoError(t, err) + + var request *Request + require.NoError(t, json.Unmarshal(b, &request)) + + require.Equal(t, request.Data.UserId, who) + require.Equal(t, "input", string(request.Output)) + + err = json.NewEncoder(w).Encode(Response{Result: []byte("output")}) + require.NoError(t, err) + }, + }, + } + + url, cleanup := testserver.StartSocketHttpServer(t, requests) + defer cleanup() + + outBuf := &bytes.Buffer{} + errBuf := &bytes.Buffer{} + input := bytes.NewBufferString("input") + + response := &accessverifier.Response{ + Who: who, + Payload: accessverifier.CustomPayload{ + Action: "geo_proxy_to_primary", + Data: accessverifier.CustomPayloadData{ + ApiEndpoints: []string{"/geo/proxy/info_refs", "/geo/proxy/push"}, + Username: "custom", + PrimaryRepo: "https://repo/path", + }, + }, + } + + cmd := &Command{ + Config: &config.Config{GitlabUrl: url}, + ReadWriter: &readwriter.ReadWriter{ErrOut: errBuf, Out: outBuf, In: input}, + } + + require.NoError(t, cmd.Execute(response)) + + // expect printing of info message, "custom" string from the first request + // and "output" string from the second request + require.Equal(t, "customoutput", outBuf.String()) +} |