Skip to content
This repository has been archived by the owner on Nov 1, 2022. It is now read-only.

Add --watch option to release command #1525

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 77 additions & 8 deletions cmd/fluxctl/release_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ import (
"context"
"fmt"
"io"
"time"

"github.com/spf13/cobra"

"github.com/weaveworks/flux"
"github.com/weaveworks/flux/api/v11"
"github.com/weaveworks/flux/api/v6"
"github.com/weaveworks/flux/cluster"
"github.com/weaveworks/flux/job"
"github.com/weaveworks/flux/update"
)
Expand All @@ -23,6 +27,7 @@ type controllerReleaseOpts struct {
dryRun bool
interactive bool
force bool
watch bool
outputOpts
cause update.Cause

Expand Down Expand Up @@ -57,6 +62,7 @@ func (opts *controllerReleaseOpts) Command() *cobra.Command {
cmd.Flags().BoolVar(&opts.dryRun, "dry-run", false, "Do not release anything; just report back what would have been done")
cmd.Flags().BoolVar(&opts.interactive, "interactive", false, "Select interactively which containers to update")
cmd.Flags().BoolVarP(&opts.force, "force", "f", false, "Disregard locks and container image filters (has no effect when used with --all or --update-all-images)")
cmd.Flags().BoolVarP(&opts.watch, "watch", "w", false, "Watch rollout progress during release")

// Deprecated
cmd.Flags().StringSliceVarP(&opts.services, "service", "s", []string{}, "Service to release")
Expand All @@ -81,6 +87,8 @@ func (opts *controllerReleaseOpts) RunE(cmd *cobra.Command, args []string) error
switch {
case len(opts.controllers) <= 0 && !opts.allControllers:
return newUsageError("please supply either --all, or at least one --controller=<controller>")
case opts.watch && opts.dryRun:
return newUsageError("cannot use --watch with --dry-run")
case opts.force && opts.allControllers && opts.allImages:
return newUsageError("--force has no effect when used with --all and --update-all-images")
case opts.force && opts.allControllers:
Expand All @@ -90,6 +98,7 @@ func (opts *controllerReleaseOpts) RunE(cmd *cobra.Command, args []string) error
}

var controllers []update.ResourceSpec

if opts.allControllers {
controllers = []update.ResourceSpec{update.ResourceSpecAll}
} else {
Expand Down Expand Up @@ -131,7 +140,7 @@ func (opts *controllerReleaseOpts) RunE(cmd *cobra.Command, args []string) error
}

if kind == update.ReleaseKindPlan {
fmt.Fprintf(cmd.OutOrStderr(), "Submitting dry-run release...\n")
fmt.Fprintf(cmd.OutOrStderr(), "Submitting dry-run release ...\n")
} else {
fmt.Fprintf(cmd.OutOrStderr(), "Submitting release ...\n")
}
Expand All @@ -153,20 +162,19 @@ func (opts *controllerReleaseOpts) RunE(cmd *cobra.Command, args []string) error
return err
}

result, err := awaitJob(ctx, opts.API, jobID)
if err != nil {
return err
}
if opts.interactive {
result, err := awaitJob(ctx, opts.API, jobID)
if err != nil {
return err
}

spec, err := promptSpec(cmd.OutOrStdout(), result, opts.verbosity)
spec.Force = opts.force
if err != nil {
fmt.Fprintln(cmd.OutOrStderr(), err.Error())
return nil
}

fmt.Fprintf(cmd.OutOrStderr(), "Submitting selected release...\n")
fmt.Fprintf(cmd.OutOrStderr(), "Submitting selected release ...\n")
jobID, err = opts.API.UpdateManifests(ctx, update.Spec{
Type: update.Containers,
Cause: opts.cause,
Expand All @@ -179,7 +187,68 @@ func (opts *controllerReleaseOpts) RunE(cmd *cobra.Command, args []string) error

opts.dryRun = false
}
return await(ctx, cmd.OutOrStdout(), cmd.OutOrStderr(), opts.API, jobID, !opts.dryRun, opts.verbosity)

err = await(ctx, cmd.OutOrStdout(), cmd.OutOrStderr(), opts.API, jobID, !opts.dryRun, opts.verbosity)
if !opts.watch || err != nil {
return err
}

fmt.Fprintf(cmd.OutOrStderr(), "Monitoring rollout ...\n")
for {
completed := 0
services, err := opts.API.ListServicesWithOptions(ctx, v11.ListServicesOptions{Services: result.Result.AffectedResources()})
if err != nil {
return err
}

for _, service := range services {
writeRolloutStatus(service, opts.verbosity)

if service.Status == cluster.StatusReady {
completed++
}

if service.Rollout.Messages != nil {
fmt.Fprintf(cmd.OutOrStderr(), "There was a problem releasing %s:\n", service.ID)
for _, msg := range service.Rollout.Messages {
fmt.Fprintf(cmd.OutOrStderr(), "%s\n", msg)
}
return nil
}
}

if completed == len(services) {
fmt.Fprintf(cmd.OutOrStderr(), "All controllers ready.\n")
return nil
}

time.Sleep(2000 * time.Millisecond)
}
}

func writeRolloutStatus(service v6.ControllerStatus, verbosity int) {
w := newTabwriter()
fmt.Fprintf(w, "CONTROLLER\tCONTAINER\tIMAGE\tRELEASE\tREPLICAS\n")

if len(service.Containers) > 0 {
c := service.Containers[0]
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%d/%d", service.ID, c.Name, c.Current.ID, service.Status, service.Rollout.Updated, service.Rollout.Desired)
if verbosity > 0 {
fmt.Fprintf(w, " (%d outdated, %d ready)", service.Rollout.Outdated, service.Rollout.Ready)
}
fmt.Fprintf(w, "\n")
for _, c := range service.Containers[1:] {
fmt.Fprintf(w, "\t%s\t%s\t\t\n", c.Name, c.Current.ID)
}
} else {
fmt.Fprintf(w, "%s\t\t\t%s\t%d/%d", service.ID, service.Status, service.Rollout.Updated, service.Rollout.Desired)
if verbosity > 0 {
fmt.Fprintf(w, " (%d outdated, %d ready)", service.Rollout.Outdated, service.Rollout.Ready)
}
fmt.Fprintf(w, "\n")
}
fmt.Fprintln(w)
w.Flush()
}

func promptSpec(out io.Writer, result job.Result, verbosity int) (update.ReleaseContainersSpec, error) {
Expand Down