-
Notifications
You must be signed in to change notification settings - Fork 880
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
Signed-off-by: Pedro Arvela <[email protected]>
- Loading branch information
Pedro Arvela
authored
Mar 24, 2021
1 parent
bff46e1
commit 92c1b16
Showing
3 changed files
with
267 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
package status | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"time" | ||
|
||
"github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/info" | ||
"github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/options" | ||
"github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/viewcontroller" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
const ( | ||
statusLong = `Watch rollout until it finishes or the timeout is exceeded. Returns success if | ||
the rollout is healthy upon completion and an error otherwise.` | ||
statusExample = ` | ||
# Watch the rollout until it succeeds | ||
%[1]s status guestbook | ||
# Watch the rollout until it succeeds, fail if it takes more than 60 seconds | ||
%[1]s status --timeout 60 guestbook | ||
` | ||
) | ||
|
||
type StatusOptions struct { | ||
Watch bool | ||
Timeout int64 | ||
|
||
options.ArgoRolloutsOptions | ||
} | ||
|
||
// NewCmdStatus returns a new instance of a `rollouts status` command | ||
func NewCmdStatus(o *options.ArgoRolloutsOptions) *cobra.Command { | ||
statusOptions := StatusOptions{ | ||
ArgoRolloutsOptions: *o, | ||
} | ||
|
||
var cmd = &cobra.Command{ | ||
Use: "status ROLLOUT_NAME", | ||
Short: "Show the status of a rollout", | ||
Long: statusLong, | ||
Example: o.Example(statusExample), | ||
SilenceUsage: true, | ||
RunE: func(c *cobra.Command, args []string) error { | ||
if len(args) != 1 { | ||
return o.UsageErr(c) | ||
} | ||
name := args[0] | ||
controller := viewcontroller.NewRolloutViewController(o.Namespace(), name, statusOptions.KubeClientset(), statusOptions.RolloutsClientset()) | ||
ctx, cancel := context.WithCancel(context.Background()) | ||
defer cancel() | ||
controller.Start(ctx) | ||
|
||
ri, err := controller.GetRolloutInfo() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if !statusOptions.Watch { | ||
fmt.Fprintln(o.Out, ri.Status) | ||
} else { | ||
rolloutUpdates := make(chan *info.RolloutInfo) | ||
defer close(rolloutUpdates) | ||
controller.RegisterCallback(func(roInfo *info.RolloutInfo) { | ||
rolloutUpdates <- roInfo | ||
}) | ||
go statusOptions.WatchStatus(ctx.Done(), cancel, statusOptions.Timeout, rolloutUpdates) | ||
controller.Run(ctx) | ||
|
||
finalRi, err := controller.GetRolloutInfo() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if finalRi.Status == "Degraded" { | ||
return fmt.Errorf("The rollout is in a degraded state with message: %s", finalRi.Message) | ||
} else if finalRi.Status != "Healthy" { | ||
return fmt.Errorf("Rollout progress exceeded timeout") | ||
} | ||
} | ||
|
||
return nil | ||
}, | ||
} | ||
cmd.Flags().BoolVarP(&statusOptions.Watch, "watch", "w", true, "Watch the status of the rollout until it's done") | ||
cmd.Flags().Int64VarP(&statusOptions.Timeout, "timeout", "t", 0, "The length of time in seconds to watch before giving up, zero means wait forever") | ||
return cmd | ||
} | ||
|
||
func (o *StatusOptions) WatchStatus(stopCh <-chan struct{}, cancelFunc context.CancelFunc, timeoutSeconds int64, rolloutUpdates chan *info.RolloutInfo) { | ||
timeout := make(chan bool) | ||
var roInfo *info.RolloutInfo | ||
var preventFlicker time.Time | ||
|
||
if timeoutSeconds != 0 { | ||
go func() { | ||
time.Sleep(time.Duration(timeoutSeconds) * time.Second) | ||
timeout <- true | ||
}() | ||
} | ||
|
||
for { | ||
select { | ||
case roInfo = <-rolloutUpdates: | ||
if roInfo != nil && roInfo.Status == "Healthy" || roInfo.Status == "Degraded" { | ||
fmt.Fprintln(o.Out, roInfo.Status) | ||
cancelFunc() | ||
return | ||
} | ||
if roInfo != nil && time.Now().After(preventFlicker.Add(200*time.Millisecond)) { | ||
fmt.Fprintf(o.Out, "%s - %s\n", roInfo.Status, roInfo.Message) | ||
preventFlicker = time.Now() | ||
} | ||
case <-stopCh: | ||
return | ||
case <-timeout: | ||
cancelFunc() | ||
return | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
package status | ||
|
||
import ( | ||
"bytes" | ||
"testing" | ||
|
||
"github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/info/testdata" | ||
options "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/options/fake" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
const noWatch = "--watch=false" | ||
|
||
func TestStatusUsage(t *testing.T) { | ||
tf, o := options.NewFakeArgoRolloutsOptions() | ||
defer tf.Cleanup() | ||
cmd := NewCmdStatus(o) | ||
cmd.PersistentPreRunE = o.PersistentPreRunE | ||
cmd.SetArgs([]string{}) | ||
err := cmd.Execute() | ||
|
||
assert.Error(t, err) | ||
} | ||
|
||
func TestStatusRolloutNotFound(t *testing.T) { | ||
tf, o := options.NewFakeArgoRolloutsOptions() | ||
defer tf.Cleanup() | ||
cmd := NewCmdStatus(o) | ||
cmd.PersistentPreRunE = o.PersistentPreRunE | ||
cmd.SetArgs([]string{"does-not-exist", noWatch}) | ||
err := cmd.Execute() | ||
|
||
assert.Error(t, err) | ||
stdout := o.Out.(*bytes.Buffer).String() | ||
stderr := o.ErrOut.(*bytes.Buffer).String() | ||
assert.Empty(t, stdout) | ||
assert.Equal(t, "Error: rollout.argoproj.io \"does-not-exist\" not found\n", stderr) | ||
} | ||
|
||
func TestWatchStatusRolloutNotFound(t *testing.T) { | ||
tf, o := options.NewFakeArgoRolloutsOptions() | ||
defer tf.Cleanup() | ||
cmd := NewCmdStatus(o) | ||
cmd.PersistentPreRunE = o.PersistentPreRunE | ||
cmd.SetArgs([]string{"does-not-exist"}) | ||
err := cmd.Execute() | ||
|
||
assert.Error(t, err) | ||
stdout := o.Out.(*bytes.Buffer).String() | ||
stderr := o.ErrOut.(*bytes.Buffer).String() | ||
assert.Empty(t, stdout) | ||
assert.Equal(t, "Error: rollout.argoproj.io \"does-not-exist\" not found\n", stderr) | ||
} | ||
|
||
func TestStatusBlueGreenRollout(t *testing.T) { | ||
rolloutObjs := testdata.NewBlueGreenRollout() | ||
|
||
tf, o := options.NewFakeArgoRolloutsOptions(rolloutObjs.AllObjects()...) | ||
o.RESTClientGetter = tf.WithNamespace(rolloutObjs.Rollouts[0].Namespace) | ||
defer tf.Cleanup() | ||
cmd := NewCmdStatus(o) | ||
cmd.PersistentPreRunE = o.PersistentPreRunE | ||
cmd.SetArgs([]string{rolloutObjs.Rollouts[0].Name, noWatch}) | ||
err := cmd.Execute() | ||
|
||
assert.NoError(t, err) | ||
stdout := o.Out.(*bytes.Buffer).String() | ||
stderr := o.ErrOut.(*bytes.Buffer).String() | ||
assert.Equal(t, "Paused\n", stdout) | ||
assert.Empty(t, stderr) | ||
} | ||
|
||
func TestStatusInvalidRollout(t *testing.T) { | ||
rolloutObjs := testdata.NewInvalidRollout() | ||
|
||
tf, o := options.NewFakeArgoRolloutsOptions(rolloutObjs.AllObjects()...) | ||
o.RESTClientGetter = tf.WithNamespace(rolloutObjs.Rollouts[0].Namespace) | ||
defer tf.Cleanup() | ||
cmd := NewCmdStatus(o) | ||
cmd.PersistentPreRunE = o.PersistentPreRunE | ||
cmd.SetArgs([]string{rolloutObjs.Rollouts[0].Name, noWatch}) | ||
err := cmd.Execute() | ||
|
||
assert.NoError(t, err) | ||
stdout := o.Out.(*bytes.Buffer).String() | ||
stderr := o.ErrOut.(*bytes.Buffer).String() | ||
assert.Equal(t, "Degraded\n", stdout) | ||
assert.Empty(t, stderr) | ||
} | ||
|
||
func TestStatusAbortedRollout(t *testing.T) { | ||
rolloutObjs := testdata.NewAbortedRollout() | ||
|
||
tf, o := options.NewFakeArgoRolloutsOptions(rolloutObjs.AllObjects()...) | ||
o.RESTClientGetter = tf.WithNamespace(rolloutObjs.Rollouts[0].Namespace) | ||
defer tf.Cleanup() | ||
cmd := NewCmdStatus(o) | ||
cmd.PersistentPreRunE = o.PersistentPreRunE | ||
cmd.SetArgs([]string{rolloutObjs.Rollouts[0].Name, noWatch}) | ||
err := cmd.Execute() | ||
|
||
assert.NoError(t, err) | ||
stdout := o.Out.(*bytes.Buffer).String() | ||
stderr := o.ErrOut.(*bytes.Buffer).String() | ||
assert.Equal(t, "Degraded\n", stdout) | ||
assert.Empty(t, stderr) | ||
} | ||
|
||
func TestWatchAbortedRollout(t *testing.T) { | ||
rolloutObjs := testdata.NewAbortedRollout() | ||
|
||
tf, o := options.NewFakeArgoRolloutsOptions(rolloutObjs.AllObjects()...) | ||
o.RESTClientGetter = tf.WithNamespace(rolloutObjs.Rollouts[0].Namespace) | ||
defer tf.Cleanup() | ||
cmd := NewCmdStatus(o) | ||
cmd.PersistentPreRunE = o.PersistentPreRunE | ||
cmd.SetArgs([]string{rolloutObjs.Rollouts[0].Name}) | ||
err := cmd.Execute() | ||
|
||
assert.Error(t, err) | ||
stdout := o.Out.(*bytes.Buffer).String() | ||
stderr := o.ErrOut.(*bytes.Buffer).String() | ||
assert.Equal(t, "Degraded\n", stdout) | ||
assert.Equal(t, "Error: The rollout is in a degraded state with message: RolloutAborted: metric \"web\" assessed Failed due to failed (1) > failureLimit (0)\n", stderr) | ||
} | ||
|
||
func TestWatchTimeoutRollout(t *testing.T) { | ||
rolloutObjs := testdata.NewBlueGreenRollout() | ||
|
||
tf, o := options.NewFakeArgoRolloutsOptions(rolloutObjs.AllObjects()...) | ||
o.RESTClientGetter = tf.WithNamespace(rolloutObjs.Rollouts[0].Namespace) | ||
defer tf.Cleanup() | ||
cmd := NewCmdStatus(o) | ||
cmd.PersistentPreRunE = o.PersistentPreRunE | ||
cmd.SetArgs([]string{rolloutObjs.Rollouts[0].Name, "--timeout=1"}) | ||
err := cmd.Execute() | ||
|
||
assert.Error(t, err) | ||
stdout := o.Out.(*bytes.Buffer).String() | ||
stderr := o.ErrOut.(*bytes.Buffer).String() | ||
assert.Equal(t, "Paused - BlueGreenPause\n", stdout) | ||
assert.Equal(t, "Error: Rollout progress exceeded timeout\n", stderr) | ||
} |