summaryrefslogtreecommitdiffstats
path: root/client/go
diff options
context:
space:
mode:
Diffstat (limited to 'client/go')
-rw-r--r--client/go/internal/cli/cmd/cert.go6
-rw-r--r--client/go/internal/cli/cmd/curl.go2
-rw-r--r--client/go/internal/cli/cmd/deploy.go6
-rw-r--r--client/go/internal/cli/cmd/document.go2
-rw-r--r--client/go/internal/cli/cmd/feed.go2
-rw-r--r--client/go/internal/cli/cmd/query.go2
-rw-r--r--client/go/internal/cli/cmd/root.go4
-rw-r--r--client/go/internal/cli/cmd/status.go52
-rw-r--r--client/go/internal/cli/cmd/status_test.go33
-rw-r--r--client/go/internal/cli/cmd/test.go2
-rw-r--r--client/go/internal/cli/cmd/waiter.go7
-rw-r--r--client/go/internal/vespa/target.go8
-rw-r--r--client/go/internal/vespa/target_test.go6
13 files changed, 83 insertions, 49 deletions
diff --git a/client/go/internal/cli/cmd/cert.go b/client/go/internal/cli/cmd/cert.go
index aca8de88fbe..1cc50b1faea 100644
--- a/client/go/internal/cli/cmd/cert.go
+++ b/client/go/internal/cli/cmd/cert.go
@@ -32,8 +32,10 @@ package specified as an argument to this command (default '.').
It's possible to override the private key and certificate used through
environment variables. This can be useful in continuous integration systems.
-It's also possible override the CA certificate which can be useful when using self-signed certificates with a
-self-hosted Vespa service. See https://docs.vespa.ai/en/operations-selfhosted/mtls.html for more information.
+It's also possible override the CA certificate which can be useful when using
+self-signed certificates with a self-hosted Vespa service.
+See https://docs.vespa.ai/en/operations-selfhosted/mtls.html for more
+information.
Example of setting the CA certificate, certificate and key in-line:
diff --git a/client/go/internal/cli/cmd/curl.go b/client/go/internal/cli/cmd/curl.go
index 3d81a5d2c0e..4a919d18d49 100644
--- a/client/go/internal/cli/cmd/curl.go
+++ b/client/go/internal/cli/cmd/curl.go
@@ -41,7 +41,7 @@ $ vespa curl -- -v --data-urlencode "yql=select * from music where album contain
}
var service *vespa.Service
useDeploy := curlService == "deploy"
- waiter := cli.waiter(false, time.Duration(waitSecs)*time.Second)
+ waiter := cli.waiter(time.Duration(waitSecs) * time.Second)
if useDeploy {
if cli.config.cluster() != "" {
return fmt.Errorf("cannot specify cluster for service %s", curlService)
diff --git a/client/go/internal/cli/cmd/deploy.go b/client/go/internal/cli/cmd/deploy.go
index 0a4d72a8a48..aee26975901 100644
--- a/client/go/internal/cli/cmd/deploy.go
+++ b/client/go/internal/cli/cmd/deploy.go
@@ -73,7 +73,7 @@ $ vespa deploy -t cloud -z perf.aws-us-east-1c`,
return err
}
}
- waiter := cli.waiter(false, timeout)
+ waiter := cli.waiter(timeout)
if _, err := waiter.DeployService(target); err != nil {
return err
}
@@ -158,7 +158,7 @@ func newActivateCmd(cli *CLI) *cobra.Command {
return err
}
timeout := time.Duration(waitSecs) * time.Second
- waiter := cli.waiter(false, timeout)
+ waiter := cli.waiter(timeout)
if _, err := waiter.DeployService(target); err != nil {
return err
}
@@ -179,7 +179,7 @@ func waitForDeploymentReady(cli *CLI, target vespa.Target, sessionOrRunID int64,
if timeout == 0 {
return nil
}
- waiter := cli.waiter(false, timeout)
+ waiter := cli.waiter(timeout)
if _, err := waiter.Deployment(target, sessionOrRunID); err != nil {
return err
}
diff --git a/client/go/internal/cli/cmd/document.go b/client/go/internal/cli/cmd/document.go
index 090060d578c..a98e9867d34 100644
--- a/client/go/internal/cli/cmd/document.go
+++ b/client/go/internal/cli/cmd/document.go
@@ -298,7 +298,7 @@ func documentService(cli *CLI, waitSecs int) (*vespa.Service, error) {
if err != nil {
return nil, err
}
- waiter := cli.waiter(false, time.Duration(waitSecs)*time.Second)
+ waiter := cli.waiter(time.Duration(waitSecs) * time.Second)
return waiter.Service(target, cli.config.cluster())
}
diff --git a/client/go/internal/cli/cmd/feed.go b/client/go/internal/cli/cmd/feed.go
index 5cdd0d7d63b..1a32ac7110d 100644
--- a/client/go/internal/cli/cmd/feed.go
+++ b/client/go/internal/cli/cmd/feed.go
@@ -108,7 +108,7 @@ func createServices(n int, timeout time.Duration, waitSecs int, cli *CLI) ([]uti
}
services := make([]util.HTTPClient, 0, n)
baseURL := ""
- waiter := cli.waiter(false, time.Duration(waitSecs)*time.Second)
+ waiter := cli.waiter(time.Duration(waitSecs) * time.Second)
for i := 0; i < n; i++ {
service, err := waiter.Service(target, cli.config.cluster())
if err != nil {
diff --git a/client/go/internal/cli/cmd/query.go b/client/go/internal/cli/cmd/query.go
index 6fb571dc36b..bf2272ca981 100644
--- a/client/go/internal/cli/cmd/query.go
+++ b/client/go/internal/cli/cmd/query.go
@@ -64,7 +64,7 @@ func query(cli *CLI, arguments []string, timeoutSecs, waitSecs int, curl bool) e
if err != nil {
return err
}
- waiter := cli.waiter(false, time.Duration(waitSecs)*time.Second)
+ waiter := cli.waiter(time.Duration(waitSecs) * time.Second)
service, err := waiter.Service(target, cli.config.cluster())
if err != nil {
return err
diff --git a/client/go/internal/cli/cmd/root.go b/client/go/internal/cli/cmd/root.go
index 352ddbcd152..068a7ed90b6 100644
--- a/client/go/internal/cli/cmd/root.go
+++ b/client/go/internal/cli/cmd/root.go
@@ -345,9 +345,7 @@ func (c *CLI) confirm(question string, confirmByDefault bool) (bool, error) {
}
}
-func (c *CLI) waiter(once bool, timeout time.Duration) *Waiter {
- return &Waiter{Once: once, Timeout: timeout, cli: c}
-}
+func (c *CLI) waiter(timeout time.Duration) *Waiter { return &Waiter{Timeout: timeout, cli: c} }
// target creates a target according the configuration of this CLI and given opts.
func (c *CLI) target(opts targetOptions) (vespa.Target, error) {
diff --git a/client/go/internal/cli/cmd/status.go b/client/go/internal/cli/cmd/status.go
index 001f4ba43f1..a0602494bff 100644
--- a/client/go/internal/cli/cmd/status.go
+++ b/client/go/internal/cli/cmd/status.go
@@ -42,7 +42,8 @@ $ vespa status --cluster mycluster --wait 600`,
if err != nil {
return err
}
- waiter := cli.waiter(true, time.Duration(waitSecs)*time.Second)
+ waiter := cli.waiter(time.Duration(waitSecs) * time.Second)
+ var failingContainers []*vespa.Service
if cluster == "" {
services, err := waiter.Services(t)
if err != nil {
@@ -52,23 +53,41 @@ $ vespa status --cluster mycluster --wait 600`,
return errHint(fmt.Errorf("no services exist"), "Deployment may not be ready yet", "Try 'vespa status deployment'")
}
for _, s := range services {
- printReadyService(s, cli)
+ if !printServiceStatus(s, waiter, cli) {
+ failingContainers = append(failingContainers, s)
+ }
}
- return nil
} else {
s, err := waiter.Service(t, cluster)
if err != nil {
return err
}
- printReadyService(s, cli)
- return nil
+ if !printServiceStatus(s, waiter, cli) {
+ failingContainers = append(failingContainers, s)
+ }
}
+ return failingServicesErr(failingContainers...)
},
}
cli.bindWaitFlag(cmd, 0, &waitSecs)
return cmd
}
+func failingServicesErr(services ...*vespa.Service) error {
+ if len(services) == 0 {
+ return nil
+ }
+ var nameOrURL []string
+ for _, s := range services {
+ if s.Name != "" {
+ nameOrURL = append(nameOrURL, s.Name)
+ } else {
+ nameOrURL = append(nameOrURL, s.BaseURL)
+ }
+ }
+ return fmt.Errorf("services not ready: %s", strings.Join(nameOrURL, ", "))
+}
+
func newStatusDeployCmd(cli *CLI) *cobra.Command {
var waitSecs int
cmd := &cobra.Command{
@@ -83,12 +102,14 @@ func newStatusDeployCmd(cli *CLI) *cobra.Command {
if err != nil {
return err
}
- waiter := cli.waiter(true, time.Duration(waitSecs)*time.Second)
+ waiter := cli.waiter(time.Duration(waitSecs) * time.Second)
s, err := waiter.DeployService(t)
if err != nil {
return err
}
- printReadyService(s, cli)
+ if !printServiceStatus(s, waiter, cli) {
+ return failingServicesErr(s)
+ }
return nil
},
}
@@ -128,7 +149,7 @@ $ vespa status deployment -t local [session-id] --wait 600
if err != nil {
return err
}
- waiter := cli.waiter(true, time.Duration(waitSecs)*time.Second)
+ waiter := cli.waiter(time.Duration(waitSecs) * time.Second)
id, err := waiter.Deployment(t, wantedID)
if err != nil {
return err
@@ -146,8 +167,19 @@ $ vespa status deployment -t local [session-id] --wait 600
return cmd
}
-func printReadyService(s *vespa.Service, cli *CLI) {
+func printServiceStatus(s *vespa.Service, waiter *Waiter, cli *CLI) bool {
desc := s.Description()
desc = strings.ToUpper(string(desc[0])) + string(desc[1:])
- log.Print(desc, " at ", color.CyanString(s.BaseURL), " is ", color.GreenString("ready"))
+ err := s.Wait(waiter.Timeout)
+ var sb strings.Builder
+ sb.WriteString(fmt.Sprintf("%s at %s is ", desc, color.CyanString(s.BaseURL)))
+ if err == nil {
+ sb.WriteString(color.GreenString("ready"))
+ } else {
+ sb.WriteString(color.RedString("not ready"))
+ sb.WriteString(": ")
+ sb.WriteString(err.Error())
+ }
+ fmt.Fprintln(cli.Stdout, sb.String())
+ return err == nil
}
diff --git a/client/go/internal/cli/cmd/status_test.go b/client/go/internal/cli/cmd/status_test.go
index 1e6c3230db3..1473e39ff54 100644
--- a/client/go/internal/cli/cmd/status_test.go
+++ b/client/go/internal/cli/cmd/status_test.go
@@ -38,12 +38,18 @@ func TestStatusCommandMultiCluster(t *testing.T) {
mockServiceStatus(client)
assert.NotNil(t, cli.Run("status"))
assert.Equal(t, "Error: no services exist\nHint: Deployment may not be ready yet\nHint: Try 'vespa status deployment'\n", stderr.String())
+ stderr.Reset()
mockServiceStatus(client, "foo", "bar")
- assert.Nil(t, cli.Run("status"))
+ client.NextStatus(200)
+ client.NextStatus(400) // One cluster is unavilable
+ assert.NotNil(t, cli.Run("status"))
assert.Equal(t, `Container bar at http://127.0.0.1:8080 is ready
-Container foo at http://127.0.0.1:8080 is ready
+Container foo at http://127.0.0.1:8080 is not ready: unhealthy container foo: status 400 at http://127.0.0.1:8080/status.html: aborting wait: got status 400
`, stdout.String())
+ assert.Equal(t,
+ "Error: services not ready: foo\n",
+ stderr.String())
stdout.Reset()
mockServiceStatus(client, "foo", "bar")
@@ -60,7 +66,7 @@ func TestStatusCommandMultiClusterWait(t *testing.T) {
client.NextStatus(400)
assert.NotNil(t, cli.Run("status", "--cluster", "foo", "--wait", "10"))
assert.Equal(t, "Waiting up to 10s for cluster discovery...\nWaiting up to 10s for container foo...\n"+
- "Error: unhealthy container foo after waiting up to 10s: status 400 at http://127.0.0.1:8080/ApplicationStatus: aborting wait: got status 400\n", stderr.String())
+ "Error: unhealthy container foo after waiting up to 10s: status 400 at http://127.0.0.1:8080/status.html: aborting wait: got status 400\n", stderr.String())
}
func TestStatusCommandWithUrlTarget(t *testing.T) {
@@ -75,18 +81,25 @@ func TestStatusError(t *testing.T) {
client := &mock.HTTPClient{}
mockServiceStatus(client, "default")
client.NextStatus(500)
- cli, _, stderr := newTestCLI(t)
+ cli, stdout, stderr := newTestCLI(t)
cli.httpClient = client
assert.NotNil(t, cli.Run("status", "container"))
assert.Equal(t,
- "Error: unhealthy container default: status 500 at http://127.0.0.1:8080/ApplicationStatus: wait timed out\n",
+ "Container default at http://127.0.0.1:8080 is not ready: unhealthy container default: status 500 at http://127.0.0.1:8080/status.html: wait timed out\n",
+ stdout.String())
+ assert.Equal(t,
+ "Error: services not ready: default\n",
stderr.String())
+ stdout.Reset()
stderr.Reset()
client.NextResponseError(io.EOF)
assert.NotNil(t, cli.Run("status", "container", "-t", "http://example.com"))
assert.Equal(t,
- "Error: unhealthy container at http://example.com/ApplicationStatus: EOF\n",
+ "Container at http://example.com is not ready: unhealthy container at http://example.com/status.html: EOF\n",
+ stdout.String())
+ assert.Equal(t,
+ "Error: services not ready: http://example.com\n",
stderr.String())
}
@@ -189,7 +202,7 @@ func assertStatus(expectedTarget string, args []string, t *testing.T) {
clusterName = "foo"
mockServiceStatus(client, clusterName)
}
- client.NextResponse(mock.HTTPResponse{URI: "/ApplicationStatus", Status: 200})
+ client.NextResponse(mock.HTTPResponse{URI: "/status.html", Status: 200})
}
cli, stdout, _ := newTestCLI(t)
cli.httpClient = client
@@ -200,14 +213,14 @@ func assertStatus(expectedTarget string, args []string, t *testing.T) {
prefix += " " + clusterName
}
assert.Equal(t, prefix+" at "+expectedTarget+" is ready\n", stdout.String())
- assert.Equal(t, expectedTarget+"/ApplicationStatus", client.LastRequest.URL.String())
+ assert.Equal(t, expectedTarget+"/status.html", client.LastRequest.URL.String())
// Test legacy command
statusArgs = []string{"status query"}
stdout.Reset()
assert.Nil(t, cli.Run(append(statusArgs, args...)...))
assert.Equal(t, prefix+" at "+expectedTarget+" is ready\n", stdout.String())
- assert.Equal(t, expectedTarget+"/ApplicationStatus", client.LastRequest.URL.String())
+ assert.Equal(t, expectedTarget+"/status.html", client.LastRequest.URL.String())
}
func assertDocumentStatus(target string, args []string, t *testing.T) {
@@ -223,5 +236,5 @@ func assertDocumentStatus(target string, args []string, t *testing.T) {
"Container (document API) at "+target+" is ready\n",
stdout.String(),
"vespa status container")
- assert.Equal(t, target+"/ApplicationStatus", client.LastRequest.URL.String())
+ assert.Equal(t, target+"/status.html", client.LastRequest.URL.String())
}
diff --git a/client/go/internal/cli/cmd/test.go b/client/go/internal/cli/cmd/test.go
index e842cab606e..f0432dd4f70 100644
--- a/client/go/internal/cli/cmd/test.go
+++ b/client/go/internal/cli/cmd/test.go
@@ -220,7 +220,7 @@ func verify(step step, defaultCluster string, defaultParameters map[string]strin
service, ok = context.clusters[cluster]
if !ok {
// Cache service so we don't have to discover it for every step
- waiter := context.cli.waiter(false, time.Duration(waitSecs)*time.Second)
+ waiter := context.cli.waiter(time.Duration(waitSecs) * time.Second)
service, err = waiter.Service(target, cluster)
if err != nil {
return "", "", err
diff --git a/client/go/internal/cli/cmd/waiter.go b/client/go/internal/cli/cmd/waiter.go
index 302bc679885..0cfb3aa76d5 100644
--- a/client/go/internal/cli/cmd/waiter.go
+++ b/client/go/internal/cli/cmd/waiter.go
@@ -11,17 +11,12 @@ import (
// Waiter waits for Vespa services to become ready, within a timeout.
type Waiter struct {
- // Once species whether we should wait at least one time, irregardless of timeout.
- Once bool
-
// Timeout specifies how long we should wait for an operation to complete.
Timeout time.Duration // TODO(mpolden): Consider making this a budget
cli *CLI
}
-func (w *Waiter) wait() bool { return w.Once || w.Timeout > 0 }
-
// DeployService returns the service providing the deploy API on given target,
func (w *Waiter) DeployService(target vespa.Target) (*vespa.Service, error) {
s, err := target.DeployService()
@@ -74,8 +69,6 @@ func (w *Waiter) Services(target vespa.Target) ([]*vespa.Service, error) {
func (w *Waiter) maybeWaitFor(service *vespa.Service) error {
if w.Timeout > 0 {
w.cli.printInfo("Waiting up to ", color.CyanString(w.Timeout.String()), " for ", service.Description(), "...")
- }
- if w.wait() {
return service.Wait(w.Timeout)
}
return nil
diff --git a/client/go/internal/vespa/target.go b/client/go/internal/vespa/target.go
index 662e7383f97..5b29990fe4e 100644
--- a/client/go/internal/vespa/target.go
+++ b/client/go/internal/vespa/target.go
@@ -125,12 +125,8 @@ func (s *Service) SetClient(client util.HTTPClient) { s.httpClient = client }
// Wait polls the health check of this service until it succeeds or timeout passes.
func (s *Service) Wait(timeout time.Duration) error {
- url := s.BaseURL
- if s.deployAPI {
- url += "/status.html" // because /ApplicationStatus is not publicly reachable in Vespa Cloud
- } else {
- url += "/ApplicationStatus"
- }
+ // A path that does not need authentication, on any target
+ url := strings.TrimRight(s.BaseURL, "/") + "/status.html"
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return err
diff --git a/client/go/internal/vespa/target_test.go b/client/go/internal/vespa/target_test.go
index 25e38a30151..4c2fda8368e 100644
--- a/client/go/internal/vespa/target_test.go
+++ b/client/go/internal/vespa/target_test.go
@@ -88,7 +88,7 @@ func TestCustomTargetWait(t *testing.T) {
client.NextResponseError(io.EOF)
}
// Then succeeds
- client.NextResponse(mock.HTTPResponse{URI: "/ApplicationStatus", Status: 200})
+ client.NextResponse(mock.HTTPResponse{URI: "/status.html", Status: 200})
assertService(t, false, target, "", time.Second)
}
@@ -153,10 +153,10 @@ func TestCloudTargetWait(t *testing.T) {
assert.Equal(t, 2, len(services))
client.NextResponse(response)
- client.NextResponse(mock.HTTPResponse{URI: "/ApplicationStatus", Status: 500})
+ client.NextResponse(mock.HTTPResponse{URI: "/status.html", Status: 500})
assertService(t, true, target, "default", 0)
client.NextResponse(response)
- client.NextResponse(mock.HTTPResponse{URI: "/ApplicationStatus", Status: 200})
+ client.NextResponse(mock.HTTPResponse{URI: "/status.html", Status: 200})
assertService(t, false, target, "feed", 0)
}