summaryrefslogtreecommitdiff
path: root/internal/command/shared
diff options
context:
space:
mode:
authorIgor Drozdov <idrozdov@gitlab.com>2020-04-07 07:00:04 +0300
committerAsh McKenzie <amckenzie@gitlab.com>2020-04-14 23:38:52 +1000
commit40b3ddc142b906a79f22c726a0a61dd113c39485 (patch)
treee0b6d0a37d049dbd01e164a95dc0f8728d51d7e2 /internal/command/shared
parent74bef786d44b270167c7f9dbb029d197fe32ac4f (diff)
downloadgitlab-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.go95
-rw-r--r--internal/command/shared/customaction/customaction_test.go85
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())
+}