Skip to content

Commit

Permalink
Add abort functionality (argoproj#224)
Browse files Browse the repository at this point in the history
  • Loading branch information
dthomson25 authored Oct 29, 2019
1 parent 773f933 commit 37bc1f7
Show file tree
Hide file tree
Showing 22 changed files with 684 additions and 32 deletions.
2 changes: 2 additions & 0 deletions manifests/crds/rollout-crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2623,6 +2623,8 @@ spec:
HPAReplicas:
format: int32
type: integer
abort:
type: boolean
availableReplicas:
format: int32
type: integer
Expand Down
2 changes: 2 additions & 0 deletions manifests/install.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10350,6 +10350,8 @@ spec:
HPAReplicas:
format: int32
type: integer
abort:
type: boolean
availableReplicas:
format: int32
type: integer
Expand Down
2 changes: 2 additions & 0 deletions manifests/namespace-install.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10350,6 +10350,8 @@ spec:
HPAReplicas:
format: int32
type: integer
abort:
type: boolean
availableReplicas:
format: int32
type: integer
Expand Down
7 changes: 7 additions & 0 deletions pkg/apis/rollouts/v1alpha1/openapi_generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions pkg/apis/rollouts/v1alpha1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,8 @@ type PauseCondition struct {

// RolloutStatus is the status for a Rollout resource
type RolloutStatus struct {
// Abort cancel the current rollout progression
Abort bool `json:"abort,omitempty"`
// PauseConditions indicates why the rollout is currently paused
PauseConditions []PauseCondition `json:"pauseConditions,omitempty"`
//ControllerPause indicates the controller has paused the rollout
Expand Down
48 changes: 48 additions & 0 deletions pkg/kubectl-argo-rollouts/cmd/abort/abort.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package abort

import (
"fmt"

"github.com/spf13/cobra"
types "k8s.io/apimachinery/pkg/types"

"github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/options"
)

const (
example = `
# Abort a rollout
%[1]s abort guestbook
`
)

const (
abortPatch = `{"status":{"abort":true}}`
)

// NewCmdAbort returns a new instance of an `rollouts abort` command
func NewCmdAbort(o *options.ArgoRolloutsOptions) *cobra.Command {
var cmd = &cobra.Command{
Use: "abort ROLLOUT",
Short: "Abort a rollout",
Example: o.Example(example),
SilenceUsage: true,
RunE: func(c *cobra.Command, args []string) error {
if len(args) == 0 {
return o.UsageErr(c)
}
ns := o.Namespace()
rolloutIf := o.RolloutsClientset().ArgoprojV1alpha1().Rollouts(ns)
for _, name := range args {
ro, err := rolloutIf.Patch(name, types.MergePatchType, []byte(abortPatch))
if err != nil {
return err
}
fmt.Fprintf(o.Out, "rollout '%s' aborted\n", ro.Name)
}
return nil
},
}
o.AddKubectlFlags(cmd)
return cmd
}
78 changes: 78 additions & 0 deletions pkg/kubectl-argo-rollouts/cmd/abort/abort_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package abort

import (
"bytes"
"testing"

"github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
kubetesting "k8s.io/client-go/testing"

"github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1"
fakeroclient "github.com/argoproj/argo-rollouts/pkg/client/clientset/versioned/fake"
options "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/options/fake"
)

func TestAbortCmdUsage(t *testing.T) {
tf, o := options.NewFakeArgoRolloutsOptions()
defer tf.Cleanup()
cmd := NewCmdAbort(o)
cmd.PersistentPreRunE = o.PersistentPreRunE
cmd.SetArgs([]string{})
err := cmd.Execute()
assert.Error(t, err)
stdout := o.Out.(*bytes.Buffer).String()
stderr := o.ErrOut.(*bytes.Buffer).String()
assert.Empty(t, stdout)
assert.Contains(t, stderr, "Usage:")
assert.Contains(t, stderr, "abort ROLLOUT")
}

func TestAbortCmd(t *testing.T) {
ro := v1alpha1.Rollout{
ObjectMeta: metav1.ObjectMeta{
Name: "guestbook",
Namespace: "test",
},
}

tf, o := options.NewFakeArgoRolloutsOptions(&ro)
defer tf.Cleanup()
fakeClient := o.RolloutsClient.(*fakeroclient.Clientset)
fakeClient.ReactionChain = nil
fakeClient.AddReactor("patch", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) {
if patchAction, ok := action.(kubetesting.PatchAction); ok {
if string(patchAction.GetPatch()) == abortPatch {
ro.Status.Abort = true
}
}
return true, &ro, nil
})

cmd := NewCmdAbort(o)
cmd.PersistentPreRunE = o.PersistentPreRunE
cmd.SetArgs([]string{"guestbook", "-n", "test"})
err := cmd.Execute()
assert.Nil(t, err)

assert.True(t, ro.Status.Abort)
stdout := o.Out.(*bytes.Buffer).String()
stderr := o.ErrOut.(*bytes.Buffer).String()
assert.Equal(t, stdout, "rollout 'guestbook' aborted\n")
assert.Empty(t, stderr)
}

func TestAbortCmdError(t *testing.T) {
tf, o := options.NewFakeArgoRolloutsOptions(&v1alpha1.Rollout{})
defer tf.Cleanup()
cmd := NewCmdAbort(o)
cmd.PersistentPreRunE = o.PersistentPreRunE
cmd.SetArgs([]string{"doesnotexist", "-n", "test"})
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: rollouts.argoproj.io \"doesnotexist\" not found\n", stderr)
}
4 changes: 4 additions & 0 deletions pkg/kubectl-argo-rollouts/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package cmd
import (
"github.com/spf13/cobra"

"github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/cmd/abort"
"github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/cmd/list"
"github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/cmd/pause"
"github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/cmd/resume"
"github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/cmd/retry"
"github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/cmd/version"
"github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/options"
)
Expand Down Expand Up @@ -35,5 +37,7 @@ func NewCmdArgoRollouts(o *options.ArgoRolloutsOptions) *cobra.Command {
cmd.AddCommand(pause.NewCmdPause(o))
cmd.AddCommand(resume.NewCmdResume(o))
cmd.AddCommand(version.NewCmdVersion(o))
cmd.AddCommand(abort.NewCmdAbort(o))
cmd.AddCommand(retry.NewCmdRetry(o))
return cmd
}
48 changes: 48 additions & 0 deletions pkg/kubectl-argo-rollouts/cmd/retry/retry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package retry

import (
"fmt"

"github.com/spf13/cobra"
types "k8s.io/apimachinery/pkg/types"

"github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/options"
)

const (
example = `
# Retry a rollout
%[1]s retry guestbook
`
)

const (
retryPatch = `{"status":{"abort":false}}`
)

// NewCmdRetry returns a new instance of an `rollouts retry` command
func NewCmdRetry(o *options.ArgoRolloutsOptions) *cobra.Command {
var cmd = &cobra.Command{
Use: "retry ROLLOUT",
Short: "Retry a rollout",
Example: o.Example(example),
SilenceUsage: true,
RunE: func(c *cobra.Command, args []string) error {
if len(args) == 0 {
return o.UsageErr(c)
}
ns := o.Namespace()
rolloutIf := o.RolloutsClientset().ArgoprojV1alpha1().Rollouts(ns)
for _, name := range args {
ro, err := rolloutIf.Patch(name, types.MergePatchType, []byte(retryPatch))
if err != nil {
return err
}
fmt.Fprintf(o.Out, "rollout '%s' retried\n", ro.Name)
}
return nil
},
}
o.AddKubectlFlags(cmd)
return cmd
}
78 changes: 78 additions & 0 deletions pkg/kubectl-argo-rollouts/cmd/retry/retry_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package retry

import (
"bytes"
"testing"

"github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
kubetesting "k8s.io/client-go/testing"

"github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1"
fakeroclient "github.com/argoproj/argo-rollouts/pkg/client/clientset/versioned/fake"
options "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/options/fake"
)

func TestRetryCmdUsage(t *testing.T) {
tf, o := options.NewFakeArgoRolloutsOptions()
defer tf.Cleanup()
cmd := NewCmdRetry(o)
cmd.PersistentPreRunE = o.PersistentPreRunE
cmd.SetArgs([]string{})
err := cmd.Execute()
assert.Error(t, err)
stdout := o.Out.(*bytes.Buffer).String()
stderr := o.ErrOut.(*bytes.Buffer).String()
assert.Empty(t, stdout)
assert.Contains(t, stderr, "Usage:")
assert.Contains(t, stderr, "retry ROLLOUT")
}

func TestRetryCmd(t *testing.T) {
ro := v1alpha1.Rollout{
ObjectMeta: metav1.ObjectMeta{
Name: "guestbook",
Namespace: "test",
},
}

tf, o := options.NewFakeArgoRolloutsOptions(&ro)
defer tf.Cleanup()
fakeClient := o.RolloutsClient.(*fakeroclient.Clientset)
fakeClient.ReactionChain = nil
fakeClient.AddReactor("patch", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) {
if patchAction, ok := action.(kubetesting.PatchAction); ok {
if string(patchAction.GetPatch()) == retryPatch {
ro.Status.Abort = true
}
}
return true, &ro, nil
})

cmd := NewCmdRetry(o)
cmd.PersistentPreRunE = o.PersistentPreRunE
cmd.SetArgs([]string{"guestbook", "-n", "test"})
err := cmd.Execute()
assert.Nil(t, err)

assert.True(t, ro.Status.Abort)
stdout := o.Out.(*bytes.Buffer).String()
stderr := o.ErrOut.(*bytes.Buffer).String()
assert.Equal(t, stdout, "rollout 'guestbook' retried\n")
assert.Empty(t, stderr)
}

