aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--client/go/internal/cli/cmd/curl.go6
-rw-r--r--client/go/internal/cli/cmd/deploy.go44
-rw-r--r--client/go/internal/cli/cmd/deploy_test.go1
-rw-r--r--client/go/internal/cli/cmd/document.go3
-rw-r--r--client/go/internal/cli/cmd/feed.go3
-rw-r--r--client/go/internal/cli/cmd/query.go3
-rw-r--r--client/go/internal/cli/cmd/root.go38
-rw-r--r--client/go/internal/cli/cmd/status.go37
-rw-r--r--client/go/internal/cli/cmd/test.go3
-rw-r--r--client/go/internal/cli/cmd/waiter.go97
-rw-r--r--client/go/internal/vespa/deploy.go6
-rw-r--r--client/go/internal/vespa/target.go5
-rw-r--r--client/go/internal/vespa/target_cloud.go16
-rw-r--r--client/go/internal/vespa/target_custom.go12
-rw-r--r--client/go/internal/vespa/target_test.go2
15 files changed, 156 insertions, 120 deletions
diff --git a/client/go/internal/cli/cmd/curl.go b/client/go/internal/cli/cmd/curl.go
index 500a2a75663..44540db9ccf 100644
--- a/client/go/internal/cli/cmd/curl.go
+++ b/client/go/internal/cli/cmd/curl.go
@@ -41,14 +41,14 @@ $ vespa curl -- -v --data-urlencode "yql=select * from music where album contain
}
var service *vespa.Service
useDeploy := curlService == "deploy"
- timeout := time.Duration(waitSecs) * time.Second
+ waiter := cli.waiter(false, time.Duration(waitSecs)*time.Second)
if useDeploy {
if cli.config.cluster() != "" {
return fmt.Errorf("cannot specify cluster for service %s", curlService)
}
- service, err = target.DeployService(timeout)
+ service, err = target.DeployService()
} else {
- service, err = cli.service(target, cli.config.cluster(), 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/deploy.go b/client/go/internal/cli/cmd/deploy.go
index b037a953b53..cf2e435fea5 100644
--- a/client/go/internal/cli/cmd/deploy.go
+++ b/client/go/internal/cli/cmd/deploy.go
@@ -73,11 +73,12 @@ $ vespa deploy -t cloud -z perf.aws-us-east-1c`,
return err
}
}
- if err := waitForDeployService(cli, target, timeout); err != nil {
+ waiter := cli.waiter(false, timeout)
+ if _, err := waiter.DeployService(target); err != nil {
return err
}
var result vespa.PrepareResult
- if err := cli.spinner(cli.Stderr, "Uploading application package ...", func() error {
+ if err := cli.spinner(cli.Stderr, "Uploading application package...", func() error {
result, err = vespa.Deploy(opts)
return err
}); err != nil {
@@ -98,7 +99,7 @@ $ vespa deploy -t cloud -z perf.aws-us-east-1c`,
opts.Target.Deployment().Application.Instance, opts.Target.Deployment().Zone.Environment, opts.Target.Deployment().Zone.Region,
result.ID)))
}
- return waitForContainerServices(cli, target, result.ID, timeout)
+ return waitForDeploymentReady(cli, target, result.ID, timeout)
},
}
cmd.Flags().StringVarP(&logLevelArg, "log-level", "l", "error", `Log level for Vespa logs. Must be "error", "warning", "info" or "debug"`)
@@ -126,7 +127,7 @@ func newPrepareCmd(cli *CLI) *cobra.Command {
}
opts := vespa.DeploymentOptions{ApplicationPackage: pkg, Target: target}
var result vespa.PrepareResult
- err = cli.spinner(cli.Stderr, "Uploading application package ...", func() error {
+ err = cli.spinner(cli.Stderr, "Uploading application package...", func() error {
result, err = vespa.Prepare(opts)
return err
})
@@ -161,7 +162,8 @@ func newActivateCmd(cli *CLI) *cobra.Command {
return err
}
timeout := time.Duration(waitSecs) * time.Second
- if err := waitForDeployService(cli, target, timeout); err != nil {
+ waiter := cli.waiter(false, timeout)
+ if _, err := waiter.DeployService(target); err != nil {
return err
}
opts := vespa.DeploymentOptions{Target: target, Timeout: timeout}
@@ -170,43 +172,23 @@ func newActivateCmd(cli *CLI) *cobra.Command {
return err
}
cli.printSuccess("Activated application with session ", sessionID)
- return waitForContainerServices(cli, target, sessionID, timeout)
+ return waitForDeploymentReady(cli, target, sessionID, timeout)
},
}
cli.bindWaitFlag(cmd, 60, &waitSecs)
return cmd
}
-func waitForDeployService(cli *CLI, target vespa.Target, timeout time.Duration) error {
+func waitForDeploymentReady(cli *CLI, target vespa.Target, sessionOrRunID int64, timeout time.Duration) error {
if timeout == 0 {
return nil
}
- s, err := target.DeployService(0)
- if err != nil {
+ waiter := cli.waiter(false, timeout)
+ if _, err := waiter.Deployment(target, sessionOrRunID); err != nil {
return err
}
- cli.printInfo("Waiting up to ", color.CyanString(timeout.String()), " for ", s.Description(), " to become ready ...")
- return s.Wait(timeout)
-}
-
-func waitForContainerServices(cli *CLI, target vespa.Target, sessionOrRunID int64, timeout time.Duration) error {
- if timeout == 0 {
- return nil
- }
- cli.printInfo("Waiting up to ", color.CyanString(timeout.String()), " for deployment to converge ...")
- if _, err := target.AwaitDeployment(sessionOrRunID, timeout); err != nil {
- return err
- }
- services, err := cli.services(target, timeout)
- if err != nil {
- return err
- }
- for _, s := range services {
- if err := s.Wait(timeout); err != nil {
- return err
- }
- }
- return nil
+ _, err := waiter.Services(target)
+ return err
}
func printPrepareLog(stderr io.Writer, result vespa.PrepareResult) {
diff --git a/client/go/internal/cli/cmd/deploy_test.go b/client/go/internal/cli/cmd/deploy_test.go
index 35c9b05050c..d578b2a4629 100644
--- a/client/go/internal/cli/cmd/deploy_test.go
+++ b/client/go/internal/cli/cmd/deploy_test.go
@@ -74,7 +74,6 @@ func TestDeployWait(t *testing.T) {
client.NextStatus(500)
// ... then becomes healthy
client.NextStatus(200)
- client.NextStatus(200)
// Deployment succeeds
client.NextResponse(mock.HTTPResponse{
URI: "/application/v2/tenant/default/prepareandactivate",
diff --git a/client/go/internal/cli/cmd/document.go b/client/go/internal/cli/cmd/document.go
index aacef9be825..21db343c11e 100644
--- a/client/go/internal/cli/cmd/document.go
+++ b/client/go/internal/cli/cmd/document.go
@@ -298,7 +298,8 @@ func documentService(cli *CLI, waitSecs int) (*vespa.Service, error) {
if err != nil {
return nil, err
}
- return cli.service(target, cli.config.cluster(), time.Duration(waitSecs)*time.Second)
+ waiter := cli.waiter(false, time.Duration(waitSecs)*time.Second)
+ return waiter.Service(target, cli.config.cluster())
}
func printResult(cli *CLI, result util.OperationResult, payloadOnlyOnSuccess bool) error {
diff --git a/client/go/internal/cli/cmd/feed.go b/client/go/internal/cli/cmd/feed.go
index 8a2bdf04ce3..8b8589baec3 100644
--- a/client/go/internal/cli/cmd/feed.go
+++ b/client/go/internal/cli/cmd/feed.go
@@ -107,8 +107,9 @@ 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)
for i := 0; i < n; i++ {
- service, err := cli.service(target, cli.config.cluster(), time.Duration(waitSecs)*time.Second)
+ service, err := waiter.Service(target, cli.config.cluster())
if err != nil {
return nil, "", err
}
diff --git a/client/go/internal/cli/cmd/query.go b/client/go/internal/cli/cmd/query.go
index 90f51b398c2..3f849fae99e 100644
--- a/client/go/internal/cli/cmd/query.go
+++ b/client/go/internal/cli/cmd/query.go
@@ -64,7 +64,8 @@ func query(cli *CLI, arguments []string, timeoutSecs, waitSecs int, curl bool) e
if err != nil {
return err
}
- service, err := cli.service(target, cli.config.cluster(), time.Duration(waitSecs)*time.Second)
+ waiter := cli.waiter(false, 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 1dec2461ff8..69fd88c1b2b 100644
--- a/client/go/internal/cli/cmd/root.go
+++ b/client/go/internal/cli/cmd/root.go
@@ -343,6 +343,10 @@ 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}
+}
+
// target creates a target according the configuration of this CLI and given opts.
func (c *CLI) target(opts targetOptions) (vespa.Target, error) {
targetType, err := c.targetType()
@@ -513,40 +517,6 @@ func (c *CLI) system(targetType string) (vespa.System, error) {
return vespa.System{}, fmt.Errorf("no default system found for %s target", targetType)
}
-// service returns the service identified by cluster ID, located at target. If timeout is positive, this waits for the
-// service to become ready.
-func (c *CLI) service(target vespa.Target, cluster string, timeout time.Duration) (*vespa.Service, error) {
- targetType, err := c.targetType()
- if err != nil {
- return nil, err
- }
- if targetType.url != "" && cluster != "" {
- return nil, fmt.Errorf("cluster cannot be specified when target is an URL")
- }
- services, err := c.services(target, timeout)
- if err != nil {
- return nil, err
- }
- service, err := vespa.FindService(cluster, services)
- if err != nil {
- return nil, errHint(err, "The --cluster option specifies the service to use")
- }
- if timeout > 0 {
- c.printInfo("Waiting up to ", color.CyanString(timeout.String()), " for ", color.CyanString(service.Description()), " to become available ...")
- if err := service.Wait(timeout); err != nil {
- return nil, err
- }
- }
- return service, nil
-}
-
-func (c *CLI) services(target vespa.Target, timeout time.Duration) ([]*vespa.Service, error) {
- if timeout > 0 {
- c.printInfo("Waiting up to ", color.CyanString(timeout.String()), " for cluster discovery ...")
- }
- return target.ContainerServices(timeout)
-}
-
// isCI returns true if running inside a continuous integration environment.
func (c *CLI) isCI() bool {
_, ok := c.Environment["CI"]
diff --git a/client/go/internal/cli/cmd/status.go b/client/go/internal/cli/cmd/status.go
index c912a0fa712..f185fde6ca1 100644
--- a/client/go/internal/cli/cmd/status.go
+++ b/client/go/internal/cli/cmd/status.go
@@ -37,24 +37,26 @@ $ vespa status --cluster mycluster`,
if err != nil {
return err
}
+ waiter := cli.waiter(true, time.Duration(waitSecs)*time.Second)
if cluster == "" {
- timeout := time.Duration(waitSecs) * time.Second
- services, err := cli.services(t, timeout)
+ services, err := waiter.Services(t)
if err != nil {
return err
}
if len(services) == 0 {
return errHint(fmt.Errorf("no services exist"), "Deployment may not be ready yet", "Try 'vespa status deployment'")
}
- for _, service := range services {
- if err := printServiceStatus(service, service.Wait(timeout), cli); err != nil {
- return err
- }
+ for _, s := range services {
+ printReadyService(s, cli)
}
return nil
} else {
- s, err := cli.service(t, cluster, 0)
- return printServiceStatus(s, err, cli)
+ s, err := waiter.Service(t, cluster)
+ if err != nil {
+ return err
+ }
+ printReadyService(s, cli)
+ return nil
}
},
}
@@ -76,11 +78,13 @@ func newStatusDeployCmd(cli *CLI) *cobra.Command {
if err != nil {
return err
}
- s, err := t.DeployService(0)
+ waiter := cli.waiter(true, time.Duration(waitSecs)*time.Second)
+ s, err := waiter.DeployService(t)
if err != nil {
return err
}
- return printServiceStatus(s, s.Wait(time.Duration(waitSecs)*time.Second), cli)
+ printReadyService(s, cli)
+ return nil
},
}
cli.bindWaitFlag(cmd, 0, &waitSecs)
@@ -112,11 +116,8 @@ $ vespa status deployment -t local [session-id]
if err != nil {
return err
}
- timeout := time.Duration(waitSecs) * time.Second
- if timeout > 0 {
- cli.printInfo("Waiting up to ", color.CyanString(timeout.String()), " for deployment to converge ...")
- }
- id, err := t.AwaitDeployment(wantedID, timeout)
+ waiter := cli.waiter(true, time.Duration(waitSecs)*time.Second)
+ id, err := waiter.Deployment(t, wantedID)
if err != nil {
return err
}
@@ -137,12 +138,8 @@ $ vespa status deployment -t local [session-id]
return cmd
}
-func printServiceStatus(s *vespa.Service, waitErr error, cli *CLI) error {
- if waitErr != nil {
- return waitErr
- }
+func printReadyService(s *vespa.Service, cli *CLI) {
desc := s.Description()
desc = strings.ToUpper(string(desc[0])) + string(desc[1:])
log.Print(desc, " at ", color.CyanString(s.BaseURL), " is ", color.GreenString("ready"))
- return nil
}
diff --git a/client/go/internal/cli/cmd/test.go b/client/go/internal/cli/cmd/test.go
index 58c254ad6e8..5b99973d879 100644
--- a/client/go/internal/cli/cmd/test.go
+++ b/client/go/internal/cli/cmd/test.go
@@ -220,7 +220,8 @@ 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
- service, err = context.cli.service(target, cluster, time.Duration(waitSecs)*time.Second)
+ waiter := context.cli.waiter(false, 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
new file mode 100644
index 00000000000..a1919de798d
--- /dev/null
+++ b/client/go/internal/cli/cmd/waiter.go
@@ -0,0 +1,97 @@
+package cmd
+
+import (
+ "fmt"
+ "time"
+
+ "github.com/fatih/color"
+ "github.com/vespa-engine/vespa/client/go/internal/vespa"
+)
+
+// 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()
+ if err != nil {
+ return nil, err
+ }
+ if w.Timeout > 0 {
+ w.cli.printInfo("Waiting up to ", color.CyanString(w.Timeout.String()), " for ", s.Description(), " to become ready...")
+ }
+ if w.wait() {
+ if err := s.Wait(w.Timeout); err != nil {
+ return nil, err
+ }
+ }
+ return s, nil
+}
+
+// Service returns the service identified by cluster ID, available on target.
+func (w *Waiter) Service(target vespa.Target, cluster string) (*vespa.Service, error) {
+ targetType, err := w.cli.targetType()
+ if err != nil {
+ return nil, err
+ }
+ if targetType.url != "" && cluster != "" {
+ return nil, fmt.Errorf("cluster cannot be specified when target is an URL")
+ }
+ services, err := w.Services(target)
+ if err != nil {
+ return nil, err
+ }
+ service, err := vespa.FindService(cluster, services)
+ if err != nil {
+ return nil, errHint(err, "The --cluster option specifies the service to use")
+ }
+ if w.Timeout > 0 {
+ w.cli.printInfo("Waiting up to ", color.CyanString(w.Timeout.String()), " for ", color.CyanString(service.Description()), " to become available...")
+ }
+ if w.wait() {
+ if err := service.Wait(w.Timeout); err != nil {
+ return nil, err
+ }
+ }
+ return service, nil
+}
+
+// Services returns all container services available on target.
+func (w *Waiter) Services(target vespa.Target) ([]*vespa.Service, error) {
+ if w.Timeout > 0 {
+ w.cli.printInfo("Waiting up to ", color.CyanString(w.Timeout.String()), " for cluster discovery...")
+ }
+ services, err := target.ContainerServices(w.Timeout)
+ if err != nil {
+ return nil, err
+ }
+ for _, s := range services {
+ if w.Timeout > 0 {
+ w.cli.printInfo("Waiting up to ", color.CyanString(w.Timeout.String()), " for ", s.Description(), "...")
+ }
+ if w.wait() {
+ if err := s.Wait(w.Timeout); err != nil {
+ return nil, err
+ }
+ }
+ }
+ return services, nil
+}
+
+// Deployment waits for a deployment to become ready, returning the ID of the converged deployment.
+func (w *Waiter) Deployment(target vespa.Target, id int64) (int64, error) {
+ if w.Timeout > 0 {
+ w.cli.printInfo("Waiting up to ", color.CyanString(w.Timeout.String()), " for deployment to converge...")
+ }
+ return target.AwaitDeployment(id, w.Timeout)
+}
diff --git a/client/go/internal/vespa/deploy.go b/client/go/internal/vespa/deploy.go
index 8fd35e4a933..3a3af0d66a0 100644
--- a/client/go/internal/vespa/deploy.go
+++ b/client/go/internal/vespa/deploy.go
@@ -92,7 +92,7 @@ func (d DeploymentOptions) String() string {
}
func (d *DeploymentOptions) url(path string) (*url.URL, error) {
- service, err := d.Target.DeployService(0)
+ service, err := d.Target.DeployService()
if err != nil {
return nil, err
}
@@ -311,7 +311,7 @@ func Submit(opts DeploymentOptions, submission Submission) error {
}
func deployServiceDo(request *http.Request, timeout time.Duration, opts DeploymentOptions) (*http.Response, error) {
- s, err := opts.Target.DeployService(0)
+ s, err := opts.Target.DeployService()
if err != nil {
return nil, err
}
@@ -373,7 +373,7 @@ func uploadApplicationPackage(url *url.URL, opts DeploymentOptions) (PrepareResu
if err != nil {
return PrepareResult{}, err
}
- service, err := opts.Target.DeployService(opts.Timeout)
+ service, err := opts.Target.DeployService()
if err != nil {
return PrepareResult{}, err
}
diff --git a/client/go/internal/vespa/target.go b/client/go/internal/vespa/target.go
index bbee85217df..3c65369f986 100644
--- a/client/go/internal/vespa/target.go
+++ b/client/go/internal/vespa/target.go
@@ -67,9 +67,8 @@ type Target interface {
// Deployment returns the deployment managed by this target.
Deployment() Deployment
- // DeployService returns the service to use for deployments. If timeout is positive, wait for the service to become
- // ready.
- DeployService(timeout time.Duration) (*Service, error)
+ // DeployService returns the service providing the deploy API on this target.
+ DeployService() (*Service, error)
// ContainerServices returns all container services of the current deployment. If timeout is positive, wait for
// services to be discovered.
diff --git a/client/go/internal/vespa/target_cloud.go b/client/go/internal/vespa/target_cloud.go
index 14b7f1f4c52..24133ba5fc3 100644
--- a/client/go/internal/vespa/target_cloud.go
+++ b/client/go/internal/vespa/target_cloud.go
@@ -98,21 +98,15 @@ func (t *cloudTarget) IsCloud() bool { return true }
func (t *cloudTarget) Deployment() Deployment { return t.deploymentOptions.Deployment }
-func (t *cloudTarget) DeployService(timeout time.Duration) (*Service, error) {
- service := &Service{
+func (t *cloudTarget) DeployService() (*Service, error) {
+ return &Service{
BaseURL: t.apiOptions.System.URL,
TLSOptions: t.apiOptions.TLSOptions,
deployAPI: true,
httpClient: t.httpClient,
auth: t.apiAuth,
retryInterval: t.retryInterval,
- }
- if timeout > 0 {
- if err := service.Wait(timeout); err != nil {
- return nil, err
- }
- }
- return service, nil
+ }, nil
}
func (t *cloudTarget) ContainerServices(timeout time.Duration) ([]*Service, error) {
@@ -160,7 +154,7 @@ func (t *cloudTarget) CheckVersion(clientVersion version.Version) error {
if err != nil {
return err
}
- deployService, err := t.DeployService(0)
+ deployService, err := t.DeployService()
if err != nil {
return err
}
@@ -244,7 +238,7 @@ func (t *cloudTarget) PrintLog(options LogOptions) error {
}
func (t *cloudTarget) deployServiceWait(fn responseFunc, reqFn requestFunc, timeout time.Duration) (int, error) {
- deployService, err := t.DeployService(0)
+ deployService, err := t.DeployService()
if err != nil {
return 0, err
}
diff --git a/client/go/internal/vespa/target_custom.go b/client/go/internal/vespa/target_custom.go
index a49654e3dff..b0ca4f8492c 100644
--- a/client/go/internal/vespa/target_custom.go
+++ b/client/go/internal/vespa/target_custom.go
@@ -79,7 +79,7 @@ func (t *customTarget) newService(url, name string, deployAPI bool) *Service {
}
}
-func (t *customTarget) DeployService(timeout time.Duration) (*Service, error) {
+func (t *customTarget) DeployService() (*Service, error) {
if t.targetType == TargetCustom {
return t.newService(t.baseURL, "", true), nil
}
@@ -87,13 +87,7 @@ func (t *customTarget) DeployService(timeout time.Duration) (*Service, error) {
if err != nil {
return nil, err
}
- service := t.newService(u.String(), "", true)
- if timeout > 0 {
- if err := service.Wait(timeout); err != nil {
- return nil, err
- }
- }
- return service, nil
+ return t.newService(u.String(), "", true), nil
}
func (t *customTarget) ContainerServices(timeout time.Duration) ([]*Service, error) {
@@ -149,7 +143,7 @@ func (t *customTarget) urlWithPort(port int) (*url.URL, error) {
}
func (t *customTarget) serviceStatus(wantedGeneration int64, timeout time.Duration) (serviceStatus, error) {
- deployService, err := t.DeployService(0)
+ deployService, err := t.DeployService()
if err != nil {
return serviceStatus{}, err
}
diff --git a/client/go/internal/vespa/target_test.go b/client/go/internal/vespa/target_test.go
index c9a5fc74de9..b208489ddce 100644
--- a/client/go/internal/vespa/target_test.go
+++ b/client/go/internal/vespa/target_test.go
@@ -265,7 +265,7 @@ func createCloudTarget(t *testing.T, logWriter io.Writer) (Target, *mock.HTTPCli
func getService(t *testing.T, target Target, name string) (*Service, error) {
t.Helper()
if name == "deploy" {
- return target.DeployService(0)
+ return target.DeployService()
}
services, err := target.ContainerServices(0)
require.Nil(t, err)