Skip to content

Commit

Permalink
feat: cli allow retry successful workflow if nodeFieldSelector is set
Browse files Browse the repository at this point in the history
Signed-off-by: or-shachar <or.shachar@wiz.io>
  • Loading branch information
or-shachar committed Jul 21, 2023
1 parent a450784 commit 3d27393
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 3 deletions.
3 changes: 3 additions & 0 deletions cmd/argo/commands/retry.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ func NewRetryCommand() *cobra.Command {
# Retry the latest workflow:
argo retry @latest
# Restart node with id 5 on successful workflow, using node-field-selector
argo retry my-wf --restart-successful --node-field-selector id=5
`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 && !retryOpts.hasSelector() {
Expand Down
3 changes: 3 additions & 0 deletions docs/cli/argo_retry.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ argo retry [WORKFLOW...] [flags]
argo retry @latest
# Restart node with id 5 on successful workflow, using node-field-selector
argo retry my-wf --restart-successful --node-field-selector id=5
```

### Options
Expand Down
4 changes: 3 additions & 1 deletion workflow/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -805,7 +805,9 @@ func FormulateRetryWorkflow(ctx context.Context, wf *wfv1.Workflow, restartSucce
switch wf.Status.Phase {
case wfv1.WorkflowFailed, wfv1.WorkflowError:
default:
return nil, nil, errors.Errorf(errors.CodeBadRequest, "workflow must be Failed/Error to retry")
if !(restartSuccessful && len(nodeFieldSelector) > 0) {
return nil, nil, errors.Errorf(errors.CodeBadRequest, "To retry either: (1) workflow must be Failed/Error (2) Provide also restartSuccessful and nodeFieldSelector value")
}
}

newWF := wf.DeepCopy()
Expand Down
55 changes: 53 additions & 2 deletions workflow/util/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,7 @@ func TestUpdateSuspendedNode(t *testing.T) {
err = updateSuspendedNode(ctx, wfIf, hydratorfake.Noop, "suspend-template", "name=suspend-template-kgfn7[0].approve", SetOperationValues{OutputParameters: map[string]string{"message2": "Hello World 2"}})
assert.NoError(t, err)

//make sure global variable was updated
// make sure global variable was updated
wf, err := wfIf.Get(ctx, "suspend-template", metav1.GetOptions{})
assert.NoError(t, err)
assert.Equal(t, "Hello World 2", wf.Status.Outputs.Parameters[0].Value.String())
Expand Down Expand Up @@ -1160,6 +1160,57 @@ func TestFormulateRetryWorkflow(t *testing.T) {
assert.Equal(t, "modified", wf.Spec.Arguments.Parameters[0].Value.String())
}
})

t.Run("Fail on successful workflow without restartSuccessful and nodeFieldSelector", func(t *testing.T) {
wf := &wfv1.Workflow{
ObjectMeta: metav1.ObjectMeta{
Name: "successful-workflow-1",
Labels: map[string]string{},
},
Status: wfv1.WorkflowStatus{
Phase: wfv1.WorkflowSucceeded,
Nodes: map[string]wfv1.NodeStatus{
"successful-workflow-1": {ID: "successful-workflow-1", Phase: wfv1.NodeSucceeded, Type: wfv1.NodeTypeTaskGroup, Children: []string{"1"}},
"1": {ID: "1", Phase: wfv1.NodeSucceeded, Type: wfv1.NodeTypeTaskGroup, BoundaryID: "successful-workflow-1", Children: []string{"2"}},
"2": {ID: "2", Phase: wfv1.NodeSucceeded, Type: wfv1.NodeTypePod, BoundaryID: "1"}},
},
}
_, err := wfClient.Create(ctx, wf, metav1.CreateOptions{})
assert.NoError(t, err)
_, _, err = FormulateRetryWorkflow(ctx, wf, false, "", nil)
assert.Error(t, err)
})

t.Run("Retry successful workflow with restartSuccessful and nodeFieldSelector", func(t *testing.T) {
wf := &wfv1.Workflow{
ObjectMeta: metav1.ObjectMeta{
Name: "successful-workflow-2",
Labels: map[string]string{},
},
Status: wfv1.WorkflowStatus{
Phase: wfv1.WorkflowSucceeded,
Nodes: map[string]wfv1.NodeStatus{
"successful-workflow-2": {ID: "successful-workflow-2", Phase: wfv1.NodeSucceeded, Type: wfv1.NodeTypeTaskGroup, Children: []string{"1"}},
"1": {ID: "1", Phase: wfv1.NodeSucceeded, Type: wfv1.NodeTypeTaskGroup, BoundaryID: "successful-workflow-2", Children: []string{"2", "4"}},
"2": {ID: "2", Phase: wfv1.NodeSucceeded, Type: wfv1.NodeTypeTaskGroup, BoundaryID: "1", Children: []string{"3"}},
"3": {ID: "3", Phase: wfv1.NodeSucceeded, Type: wfv1.NodeTypePod, BoundaryID: "2"},
"4": {ID: "4", Phase: wfv1.NodeSucceeded, Type: wfv1.NodeTypePod, BoundaryID: "1"}},
},
}
_, err := wfClient.Create(ctx, wf, metav1.CreateOptions{})
assert.NoError(t, err)
wf, _, err = FormulateRetryWorkflow(ctx, wf, true, "id=4", nil)
if assert.NoError(t, err) {
// Node #4 is deleted and will be recreated so only 4 nodes left in wf.Status.Nodes
if assert.Len(t, wf.Status.Nodes, 4) {
assert.Equal(t, wfv1.NodeSucceeded, wf.Status.Nodes["successful-workflow-2"].Phase)
// The parent group nodes should be running.
assert.Equal(t, wfv1.NodeRunning, wf.Status.Nodes["1"].Phase)
assert.Equal(t, wfv1.NodeSucceeded, wf.Status.Nodes["2"].Phase)
assert.Equal(t, wfv1.NodeSucceeded, wf.Status.Nodes["3"].Phase)
}
}
})
}

func TestFromUnstructuredObj(t *testing.T) {
Expand Down Expand Up @@ -1892,6 +1943,6 @@ func TestRetryWorkflowWithNestedDAGsWithSuspendNodes(t *testing.T) {
assert.Equal(t, wfv1.NodeSucceeded, wf.Status.Nodes.FindByName("fail-two-nested-dag-suspend.dag1-step3-middle2.dag2-branch2-step1.dag3-step1").Phase)
assert.Equal(t, wfv1.NodeSucceeded, wf.Status.Nodes.FindByName("fail-two-nested-dag-suspend.dag1-step3-middle2.dag2-branch2-step1.dag3-step2").Phase)
assert.Equal(t, wfv1.NodeSucceeded, wf.Status.Nodes.FindByName("fail-two-nested-dag-suspend.dag1-step3-middle2.dag2-branch2-step2").Phase)
assert.Equal(t, wfv1.NodeSucceeded, wf.Status.Nodes.FindByName("fail-two-nested-dag-suspend.dag1-step4").Phase)
assert.Equal(t, wfv1.NodeSucceeded, wf.Status.Nodes.FindByName("afail-two-nested-dag-suspend.dag1-step4").Phase)
assert.Equal(t, 1, len(podsToDelete))
}

0 comments on commit 3d27393

Please sign in to comment.