summaryrefslogtreecommitdiffstats
path: root/client
diff options
context:
space:
mode:
authorMartin Polden <mpolden@mpolden.no>2021-08-26 11:46:20 +0200
committerMartin Polden <mpolden@mpolden.no>2021-08-26 11:46:20 +0200
commitfee0f6489a6aa734e008b10ef275e464e66bfc17 (patch)
tree80bc9272a7b09d22f762c392130608c872246126 /client
parent58224ccdae4531dac1b0634d27cf894a6a61a779 (diff)
Cleanup printing and colorization
Diffstat (limited to 'client')
-rw-r--r--client/go/cmd/activate.go3
-rw-r--r--client/go/cmd/cert.go31
-rw-r--r--client/go/cmd/command_tester.go10
-rw-r--r--client/go/cmd/config.go6
-rw-r--r--client/go/cmd/deploy.go21
-rw-r--r--client/go/cmd/deploy_test.go10
-rw-r--r--client/go/cmd/document.go25
-rw-r--r--client/go/cmd/document_test.go10
-rw-r--r--client/go/cmd/init.go45
-rw-r--r--client/go/cmd/init_test.go2
-rw-r--r--client/go/cmd/prepare.go7
-rw-r--r--client/go/cmd/query.go21
-rw-r--r--client/go/cmd/query_test.go4
-rw-r--r--client/go/cmd/root.go20
-rw-r--r--client/go/cmd/status.go13
-rw-r--r--client/go/cmd/status_test.go10
-rw-r--r--client/go/cmd/target.go4
-rw-r--r--client/go/go.mod3
-rw-r--r--client/go/go.sum4
-rw-r--r--client/go/util/http.go21
-rw-r--r--client/go/util/http_test.go9
-rw-r--r--client/go/util/io.go18
-rw-r--r--client/go/util/print.go78
-rw-r--r--client/go/vespa/deploy.go38
24 files changed, 192 insertions, 221 deletions
diff --git a/client/go/cmd/activate.go b/client/go/cmd/activate.go
index bb75fff2a49..b7b3fa22a05 100644
--- a/client/go/cmd/activate.go
+++ b/client/go/cmd/activate.go
@@ -6,7 +6,6 @@ package cmd
import (
"github.com/spf13/cobra"
- "github.com/vespa-engine/vespa/vespa"
)
func init() {
@@ -20,6 +19,6 @@ var activateCmd = &cobra.Command{
Short: "Activates (deploys) the previously prepared application package",
Long: `TODO`,
Run: func(cmd *cobra.Command, args []string) {
- vespa.Deploy(true, "", deployTarget())
+ deploy(true, nil)
},
}
diff --git a/client/go/cmd/cert.go b/client/go/cmd/cert.go
index bb1480ddb37..88d489fd229 100644
--- a/client/go/cmd/cert.go
+++ b/client/go/cmd/cert.go
@@ -4,12 +4,11 @@
package cmd
import (
- "fmt"
+ "log"
"os"
"path/filepath"
"github.com/spf13/cobra"
- "github.com/vespa-engine/vespa/util"
"github.com/vespa-engine/vespa/vespa"
)
@@ -33,13 +32,13 @@ var certCmd = &cobra.Command{
} else {
var err error
path, err = os.Getwd()
- util.FatalIfErr(err)
+ fatalIfErr(err)
}
pkg, err := vespa.FindApplicationPackage(path)
- util.FatalIfErr(err)
+ fatalIfErr(err)
if pkg.HasCertificate() && !overwriteCertificate {
- util.Print("Certificate already exists")
+ log.Print("Certificate already exists. Use -f option to recreate")
return
}
@@ -51,20 +50,26 @@ var certCmd = &cobra.Command{
pkgCertificateFile := filepath.Join(securityDir, "clients.pem")
keyPair, err := vespa.CreateKeyPair()
- util.FatalIfErr(err)
+ fatalIfErr(err)
err = os.MkdirAll(securityDir, 0755)
- util.FatalIfErr(err)
+ fatalIfErr(err)
err = keyPair.WriteCertificateFile(pkgCertificateFile, overwriteCertificate)
- util.FatalIfErr(err)
+ fatalIfErr(err)
err = keyPair.WriteCertificateFile(certificateFile, overwriteCertificate)
- util.FatalIfErr(err)
+ fatalIfErr(err)
err = keyPair.WritePrivateKeyFile(privateKeyFile, overwriteCertificate)
- util.FatalIfErr(err)
+ fatalIfErr(err)
// TODO: Just use log package, which has Printf
- util.Print(fmt.Sprintf("Certificate written to %s", pkgCertificateFile))
- util.Print(fmt.Sprintf("Certificate written to %s", certificateFile))
- util.Print(fmt.Sprintf("Private key written to %s", privateKeyFile))
+ log.Printf("Certificate written to %s", color.Green(pkgCertificateFile))
+ log.Printf("Certificate written to %s", color.Green(certificateFile))
+ log.Printf("Private key written to %s", color.Green(privateKeyFile))
},
}
+
+func fatalIfErr(err error) {
+ if err != nil {
+ log.Fatal(err)
+ }
+}
diff --git a/client/go/cmd/command_tester.go b/client/go/cmd/command_tester.go
index 50684d87a1b..6078167df40 100644
--- a/client/go/cmd/command_tester.go
+++ b/client/go/cmd/command_tester.go
@@ -6,25 +6,27 @@ package cmd
import (
"bytes"
- "github.com/stretchr/testify/assert"
- "github.com/vespa-engine/vespa/util"
"io/ioutil"
+ "log"
"net/http"
"strconv"
"testing"
"time"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/vespa-engine/vespa/util"
)
func executeCommand(t *testing.T, client *mockHttpClient, args []string, moreArgs []string) (standardout string) {
util.ActiveHttpClient = client
// Reset - persistent flags in Cobra persists over tests
- util.Out = bytes.NewBufferString("")
+ log.SetOutput(bytes.NewBufferString(""))
rootCmd.SetArgs([]string{"status", "-t", ""})
rootCmd.Execute()
b := bytes.NewBufferString("")
- util.Out = b
+ log.SetOutput(b)
rootCmd.SetArgs(append(args, moreArgs...))
rootCmd.Execute()
out, err := ioutil.ReadAll(b)
diff --git a/client/go/cmd/config.go b/client/go/cmd/config.go
index 9435a97d999..15f6e623a33 100644
--- a/client/go/cmd/config.go
+++ b/client/go/cmd/config.go
@@ -5,12 +5,12 @@
package cmd
import (
+ "log"
"os"
"path/filepath"
"github.com/spf13/cobra"
"github.com/spf13/viper"
- "github.com/vespa-engine/vespa/util"
)
func init() {
@@ -54,12 +54,12 @@ func writeConfig() {
_, statErr := os.Stat(configPath)
if !os.IsExist(statErr) {
if _, createErr := os.Create(configPath); createErr != nil {
- util.Error("Warning: Can not remember flag parameters: " + createErr.Error())
+ log.Printf("Warning: Can not remember flag parameters: %s", color.Red(createErr))
}
}
writeErr := viper.WriteConfig()
if writeErr != nil {
- util.Error("Could not write config:", writeErr.Error())
+ log.Printf("Could not write config: %s", color.Red(writeErr))
}
}
diff --git a/client/go/cmd/deploy.go b/client/go/cmd/deploy.go
index 6377abda00c..7a180deb03b 100644
--- a/client/go/cmd/deploy.go
+++ b/client/go/cmd/deploy.go
@@ -5,6 +5,8 @@
package cmd
import (
+ "log"
+
"github.com/spf13/cobra"
"github.com/vespa-engine/vespa/vespa"
)
@@ -18,10 +20,19 @@ var deployCmd = &cobra.Command{
Short: "Deploys (prepares and activates) an application package",
Long: `TODO`,
Run: func(cmd *cobra.Command, args []string) {
- if len(args) == 0 {
- vespa.Deploy(false, "", deployTarget())
- } else {
- vespa.Deploy(false, args[0], deployTarget())
- }
+ deploy(false, args)
},
}
+
+func deploy(prepare bool, args []string) {
+ var application string
+ if len(args) > 0 {
+ application = args[0]
+ }
+ path, err := vespa.Deploy(false, application, deployTarget())
+ if err != nil {
+ log.Print(color.Red(err))
+ } else {
+ log.Print("Deployed ", color.Green(path), " successfully")
+ }
+}
diff --git a/client/go/cmd/deploy_test.go b/client/go/cmd/deploy_test.go
index 4a40739ec32..4c031f964bd 100644
--- a/client/go/cmd/deploy_test.go
+++ b/client/go/cmd/deploy_test.go
@@ -22,7 +22,7 @@ func TestDeployZipWithURLTargetArgument(t *testing.T) {
client := &mockHttpClient{}
assert.Equal(t,
- "\x1b[32mDeployed "+applicationPackage+"\x1b[0m\n",
+ "Deployed "+applicationPackage+" successfully\n",
executeCommand(t, client, arguments, []string{}))
assertDeployRequestMade("http://target:19071", client, t)
}
@@ -50,7 +50,7 @@ func TestDeployApplicationDirectoryWithPomAndTarget(t *testing.T) {
func TestDeployApplicationDirectoryWithPomAndEmptyTarget(t *testing.T) {
client := &mockHttpClient{}
assert.Equal(t,
- "\x1b[31mpom.xml exists but no target/application.zip. Run mvn package first\x1b[0m\n",
+ "pom.xml exists but no target/application.zip. Run mvn package first\n",
executeCommand(t, client, []string{"deploy", "testdata/applications/withEmptyTarget"}, []string{}))
}
@@ -65,7 +65,7 @@ func TestDeployError(t *testing.T) {
func assertDeploy(applicationPackage string, arguments []string, t *testing.T) {
client := &mockHttpClient{}
assert.Equal(t,
- "\x1b[32mDeployed "+applicationPackage+"\x1b[0m\n",
+ "Deployed "+applicationPackage+" successfully\n",
executeCommand(t, client, arguments, []string{}))
assertDeployRequestMade("http://127.0.0.1:19071", client, t)
}
@@ -84,13 +84,13 @@ func assertDeployRequestMade(target string, client *mockHttpClient, t *testing.T
func assertApplicationPackageError(t *testing.T, status int, errorMessage string) {
client := &mockHttpClient{nextStatus: status, nextBody: errorMessage}
assert.Equal(t,
- "\x1b[31mInvalid application package (Status "+strconv.Itoa(status)+"):\x1b[0m\n"+errorMessage+"\n",
+ "Invalid application package (Status "+strconv.Itoa(status)+"):\n"+errorMessage+"\n",
executeCommand(t, client, []string{"deploy", "testdata/applications/withTarget/target/application.zip"}, []string{}))
}
func assertDeployServerError(t *testing.T, status int, errorMessage string) {
client := &mockHttpClient{nextStatus: status, nextBody: errorMessage}
assert.Equal(t,
- "\x1b[31mError from deploy service at 127.0.0.1:19071 (Status "+strconv.Itoa(status)+"):\x1b[0m\n"+errorMessage+"\n",
+ "Error from deploy service at 127.0.0.1:19071 (Status "+strconv.Itoa(status)+"):\n"+errorMessage+"\n",
executeCommand(t, client, []string{"deploy", "testdata/applications/withTarget/target/application.zip"}, []string{}))
}
diff --git a/client/go/cmd/document.go b/client/go/cmd/document.go
index 68e583d4c29..41f8c8f3320 100644
--- a/client/go/cmd/document.go
+++ b/client/go/cmd/document.go
@@ -7,14 +7,16 @@ package cmd
import (
"bytes"
"encoding/json"
- "github.com/spf13/cobra"
- "github.com/vespa-engine/vespa/util"
"io/ioutil"
+ "log"
"net/http"
"net/url"
"os"
"strings"
"time"
+
+ "github.com/spf13/cobra"
+ "github.com/vespa-engine/vespa/util"
)
func init() {
@@ -67,8 +69,7 @@ func post(documentId string, jsonFile string) {
fileReader, fileError := os.Open(jsonFile)
if fileError != nil {
- util.Error("Could not open file at " + jsonFile)
- util.Detail(fileError.Error())
+ log.Printf("Could not open file at %s: %s", color.Cyan(jsonFile), fileError)
return
}
@@ -82,7 +83,7 @@ func post(documentId string, jsonFile string) {
} else if doc["put"] != nil {
documentId = doc["put"].(string) // document feeder format
} else {
- util.Error("No document id given neither as argument or an 'id' key in the json file")
+ log.Print("No document id given neither as argument or an 'id' key in the json file")
return
}
}
@@ -96,19 +97,19 @@ func post(documentId string, jsonFile string) {
Body: ioutil.NopCloser(bytes.NewReader(documentData)),
}
serviceDescription := "Container (document API)"
- response := util.HttpDo(request, time.Second*60, serviceDescription)
+ response, err := util.HttpDo(request, time.Second*60, serviceDescription)
if response == nil {
- return
+ log.Print("Request failed: ", color.Red(err))
}
defer response.Body.Close()
if response.StatusCode == 200 {
- util.Success(documentId)
+ log.Print(color.Green(documentId))
} else if response.StatusCode/100 == 4 {
- util.Error("Invalid document (" + response.Status + "):")
- util.PrintReader(response.Body)
+ log.Printf("Invalid document (%s):", color.Red(response.Status))
+ log.Print(util.ReaderToJSON(response.Body))
} else {
- util.Error("Error from", strings.ToLower(serviceDescription), "at", request.URL.Host, "("+response.Status+"):")
- util.PrintReader(response.Body)
+ log.Printf("Error from %s at %s (%s):", color.Cyan(strings.ToLower(serviceDescription)), color.Cyan(request.URL.Host), color.Red(response.Status))
+ log.Print(util.ReaderToJSON(response.Body))
}
}
diff --git a/client/go/cmd/document_test.go b/client/go/cmd/document_test.go
index f1e4bc007cb..93532cdd69e 100644
--- a/client/go/cmd/document_test.go
+++ b/client/go/cmd/document_test.go
@@ -37,7 +37,7 @@ func TestDocumentIdNotSpecified(t *testing.T) {
arguments := []string{"document", "post", "testdata/A-Head-Full-of-Dreams.json"}
client := &mockHttpClient{}
assert.Equal(t,
- "\x1b[31mNo document id given neither as argument or an 'id' key in the json file\x1b[0m\n",
+ "No document id given neither as argument or an 'id' key in the json file\n",
executeCommand(t, client, arguments, []string{}))
}
@@ -52,7 +52,7 @@ func TestDocumentPostServerError(t *testing.T) {
func assertDocumentPost(arguments []string, documentId string, jsonFile string, t *testing.T) {
client := &mockHttpClient{}
assert.Equal(t,
- "\x1b[32m"+documentId+"\x1b[0m\n",
+ documentId+"\n",
executeCommand(t, client, arguments, []string{}))
target := getTarget(documentContext).document
assert.Equal(t, target+"/document/v1/"+documentId, client.lastRequest.URL.String())
@@ -66,7 +66,7 @@ func assertDocumentPost(arguments []string, documentId string, jsonFile string,
func assertDocumentPostShortForm(documentId string, jsonFile string, t *testing.T) {
client := &mockHttpClient{}
assert.Equal(t,
- "\x1b[32mSuccess\n",
+ "Success\n",
executeCommand(t, client, []string{"document", jsonFile}, []string{}))
target := getTarget(documentContext).document
assert.Equal(t, target+"/document/v1/"+documentId, client.lastRequest.URL.String())
@@ -75,7 +75,7 @@ func assertDocumentPostShortForm(documentId string, jsonFile string, t *testing.
func assertDocumentError(t *testing.T, status int, errorMessage string) {
client := &mockHttpClient{nextStatus: status, nextBody: errorMessage}
assert.Equal(t,
- "\x1b[31mInvalid document (Status "+strconv.Itoa(status)+"):\x1b[0m\n"+errorMessage+"\n",
+ "Invalid document (Status "+strconv.Itoa(status)+"):\n"+errorMessage+"\n",
executeCommand(t, client, []string{"document", "post",
"mynamespace/music/docid/1",
"testdata/A-Head-Full-of-Dreams.json"}, []string{}))
@@ -84,7 +84,7 @@ func assertDocumentError(t *testing.T, status int, errorMessage string) {
func assertDocumentServerError(t *testing.T, status int, errorMessage string) {
client := &mockHttpClient{nextStatus: status, nextBody: errorMessage}
assert.Equal(t,
- "\x1b[31mError from container (document api) at 127.0.0.1:8080 (Status "+strconv.Itoa(status)+"):\x1b[0m\n"+errorMessage+"\n",
+ "Error from container (document api) at 127.0.0.1:8080 (Status "+strconv.Itoa(status)+"):\n"+errorMessage+"\n",
executeCommand(t, client, []string{"document", "post",
"mynamespace/music/docid/1",
"testdata/A-Head-Full-of-Dreams.json"}, []string{}))
diff --git a/client/go/cmd/init.go b/client/go/cmd/init.go
index bae5aa1908b..8cbaf163336 100644
--- a/client/go/cmd/init.go
+++ b/client/go/cmd/init.go
@@ -7,16 +7,18 @@ package cmd
import (
"archive/zip"
"errors"
- "github.com/spf13/cobra"
- "github.com/vespa-engine/vespa/util"
"io"
"io/ioutil"
+ "log"
"net/http"
"net/url"
"os"
"path/filepath"
"strings"
"time"
+
+ "github.com/spf13/cobra"
+ "github.com/vespa-engine/vespa/util"
)
// Set this to test without downloading this file from github
@@ -54,15 +56,15 @@ func initApplication(name string, source string) {
createErr := os.Mkdir(name, 0755)
if createErr != nil {
- util.Error("Could not create directory '" + name + "'")
- util.Detail(createErr.Error())
+ log.Print("Could not create directory '", color.Cyan(name), "'")
+ log.Print(createErr)
return
}
zipReader, zipOpenError := zip.OpenReader(zipFile.Name())
if zipOpenError != nil {
- util.Error("Could not open sample apps zip '" + zipFile.Name() + "'")
- util.Detail(zipOpenError.Error())
+ log.Print("Could not open sample apps zip '", color.Cyan(zipFile.Name()), "'")
+ log.Print(zipOpenError)
}
defer zipReader.Close()
@@ -73,16 +75,16 @@ func initApplication(name string, source string) {
found = true
copyError := copy(f, name, zipEntryPrefix)
if copyError != nil {
- util.Error("Could not copy zip entry '" + f.Name + "' to " + name)
- util.Detail(copyError.Error())
+ log.Print("Could not copy zip entry '", color.Cyan(f.Name), "' to ", color.Cyan(name))
+ log.Print(copyError)
return
}
}
}
if !found {
- util.Error("Could not find source application '" + source + "'")
+ log.Print("Could not find source application '", color.Cyan(source), "'")
} else {
- util.Success("Created " + name)
+ log.Print("Created ", color.Green(name))
}
}
@@ -90,38 +92,41 @@ func getSampleAppsZip() *os.File {
if existingSampleAppsZip != "" {
existing, openExistingError := os.Open(existingSampleAppsZip)
if openExistingError != nil {
- util.Error("Could not open existing sample apps zip file '" + existingSampleAppsZip + "'")
- util.Detail(openExistingError.Error())
+ log.Print("Could not open existing sample apps zip file '", color.Cyan(existingSampleAppsZip), "'")
+ log.Print(openExistingError)
}
return existing
}
// TODO: Cache it?
- util.Detail("Downloading sample apps ...") // TODO: Spawn thread to indicate progress
+ log.Print("Downloading sample apps ...") // TODO: Spawn thread to indicate progress
zipUrl, _ := url.Parse("https://github.com/vespa-engine/sample-apps/archive/refs/heads/master.zip")
request := &http.Request{
URL: zipUrl,
Method: "GET",
}
- response := util.HttpDo(request, time.Minute*60, "GitHub")
+ response, reqErr := util.HttpDo(request, time.Minute*60, "GitHub")
+ if reqErr != nil {
+ log.Print("Request failed: ", color.Red(reqErr))
+ return nil
+ }
defer response.Body.Close()
if response.StatusCode != 200 {
- util.Error("Could not download sample apps from github")
- util.Detail(response.Status)
+ log.Printf("Could not download sample apps from github (%s)", color.Red(response.StatusCode))
return nil
}
destination, tempFileError := ioutil.TempFile("", "prefix")
if tempFileError != nil {
- util.Error("Could not create a temp file to hold sample apps")
- util.Detail(tempFileError.Error())
+ log.Print("Could not create a temp file to hold sample apps")
+ log.Print(tempFileError)
}
// destination, _ := os.Create("./" + name + "/sample-apps.zip")
// defer destination.Close()
_, err := io.Copy(destination, response.Body)
if err != nil {
- util.Error("Could not download sample apps from GitHub")
- util.Detail(err.Error())
+ log.Print("Could not download sample apps from GitHub")
+ log.Print(err)
return nil
}
return destination
diff --git a/client/go/cmd/init_test.go b/client/go/cmd/init_test.go
index d855acb08f3..8bca3c96fb7 100644
--- a/client/go/cmd/init_test.go
+++ b/client/go/cmd/init_test.go
@@ -21,7 +21,7 @@ func assertCreated(app string, sampleAppName string, t *testing.T) {
existingSampleAppsZip = "testdata/sample-apps-master.zip"
standardOut := executeCommand(t, &mockHttpClient{}, []string{"init", app, sampleAppName}, []string{})
defer os.RemoveAll(app)
- assert.Equal(t, "\x1b[32mCreated "+app+"\x1b[0m\n", standardOut)
+ assert.Equal(t, "Created "+app+"\n", standardOut)
assert.True(t, util.PathExists(filepath.Join(app, "README.md")))
assert.True(t, util.PathExists(filepath.Join(app, "src", "main", "application")))
assert.True(t, util.IsDirectory(filepath.Join(app, "src", "main", "application")))
diff --git a/client/go/cmd/prepare.go b/client/go/cmd/prepare.go
index 7dfa5f92c33..1790629ca2c 100644
--- a/client/go/cmd/prepare.go
+++ b/client/go/cmd/prepare.go
@@ -6,7 +6,6 @@ package cmd
import (
"github.com/spf13/cobra"
- "github.com/vespa-engine/vespa/vespa"
)
func init() {
@@ -20,10 +19,6 @@ var prepareCmd = &cobra.Command{
Short: "Prepares an application package for activation",
Long: `TODO`,
Run: func(cmd *cobra.Command, args []string) {
- if len(args) == 0 {
- vespa.Deploy(true, "", deployTarget())
- } else {
- vespa.Deploy(true, args[0], deployTarget())
- }
+ deploy(true, args)
},
}
diff --git a/client/go/cmd/query.go b/client/go/cmd/query.go
index 7a6a569eb70..dea7e70b19e 100644
--- a/client/go/cmd/query.go
+++ b/client/go/cmd/query.go
@@ -6,12 +6,14 @@ package cmd
import (
"errors"
- "github.com/spf13/cobra"
- "github.com/vespa-engine/vespa/util"
+ "log"
"net/http"
"net/url"
"strings"
"time"
+
+ "github.com/spf13/cobra"
+ "github.com/vespa-engine/vespa/util"
)
func init() {
@@ -43,20 +45,21 @@ func query(arguments []string) {
}
url.RawQuery = urlQuery.Encode()
- response := util.HttpDo(&http.Request{URL: url}, time.Second*10, "Container")
- if response == nil {
+ response, err := util.HttpDo(&http.Request{URL: url}, time.Second*10, "Container")
+ if err != nil {
+ log.Print("Request failed: ", color.Red(err))
return
}
defer response.Body.Close()
if response.StatusCode == 200 {
- util.PrintReader(response.Body)
+ log.Print(util.ReaderToJSON(response.Body))
} else if response.StatusCode/100 == 4 {
- util.Error("Invalid query (" + response.Status + "):")
- util.PrintReader(response.Body)
+ log.Printf("Invalid query (%s):", color.Red(response.Status))
+ log.Print(util.ReaderToJSON(response.Body))
} else {
- util.Error("Error from container at", url.Host, "("+response.Status+"):")
- util.PrintReader(response.Body)
+ log.Printf("Error from container at %s (%s):", color.Cyan(url.Host), color.Red(response.Status))
+ log.Print(util.ReaderToJSON(response.Body))
}
}
diff --git a/client/go/cmd/query_test.go b/client/go/cmd/query_test.go
index 5524c95ec0b..0ab7a110be9 100644
--- a/client/go/cmd/query_test.go
+++ b/client/go/cmd/query_test.go
@@ -64,7 +64,7 @@ func assertQueryNonJsonResult(t *testing.T, expectedQuery string, query ...strin
func assertQueryError(t *testing.T, status int, errorMessage string) {
client := &mockHttpClient{nextStatus: status, nextBody: errorMessage}
assert.Equal(t,
- "\x1b[31mInvalid query (Status "+strconv.Itoa(status)+"):\x1b[0m\n"+errorMessage+"\n",
+ "Invalid query (Status "+strconv.Itoa(status)+"):\n"+errorMessage+"\n",
executeCommand(t, client, []string{"query"}, []string{"yql=select from sources * where title contains 'foo'"}),
"error output")
}
@@ -72,7 +72,7 @@ func assertQueryError(t *testing.T, status int, errorMessage string) {
func assertQueryServiceError(t *testing.T, status int, errorMessage string) {
client := &mockHttpClient{nextStatus: status, nextBody: errorMessage}
assert.Equal(t,
- "\x1b[31mError from container at 127.0.0.1:8080 (Status "+strconv.Itoa(status)+"):\x1b[0m\n"+errorMessage+"\n",
+ "Error from container at 127.0.0.1:8080 (Status "+strconv.Itoa(status)+"):\n"+errorMessage+"\n",
executeCommand(t, client, []string{"query"}, []string{"yql=select from sources * where title contains 'foo'"}),
"error output")
}
diff --git a/client/go/cmd/root.go b/client/go/cmd/root.go
index b3306bbe617..bb74e9b5539 100644
--- a/client/go/cmd/root.go
+++ b/client/go/cmd/root.go
@@ -5,6 +5,12 @@
package cmd
import (
+ "log"
+ "os"
+
+ "github.com/logrusorgru/aurora"
+ "github.com/mattn/go-colorable"
+ "github.com/mattn/go-isatty"
"github.com/spf13/cobra"
)
@@ -20,15 +26,21 @@ var (
Long: `TO
DO`,
}
+
+ color aurora.Aurora
)
+func configureLogger() {
+ color = aurora.NewAurora(isatty.IsTerminal(os.Stdout.Fd()))
+ log.SetFlags(0) // No timestamps
+ log.SetOutput(colorable.NewColorableStdout())
+}
+
func init() {
+ configureLogger()
cobra.OnInitialize(readConfig)
rootCmd.PersistentFlags().StringVarP(&targetArgument, "target", "t", "local", "The name or URL of the recipient of this command")
}
// Execute executes the root command.
-func Execute() error {
- err := rootCmd.Execute()
- return err
-}
+func Execute() error { return rootCmd.Execute() }
diff --git a/client/go/cmd/status.go b/client/go/cmd/status.go
index f1cbc0f9d2d..fa70aec302f 100644
--- a/client/go/cmd/status.go
+++ b/client/go/cmd/status.go
@@ -5,6 +5,8 @@
package cmd
import (
+ "log"
+
"github.com/spf13/cobra"
"github.com/vespa-engine/vespa/util"
)
@@ -54,16 +56,17 @@ var statusDeployCmd = &cobra.Command{
func status(target string, description string) {
path := "/ApplicationStatus"
- response := util.HttpGet(target, path, description)
- if response == nil {
+ response, err := util.HttpGet(target, path, description)
+ if err != nil {
+ log.Print("Request failed: ", color.Red(err))
return
}
defer response.Body.Close()
if response.StatusCode != 200 {
- util.Error(description, "at", target, "is not ready")
- util.Detail(response.Status)
+ log.Print(description, " at ", color.Cyan(target), " is ", color.Yellow("not ready"))
+ log.Print(response.Status)
} else {
- util.Success(description, "at", target, "is ready")
+ log.Print(description, " at ", color.Cyan(target), " is ", color.Green("ready"))
}
}
diff --git a/client/go/cmd/status_test.go b/client/go/cmd/status_test.go
index 9fb9091b0bf..848dab3db26 100644
--- a/client/go/cmd/status_test.go
+++ b/client/go/cmd/status_test.go
@@ -45,7 +45,7 @@ func TestStatusErrorResponse(t *testing.T) {
func assertDeployStatus(target string, args []string, t *testing.T) {
client := &mockHttpClient{}
assert.Equal(t,
- "\x1b[32mDeploy API at "+target+" is ready\x1b[0m\n",
+ "Deploy API at "+target+" is ready\n",
executeCommand(t, client, []string{"status", "deploy"}, args),
"vespa status config-server")
assert.Equal(t, target+"/ApplicationStatus", client.lastRequest.URL.String())
@@ -54,13 +54,13 @@ func assertDeployStatus(target string, args []string, t *testing.T) {
func assertQueryStatus(target string, args []string, t *testing.T) {
client := &mockHttpClient{}
assert.Equal(t,
- "\x1b[32mQuery API at "+target+" is ready\x1b[0m\n",
+ "Query API at "+target+" is ready\n",
executeCommand(t, client, []string{"status", "query"}, args),
"vespa status container")
assert.Equal(t, target+"/ApplicationStatus", client.lastRequest.URL.String())
assert.Equal(t,
- "\x1b[32mQuery API at "+target+" is ready\x1b[0m\n",
+ "Query API at "+target+" is ready\n",
executeCommand(t, client, []string{"status"}, args),
"vespa status (the default)")
assert.Equal(t, target+"/ApplicationStatus", client.lastRequest.URL.String())
@@ -69,7 +69,7 @@ func assertQueryStatus(target string, args []string, t *testing.T) {
func assertDocumentStatus(target string, args []string, t *testing.T) {
client := &mockHttpClient{}
assert.Equal(t,
- "\x1b[32mDocument API at "+target+" is ready\x1b[0m\n",
+ "Document API at "+target+" is ready\n",
executeCommand(t, client, []string{"status", "document"}, args),
"vespa status container")
assert.Equal(t, target+"/ApplicationStatus", client.lastRequest.URL.String())
@@ -78,7 +78,7 @@ func assertDocumentStatus(target string, args []string, t *testing.T) {
func assertQueryStatusError(target string, args []string, t *testing.T) {
client := &mockHttpClient{nextStatus: 500}
assert.Equal(t,
- "\x1b[31mQuery API at "+target+" is not ready\x1b[0m\n\x1b[33mStatus 500\x1b[0m\n",
+ "Query API at "+target+" is not ready\nStatus 500\n",
executeCommand(t, client, []string{"status", "container"}, args),
"vespa status container")
}
diff --git a/client/go/cmd/target.go b/client/go/cmd/target.go
index bf6487bf790..09e63bc1d3b 100644
--- a/client/go/cmd/target.go
+++ b/client/go/cmd/target.go
@@ -5,7 +5,7 @@
package cmd
import (
- "github.com/vespa-engine/vespa/util"
+ "log"
"strings"
)
@@ -68,6 +68,6 @@ func getTarget(targetContext context) *target {
return nil // TODO
}
- util.Error("Unknown target argument '" + targetArgument + ": Use 'local', 'cloud' or an URL")
+ log.Printf("Unknown target '%s': Use %s, %s or an URL", color.Red(targetArgument), color.Cyan("local"), color.Cyan("cloud"))
return nil
}
diff --git a/client/go/go.mod b/client/go/go.mod
index 40499f09e04..ff444215332 100644
--- a/client/go/go.mod
+++ b/client/go/go.mod
@@ -3,6 +3,9 @@ module github.com/vespa-engine/vespa
go 1.16
require (
+ github.com/logrusorgru/aurora v2.0.3+incompatible
+ github.com/mattn/go-colorable v0.0.9
+ github.com/mattn/go-isatty v0.0.3
github.com/spf13/cobra v1.2.1
github.com/spf13/viper v1.8.1
github.com/stretchr/testify v1.7.0
diff --git a/client/go/go.sum b/client/go/go.sum
index d6ff538bc63..cec273d1506 100644
--- a/client/go/go.sum
+++ b/client/go/go.sum
@@ -177,9 +177,13 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
+github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
+github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
+github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
diff --git a/client/go/util/http.go b/client/go/util/http.go
index 6a4bab6c108..38224d2a842 100644
--- a/client/go/util/http.go
+++ b/client/go/util/http.go
@@ -5,6 +5,7 @@
package util
import (
+ "fmt"
"net/http"
"net/url"
"strings"
@@ -36,20 +37,18 @@ func CreateClient(timeout time.Duration) HttpClient {
}
// Convenience function for doing a HTTP GET
-func HttpGet(host string, path string, description string) *http.Response {
- url, urlError := url.Parse(host + path)
- if urlError != nil {
- Error("Invalid target url '" + host + path + "'")
- return nil
+func HttpGet(host string, path string, description string) (*http.Response, error) {
+ url, err := url.Parse(host + path)
+ if err != nil {
+ return nil, fmt.Errorf("Invalid target URL: %s: %w", host+path, err)
}
return HttpDo(&http.Request{URL: url}, time.Second*10, description)
}
-func HttpDo(request *http.Request, timeout time.Duration, description string) *http.Response {
- response, error := ActiveHttpClient.Do(request, timeout)
- if error != nil {
- Error("Could not connect to", strings.ToLower(description), "at", request.URL.Host)
- Detail(error.Error())
+func HttpDo(request *http.Request, timeout time.Duration, description string) (*http.Response, error) {
+ response, err := ActiveHttpClient.Do(request, timeout)
+ if err != nil {
+ return nil, fmt.Errorf("Could not connect to %s at %s: %w", strings.ToLower(description), request.URL.Host, err)
}
- return response
+ return response, nil
}
diff --git a/client/go/util/http_test.go b/client/go/util/http_test.go
index 731fc935a1d..54114aefb64 100644
--- a/client/go/util/http_test.go
+++ b/client/go/util/http_test.go
@@ -6,11 +6,12 @@ package util
import (
"bytes"
- "github.com/stretchr/testify/assert"
"io/ioutil"
"net/http"
"testing"
"time"
+
+ "github.com/stretchr/testify/assert"
)
type mockHttpClient struct{}
@@ -37,9 +38,11 @@ func (c mockHttpClient) Do(request *http.Request, timeout time.Duration) (respon
func TestHttpRequest(t *testing.T) {
ActiveHttpClient = mockHttpClient{}
- response := HttpGet("http://host", "/okpath", "description")
+ response, err := HttpGet("http://host", "/okpath", "description")
+ assert.Nil(t, err)
assert.Equal(t, 200, response.StatusCode)
- response = HttpGet("http://host", "/otherpath", "description")
+ response, err = HttpGet("http://host", "/otherpath", "description")
+ assert.Nil(t, err)
assert.Equal(t, 500, response.StatusCode)
}
diff --git a/client/go/util/io.go b/client/go/util/io.go
index cbde22ad0eb..5ce9708ed7a 100644
--- a/client/go/util/io.go
+++ b/client/go/util/io.go
@@ -6,6 +6,7 @@ package util
import (
"bytes"
+ "encoding/json"
"errors"
"io"
"os"
@@ -26,14 +27,25 @@ func IsDirectory(path string) bool {
// Returns the content of a reader as a string
func ReaderToString(reader io.Reader) string {
- buffer := new(strings.Builder)
- io.Copy(buffer, reader)
+ var buffer strings.Builder
+ io.Copy(&buffer, reader)
return buffer.String()
}
// Returns the content of a reader as a byte array
func ReaderToBytes(reader io.Reader) []byte {
- buffer := new(bytes.Buffer)
+ var buffer bytes.Buffer
buffer.ReadFrom(reader)
return buffer.Bytes()
}
+
+// Returns the contents of reader as indented JSON
+func ReaderToJSON(reader io.Reader) string {
+ bodyBytes := ReaderToBytes(reader)
+ var prettyJSON bytes.Buffer
+ parseError := json.Indent(&prettyJSON, bodyBytes, "", " ")
+ if parseError != nil { // Not JSON: Print plainly
+ return string(bodyBytes)
+ }
+ return string(prettyJSON.Bytes())
+}
diff --git a/client/go/util/print.go b/client/go/util/print.go
deleted file mode 100644
index ce116378222..00000000000
--- a/client/go/util/print.go
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-// Print functions for color-coded text.
-// Author: bratseth
-
-package util
-
-import (
- "bytes"
- "encoding/json"
- "fmt"
- "io"
- "os"
- "strings"
-)
-
-// Set this to have output written somewhere else than os.Stdout
-var Out io.Writer
-
-func init() {
- Out = os.Stdout
-}
-
-// Prints in default color
-func Print(messages ...string) {
- print("", messages)
-}
-
-// Prints in a color appropriate for errors
-func Error(messages ...string) {
- print("\033[31m", messages)
-}
-
-// FatalIfError prints error and exists if given err is non-nill.
-func FatalIfErr(err error) {
- if err != nil {
- Error(err.Error())
- os.Exit(1)
- }
-}
-
-// Prints in a color appropriate for success messages
-func Success(messages ...string) {
- print("\033[32m", messages)
-}
-
-// Prints in a color appropriate for detail messages
-func Detail(messages ...string) {
- print("\033[33m", messages)
-}
-
-// Prints all the text of the given reader
-func PrintReader(reader io.Reader) {
- bodyBytes := ReaderToBytes(reader)
- var prettyJSON bytes.Buffer
- parseError := json.Indent(&prettyJSON, bodyBytes, "", " ")
- if parseError != nil { // Not JSON: Print plainly
- Print(string(bodyBytes))
- } else {
- Print(string(prettyJSON.Bytes()))
- }
-}
-
-func print(prefix string, messages []string) {
- fmt.Fprint(Out, prefix)
- for i := 0; i < len(messages); i++ {
- fmt.Fprint(Out, messages[i])
- if i < len(messages)-1 {
- fmt.Fprint(Out, " ")
- }
- }
- // TODO: Use "log" instead of this package and something like https://github.com/logrusorgru/aurora for colorization
- // Since automatic colorisation needs to support Windows, we probably need github.com/mattn/go-isatty to
- // detect TTY
- if strings.HasPrefix(prefix, "\033") {
- fmt.Fprint(Out, "\033[0m") // Terminate colors
- }
- fmt.Fprintln(Out, "")
-}
diff --git a/client/go/vespa/deploy.go b/client/go/vespa/deploy.go
index badc97aa44b..9973d3fd490 100644
--- a/client/go/vespa/deploy.go
+++ b/client/go/vespa/deploy.go
@@ -7,6 +7,7 @@ package vespa
import (
"archive/zip"
"errors"
+ "fmt"
"io"
"io/ioutil"
"net/http"
@@ -56,26 +57,22 @@ func (ap *ApplicationPackage) HasCertificate() bool {
func isZip(filename string) bool { return filepath.Ext(filename) == ".zip" }
-func Deploy(prepare bool, application string, target string) {
+func Deploy(prepare bool, application string, target string) (string, error) {
pkg, noSourceError := FindApplicationPackage(application)
if noSourceError != nil {
- util.Error(noSourceError.Error())
- return
+ return "", noSourceError
}
zippedSource := pkg.Path
if !pkg.IsZip() { // create zip
tempZip, error := ioutil.TempFile("", "application.zip")
if error != nil {
- util.Error("Could not create a temporary zip file for the application package")
- util.Detail(error.Error())
- return
+ return "", fmt.Errorf("Could not create a temporary zip file for the application package: %w", error)
}
error = zipDir(pkg.Path, tempZip.Name())
if error != nil {
- util.Error(error.Error())
- return
+ return "", error
}
defer os.Remove(tempZip.Name())
zippedSource = tempZip.Name()
@@ -83,9 +80,7 @@ func Deploy(prepare bool, application string, target string) {
zipFileReader, zipFileError := os.Open(zippedSource)
if zipFileError != nil {
- util.Error("Could not open application package at " + pkg.Path)
- util.Detail(zipFileError.Error())
- return
+ return "", fmt.Errorf("Could not open application package at %s: %w", pkg.Path, zipFileError)
}
var deployUrl *url.URL
@@ -106,21 +101,18 @@ func Deploy(prepare bool, application string, target string) {
Body: ioutil.NopCloser(zipFileReader),
}
serviceDescription := "Deploy service"
- response := util.HttpDo(request, time.Minute*10, serviceDescription)
- if response == nil {
- return
+ response, err := util.HttpDo(request, time.Minute*10, serviceDescription)
+ if err != nil {
+ return "", err
}
-
defer response.Body.Close()
- if response.StatusCode == 200 {
- util.Success("Deployed", pkg.Path)
- } else if response.StatusCode/100 == 4 {
- util.Error("Invalid application package", "("+response.Status+"):")
- util.PrintReader(response.Body)
- } else {
- util.Error("Error from", strings.ToLower(serviceDescription), "at", request.URL.Host, "("+response.Status+"):")
- util.PrintReader(response.Body)
+
+ if response.StatusCode/100 == 4 {
+ return "", fmt.Errorf("Invalid application package (%s):\n%s", response.Status, util.ReaderToJSON(response.Body))
+ } else if response.StatusCode != 200 {
+ return "", fmt.Errorf("Error from %s at %s (%s):\n%s", strings.ToLower(serviceDescription), request.URL.Host, response.Status, util.ReaderToJSON(response.Body))
}
+ return pkg.Path, nil
}
func zipDir(dir string, destination string) error {