diff options
author | Martin Polden <mpolden@mpolden.no> | 2023-08-21 15:31:44 +0200 |
---|---|---|
committer | Martin Polden <mpolden@mpolden.no> | 2023-08-21 15:49:17 +0200 |
commit | 5d6cf5d65fc06341d1127d5de586276fb3a1659e (patch) | |
tree | 0ed1002ba1b55f8bc47ed0818370197971aa0796 /client/go/internal/cli/cmd | |
parent | ed2099510c7b46a4d8fa643c0469c85c4018a7da (diff) |
Extract Waiter
Diffstat (limited to 'client/go/internal/cli/cmd')
-rw-r--r-- | client/go/internal/cli/cmd/curl.go | 6 | ||||
-rw-r--r-- | client/go/internal/cli/cmd/deploy.go | 44 | ||||
-rw-r--r-- | client/go/internal/cli/cmd/deploy_test.go | 1 | ||||
-rw-r--r-- | client/go/internal/cli/cmd/document.go | 3 | ||||
-rw-r--r-- | client/go/internal/cli/cmd/feed.go | 3 | ||||
-rw-r--r-- | client/go/internal/cli/cmd/query.go | 3 | ||||
-rw-r--r-- | client/go/internal/cli/cmd/root.go | 38 | ||||
-rw-r--r-- | client/go/internal/cli/cmd/status.go | 37 | ||||
-rw-r--r-- | client/go/internal/cli/cmd/test.go | 3 | ||||
-rw-r--r-- | client/go/internal/cli/cmd/waiter.go | 97 |
10 files changed, 142 insertions, 93 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) +} |