Skip to content

Commit 488660e

Browse files
isubasingheJoibel
authored andcommitted
fix: mark node failed if pod absent. Fixes argoproj#12993 (argoproj#13454)
Signed-off-by: isubasinghe <isitha@pipekit.io>
1 parent 645a7ff commit 488660e

3 files changed

Lines changed: 55 additions & 5 deletions

File tree

docs/environment-variables.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ most users. Environment variables may be removed at any time.
2828
| `CRON_SYNC_PERIOD` | `time.Duration` | `10s` | How often to sync cron workflows. |
2929
| `DEFAULT_REQUEUE_TIME` | `time.Duration` | `10s` | The re-queue time for the rate limiter of the workflow queue. |
3030
| `DISABLE_MAX_RECURSION` | `bool` | `false` | Set to true to disable the recursion preventer, which will stop a workflow running which has called into a child template 100 times |
31+
| `POD_ABSENT_TIMEOUT` | `time.Duration` | `2m` | The time used to determine if pod absence should imply node failure |
3132
| `EXPRESSION_TEMPLATES` | `bool` | `true` | Escape hatch to disable expression templates. |
3233
| `EVENT_AGGREGATION_WITH_ANNOTATIONS` | `bool` | `false` | Whether event annotations will be used when aggregating events. |
3334
| `GRPC_MESSAGE_SIZE` | `string` | Use different GRPC Max message size for Argo server deployment (supporting huge workflows). |

workflow/controller/operator.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,10 @@ func (woc *wfOperationCtx) operate(ctx context.Context) {
278278
woc.addArtifactGCFinalizer()
279279

280280
// Reconciliation of Outputs (Artifacts). See ReportOutputs() of executor.go.
281-
woc.taskResultReconciliation()
281+
err = woc.taskResultReconciliation()
282+
if err != nil {
283+
woc.markWorkflowError(ctx, fmt.Errorf("failed to reconcile: %v", err))
284+
}
282285

283286
// Do artifact GC if task result reconciliation is complete.
284287
if woc.wf.Status.Fulfilled() {
@@ -1355,6 +1358,19 @@ func (woc *wfOperationCtx) getAllWorkflowPods() ([]*apiv1.Pod, error) {
13551358
return pods, nil
13561359
}
13571360

1361+
func (woc *wfOperationCtx) getAllWorkflowPodsMap() (map[string]*apiv1.Pod, error) {
1362+
podList, err := woc.getAllWorkflowPods()
1363+
if err != nil {
1364+
return nil, err
1365+
}
1366+
podMap := make(map[string]*apiv1.Pod)
1367+
for _, pod := range podList {
1368+
nodeID := woc.nodeID(pod)
1369+
podMap[nodeID] = pod
1370+
}
1371+
return podMap, nil
1372+
}
1373+
13581374
func printPodSpecLog(pod *apiv1.Pod, wfName string) {
13591375
podSpecByte, err := json.Marshal(pod)
13601376
log := log.WithField("workflow", wfName).
@@ -2291,7 +2307,6 @@ func (woc *wfOperationCtx) executeTemplate(ctx context.Context, nodeName string,
22912307
}
22922308

22932309
if !retryNode.Fulfilled() && node.Fulfilled() { // if the retry child has completed we need to update outself
2294-
22952310
retryNode, err = woc.executeTemplate(ctx, retryNodeName, orgTmpl, tmplCtx, args, opts)
22962311
if err != nil {
22972312
return woc.markNodeError(node.Name, err), err

workflow/controller/taskresult.go

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111

1212
wfv1 "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1"
1313
wfextvv1alpha1 "github.com/argoproj/argo-workflows/v3/pkg/client/informers/externalversions/workflow/v1alpha1"
14+
envutil "github.com/argoproj/argo-workflows/v3/util/env"
1415
"github.com/argoproj/argo-workflows/v3/workflow/common"
1516
"github.com/argoproj/argo-workflows/v3/workflow/controller/indexes"
1617
)
@@ -52,26 +53,58 @@ func (wfc *WorkflowController) newWorkflowTaskResultInformer() cache.SharedIndex
5253
return informer
5354
}
5455

55-
func (woc *wfOperationCtx) taskResultReconciliation() {
56+
func podAbsentTimeout(node *wfv1.NodeStatus) bool {
57+
return time.Since(node.StartedAt.Time) <= envutil.LookupEnvDurationOr("POD_ABSENT_TIMEOUT", 2*time.Minute)
58+
}
5659

60+
func (woc *wfOperationCtx) taskResultReconciliation() error {
5761
objs, _ := woc.controller.taskResultInformer.GetIndexer().ByIndex(indexes.WorkflowIndex, woc.wf.Namespace+"/"+woc.wf.Name)
5862
woc.log.WithField("numObjs", len(objs)).Info("Task-result reconciliation")
63+
64+
podMap, err := woc.getAllWorkflowPodsMap()
65+
if err != nil {
66+
return err
67+
}
5968
for _, obj := range objs {
6069
result := obj.(*wfv1.WorkflowTaskResult)
6170
resultName := result.GetName()
6271

6372
woc.log.Debugf("task result:\n%+v", result)
6473
woc.log.Debugf("task result name:\n%+v", resultName)
6574

75+
label := result.Labels[common.LabelKeyReportOutputsCompleted]
76+
6677
// If the task result is completed, set the state to true.
67-
if result.Labels[common.LabelKeyReportOutputsCompleted] == "true" {
78+
if label == "true" {
6879
woc.log.Debugf("Marking task result complete %s", resultName)
6980
woc.wf.Status.MarkTaskResultComplete(resultName)
70-
} else {
81+
} else if label == "false" {
7182
woc.log.Debugf("Marking task result incomplete %s", resultName)
7283
woc.wf.Status.MarkTaskResultIncomplete(resultName)
7384
}
7485

86+
_, foundPod := podMap[result.Name]
87+
node, err := woc.wf.Status.Nodes.Get(result.Name)
88+
if err != nil {
89+
if foundPod {
90+
// how does this path make any sense?
91+
// pod created but informer not yet updated
92+
woc.log.Errorf("couldn't obtain node for %s, but found pod, this is not expected, doing nothing", result.Name)
93+
}
94+
continue
95+
}
96+
97+
if !foundPod && !node.Completed() {
98+
if podAbsentTimeout(node) {
99+
woc.log.Infof("Determined controller should timeout for %s", result.Name)
100+
woc.wf.Status.MarkTaskResultComplete(resultName)
101+
102+
woc.markNodePhase(node.Name, wfv1.NodeFailed, "pod was absent")
103+
} else {
104+
woc.log.Debugf("Determined controller shouldn't timeout %s", result.Name)
105+
}
106+
}
107+
75108
nodeID := result.Name
76109
old, err := woc.wf.Status.Nodes.Get(nodeID)
77110
if err != nil {
@@ -98,4 +131,5 @@ func (woc *wfOperationCtx) taskResultReconciliation() {
98131
woc.updated = true
99132
}
100133
}
134+
return nil
101135
}

0 commit comments

Comments
 (0)