diff options
author | Martin Polden <mpolden@mpolden.no> | 2021-09-22 09:03:48 +0200 |
---|---|---|
committer | Martin Polden <mpolden@mpolden.no> | 2021-09-22 10:40:12 +0200 |
commit | b6d3d1814961d95c84d4dac12e875b6fa3bdc3d6 (patch) | |
tree | 3fad3696cdaa3f65975fb7a931d76a84eab7c4a2 /client | |
parent | 302ddbdeee930c9e951aa5d0b2004c731b621e3d (diff) |
Extract curl package
Diffstat (limited to 'client')
-rw-r--r-- | client/go/cmd/curl.go | 95 | ||||
-rw-r--r-- | client/go/cmd/curl_test.go | 37 | ||||
-rw-r--r-- | client/go/curl/curl.go | 104 | ||||
-rw-r--r-- | client/go/curl/curl_test.go | 45 |
4 files changed, 168 insertions, 113 deletions
diff --git a/client/go/cmd/curl.go b/client/go/cmd/curl.go index f6b40e10f35..bd9fad1b47e 100644 --- a/client/go/cmd/curl.go +++ b/client/go/cmd/curl.go @@ -1,22 +1,19 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package cmd import ( - "fmt" "log" "os" - "os/exec" "strings" - "github.com/kballard/go-shellquote" "github.com/spf13/cobra" + "github.com/vespa-engine/vespa/client/go/curl" ) var curlDryRun bool -var curlPath string func init() { rootCmd.AddCommand(curlCmd) - curlCmd.Flags().StringVarP(&curlPath, "path", "p", "", "The path to curl. If this is unset, curl from PATH is used") curlCmd.Flags().BoolVarP(&curlDryRun, "dry-run", "n", false, "Print the curl command that would be executed") } @@ -50,16 +47,20 @@ $ vespa curl -t local -- -v /search/?yql=query return } service := getService("query", 0) - c := &curl{privateKeyPath: privateKeyFile, certificatePath: certificateFile} + url := joinURL(service.BaseURL, args[len(args)-1]) + rawArgs := args[:len(args)-1] + c, err := curl.RawArgs(url, rawArgs...) + if err != nil { + fatalErr(err) + return + } + c.PrivateKey = privateKeyFile + c.Certificate = certificateFile + if curlDryRun { - cmd, err := c.command(service.BaseURL, args...) - if err != nil { - fatalErr(err, "Failed to create curl command") - return - } - log.Print(shellquote.Join(cmd.Args...)) + log.Print(c.String()) } else { - if err := c.run(service.BaseURL, args...); err != nil { + if err := c.Run(os.Stdout, os.Stderr); err != nil { fatalErr(err, "Failed to run curl") return } @@ -67,72 +68,8 @@ $ vespa curl -t local -- -v /search/?yql=query }, } -type curl struct { - path string - certificatePath string - privateKeyPath string -} - -func (c *curl) run(baseURL string, args ...string) error { - cmd, err := c.command(baseURL, args...) - if err != nil { - return err - } - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - if err := cmd.Start(); err != nil { - return err - } - return cmd.Wait() -} - -func (c *curl) command(baseURL string, args ...string) (*exec.Cmd, error) { - if len(args) == 0 { - return nil, fmt.Errorf("need at least one argument") - } - - if c.path == "" { - resolvedPath, err := resolveCurlPath() - if err != nil { - return nil, err - } - c.path = resolvedPath - } - - path := args[len(args)-1] - args = args[:len(args)-1] - if !hasOption("--key", args) && c.privateKeyPath != "" { - args = append(args, "--key", c.privateKeyPath) - } - if !hasOption("--cert", args) && c.certificatePath != "" { - args = append(args, "--cert", c.certificatePath) - } - +func joinURL(baseURL, path string) string { baseURL = strings.TrimSuffix(baseURL, "/") path = strings.TrimPrefix(path, "/") - args = append(args, baseURL+"/"+path) - - return exec.Command(c.path, args...), nil -} - -func hasOption(option string, args []string) bool { - for _, arg := range args { - if arg == option { - return true - } - } - return false -} - -func resolveCurlPath() (string, error) { - var curlPath string - var err error - curlPath, err = exec.LookPath("curl") - if err != nil { - curlPath, err = exec.LookPath("curl.exe") - if err != nil { - return "", err - } - } - return curlPath, nil + return baseURL + "/" + path } diff --git a/client/go/cmd/curl_test.go b/client/go/cmd/curl_test.go index c3163e731ce..340eacd0bd3 100644 --- a/client/go/cmd/curl_test.go +++ b/client/go/cmd/curl_test.go @@ -1,9 +1,9 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package cmd import ( "fmt" "path/filepath" - "strings" "testing" "github.com/stretchr/testify/assert" @@ -13,41 +13,10 @@ func TestCurl(t *testing.T) { homeDir := t.TempDir() httpClient := &mockHttpClient{} convergeServices(httpClient) - out := execute(command{homeDir: homeDir, args: []string{"curl", "-n", "-p", "/usr/bin/curl", "-a", "t1.a1.i1", "--", "-v", "--data-urlencode", "arg=with space", "/search"}}, t, httpClient) + out, _ := execute(command{homeDir: homeDir, args: []string{"curl", "-n", "-a", "t1.a1.i1", "--", "-v", "--data-urlencode", "arg=with space", "/search"}}, t, httpClient) - expected := fmt.Sprintf("/usr/bin/curl -v --data-urlencode 'arg=with space' --key %s --cert %s https://127.0.0.1:8080/search\n", + expected := fmt.Sprintf("curl --key %s --cert %s -v --data-urlencode 'arg=with space' https://127.0.0.1:8080/search\n", filepath.Join(homeDir, ".vespa", "t1.a1.i1", "data-plane-private-key.pem"), filepath.Join(homeDir, ".vespa", "t1.a1.i1", "data-plane-public-cert.pem")) assert.Equal(t, expected, out) } - -func TestCurlCommand(t *testing.T) { - c := &curl{path: "/usr/bin/curl", privateKeyPath: "/tmp/priv-key", certificatePath: "/tmp/cert-key"} - assertCurl(t, c, "/usr/bin/curl -v --key /tmp/priv-key --cert /tmp/cert-key https://example.com/", "-v", "/") - - c = &curl{path: "/usr/bin/curl", privateKeyPath: "/tmp/priv-key", certificatePath: "/tmp/cert-key"} - assertCurl(t, c, "/usr/bin/curl -v --cert my-cert --key my-key https://example.com/", "-v", "--cert", "my-cert", "--key", "my-key", "/") - - c = &curl{path: "/usr/bin/curl2"} - assertCurl(t, c, "/usr/bin/curl2 -v https://example.com/foo", "-v", "/foo") - - c = &curl{path: "/usr/bin/curl"} - assertCurl(t, c, "/usr/bin/curl -v https://example.com/foo/bar", "-v", "/foo/bar") - - c = &curl{path: "/usr/bin/curl"} - assertCurl(t, c, "/usr/bin/curl -v https://example.com/foo/bar", "-v", "foo/bar") - - c = &curl{path: "/usr/bin/curl"} - assertCurlURL(t, c, "/usr/bin/curl -v https://example.com/foo/bar", "https://example.com/", "-v", "foo/bar") -} - -func assertCurl(t *testing.T, c *curl, expectedOutput string, args ...string) { - assertCurlURL(t, c, expectedOutput, "https://example.com", args...) -} - -func assertCurlURL(t *testing.T, c *curl, expectedOutput string, url string, args ...string) { - cmd, err := c.command("https://example.com", args...) - assert.Nil(t, err) - - assert.Equal(t, expectedOutput, strings.Join(cmd.Args, " ")) -} diff --git a/client/go/curl/curl.go b/client/go/curl/curl.go new file mode 100644 index 00000000000..44c3a0ad2a9 --- /dev/null +++ b/client/go/curl/curl.go @@ -0,0 +1,104 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package curl + +import ( + "io" + "net/url" + "os/exec" + "runtime" + + "github.com/kballard/go-shellquote" +) + +type header struct { + key string + value string +} + +type Command struct { + Path string + Method string + PrivateKey string + Certificate string + BodyFile string + url *url.URL + headers []header + rawArgs []string +} + +func (c *Command) Args() []string { + var args []string + if c.PrivateKey != "" { + args = append(args, "--key", c.PrivateKey) + } + if c.Certificate != "" { + args = append(args, "--cert", c.Certificate) + } + if c.Method != "" { + args = append(args, "-X", c.Method) + } + for _, header := range c.headers { + args = append(args, "-H", header.key+": "+header.value) + } + if c.BodyFile != "" { + args = append(args, "--data-binary", "@"+c.BodyFile) + } + args = append(args, c.rawArgs...) + args = append(args, c.url.String()) + return args +} + +func (c *Command) String() string { + args := []string{c.Path} + args = append(args, c.Args()...) + return shellquote.Join(args...) +} + +func (c *Command) Header(key, value string) { + c.headers = append(c.headers, header{key: key, value: value}) +} + +func (c *Command) Param(key, value string) { + query := c.url.Query() + query.Set(key, value) + c.url.RawQuery = query.Encode() +} + +func (c *Command) Run(stdout, stderr io.Writer) error { + cmd := exec.Command(c.Path, c.Args()...) + cmd.Stdout = stdout + cmd.Stderr = stderr + if err := cmd.Start(); err != nil { + return err + } + return cmd.Wait() +} + +func Post(url string) (*Command, error) { return curl("POST", url) } + +func Get(url string) (*Command, error) { return curl("", url) } + +func RawArgs(url string, args ...string) (*Command, error) { + c, err := curl("", url) + if err != nil { + return nil, err + } + c.rawArgs = args + return c, nil +} + +func curl(method, rawurl string) (*Command, error) { + path := "curl" + if runtime.GOOS == "windows" { + path = "curl.exe" + } + realURL, err := url.Parse(rawurl) + if err != nil { + return nil, err + } + return &Command{ + Path: path, + Method: method, + url: realURL, + }, nil +} diff --git a/client/go/curl/curl_test.go b/client/go/curl/curl_test.go new file mode 100644 index 00000000000..90bf274f7a2 --- /dev/null +++ b/client/go/curl/curl_test.go @@ -0,0 +1,45 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package curl + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestPost(t *testing.T) { + c, err := Post("https://example.com") + if err != nil { + t.Fatal(err) + } + c.PrivateKey = "key.pem" + c.Certificate = "cert.pem" + c.BodyFile = "file.json" + c.Header("Content-Type", "application/json") + + assert.Equal(t, "curl --key key.pem --cert cert.pem -X POST -H 'Content-Type: application/json' --data-binary @file.json https://example.com", c.String()) +} + +func TestGet(t *testing.T) { + c, err := Get("https://example.com") + if err != nil { + t.Fatal(err) + } + c.PrivateKey = "key.pem" + c.Certificate = "cert.pem" + c.Param("yql", "select * from sources * where title contains 'foo';") + c.Param("hits", "5") + + assert.Equal(t, `curl --key key.pem --cert cert.pem https://example.com\?hits=5\&yql=select+%2A+from+sources+%2A+where+title+contains+%27foo%27%3B`, c.String()) +} + +func TestRawArgs(t *testing.T) { + c, err := RawArgs("https://example.com/search", "-v", "-m", "10", "-H", "foo: bar") + if err != nil { + t.Fatal(err) + } + c.PrivateKey = "key.pem" + c.Certificate = "cert.pem" + + assert.Equal(t, `curl --key key.pem --cert cert.pem -v -m 10 -H 'foo: bar' https://example.com/search`, c.String()) +} |