(ctx context.Context, dagCtx *dagContext, task *wfv1.DAGTask)
| 666 | } |
| 667 | |
| 668 | func (woc *wfOperationCtx) buildLocalScopeFromTask(ctx context.Context, dagCtx *dagContext, task *wfv1.DAGTask) (*wfScope, error) { |
| 669 | // build up the scope |
| 670 | scope := createScope(dagCtx.tmpl) |
| 671 | woc.addOutputsToLocalScope("workflow", woc.wf.Status.Outputs, scope) |
| 672 | |
| 673 | ancestors := common.GetTaskAncestry(ctx, dagCtx, task.Name) |
| 674 | for _, ancestor := range ancestors { |
| 675 | ancestorNode := dagCtx.getTaskNode(ctx, ancestor) |
| 676 | if ancestorNode == nil { |
| 677 | return nil, argoerrors.InternalErrorf("Ancestor task node %s not found", ancestor) |
| 678 | } |
| 679 | prefix := fmt.Sprintf("tasks.%s", ancestor) |
| 680 | if ancestorNode.Type == wfv1.NodeTypeTaskGroup { |
| 681 | var ancestorNodes []wfv1.NodeStatus |
| 682 | for _, node := range woc.wf.Status.Nodes { |
| 683 | if node.BoundaryID == dagCtx.boundaryID && strings.HasPrefix(node.Name, ancestorNode.Name+"(") { |
| 684 | ancestorNodes = append(ancestorNodes, node) |
| 685 | } |
| 686 | } |
| 687 | _, _, templateStored, err := dagCtx.tmplCtx.ResolveTemplate(ctx, ancestorNode) |
| 688 | if err != nil { |
| 689 | return nil, argoerrors.InternalWrapError(err) |
| 690 | } |
| 691 | // A new template was stored during resolution, persist it |
| 692 | if templateStored { |
| 693 | woc.updated = true |
| 694 | } |
| 695 | |
| 696 | err = woc.processAggregateNodeOutputs(scope, prefix, ancestorNodes) |
| 697 | if err != nil { |
| 698 | return nil, argoerrors.InternalWrapError(err) |
| 699 | } |
| 700 | } |
| 701 | woc.buildLocalScope(scope, prefix, ancestorNode) |
| 702 | |
| 703 | // For skipped/omitted ancestors that produced no outputs, populate scope with |
| 704 | // empty values for the template's declared output parameters. This prevents |
| 705 | // downstream tasks from getting stuck in a requeue loop when they reference |
| 706 | // outputs from a dependency that was legitimately skipped. |
| 707 | if (ancestorNode.Phase == wfv1.NodeSkipped || ancestorNode.Phase == wfv1.NodeOmitted) && ancestorNode.Outputs == nil { |
| 708 | ancestorTask := dagCtx.GetTask(ctx, ancestor) |
| 709 | _, tmpl, _, err := dagCtx.tmplCtx.ResolveTemplate(ctx, ancestorTask) |
| 710 | if err == nil && tmpl != nil { |
| 711 | for _, param := range tmpl.Outputs.Parameters { |
| 712 | key := fmt.Sprintf("%s.outputs.parameters.%s", prefix, param.Name) |
| 713 | scope.addParamToScope(key, "") |
| 714 | } |
| 715 | if tmpl.Outputs.Result != nil { |
| 716 | scope.addParamToScope(fmt.Sprintf("%s.outputs.result", prefix), "") |
| 717 | } |
| 718 | } |
| 719 | } |
| 720 | } |
| 721 | return scope, nil |
| 722 | } |
| 723 | |
| 724 | // resolveDependencyReferences replaces any references to outputs of task dependencies, or artifacts in the inputs |
| 725 | // NOTE: by now, input parameters should have been substituted throughout the template |
no test coverage detected