diff options
author | Jon Bratseth <bratseth@oath.com> | 2021-08-23 14:24:11 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-08-23 14:24:11 +0200 |
commit | 46e5396bf12a33a3721bc7f02f23988c457f6d13 (patch) | |
tree | e6b4aaf89909ca11dc11e436603a50d118416392 | |
parent | b6b0d23cd33fad88862f21fe3d010842cc28c2c9 (diff) | |
parent | 1f5a381d66339ec454530c6f45d1b03f51bb91ab (diff) |
Merge pull request #18812 from vespa-engine/bratseth/cli-2
Bratseth/cli 2
-rw-r--r-- | client/go/src/cmd/deploy.go | 61 | ||||
-rw-r--r-- | client/go/src/cmd/deploy_test.go | 11 | ||||
-rw-r--r-- | client/go/src/cmd/document.go | 6 | ||||
-rw-r--r-- | client/go/src/cmd/init.go | 4 | ||||
-rw-r--r-- | client/go/src/cmd/query.go | 42 | ||||
-rw-r--r-- | client/go/src/cmd/query_test.go | 25 |
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()) } |