-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(controller): Add failFast flag to DAG and Step templates #5315
Changes from 5 commits
348bea3
18801e3
70bb402
a77d12c
d6e8886
c61be2b
e92660c
7a587fc
19750b4
e46a2bd
bf0bf63
404e165
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
package controller | ||
|
||
import ( | ||
wfv1 "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" | ||
) | ||
|
||
type counter struct { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This refactoring is a good idea |
||
key counterType | ||
ifNode func(wfv1.NodeStatus) bool | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you comment these fields? |
||
} | ||
|
||
type counterType string | ||
|
||
const ( | ||
counterTypeActivePods = "activePods" | ||
counterTypeActiveChildren = "activeChildren" | ||
counterTypeFailedOrErroredChildren = "failedOrErroredChildren" | ||
) | ||
|
||
func getActivePodsCounter(boundaryID string) counter { | ||
return counter{ | ||
key: counterTypeActivePods, | ||
ifNode: func(node wfv1.NodeStatus) bool { | ||
return node.Type == wfv1.NodeTypePod && | ||
// Only count pods that match the provided boundaryID, or all if no boundaryID was provided | ||
(boundaryID == "" || node.BoundaryID == boundaryID) && | ||
// Only count Running or Pending pods | ||
(node.Phase == wfv1.NodePending || node.Phase == wfv1.NodeRunning) && | ||
// Only count pods that are NOT waiting for a lock | ||
(node.SynchronizationStatus == nil || node.SynchronizationStatus.Waiting == "") | ||
}, | ||
} | ||
} | ||
|
||
func getActiveChildrenCounter(boundaryID string) counter { | ||
return counter{ | ||
key: counterTypeActiveChildren, | ||
ifNode: func(node wfv1.NodeStatus) bool { | ||
return node.BoundaryID == boundaryID && | ||
// Only count Pods, Steps, or DAGs | ||
(node.Type == wfv1.NodeTypePod || node.Type == wfv1.NodeTypeSteps || node.Type == wfv1.NodeTypeDAG) && | ||
// Only count Running or Pending nodes | ||
(node.Phase == wfv1.NodePending || node.Phase == wfv1.NodeRunning) | ||
}, | ||
} | ||
} | ||
|
||
func getFailedOrErroredChildrenCounter(boundaryID string) counter { | ||
return counter{ | ||
key: counterTypeFailedOrErroredChildren, | ||
ifNode: func(node wfv1.NodeStatus) bool { | ||
return node.BoundaryID == boundaryID && | ||
// Only count Pods, Steps, or DAGs | ||
(node.Type == wfv1.NodeTypePod || node.Type == wfv1.NodeTypeSteps || node.Type == wfv1.NodeTypeDAG) && | ||
// Only count Failed or Errored nodes | ||
(node.Phase == wfv1.NodeFailed || node.Phase == wfv1.NodeError) | ||
}, | ||
} | ||
} | ||
|
||
type count map[counterType]int | ||
|
||
func (c count) addKeyIfNotPresent(key counterType) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I’d merge this logic into “count” |
||
if _, ok := c[key]; !ok { | ||
c[key] = 0 | ||
} | ||
} | ||
|
||
func (c count) count(key counterType) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe call this “inc” |
||
c[key]++ | ||
} | ||
|
||
func (c count) getCount() int { | ||
if len(c) != 1 { | ||
panic("getCount applied to a count with multiple types") | ||
} | ||
for _, val := range c { | ||
return val | ||
} | ||
panic("unreachable: we know count has exactly one element and it wasn't returned") | ||
} | ||
|
||
func (c count) getCountType(key counterType) int { | ||
return c[key] | ||
} | ||
|
||
func (woc *wfOperationCtx) countNodes(counters ...counter) count { | ||
count := make(count) | ||
for _, node := range woc.wf.Status.Nodes { | ||
for _, counter := range counters { | ||
count.addKeyIfNotPresent(counter.key) | ||
if counter.ifNode(node) { | ||
count.count(counter.key) | ||
} | ||
} | ||
} | ||
return count | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package controller | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
|
||
"github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" | ||
) | ||
|
||
func getWfOperationCtx() *wfOperationCtx { | ||
return &wfOperationCtx{ | ||
wf: &v1alpha1.Workflow{ | ||
Status: v1alpha1.WorkflowStatus{ | ||
Nodes: map[string]v1alpha1.NodeStatus{ | ||
"1": {Type: v1alpha1.NodeTypePod, Phase: v1alpha1.NodeSucceeded, BoundaryID: "1"}, | ||
"2": {Type: v1alpha1.NodeTypePod, Phase: v1alpha1.NodeFailed, BoundaryID: "1"}, | ||
"3": {Type: v1alpha1.NodeTypeSteps, Phase: v1alpha1.NodeFailed, BoundaryID: "1"}, | ||
"4": {Type: v1alpha1.NodeTypeDAG, Phase: v1alpha1.NodeError, BoundaryID: "1"}, | ||
"5": {Type: v1alpha1.NodeTypePod, Phase: v1alpha1.NodeRunning, BoundaryID: "1"}, | ||
"5a": {Type: v1alpha1.NodeTypePod, Phase: v1alpha1.NodeRunning, BoundaryID: "1", SynchronizationStatus: &v1alpha1.NodeSynchronizationStatus{Waiting: "yes"}}, | ||
"6": {Type: v1alpha1.NodeTypePod, Phase: v1alpha1.NodePending, BoundaryID: "1"}, | ||
"7": {Type: v1alpha1.NodeTypeSteps, Phase: v1alpha1.NodeRunning, BoundaryID: "1"}, | ||
"8": {Type: v1alpha1.NodeTypeDAG, Phase: v1alpha1.NodePending, BoundaryID: "1"}, | ||
|
||
"9": {Type: v1alpha1.NodeTypeSteps, Phase: v1alpha1.NodeFailed, BoundaryID: "2"}, | ||
"10": {Type: v1alpha1.NodeTypeDAG, Phase: v1alpha1.NodeError, BoundaryID: "2"}, | ||
"11": {Type: v1alpha1.NodeTypePod, Phase: v1alpha1.NodeRunning, BoundaryID: "2"}, | ||
"12": {Type: v1alpha1.NodeTypePod, Phase: v1alpha1.NodePending, BoundaryID: "2"}, | ||
}, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func TestCounters(t *testing.T) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good stuff There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I’m assuming you’ve checked your code coverage. |
||
woc := getWfOperationCtx() | ||
|
||
activePod := woc.countNodes(getActivePodsCounter("1")).getCount() | ||
assert.Equal(t, 2, activePod) | ||
|
||
// No BoundaryID requested | ||
activePod = woc.countNodes(getActivePodsCounter("")).getCount() | ||
assert.Equal(t, 4, activePod) | ||
|
||
activeChild := woc.countNodes(getActiveChildrenCounter("1")).getCount() | ||
assert.Equal(t, 5, activeChild) | ||
|
||
failedOrErroredChildren := woc.countNodes(getFailedOrErroredChildrenCounter("1")).getCount() | ||
assert.Equal(t, 3, failedOrErroredChildren) | ||
|
||
counts := woc.countNodes(getActivePodsCounter("1"), getActiveChildrenCounter("1"), getFailedOrErroredChildrenCounter("1")) | ||
assert.Len(t, counts, 3) | ||
assert.Panics(t, func() { | ||
// counts has more than one element, the getCount shortcut shouldn't work | ||
counts.getCount() | ||
}) | ||
assert.Equal(t, 2, counts.getCountType(counterTypeActivePods)) | ||
assert.Equal(t, 5, counts.getCountType(counterTypeActiveChildren)) | ||
assert.Equal(t, 3, counts.getCountType(counterTypeFailedOrErroredChildren)) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adds a grep string for goreman output