Skip to content

Commit

Permalink
fix: Exit lifecycle hook should respect expression. Fixes #8742 (#8744)
Browse files Browse the repository at this point in the history
Signed-off-by: Yuan Tang <[email protected]>
  • Loading branch information
terrytangyuan committed May 13, 2022
1 parent 91fc3ea commit 354dee8
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 18 deletions.
36 changes: 33 additions & 3 deletions test/e2e/hooks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ spec:
}).ExpectWorkflowNode(func(status v1alpha1.NodeStatus) bool {
return strings.Contains(status.Name, "hook")
}, func(t *testing.T, status *v1alpha1.NodeStatus, pod *apiv1.Pod) {

assert.Equal(t, v1alpha1.NodeSucceeded, status.Phase)
})
}
Expand All @@ -71,7 +70,6 @@ spec:
- - name: step-1
hooks:
exit:
expression: steps["step-1"].status == "Running"
template: http
success:
expression: steps["step-1"].status == "Succeeded"
Expand All @@ -85,11 +83,43 @@ spec:
WaitForWorkflow(fixtures.ToBeSucceeded).
Then().
ExpectWorkflow(func(t *testing.T, metadata *v1.ObjectMeta, status *v1alpha1.WorkflowStatus) {
assert.Equal(t, status.Phase, v1alpha1.WorkflowSucceeded)
assert.Equal(t, v1alpha1.WorkflowSucceeded, status.Phase)
}).ExpectWorkflowNode(func(status v1alpha1.NodeStatus) bool {
return strings.Contains(status.Name, "hook")
}, func(t *testing.T, status *v1alpha1.NodeStatus, pod *apiv1.Pod) {
assert.Equal(t, v1alpha1.NodeSucceeded, status.Phase)
})
}

func (s *HooksSuite) TestExitHookWithExpression() {
s.Given().
Workflow(`apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: lifecycle-hook-exit-hook-
spec:
entrypoint: main
templates:
- name: main
steps:
- - name: step-1
hooks:
exit:
expression: steps["step-1"].status == "Running"
template: http
template: http
- name: http
http:
url: "https://httpstat.us"
`).When().
SubmitWorkflow().
WaitForWorkflow(fixtures.ToBeSucceeded).
Then().
ExpectWorkflow(func(t *testing.T, metadata *v1.ObjectMeta, status *v1alpha1.WorkflowStatus) {
assert.Equal(t, v1alpha1.WorkflowRunning, status.Phase)
}).ExpectWorkflowNode(func(status v1alpha1.NodeStatus) bool {
return strings.Contains(status.Name, "hook")
}, func(t *testing.T, status *v1alpha1.NodeStatus, pod *apiv1.Pod) {
assert.Equal(t, v1alpha1.NodeSucceeded, status.Phase)
})
}
Expand Down
38 changes: 24 additions & 14 deletions workflow/controller/exit_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"fmt"

wfv1 "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1"
"github.com/argoproj/argo-workflows/v3/util/expr/argoexpr"
"github.com/argoproj/argo-workflows/v3/util/expr/env"
"github.com/argoproj/argo-workflows/v3/util/template"
"github.com/argoproj/argo-workflows/v3/workflow/common"
"github.com/argoproj/argo-workflows/v3/workflow/templateresolution"
Expand All @@ -17,27 +19,35 @@ func (woc *wfOperationCtx) runOnExitNode(ctx context.Context, exitHook *wfv1.Lif
lastChildNode := getChildNodeIndex(parentNode, woc.wf.Status.Nodes, -1)
outputs = lastChildNode.Outputs
}

if exitHook != nil && woc.GetShutdownStrategy().ShouldExecute(true) {
woc.log.WithField("lifeCycleHook", exitHook).Infof("Running OnExit handler")

onExitNodeName := common.GenerateOnExitNodeName(parentNode.Name)
resolvedArgs := exitHook.Arguments
execute := true
var err error
if !resolvedArgs.IsEmpty() && outputs != nil {
resolvedArgs, err = woc.resolveExitTmplArgument(exitHook.Arguments, prefix, outputs)
if exitHook.Expression != "" {
execute, err = argoexpr.EvalBool(exitHook.Expression, env.GetFuncMap(template.EnvMap(woc.globalParams)))
if err != nil {
return true, nil, err
}

}
onExitNode, err := woc.executeTemplate(ctx, onExitNodeName, &wfv1.WorkflowStep{Template: exitHook.Template, TemplateRef: exitHook.TemplateRef}, tmplCtx, resolvedArgs, &executeTemplateOpts{
if execute {
woc.log.WithField("lifeCycleHook", exitHook).Infof("Running OnExit handler")

onExitNodeName := common.GenerateOnExitNodeName(parentNode.Name)
resolvedArgs := exitHook.Arguments
if !resolvedArgs.IsEmpty() && outputs != nil {
resolvedArgs, err = woc.resolveExitTmplArgument(exitHook.Arguments, prefix, outputs)
if err != nil {
return true, nil, err
}

boundaryID: boundaryID,
onExitTemplate: true,
})
woc.addChildNode(parentNode.Name, onExitNodeName)
return true, onExitNode, err
}
onExitNode, err := woc.executeTemplate(ctx, onExitNodeName, &wfv1.WorkflowStep{Template: exitHook.Template, TemplateRef: exitHook.TemplateRef}, tmplCtx, resolvedArgs, &executeTemplateOpts{

boundaryID: boundaryID,
onExitTemplate: true,
})
woc.addChildNode(parentNode.Name, onExitNodeName)
return true, onExitNode, err
}
}
return false, nil, nil
}
Expand Down
2 changes: 1 addition & 1 deletion workflow/controller/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (

func (woc *wfOperationCtx) executeWfLifeCycleHook(ctx context.Context, tmplCtx *templateresolution.Context) error {
for hookName, hook := range woc.execWf.Spec.Hooks {
//exit hook will be execute in runOnExitNode
//exit hook will be executed in runOnExitNode
if hookName == wfv1.ExitLifecycleEvent {
continue
}
Expand Down

0 comments on commit 354dee8

Please sign in to comment.