executeStepGroup examines a list of parallel steps and executes them in parallel. Handles referencing of variables in scope, expands `withItem` clauses, and evaluates `when` expressions
(ctx context.Context, stepGroup []wfv1.WorkflowStep, sgNodeName string, stepsCtx *stepsContext)
| 232 | // executeStepGroup examines a list of parallel steps and executes them in parallel. |
| 233 | // Handles referencing of variables in scope, expands `withItem` clauses, and evaluates `when` expressions |
| 234 | func (woc *wfOperationCtx) executeStepGroup(ctx context.Context, stepGroup []wfv1.WorkflowStep, sgNodeName string, stepsCtx *stepsContext) (*wfv1.NodeStatus, error) { |
| 235 | node, err := woc.wf.GetNodeByName(sgNodeName) |
| 236 | if err != nil { |
| 237 | return nil, err |
| 238 | } |
| 239 | if node.Fulfilled() && woc.childrenFulfilled(node) { |
| 240 | woc.log.WithField("node", node).Debug(ctx, "Step group node already marked completed") |
| 241 | return node, nil |
| 242 | } |
| 243 | |
| 244 | // First, resolve any references to outputs from previous steps, and perform substitution |
| 245 | stepGroup, err = woc.resolveReferences(ctx, stepGroup, stepsCtx.scope) |
| 246 | if err != nil { |
| 247 | if errors.Is(err, ErrRequeue) { |
| 248 | return node, nil |
| 249 | } |
| 250 | return woc.markNodeError(ctx, sgNodeName, err), nil |
| 251 | } |
| 252 | |
| 253 | // Next, expand the step's withItems (if any) |
| 254 | stepGroup, err = woc.expandStepGroup(ctx, sgNodeName, stepGroup, stepsCtx) |
| 255 | if err != nil { |
| 256 | return woc.markNodeError(ctx, sgNodeName, err), nil |
| 257 | } |
| 258 | |
| 259 | // Maps nodes to their steps |
| 260 | nodeSteps := make(map[string]wfv1.WorkflowStep) |
| 261 | |
| 262 | // The template scope of this step group. |
| 263 | stepTemplateScope := stepsCtx.tmplCtx.GetTemplateScope() |
| 264 | |
| 265 | // Kick off all parallel steps in the group |
| 266 | for _, step := range stepGroup { |
| 267 | childNodeName := fmt.Sprintf("%s.%s", sgNodeName, step.Name) |
| 268 | |
| 269 | // Check the step's when clause to decide if it should execute |
| 270 | proceed, err := shouldExecute(step.When) |
| 271 | if err != nil { |
| 272 | woc.initializeNode(ctx, childNodeName, wfv1.NodeTypeSkipped, stepTemplateScope, &step, stepsCtx.boundaryID, wfv1.NodeError, &wfv1.NodeFlag{}, true, err.Error()) |
| 273 | woc.addChildNode(ctx, sgNodeName, childNodeName) |
| 274 | woc.markNodeError(ctx, childNodeName, err) |
| 275 | return woc.markNodeError(ctx, sgNodeName, err), nil |
| 276 | } |
| 277 | if !proceed { |
| 278 | if _, err := woc.wf.GetNodeByName(childNodeName); err != nil { |
| 279 | skipReason := fmt.Sprintf("when '%s' evaluated false", step.When) |
| 280 | woc.log.WithFields(logging.Fields{"childNodeName": childNodeName, "skipReason": skipReason}).Info(ctx, "Skipping") |
| 281 | woc.initializeNode(ctx, childNodeName, wfv1.NodeTypeSkipped, stepTemplateScope, &step, stepsCtx.boundaryID, wfv1.NodeSkipped, &wfv1.NodeFlag{}, true, skipReason) |
| 282 | woc.addChildNode(ctx, sgNodeName, childNodeName) |
| 283 | } |
| 284 | continue |
| 285 | } |
| 286 | |
| 287 | if stepsCtx.boundaryID == "" { |
| 288 | woc.log.Warn(ctx, "boundaryID was nil") |
| 289 | } |
| 290 | childNode, err := woc.executeTemplate(ctx, childNodeName, &step, stepsCtx.tmplCtx, step.Arguments, &executeTemplateOpts{boundaryID: stepsCtx.boundaryID, onExitTemplate: stepsCtx.onExitTemplate}) |
| 291 | if err != nil { |
no test coverage detected