func TestRetryCmdError(t *testing.T) {
tf, o := options.NewFakeArgoRolloutsOptions(&v1alpha1.Rollout{})
defer tf.Cleanup()
cmd := NewCmdRetry(o)
cmd.PersistentPreRunE = o.PersistentPreRunE
cmd.SetArgs([]string{"doesnotexist", "-n", "test"})
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: rollouts.argoproj.io \"doesnotexist\" not found\n", stderr)
}
27 changes: 22 additions & 5 deletions rollout/analysis.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,12 @@ func (c *RolloutController) getAnalysisRunsForRollout(rollout *v1alpha1.Rollout)
}

func (c *RolloutController) reconcileAnalysisRuns(roCtx *canaryContext) error {
rollout := roCtx.Rollout()
otherArs := roCtx.OtherAnalysisRuns()
if len(rollout.Status.PauseConditions) > 0 {
return nil
if roCtx.PauseContext().IsAborted() {
allArs := append(roCtx.CurrentAnalysisRuns(), otherArs...)
return c.cancelAnalysisRuns(roCtx, allArs)
}

newCurrentAnalysisRuns := []*v1alpha1.AnalysisRun{}

stepAnalysisRun, err := c.reconcileStepBasedAnalysisRun(roCtx)
Expand All @@ -64,6 +65,9 @@ func (c *RolloutController) reconcileAnalysisRuns(roCtx *canaryContext) error {
if backgroundAnalysisRun != nil {
newCurrentAnalysisRuns = append(newCurrentAnalysisRuns, backgroundAnalysisRun)
}
if roCtx.PauseContext().HasAddPause() {
return nil
}

err = c.cancelAnalysisRuns(roCtx, otherArs)
if err != nil {
Expand Down Expand Up @@ -102,6 +106,14 @@ func (c *RolloutController) reconcileBackgroundAnalysisRun(roCtx *canaryContext)
}
return currentAr, err
}
if currentAr.Status != nil {
switch currentAr.Status.Status {
case v1alpha1.AnalysisStatusInconclusive:
roCtx.PauseContext().AddPauseCondition(v1alpha1.PauseReasonInconclusiveAnalysis)
case v1alpha1.AnalysisStatusError, v1alpha1.AnalysisStatusFailed:
roCtx.PauseContext().AddAbort()
}
}
return currentAr, nil
}

Expand Down Expand Up @@ -147,8 +159,13 @@ func (c *RolloutController) reconcileStepBasedAnalysisRun(roCtx *canaryContext)
return currentAr, err
}

if currentAr.Status != nil && currentAr.Status.Status == v1alpha1.AnalysisStatusInconclusive {
roCtx.PauseContext().AddPauseCondition(v1alpha1.PauseReasonInconclusiveAnalysis)
if currentAr.Status != nil {
switch currentAr.Status.Status {
case v1alpha1.AnalysisStatusInconclusive:
roCtx.PauseContext().AddPauseCondition(v1alpha1.PauseReasonInconclusiveAnalysis)
case v1alpha1.AnalysisStatusError, v1alpha1.AnalysisStatusFailed:
roCtx.PauseContext().AddAbort()
}
}

return currentAr, nil
Expand Down
Loading

0 comments on commit 37bc1f7

Please sign in to comment.