Skip to content

Commit

Permalink
feat: add an allowFailure field to WorkflowStepSpec
Browse files Browse the repository at this point in the history
* do not fail the entire workflow if a step labeled with
  allowFailure throws an error

Signed-off-by: Alex Johnson <[email protected]>
  • Loading branch information
Alex Johnson committed Jun 13, 2024
1 parent 57aa5a8 commit 56e6b9a
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 12 deletions.
13 changes: 13 additions & 0 deletions core/src/commands/workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,16 @@ export class WorkflowCommand extends Command<Args, {}> {
}
} catch (rawErr) {
const err = toGardenError(rawErr)
if (step.continueOnError) {
result.steps[stepName] = {
number: index + 1,
outputs: {
stderr: err.toString(),
},
log: stepBodyLog.toString((entry) => entry.level <= LogLevel.info),
}
continue
}
garden.events.emit("workflowStepError", getStepEndEvent(index, stepStartedAt))
stepErrors[index] = [err]
printStepDuration({ ...stepParams, success: false })
Expand All @@ -201,6 +211,9 @@ export class WorkflowCommand extends Command<Args, {}> {
stepBodyLog.root.storeEntries = initSaveLogState

if (stepResult.errors && stepResult.errors.length > 0) {
if (step.continueOnError) {
continue
}
garden.events.emit("workflowStepError", getStepEndEvent(index, stepStartedAt))
logErrors(outerLog, stepResult.errors, index, steps.length, step.description)
stepErrors[index] = stepResult.errors
Expand Down
2 changes: 2 additions & 0 deletions core/src/config/workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ export interface WorkflowStepSpec {
script?: string
skip?: boolean
when?: workflowStepModifier
continueOnError?: boolean
}

export const workflowStepSchema = createSchema({
Expand Down Expand Up @@ -265,6 +266,7 @@ export const workflowStepSchema = createSchema({
See the [workflows guide](${DOCS_BASE_URL}/using-garden/workflows#the-skip-and-when-options) for details
and examples.
`),
continueOnError: joi.boolean().description(`Set to true to continue if the step errors.`).default(false),
}),
xor: [["command", "script"]],
})
Expand Down
29 changes: 29 additions & 0 deletions core/test/unit/src/commands/workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,35 @@ describe("RunWorkflowCommand", () => {
expect(result.errors || []).to.eql([])
})

it("should allow failure for error in step", async () => {
garden.setWorkflowConfigs([
{
apiVersion: GardenApiVersion.v0,
name: "workflow-a",
kind: "Workflow",
internal: {
basePath: garden.projectRoot,
},
files: [],
envVars: {},
resources: defaultWorkflowResources,
steps: [
{ script: "echo error!; exit 1", continueOnError: true }, // <-- error thrown here
{ command: ["echo", "success!"] },
],
},
])

const { result, errors } = await cmd.action({ ...defaultParams, args: { workflow: "workflow-a" } })

expect(result).to.exist
expect(errors).to.not.exist
expect(result?.steps).to.have.property("step-1")
expect(result?.steps["step-1"].outputs).to.have.property("stderr")
expect(result?.steps).to.have.property("step-2")
expect(result?.steps["step-2"].outputs).to.not.have.property("stderr")
})

it("should add workflowStep metadata to log entries provided to steps", async () => {
const _garden = await makeTestGardenA(undefined)
// Ensure log entries are empty
Expand Down
57 changes: 45 additions & 12 deletions core/test/unit/src/config/workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import { expect } from "chai"
import type { TestGarden } from "../../../helpers.js"
import { expectError, getDataDir, makeTestGarden, makeTestGardenA } from "../../../helpers.js"
import type { WorkflowConfig, TriggerSpec } from "../../../../src/config/workflow.js"
import type { WorkflowConfig, WorkflowStepSpec, TriggerSpec } from "../../../../src/config/workflow.js"
import {
resolveWorkflowConfig,
populateNamespaceForTriggers,
Expand Down Expand Up @@ -39,6 +39,12 @@ describe("resolveWorkflowConfig", () => {
keepAliveHours: 48,
}

const defaultWorkflowStep: WorkflowStepSpec = {
skip: false,
when: "onSuccess",
continueOnError: false,
}

before(async () => {
garden = await makeTestGardenA()
garden["secrets"] = { foo: "bar", bar: "baz", baz: "banana" }
Expand All @@ -55,8 +61,15 @@ describe("resolveWorkflowConfig", () => {
description: "Sample workflow",
envVars: {},
steps: [
{ description: "Deploy the stack", command: ["deploy"], skip: false, when: "onSuccess", envVars: {} },
{ command: ["test"], skip: false, when: "onSuccess", envVars: {} },
{
...defaultWorkflowStep,
description: "Deploy the stack",
command: ["deploy"],
skip: false,
when: "onSuccess",
envVars: {},
},
{ ...defaultWorkflowStep, command: ["test"], skip: false, when: "onSuccess", envVars: {} },
],
triggers: [
{
Expand Down Expand Up @@ -85,8 +98,15 @@ describe("resolveWorkflowConfig", () => {
envVars: {},
limits: minimumWorkflowLimits, // <----
steps: [
{ description: "Deploy the stack", command: ["deploy"], skip: false, when: "onSuccess", envVars: {} },
{ command: ["test"], skip: false, when: "onSuccess", envVars: {} },
{
...defaultWorkflowStep,
description: "Deploy the stack",
command: ["deploy"],
skip: false,
when: "onSuccess",
envVars: {},
},
{ ...defaultWorkflowStep, command: ["test"], skip: false, when: "onSuccess", envVars: {} },
],
triggers: [
{
Expand Down Expand Up @@ -145,12 +165,13 @@ describe("resolveWorkflowConfig", () => {
envVars: {},
steps: [
{
...defaultWorkflowStep,
description: "Deploy the stack",
command: ["deploy", "${var.foo}"],
skip: false,
when: "onSuccess",
},
{ script: "echo ${var.foo}", skip: false, when: "onSuccess" },
{ ...defaultWorkflowStep, script: "echo ${var.foo}", skip: false, when: "onSuccess" },
],
}

Expand All @@ -169,8 +190,14 @@ describe("resolveWorkflowConfig", () => {

envVars: {},
steps: [
{ description: "Deploy the stack", command: ["deploy"], skip: false, when: "onSuccess" },
{ command: ["test"], skip: false, when: "onSuccess" },
{
...defaultWorkflowStep,
description: "Deploy the stack",
command: ["deploy"],
skip: false,
when: "onSuccess",
},
{ ...defaultWorkflowStep, command: ["test"], skip: false, when: "onSuccess" },
],
}

Expand All @@ -186,8 +213,14 @@ describe("resolveWorkflowConfig", () => {

envVars: {},
steps: [
{ description: "Deploy the stack", command: ["deploy"], skip: false, when: "onSuccess" },
{ command: ["test"], skip: false, when: "onSuccess" },
{
...defaultWorkflowStep,
description: "Deploy the stack",
command: ["deploy"],
skip: false,
when: "onSuccess",
},
{ ...defaultWorkflowStep, command: ["test"], skip: false, when: "onSuccess" },
],
triggers: [
{
Expand Down Expand Up @@ -221,8 +254,8 @@ describe("resolveWorkflowConfig", () => {
...defaults,
...config,
steps: [
{ description: "Deploy the stack", command: ["deploy"], skip: false, when: "onSuccess", envVars: {} },
{ command: ["test"], skip: false, when: "onSuccess", envVars: {} },
{ ...defaultWorkflowStep, description: "Deploy the stack", command: ["deploy"], envVars: {} },
{ ...defaultWorkflowStep, command: ["test"], envVars: {} },
],
})
})
Expand Down
3 changes: 3 additions & 0 deletions docs/reference/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -2794,6 +2794,9 @@ workflowConfigs:
# and examples.
when:

# Set to true to continue if the step errors.
continueOnError:

# A list of triggers that determine when the workflow should be run, and which environment should be used (Garden
# Cloud only).
triggers:
Expand Down
13 changes: 13 additions & 0 deletions docs/reference/workflow-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ steps:
# and examples.
when: onSuccess

# Set to true to continue if the step errors.
continueOnError: false

# A list of triggers that determine when the workflow should be run, and which environment should be used (Garden
# Cloud only).
triggers:
Expand Down Expand Up @@ -484,6 +487,16 @@ and examples.
| -------- | ------------- | -------- |
| `string` | `"onSuccess"` | No |

### `steps[].continueOnError`

[steps](#steps) > continueOnError

Set to true to continue if the step errors.

| Type | Default | Required |
| --------- | ------- | -------- |
| `boolean` | `false` | No |

### `triggers[]`

A list of triggers that determine when the workflow should be run, and which environment should be used (Garden Cloud only).
Expand Down

0 comments on commit 56e6b9a

Please sign in to comment.