Fix: report step failure when the CUE template fails to compile
Description of your changes
The check that surfaces a step template error read exec.stepStatus — the previous reconcile's phase — where exec.wfStatus, this reconcile's, was intended. wfStatus is re-seeded to the neutral Succeeded default in the task generator on every reconcile; stepStatus is loaded from the persisted status.
So the first reconcile caught the error and recorded Failed, and every later reconcile read that Failed, skipped the check, and returned the untouched Succeeded default — reporting success for a step whose actions never ran. Recording the failure disabled the check that found it. For a standalone WorkflowRun the false succeeded was permanent, since a finished run is skipped on reconcile.
Reading wfStatus leaves the in-flight paths alone: Wait() and Suspend() set it to Running and Suspending while CompileString evaluates the template, so a mid-flight step still bypasses the check.
Fixes kubevela/kubevela#7275
I have:
- Read and followed KubeVela's contribution process.
- Related Docs updated properly. In a new feature or configuration option, an update to the documentation is necessary.
- Run
make reviewableto ensure this PR is ready for review. - Added
backport release-x.ylabels to auto-backport this PR if necessary.
How has this code been tested
New test TestTemplateErrorPersistsAcrossReconciles in pkg/tasks/custom/task_test.go. It runs a step whose template fails to evaluate, then runs it a second time with the first pass's status fed back in as options.StepStatus, and asserts the second pass is still Failed with a non-empty message. On master the second pass returns succeeded with an empty message; with this change it stays Failed.
Also added TestInFlightStatusSurvivesSecondReconcile, which asserts a Wait() step stays Running and a Suspend() step stays Suspending across two passes.
go test ./pkg/tasks/... passes. Originally found in a live cluster: a WorkflowStepDefinition with an unused CUE import (rejected by CUE 0.14.x) whose only action deleted a pod. The run reported succeeded while the pod was untouched.
Special notes for your reviewer
Two things are needed to reach this bug, which is why no existing test caught it. The status has to be fed back in as options.StepStatus, and the runner has to be regenerated for the second pass — the executor struct, and therefore the optimistic seed on wfStatus, is created per generator call rather than per Run call. Reusing one runner carries pass 1's Failed into pass 2 and hides the defect. Real reconciles regenerate runners (controllers/workflowrun_controller.go:134). The existing failed-after-retries case loops but omits the first ingredient.
Why this is narrow. On the first reconcile stepStatus and wfStatus both hold the same default, so behaviour there is unchanged. The fix only affects reconciles after the first, making them behave as the first already does — so it cannot introduce a failure mode that does not already occur.
One behaviour change worth calling out. A step with a persistently broken template now consumes errorRetryTimes and fails the workflow, instead of reporting success on the retry. A run that completes in seconds today will retry and then fail. That is the intent, but it will be visible to anyone tracking run duration or alerting on workflow failures.
On the phase condition itself. I read it as protecting steps that are legitimately mid-flight, but that is inference. Deleting the condition outright leaves the wait and suspend tests passing, because those steps leave a clean taskv and never reach the taskv.Err() != nil half. I kept it since it is clearly deliberate and harmless, but if you know the original intent I would rather match it.
Origin. Introduced in 7d94489 (#162, "Chore: refactor the cue engine with cuex"), which replaced an explicit doSteps() call and its unconditional error handling with the post-hoc taskv.Err() inspection.
Summary by cubic
Fixes steps incorrectly reported as succeeded after a CUE template compile error. We now read wfStatus (this reconcile) instead of stepStatus (previous reconcile) so compile failures are surfaced every time.
-
Bug Fixes
- Gate template-error reporting on
exec.wfStatusrather thanexec.stepStatus. - Preserve in-flight states:
Wait()stays Running andSuspend()stays Suspending. - Added tests:
TestTemplateErrorPersistsAcrossReconciles,TestInFlightStatusSurvivesSecondReconcile. - e2e mock server now returns valid JSON with
application/jsonto avoidjson.Unmarshaltemplate errors.
- Gate template-error reporting on
-
Migration
- Steps with persistently broken templates now consume
errorRetryTimesand fail the workflow, instead of falsely succeeding.
- Steps with persistently broken templates now consume