summaryrefslogtreecommitdiffstats
path: root/client
diff options
context:
space:
mode:
authorMartin Polden <mpolden@mpolden.no>2021-09-22 10:30:19 +0200
committerMartin Polden <mpolden@mpolden.no>2021-09-22 10:45:04 +0200
commit2239bed79760f5eedf25771a1bd6c84114ea7ade (patch)
tree7174ae81e5b9407a1530f7a3a08a00c8763f67fc /client
parentb6d3d1814961d95c84d4dac12e875b6fa3bdc3d6 (diff)
Support printing curl command for document operations
Diffstat (limited to 'client')
-rw-r--r--client/go/cmd/api_key_test.go6
-rw-r--r--client/go/cmd/cert_test.go10
-rw-r--r--client/go/cmd/command_tester.go18
-rw-r--r--client/go/cmd/config_test.go37
-rw-r--r--client/go/cmd/deploy_test.go5
-rw-r--r--client/go/cmd/document.go30
-rw-r--r--client/go/cmd/document_test.go26
-rw-r--r--client/go/cmd/helpers.go13
-rw-r--r--client/go/cmd/man_test.go2
-rw-r--r--client/go/cmd/root.go4
-rw-r--r--client/go/cmd/version_test.go4
-rw-r--r--client/go/vespa/document.go53
-rw-r--r--client/go/vespa/target.go33
-rw-r--r--client/go/vespa/target_test.go2
14 files changed, 158 insertions, 85 deletions
diff --git a/client/go/cmd/api_key_test.go b/client/go/cmd/api_key_test.go
index c00f520aa25..2497568604f 100644
--- a/client/go/cmd/api_key_test.go
+++ b/client/go/cmd/api_key_test.go
@@ -1,4 +1,4 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
// Author: mpolden
package cmd
@@ -14,10 +14,10 @@ func TestAPIKey(t *testing.T) {
homeDir := t.TempDir()
keyFile := homeDir + "/.vespa/t1.api-key.pem"
- out := execute(command{args: []string{"api-key", "-a", "t1.a1.i1"}, homeDir: homeDir}, t, nil)
+ out, _ := execute(command{args: []string{"api-key", "-a", "t1.a1.i1"}, homeDir: homeDir}, t, nil)
assert.True(t, strings.HasPrefix(out, "Success: API private key written to "+keyFile+"\n"))
- out = execute(command{args: []string{"api-key", "-a", "t1.a1.i1"}, homeDir: homeDir}, t, nil)
+ out, _ = execute(command{args: []string{"api-key", "-a", "t1.a1.i1"}, homeDir: homeDir}, t, nil)
assert.True(t, strings.HasPrefix(out, "Error: File "+keyFile+" already exists\nHint: Use -f to overwrite it\n"))
assert.True(t, strings.Contains(out, "This is your public key"))
}
diff --git a/client/go/cmd/cert_test.go b/client/go/cmd/cert_test.go
index 36abdae1787..d93def2fa70 100644
--- a/client/go/cmd/cert_test.go
+++ b/client/go/cmd/cert_test.go
@@ -1,4 +1,4 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
// Author: mpolden
package cmd
@@ -16,7 +16,7 @@ import (
func TestCert(t *testing.T) {
homeDir := t.TempDir()
pkgDir := mockApplicationPackage(t, false)
- out := execute(command{args: []string{"cert", "-a", "t1.a1.i1", pkgDir}, homeDir: homeDir}, t, nil)
+ out, _ := execute(command{args: []string{"cert", "-a", "t1.a1.i1", pkgDir}, homeDir: homeDir}, t, nil)
app, err := vespa.ApplicationFromString("t1.a1.i1")
assert.Nil(t, err)
@@ -28,7 +28,7 @@ func TestCert(t *testing.T) {
assert.Equal(t, fmt.Sprintf("Success: Certificate written to %s\nSuccess: Certificate written to %s\nSuccess: Private key written to %s\n", pkgCertificate, certificate, privateKey), out)
- out = execute(command{args: []string{"cert", "-a", "t1.a1.i1", pkgDir}, homeDir: homeDir}, t, nil)
+ out, _ = execute(command{args: []string{"cert", "-a", "t1.a1.i1", pkgDir}, homeDir: homeDir}, t, nil)
assert.Contains(t, out, fmt.Sprintf("Error: Application package %s already contains a certificate", appDir))
}
@@ -41,13 +41,13 @@ func TestCertCompressedPackage(t *testing.T) {
_, err = os.Create(zipFile)
assert.Nil(t, err)
- out := execute(command{args: []string{"cert", "-a", "t1.a1.i1", pkgDir}, homeDir: homeDir}, t, nil)
+ out, _ := execute(command{args: []string{"cert", "-a", "t1.a1.i1", pkgDir}, homeDir: homeDir}, t, nil)
assert.Contains(t, out, "Error: Cannot add certificate to compressed application package")
err = os.Remove(zipFile)
assert.Nil(t, err)
- out = execute(command{args: []string{"cert", "-f", "-a", "t1.a1.i1", pkgDir}, homeDir: homeDir}, t, nil)
+ out, _ = execute(command{args: []string{"cert", "-f", "-a", "t1.a1.i1", pkgDir}, homeDir: homeDir}, t, nil)
assert.Contains(t, out, "Success: Certificate written to")
assert.Contains(t, out, "Success: Private key written to")
}
diff --git a/client/go/cmd/command_tester.go b/client/go/cmd/command_tester.go
index 3d19e772875..f455ffa9957 100644
--- a/client/go/cmd/command_tester.go
+++ b/client/go/cmd/command_tester.go
@@ -1,4 +1,4 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
// A helper for testing commands
// Author: bratseth
@@ -17,7 +17,6 @@ import (
"github.com/spf13/pflag"
"github.com/spf13/viper"
- "github.com/stretchr/testify/assert"
"github.com/vespa-engine/vespa/client/go/util"
)
@@ -27,7 +26,7 @@ type command struct {
moreArgs []string
}
-func execute(cmd command, t *testing.T, client *mockHttpClient) string {
+func execute(cmd command, t *testing.T, client *mockHttpClient) (string, string) {
if client != nil {
util.ActiveHttpClient = client
}
@@ -56,19 +55,20 @@ func execute(cmd command, t *testing.T, client *mockHttpClient) string {
exitFunc = func(code int) {}
// Capture stdout and execute command
- var b bytes.Buffer
- stdout = &b
+ var capturedOut bytes.Buffer
+ var capturedErr bytes.Buffer
+ stdout = &capturedOut
+ stderr = &capturedErr
// Execute command and return output
rootCmd.SetArgs(append(cmd.args, cmd.moreArgs...))
rootCmd.Execute()
- out, err := ioutil.ReadAll(&b)
- assert.Nil(t, err, "No error")
- return string(out)
+ return capturedOut.String(), capturedErr.String()
}
func executeCommand(t *testing.T, client *mockHttpClient, args []string, moreArgs []string) string {
- return execute(command{args: args, moreArgs: moreArgs}, t, client)
+ out, _ := execute(command{args: args, moreArgs: moreArgs}, t, client)
+ return out
}
type mockHttpClient struct {
diff --git a/client/go/cmd/config_test.go b/client/go/cmd/config_test.go
index 70ad5d558e1..cf50f561f0f 100644
--- a/client/go/cmd/config_test.go
+++ b/client/go/cmd/config_test.go
@@ -8,23 +8,28 @@ import (
func TestConfig(t *testing.T) {
homeDir := t.TempDir()
- assert.Equal(t, "invalid option or value: \"foo\": \"bar\"\n", execute(command{homeDir: homeDir, args: []string{"config", "set", "foo", "bar"}}, t, nil))
- assert.Equal(t, "foo = <unset>\n", execute(command{homeDir: homeDir, args: []string{"config", "get", "foo"}}, t, nil))
- assert.Equal(t, "target = local\n", execute(command{homeDir: homeDir, args: []string{"config", "get", "target"}}, t, nil))
- assert.Equal(t, "", execute(command{homeDir: homeDir, args: []string{"config", "set", "target", "cloud"}}, t, nil))
- assert.Equal(t, "target = cloud\n", execute(command{homeDir: homeDir, args: []string{"config", "get", "target"}}, t, nil))
- assert.Equal(t, "", execute(command{homeDir: homeDir, args: []string{"config", "set", "target", "http://127.0.0.1:8080"}}, t, nil))
- assert.Equal(t, "", execute(command{homeDir: homeDir, args: []string{"config", "set", "target", "https://127.0.0.1"}}, t, nil))
- assert.Equal(t, "target = https://127.0.0.1\n", execute(command{homeDir: homeDir, args: []string{"config", "get", "target"}}, t, nil))
+ assertConfigCommand(t, "invalid option or value: \"foo\": \"bar\"\n", homeDir, "config", "set", "foo", "bar")
+ assertConfigCommand(t, "foo = <unset>\n", homeDir, "config", "get", "foo")
+ assertConfigCommand(t, "target = local\n", homeDir, "config", "get", "target")
+ assertConfigCommand(t, "", homeDir, "config", "set", "target", "cloud")
+ assertConfigCommand(t, "target = cloud\n", homeDir, "config", "get", "target")
+ assertConfigCommand(t, "", homeDir, "config", "set", "target", "http://127.0.0.1:8080")
+ assertConfigCommand(t, "", homeDir, "config", "set", "target", "https://127.0.0.1")
+ assertConfigCommand(t, "target = https://127.0.0.1\n", homeDir, "config", "get", "target")
- assert.Equal(t, "invalid application: \"foo\"\n", execute(command{homeDir: homeDir, args: []string{"config", "set", "application", "foo"}}, t, nil))
- assert.Equal(t, "application = <unset>\n", execute(command{homeDir: homeDir, args: []string{"config", "get", "application"}}, t, nil))
- assert.Equal(t, "", execute(command{homeDir: homeDir, args: []string{"config", "set", "application", "t1.a1.i1"}}, t, nil))
- assert.Equal(t, "application = t1.a1.i1\n", execute(command{homeDir: homeDir, args: []string{"config", "get", "application"}}, t, nil))
+ assertConfigCommand(t, "invalid application: \"foo\"\n", homeDir, "config", "set", "application", "foo")
+ assertConfigCommand(t, "application = <unset>\n", homeDir, "config", "get", "application")
+ assertConfigCommand(t, "", homeDir, "config", "set", "application", "t1.a1.i1")
+ assertConfigCommand(t, "application = t1.a1.i1\n", homeDir, "config", "get", "application")
- assert.Equal(t, "application = t1.a1.i1\ncolor = auto\ntarget = https://127.0.0.1\nwait = 0\n", execute(command{homeDir: homeDir, args: []string{"config", "get"}}, t, nil))
+ assertConfigCommand(t, "application = t1.a1.i1\ncolor = auto\ntarget = https://127.0.0.1\nwait = 0\n", homeDir, "config", "get")
- assert.Equal(t, "", execute(command{homeDir: homeDir, args: []string{"config", "set", "wait", "60"}}, t, nil))
- assert.Equal(t, "wait option must be an integer >= 0, got \"foo\"\n", execute(command{homeDir: homeDir, args: []string{"config", "set", "wait", "foo"}}, t, nil))
- assert.Equal(t, "wait = 60\n", execute(command{homeDir: homeDir, args: []string{"config", "get", "wait"}}, t, nil))
+ assertConfigCommand(t, "", homeDir, "config", "set", "wait", "60")
+ assertConfigCommand(t, "wait option must be an integer >= 0, got \"foo\"\n", homeDir, "config", "set", "wait", "foo")
+ assertConfigCommand(t, "wait = 60\n", homeDir, "config", "get", "wait")
+}
+
+func assertConfigCommand(t *testing.T, expected, homeDir string, args ...string) {
+ out, _ := execute(command{homeDir: homeDir, args: args}, t, nil)
+ assert.Equal(t, expected, out)
}
diff --git a/client/go/cmd/deploy_test.go b/client/go/cmd/deploy_test.go
index f24ba0829f9..443f7e8846f 100644
--- a/client/go/cmd/deploy_test.go
+++ b/client/go/cmd/deploy_test.go
@@ -1,4 +1,4 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
// deploy command tests
// Author: bratseth
@@ -130,9 +130,10 @@ func assertActivate(applicationPackage string, arguments []string, t *testing.T)
if err := cfg.WriteSessionID(vespa.DefaultApplication, 42); err != nil {
t.Fatal(err)
}
+ out, _ := execute(command{args: arguments, homeDir: homeDir}, t, client)
assert.Equal(t,
"Success: Activated "+applicationPackage+" with session 42\n",
- execute(command{args: arguments, homeDir: homeDir}, t, client))
+ out)
url := "http://127.0.0.1:19071/application/v2/tenant/default/session/42/active"
assert.Equal(t, url, client.lastRequest.URL.String())
assert.Equal(t, "PUT", client.lastRequest.Method)
diff --git a/client/go/cmd/document.go b/client/go/cmd/document.go
index 78c6596f511..cc5fb948e3b 100644
--- a/client/go/cmd/document.go
+++ b/client/go/cmd/document.go
@@ -1,10 +1,12 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
// vespa document command
// author: bratseth
package cmd
import (
+ "io"
+ "io/ioutil"
"log"
"strings"
@@ -13,12 +15,15 @@ import (
"github.com/vespa-engine/vespa/client/go/vespa"
)
+var printCurl bool
+
func init() {
rootCmd.AddCommand(documentCmd)
documentCmd.AddCommand(documentPutCmd)
documentCmd.AddCommand(documentUpdateCmd)
documentCmd.AddCommand(documentRemoveCmd)
documentCmd.AddCommand(documentGetCmd)
+ documentCmd.PersistentFlags().BoolVarP(&printCurl, "verbose", "v", false, "Print the equivalent curl command for the document operation")
}
var documentCmd = &cobra.Command{
@@ -38,7 +43,7 @@ should be used instead of this.`,
DisableAutoGenTag: true,
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
- printResult(vespa.Send(args[0], documentService()), false)
+ printResult(vespa.Send(args[0], documentService(), curlOutput()), false)
},
}
@@ -54,9 +59,9 @@ $ vespa document put id:mynamespace:music::a-head-full-of-dreams src/test/resour
DisableAutoGenTag: true,
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 1 {
- printResult(vespa.Put("", args[0], documentService()), false)
+ printResult(vespa.Put("", args[0], documentService(), curlOutput()), false)
} else {
- printResult(vespa.Put(args[0], args[1], documentService()), false)
+ printResult(vespa.Put(args[0], args[1], documentService(), curlOutput()), false)
}
},
}
@@ -72,9 +77,9 @@ $ vespa document update id:mynamespace:music::a-head-full-of-dreams src/test/res
DisableAutoGenTag: true,
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 1 {
- printResult(vespa.Update("", args[0], documentService()), false)
+ printResult(vespa.Update("", args[0], documentService(), curlOutput()), false)
} else {
- printResult(vespa.Update(args[0], args[1], documentService()), false)
+ printResult(vespa.Update(args[0], args[1], documentService(), curlOutput()), false)
}
},
}
@@ -90,9 +95,9 @@ $ vespa document remove id:mynamespace:music::a-head-full-of-dreams`,
DisableAutoGenTag: true,
Run: func(cmd *cobra.Command, args []string) {
if strings.HasPrefix(args[0], "id:") {
- printResult(vespa.RemoveId(args[0], documentService()), false)
+ printResult(vespa.RemoveId(args[0], documentService(), curlOutput()), false)
} else {
- printResult(vespa.RemoveOperation(args[0], documentService()), false)
+ printResult(vespa.RemoveOperation(args[0], documentService(), curlOutput()), false)
}
},
}
@@ -104,12 +109,19 @@ var documentGetCmd = &cobra.Command{
DisableAutoGenTag: true,
Example: `$ vespa document get id:mynamespace:music::a-head-full-of-dreams`,
Run: func(cmd *cobra.Command, args []string) {
- printResult(vespa.Get(args[0], documentService()), true)
+ printResult(vespa.Get(args[0], documentService(), curlOutput()), true)
},
}
func documentService() *vespa.Service { return getService("document", 0) }
+func curlOutput() io.Writer {
+ if printCurl {
+ return stderr
+ }
+ return ioutil.Discard
+}
+
func printResult(result util.OperationResult, payloadOnlyOnSuccess bool) {
if !result.Success {
log.Print(color.Red("Error: "), result.Message)
diff --git a/client/go/cmd/document_test.go b/client/go/cmd/document_test.go
index c298d5ef285..8aecb538f89 100644
--- a/client/go/cmd/document_test.go
+++ b/client/go/cmd/document_test.go
@@ -1,4 +1,4 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
// document command tests
// Author: bratseth
@@ -19,6 +19,11 @@ func TestDocumentSendPut(t *testing.T) {
"put", "POST", "id:mynamespace:music::a-head-full-of-dreams", "testdata/A-Head-Full-of-Dreams-Put.json", t)
}
+func TestDocumentSendPutVerbose(t *testing.T) {
+ assertDocumentSend([]string{"document", "-v", "testdata/A-Head-Full-of-Dreams-Put.json"},
+ "put", "POST", "id:mynamespace:music::a-head-full-of-dreams", "testdata/A-Head-Full-of-Dreams-Put.json", t)
+}
+
func TestDocumentSendUpdate(t *testing.T) {
assertDocumentSend([]string{"document", "testdata/A-Head-Full-of-Dreams-Update.json"},
"update", "PUT", "id:mynamespace:music::a-head-full-of-dreams", "testdata/A-Head-Full-of-Dreams-Update.json", t)
@@ -93,11 +98,22 @@ func TestDocumentGet(t *testing.T) {
func assertDocumentSend(arguments []string, expectedOperation string, expectedMethod string, expectedDocumentId string, expectedPayloadFile string, t *testing.T) {
client := &mockHttpClient{}
documentURL := documentServiceURL(client)
- assert.Equal(t,
- "Success: "+expectedOperation+" "+expectedDocumentId+"\n",
- executeCommand(t, client, arguments, []string{}))
expectedPath, _ := vespa.IdToURLPath(expectedDocumentId)
- assert.Equal(t, documentURL+"/document/v1/"+expectedPath, client.lastRequest.URL.String())
+ expectedURL := documentURL + "/document/v1/" + expectedPath
+ out, errOut := execute(command{args: arguments}, t, client)
+
+ verbose := false
+ for _, a := range arguments {
+ if a == "-v" {
+ verbose = true
+ }
+ }
+ if verbose {
+ expectedCurl := "curl -X " + expectedMethod + " -H 'Content-Type: application/json' --data-binary @" + expectedPayloadFile + " " + expectedURL + "\n"
+ assert.Equal(t, expectedCurl, errOut)
+ }
+ assert.Equal(t, "Success: "+expectedOperation+" "+expectedDocumentId+"\n", out)
+ assert.Equal(t, expectedURL, client.lastRequest.URL.String())
assert.Equal(t, "application/json", client.lastRequest.Header.Get("Content-Type"))
assert.Equal(t, expectedMethod, client.lastRequest.Method)
diff --git a/client/go/cmd/helpers.go b/client/go/cmd/helpers.go
index 3493a4b32a8..f29a842aed2 100644
--- a/client/go/cmd/helpers.go
+++ b/client/go/cmd/helpers.go
@@ -1,4 +1,4 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
// Helpers used by multiple sub-commands.
// Author: mpolden
@@ -147,7 +147,16 @@ func getTarget() vespa.Target {
if err != nil {
fatalErrHint(err, "Deployment to cloud requires a certificate. Try 'vespa cert'")
}
- return vespa.CloudTarget(deployment, kp, apiKey, vespa.LogOptions{Writer: stdout, Level: vespa.LogLevel(logLevelArg)})
+ return vespa.CloudTarget(deployment, apiKey,
+ vespa.TLSOptions{
+ KeyPair: kp,
+ CertificateFile: certificateFile,
+ PrivateKeyFile: privateKeyFile,
+ },
+ vespa.LogOptions{
+ Writer: stdout,
+ Level: vespa.LogLevel(logLevelArg),
+ })
}
fatalErrHint(fmt.Errorf("Invalid target: %s", targetType), "Valid targets are 'local', 'cloud' or an URL")
return nil
diff --git a/client/go/cmd/man_test.go b/client/go/cmd/man_test.go
index 59efc64b8de..f7c33c8b3a1 100644
--- a/client/go/cmd/man_test.go
+++ b/client/go/cmd/man_test.go
@@ -11,7 +11,7 @@ import (
func TestMan(t *testing.T) {
tmpDir := t.TempDir()
- out := execute(command{args: []string{"man", tmpDir}}, t, nil)
+ out, _ := execute(command{args: []string{"man", tmpDir}}, t, nil)
assert.Equal(t, fmt.Sprintf("Success: Man pages written to %s\n", tmpDir), out)
assert.True(t, util.PathExists(filepath.Join(tmpDir, "vespa.1")))
}
diff --git a/client/go/cmd/root.go b/client/go/cmd/root.go
index 6bfca7fd613..cd8427c3ac6 100644
--- a/client/go/cmd/root.go
+++ b/client/go/cmd/root.go
@@ -1,4 +1,4 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
// Root Cobra command: vespa
// author: bratseth
@@ -17,7 +17,6 @@ import (
var (
// TODO: add timeout flag
- // TODO: add flag to show http request made
rootCmd = &cobra.Command{
Use: "vespa command-name",
Short: "The command-line tool for Vespa.ai",
@@ -40,6 +39,7 @@ Vespa documentation: https://docs.vespa.ai`,
color = aurora.NewAurora(false)
stdout = colorable.NewColorableStdout()
+ stderr = colorable.NewColorableStderr()
)
const (
diff --git a/client/go/cmd/version_test.go b/client/go/cmd/version_test.go
index 3b0f73de408..9eeaaaa4692 100644
--- a/client/go/cmd/version_test.go
+++ b/client/go/cmd/version_test.go
@@ -14,7 +14,7 @@ func TestVersion(t *testing.T) {
util.ActiveHttpClient = c
sp = &mockSubprocess{}
- out := execute(command{args: []string{"version"}}, t, nil)
+ out, _ := execute(command{args: []string{"version"}}, t, nil)
assert.Contains(t, out, "vespa version 0.0.0-devel compiled with")
assert.Contains(t, out, "New release available: 1.2.3\nhttps://github.com/vespa-engine/vespa/releases/tag/v1.2.3")
}
@@ -25,7 +25,7 @@ func TestVersionCheckHomebrew(t *testing.T) {
util.ActiveHttpClient = c
sp = &mockSubprocess{programPath: "/usr/local/bin/vespa", output: "/usr/local"}
- out := execute(command{args: []string{"version"}}, t, nil)
+ out, _ := execute(command{args: []string{"version"}}, t, nil)
assert.Contains(t, out, "vespa version 0.0.0-devel compiled with")
assert.Contains(t, out, "New release available: 1.2.3\n"+
"https://github.com/vespa-engine/vespa/releases/tag/v1.2.3\n"+
diff --git a/client/go/vespa/document.go b/client/go/vespa/document.go
index 7b750b86728..cfac1930199 100644
--- a/client/go/vespa/document.go
+++ b/client/go/vespa/document.go
@@ -1,4 +1,4 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
// vespa document API client
// Author: bratseth
@@ -7,34 +7,36 @@ package vespa
import (
"bytes"
"encoding/json"
+ "io"
"io/ioutil"
"net/http"
"net/url"
"os"
"time"
+ "github.com/vespa-engine/vespa/client/go/curl"
"github.com/vespa-engine/vespa/client/go/util"
)
// Sends the operation given in the file
-func Send(jsonFile string, service *Service) util.OperationResult {
- return sendOperation("", jsonFile, service, anyOperation)
+func Send(jsonFile string, service *Service, curlOutput io.Writer) util.OperationResult {
+ return sendOperation("", jsonFile, service, anyOperation, curlOutput)
}
-func Put(documentId string, jsonFile string, service *Service) util.OperationResult {
- return sendOperation(documentId, jsonFile, service, putOperation)
+func Put(documentId string, jsonFile string, service *Service, curlOutput io.Writer) util.OperationResult {
+ return sendOperation(documentId, jsonFile, service, putOperation, curlOutput)
}
-func Update(documentId string, jsonFile string, service *Service) util.OperationResult {
- return sendOperation(documentId, jsonFile, service, updateOperation)
+func Update(documentId string, jsonFile string, service *Service, curlOutput io.Writer) util.OperationResult {
+ return sendOperation(documentId, jsonFile, service, updateOperation, curlOutput)
}
-func RemoveId(documentId string, service *Service) util.OperationResult {
- return sendOperation(documentId, "", service, removeOperation)
+func RemoveId(documentId string, service *Service, curlOutput io.Writer) util.OperationResult {
+ return sendOperation(documentId, "", service, removeOperation, curlOutput)
}
-func RemoveOperation(jsonFile string, service *Service) util.OperationResult {
- return sendOperation("", jsonFile, service, removeOperation)
+func RemoveOperation(jsonFile string, service *Service, curlOutput io.Writer) util.OperationResult {
+ return sendOperation("", jsonFile, service, removeOperation, curlOutput)
}
const (
@@ -44,7 +46,7 @@ const (
removeOperation string = "remove"
)
-func sendOperation(documentId string, jsonFile string, service *Service, operation string) util.OperationResult {
+func sendOperation(documentId string, jsonFile string, service *Service, operation string, curlOutput io.Writer) util.OperationResult {
header := http.Header{}
header.Add("Content-Type", "application/json")
@@ -93,7 +95,7 @@ func sendOperation(documentId string, jsonFile string, service *Service, operati
Header: header,
Body: ioutil.NopCloser(bytes.NewReader(documentData)),
}
- response, err := service.Do(request, time.Second*60)
+ response, err := serviceDo(service, request, jsonFile, curlOutput)
if response == nil {
return util.Failure("Request failed: " + err.Error())
}
@@ -132,7 +134,28 @@ func operationToHTTPMethod(operation string) string {
panic("Unexpected document operation ''" + operation + "'")
}
-func Get(documentId string, service *Service) util.OperationResult {
+func serviceDo(service *Service, request *http.Request, filename string, curlOutput io.Writer) (*http.Response, error) {
+ cmd, err := curl.RawArgs(request.URL.String())
+ if err != nil {
+ return nil, err
+ }
+ cmd.Method = request.Method
+ for k, vs := range request.Header {
+ for _, v := range vs {
+ cmd.Header(k, v)
+ }
+ }
+ cmd.BodyFile = filename
+ cmd.Certificate = service.TLSOptions.CertificateFile
+ cmd.PrivateKey = service.TLSOptions.PrivateKeyFile
+ out := cmd.String() + "\n"
+ if _, err := io.WriteString(curlOutput, out); err != nil {
+ return nil, err
+ }
+ return service.Do(request, time.Second*60)
+}
+
+func Get(documentId string, service *Service, curlOutput io.Writer) util.OperationResult {
documentPath, documentPathError := IdToURLPath(documentId)
if documentPathError != nil {
return util.Failure("Invalid document id '" + documentId + "': " + documentPathError.Error())
@@ -147,7 +170,7 @@ func Get(documentId string, service *Service) util.OperationResult {
URL: url,
Method: "GET",
}
- response, err := service.Do(request, time.Second*60)
+ response, err := serviceDo(service, request, "", curlOutput)
if response == nil {
return util.Failure("Request failed: " + err.Error())
}
diff --git a/client/go/vespa/target.go b/client/go/vespa/target.go
index 065471d2a1e..69dc876c1c8 100644
--- a/client/go/vespa/target.go
+++ b/client/go/vespa/target.go
@@ -31,9 +31,9 @@ const (
// Service represents a Vespa service.
type Service struct {
- BaseURL string
- Name string
- certificate tls.Certificate
+ BaseURL string
+ Name string
+ TLSOptions TLSOptions
}
// Target represents a Vespa platform, running named Vespa services.
@@ -48,6 +48,13 @@ type Target interface {
DiscoverServices(timeout time.Duration, runID int64) error
}
+// TLSOptions configures the certificate to use for service requests.
+type TLSOptions struct {
+ KeyPair tls.Certificate
+ CertificateFile string
+ PrivateKeyFile string
+}
+
// LogOptions configures the log output to produce when waiting for services.
type LogOptions struct {
Writer io.Writer
@@ -61,8 +68,8 @@ type customTarget struct {
// Do sends request to this service. Any required authentication happens automatically.
func (s *Service) Do(request *http.Request, timeout time.Duration) (*http.Response, error) {
- if s.certificate.Certificate != nil {
- util.ActiveHttpClient.UseCertificate(s.certificate)
+ if s.TLSOptions.KeyPair.Certificate != nil {
+ util.ActiveHttpClient.UseCertificate(s.TLSOptions.KeyPair)
}
return util.HttpDo(request, timeout, s.Description())
}
@@ -83,7 +90,7 @@ func (s *Service) Wait(timeout time.Duration) (int, error) {
return 0, err
}
okFunc := func(status int, response []byte) (bool, error) { return status/100 == 2, nil }
- return wait(okFunc, func() *http.Request { return req }, &s.certificate, timeout)
+ return wait(okFunc, func() *http.Request { return req }, &s.TLSOptions.KeyPair, timeout)
}
func (s *Service) Description() string {
@@ -167,8 +174,8 @@ type cloudTarget struct {
cloudAPI string
targetType string
deployment Deployment
- keyPair tls.Certificate
apiKey []byte
+ tlsOptions TLSOptions
logOptions LogOptions
queryURL string
@@ -185,12 +192,12 @@ func (t *cloudTarget) Service(name string) (*Service, error) {
if t.queryURL == "" {
return nil, fmt.Errorf("service %s not discovered", name)
}
- return &Service{Name: name, BaseURL: t.queryURL, certificate: t.keyPair}, nil
+ return &Service{Name: name, BaseURL: t.queryURL, TLSOptions: t.tlsOptions}, nil
case documentService:
if t.documentURL == "" {
return nil, fmt.Errorf("service %s not discovered", name)
}
- return &Service{Name: name, BaseURL: t.documentURL, certificate: t.keyPair}, nil
+ return &Service{Name: name, BaseURL: t.documentURL, TLSOptions: t.tlsOptions}, nil
}
return nil, fmt.Errorf("unknown service: %s", name)
}
@@ -245,7 +252,7 @@ func (t *cloudTarget) waitForRun(signer *RequestSigner, runID int64, timeout tim
}
return true, nil
}
- _, err = wait(jobSuccessFunc, requestFunc, &t.keyPair, timeout)
+ _, err = wait(jobSuccessFunc, requestFunc, &t.tlsOptions.KeyPair, timeout)
return err
}
@@ -298,7 +305,7 @@ func (t *cloudTarget) discoverEndpoints(signer *RequestSigner, timeout time.Dura
endpointURL = resp.Endpoints[0].URL
return true, nil
}
- if _, err = wait(endpointFunc, func() *http.Request { return req }, &t.keyPair, timeout); err != nil {
+ if _, err = wait(endpointFunc, func() *http.Request { return req }, &t.tlsOptions.KeyPair, timeout); err != nil {
return err
}
if endpointURL == "" {
@@ -320,13 +327,13 @@ func CustomTarget(baseURL string) Target {
}
// CloudTarget creates a Target for the Vespa Cloud platform.
-func CloudTarget(deployment Deployment, keyPair tls.Certificate, apiKey []byte, logOptions LogOptions) Target {
+func CloudTarget(deployment Deployment, apiKey []byte, tlsOptions TLSOptions, logOptions LogOptions) Target {
return &cloudTarget{
cloudAPI: defaultCloudAPI,
targetType: cloudTargetType,
deployment: deployment,
- keyPair: keyPair,
apiKey: apiKey,
+ tlsOptions: tlsOptions,
logOptions: logOptions,
}
}
diff --git a/client/go/vespa/target_test.go b/client/go/vespa/target_test.go
index b524f73c5d3..31f145f0db3 100644
--- a/client/go/vespa/target_test.go
+++ b/client/go/vespa/target_test.go
@@ -106,8 +106,8 @@ func TestCloudTargetWait(t *testing.T) {
Application: ApplicationID{Tenant: "t1", Application: "a1", Instance: "i1"},
Zone: ZoneID{Environment: "dev", Region: "us-north-1"},
},
- x509KeyPair,
apiKey,
+ TLSOptions{KeyPair: x509KeyPair},
LogOptions{Writer: &logWriter})
if ct, ok := target.(*cloudTarget); ok {
ct.cloudAPI = srv.URL