summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJon Bratseth <bratseth@oath.com>2021-08-23 14:24:11 +0200
committerGitHub <noreply@github.com>2021-08-23 14:24:11 +0200
commit46e5396bf12a33a3721bc7f02f23988c457f6d13 (patch)
treee6b4aaf89909ca11dc11e436603a50d118416392
parentb6b0d23cd33fad88862f21fe3d010842cc28c2c9 (diff)
parent1f5a381d66339ec454530c6f45d1b03f51bb91ab (diff)
Merge pull request #18812 from vespa-engine/bratseth/cli-2
Bratseth/cli 2
-rw-r--r--client/go/src/cmd/deploy.go61
-rw-r--r--client/go/src/cmd/deploy_test.go11
-rw-r--r--client/go/src/cmd/document.go6
-rw-r--r--client/go/src/cmd/init.go4
-rw-r--r--client/go/src/cmd/query.go42
-rw-r--r--client/go/src/cmd/query_test.go25
6 files changed, 95 insertions, 54 deletions
diff --git a/client/go/src/cmd/deploy.go b/client/go/src/cmd/deploy.go
index 4caeef2b859..9e6940179da 100644
--- a/client/go/src/cmd/deploy.go
+++ b/client/go/src/cmd/deploy.go
@@ -21,29 +21,64 @@ import (
func init() {
rootCmd.AddCommand(deployCmd)
+ deployCmd.AddCommand(deployPrepareCmd)
+ deployCmd.AddCommand(deployActivateCmd)
}
var deployCmd = &cobra.Command{
- Use: "deploy application-package-dir OR application.zip",
+ Use: "deploy",
Short: "Deploys an application package",
- Long: `TODO`,
+ Long: `TODO: Use prepare or deploy activate`,
+ Run: func(cmd *cobra.Command, args []string) {
+ utils.Error("Use either deploy prepare or deploy activate")
+ },
+}
+
+var deployPrepareCmd = &cobra.Command{
+ Use: "prepare",
+ Short: "Prepares an application for activation",
+ Long: `TODO: prepare application-package-dir OR application.zip`,
Args: func(cmd *cobra.Command, args []string) error {
if len(args) > 1 {
- return errors.New("Expected an application as the only argument")
+ return errors.New("Expected an application package as the only argument")
}
return nil
},
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 {
- deploy("src/main/application")
+ deploy(true, "src/main/application")
} else {
- deploy(args[0])
+ deploy(true, args[0])
}
},
}
-func deploy(application string) {
- if ! strings.HasSuffix(application, ".zip") {
+var deployActivateCmd = &cobra.Command{
+ Use: "activate",
+ Short: "Activates an application package. If no package argument, the previously prepared package is activated.",
+ Long: `TODO: activate [application-package-dir OR application.zip]`,
+ Args: func(cmd *cobra.Command, args []string) error {
+ if len(args) > 1 {
+ return errors.New("Expected an application package as the only argument")
+ }
+ return nil
+ },
+ Run: func(cmd *cobra.Command, args []string) {
+ if len(args) == 0 {
+ deploy(false, "")
+ } else {
+ deploy(false, args[0])
+ }
+ },
+}
+
+func deploy(prepare bool, application string) {
+ // TODO: Support no application (activate)
+ // TODO: Support application home as argument instead of src/main and
+ // - if target exists, use target/application.zip
+ // - else if src/main/application exists, use that
+ // - else if current dir has services.xml use that
+ if filepath.Ext(application) != ".zip" {
tempZip, error := ioutil.TempFile("", "application.zip")
if error != nil {
utils.Error("Could not create a temporary zip file for the application package")
@@ -67,11 +102,19 @@ func deploy(application string) {
return
}
- url, _ := url.Parse(getTarget(deployContext).deploy + "/application/v2/tenant/default/prepareandactivate")
+ var deployUrl *url.URL
+ if prepare {
+ deployUrl, _ = url.Parse(getTarget(deployContext).deploy + "/application/v2/tenant/default/prepare")
+ } else if application == "" {
+ deployUrl, _ = url.Parse(getTarget(deployContext).deploy + "/application/v2/tenant/default/activate")
+ } else {
+ deployUrl, _ = url.Parse(getTarget(deployContext).deploy + "/application/v2/tenant/default/prepareandactivate")
+ }
+
header := http.Header{}
header.Add("Content-Type", "application/zip")
request := &http.Request{
- URL: url,
+ URL: deployUrl,
Method: "POST",
Header: header,
Body: ioutil.NopCloser(zipFileReader),
diff --git a/client/go/src/cmd/deploy_test.go b/client/go/src/cmd/deploy_test.go
index d7f1353dd23..99cfedebc8f 100644
--- a/client/go/src/cmd/deploy_test.go
+++ b/client/go/src/cmd/deploy_test.go
@@ -13,7 +13,7 @@ func TestDeployZip(t *testing.T) {
client := &mockHttpClient{}
assert.Equal(t,
"\x1b[32mSuccess\n",
- executeCommand(t, client, []string{"deploy", "testdata/application.zip"}, []string{}))
+ executeCommand(t, client, []string{"deploy", "activate", "testdata/application.zip"}, []string{}))
assertDeployRequestMade("http://127.0.0.1:19071", client, t)
}
@@ -21,7 +21,7 @@ func TestDeployZipWithURLTargetArgument(t *testing.T) {
client := &mockHttpClient{}
assert.Equal(t,
"\x1b[32mSuccess\n",
- executeCommand(t, client, []string{"deploy", "testdata/application.zip", "-t", "http://target:19071"}, []string{}))
+ executeCommand(t, client, []string{"deploy", "activate", "testdata/application.zip", "-t", "http://target:19071"}, []string{}))
assertDeployRequestMade("http://target:19071", client, t)
}
@@ -29,7 +29,7 @@ func TestDeployZipWitLocalTargetArgument(t *testing.T) {
client := &mockHttpClient{}
assert.Equal(t,
"\x1b[32mSuccess\n",
- executeCommand(t, client, []string{"deploy", "testdata/application.zip", "-t", "local"}, []string{}))
+ executeCommand(t, client, []string{"deploy", "activate", "testdata/application.zip", "-t", "local"}, []string{}))
assertDeployRequestMade("http://127.0.0.1:19071", client, t)
}
@@ -37,10 +37,13 @@ func TestDeployDirectory(t *testing.T) {
client := &mockHttpClient{}
assert.Equal(t,
"\x1b[32mSuccess\n",
- executeCommand(t, client, []string{"deploy", "testdata/src/main/application"}, []string{}))
+ executeCommand(t, client, []string{"deploy", "activate", "testdata/src/main/application"}, []string{}))
assertDeployRequestMade("http://127.0.0.1:19071", client, t)
}
+// TODO: Test error replies (5xx and 4xx with error message)
+// TODO: Test prepare and activate prepared
+
func assertDeployRequestMade(target string, client *mockHttpClient, t *testing.T) {
assert.Equal(t, target + "/application/v2/tenant/default/prepareandactivate", client.lastRequest.URL.String())
assert.Equal(t, "application/zip", client.lastRequest.Header.Get("Content-Type"))
diff --git a/client/go/src/cmd/document.go b/client/go/src/cmd/document.go
index 86471a6116f..4e54e3fcb33 100644
--- a/client/go/src/cmd/document.go
+++ b/client/go/src/cmd/document.go
@@ -22,9 +22,9 @@ func init() {
}
var documentCmd = &cobra.Command{
- Use: "document mynamespace/mydocumenttype/myid document.json",
+ Use: "document",
Short: "Issue document operations (put by default)",
- Long: `TODO`,
+ Long: `TODO: Example mynamespace/mydocumenttype/myid document.json`,
// TODO: Check args
Run: func(cmd *cobra.Command, args []string) {
put(args[0], args[1])
@@ -84,7 +84,7 @@ func put(documentId string, jsonFile string) {
} else if response.StatusCode == 200 {
utils.Success("Success") // TODO: Change to something including document id
} else if response.StatusCode % 100 == 4 {
- utils.Error("Invalid document JSON")
+ utils.Error("Invalid document")
utils.Detail(response.Status)
// TODO: Output error in body
} else {
diff --git a/client/go/src/cmd/init.go b/client/go/src/cmd/init.go
index f5b20115794..7a5a7729246 100644
--- a/client/go/src/cmd/init.go
+++ b/client/go/src/cmd/init.go
@@ -29,9 +29,9 @@ func init() {
var initCmd = &cobra.Command{
// TODO: "application" and "list" subcommands?
- Use: "init applicationName source",
+ Use: "init",
Short: "Creates the files and directory structure for a new Vespa application",
- Long: `TODO`,
+ Long: `TODO: vespa init applicationName source`,
Args: func(cmd *cobra.Command, args []string) error {
if len(args) != 2 {
return errors.New("vespa init requires a project name and source")
diff --git a/client/go/src/cmd/query.go b/client/go/src/cmd/query.go
index 2e33974a6b5..bf678b19f4d 100644
--- a/client/go/src/cmd/query.go
+++ b/client/go/src/cmd/query.go
@@ -9,9 +9,10 @@ import (
"errors"
"github.com/spf13/cobra"
"github.com/vespa-engine/vespa/utils"
- "regexp"
"strings"
+ "net/http"
"net/url"
+ "time"
)
func init() {
@@ -19,9 +20,10 @@ func init() {
}
var queryCmd = &cobra.Command{
- Use: "query \"yql=select from sources * where title contains 'foo'\" hits=5",
+ Use: "query",
Short: "Issue a query to Vespa",
- Long: `TODO`,
+ Long: `TODO, example \"yql=select from sources * where title contains 'foo'\" hits=5`,
+ // TODO: Support referencing a query json file
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return errors.New("vespa query requires at least one argument containing the query string")
@@ -29,28 +31,25 @@ var queryCmd = &cobra.Command{
return nil
},
Run: func(cmd *cobra.Command, args []string) {
- query(args[0])
+ query(args)
},
}
-func query(argument string) {
- if ! startsByParameter(argument) { // Default parameter
- argument = "yql=" + argument
+func query(arguments []string) {
+ url, _ := url.Parse(getTarget(queryContext).query + "/search/")
+ urlQuery := url.Query()
+ for i := 0; i < len(arguments); i++ {
+ key, value := splitArg(arguments[i])
+ urlQuery.Set(key, value)
}
+ url.RawQuery = urlQuery.Encode()
- argument = escapePayload(argument)
- if argument == "" {
- return
- }
-
- path := "/search/?" + argument
- response := utils.HttpGet(getTarget(queryContext).query, path, "Container")
+ response := utils.HttpDo(&http.Request{URL: url,}, time.Second * 10, "Container")
if (response == nil) {
return
}
defer response.Body.Close()
- defer response.Body.Close()
if (response.StatusCode == 200) {
// TODO: Pretty-print body
scanner := bufio.NewScanner(response.Body)
@@ -69,16 +68,11 @@ func query(argument string) {
}
}
-func startsByParameter(argument string) bool {
- match, _ := regexp.MatchString("[a-zA-Z0-9_]+=", "peach") // TODO: Allow dot in parameters
- return match
-}
-
-func escapePayload(argument string) string {
+func splitArg(argument string) (string, string) {
equalsIndex := strings.Index(argument, "=")
if equalsIndex < 1 {
- utils.Error("A query argument must be on the form parameter=value, but was '" + argument + "'")
- return ""
+ return "yql", argument
+ } else {
+ return argument[0:equalsIndex], argument[equalsIndex + 1:len(argument)]
}
- return argument[0:equalsIndex] + "=" + url.QueryEscape(argument[equalsIndex + 1:len(argument)])
}
diff --git a/client/go/src/cmd/query_test.go b/client/go/src/cmd/query_test.go
index 2783bf34484..e062e9c718a 100644
--- a/client/go/src/cmd/query_test.go
+++ b/client/go/src/cmd/query_test.go
@@ -10,27 +10,28 @@ import (
)
func TestQuery(t *testing.T) {
- assertQuery("?yql=select+from+sources+%2A+where+title+contains+%27foo%27",
- "select from sources * where title contains 'foo'", t)
+ assertQuery(t,
+ "?yql=select+from+sources+%2A+where+title+contains+%27foo%27",
+ "select from sources * where title contains 'foo'")
}
-func IgnoreTestQueryWithParameters(t *testing.T) {
- assertQuery("?", "select from sources * where title contains 'foo'&hits=5", t)
+func TestQueryWithMultipleParameters(t *testing.T) {
+ assertQuery(t,
+ "?hits=5&yql=select+from+sources+%2A+where+title+contains+%27foo%27",
+ "select from sources * where title contains 'foo'", "hits=5")
}
-func IgnoreTestSimpleQueryMissingQuestionMark(t *testing.T) {
- assertQuery("?", "query=select from sources * where title contains 'foo'", t)
+func TestQueryWithExplicitYqlParameter(t *testing.T) {
+ assertQuery(t,
+ "?yql=select+from+sources+%2A+where+title+contains+%27foo%27",
+ "yql=select from sources * where title contains 'foo'")
}
-func IgnoreTestSimpleQueryMissingQuestionMarkAndQueryEquals(t *testing.T) {
- assertQuery("?query=", "select from sources * where text contains 'foo'", t)
-}
-
-func assertQuery(expectedQuery string, query string, t *testing.T) {
+func assertQuery(t *testing.T, expectedQuery string, query ...string) {
client := &mockHttpClient{ nextBody: "query result", }
assert.Equal(t,
"query result\n",
- executeCommand(t, client, []string{"query", query},[]string{}),
+ executeCommand(t, client, []string{"query"}, query),
"query output")
assert.Equal(t, getTarget(queryContext).query + "/search/" + expectedQuery, client.lastRequest.URL.String())
}