diff --git a/docs/matrix.md b/docs/matrix.md index e0b21d3ba5b..ad820550799 100644 --- a/docs/matrix.md +++ b/docs/matrix.md @@ -166,40 +166,28 @@ Consuming `Results` from previous `TaskRuns` or `Runs` in a `Matrix`, which woul `TaskRuns` or `Runs` from the fanned out `PipelineTask`, is supported. Producing `Results` in from a `PipelineTask` with a `Matrix` is not yet supported - see [further details](#results-from-fanned-out-pipelinetasks). -`Matrix` supports Results of type String that are passed in individually: +`Matrix` supports Results of type String that are passed in individually, and of type Array, either individual +values by index or the entire array: ```yaml tasks: ... -- name: task-4 +- name: task-5 taskRef: - name: task-4 + name: task-5 matrix: params: - name: values value: - - (tasks.task-1.results.foo) # string - - (tasks.task-2.results.bar) # string - - (tasks.task-3.results.rad) # string + - $(tasks.task-1.results.foo) # string + - $(tasks.task-2.results.bar) # string + - $(tasks.task-3.results.rad[0]) # array index + - name: otherValues + value: $(tasks.task-4.results.someArray[*]) ``` For further information, see the example in [`PipelineRun` with `Matrix` and `Results`][pr-with-matrix-and-results]. -When we support `Results` of type Array at the `Pipeline` level, we will support passing Results into the `Matrix`. -> Note: Results of type Array are not yet supported in the Pipeline level. - -```yaml -tasks: -... -- name: task-5 - taskRef: - name: task-5 - matrix: - params: - - name: values - value: (tasks.task-4.results.foo) # array -``` - #### Results from fanned out PipelineTasks Consuming `Results` from fanned out `PipelineTasks` will not be in the supported in the initial iteration diff --git a/examples/v1beta1/pipelineruns/alpha/pipelinerun-with-matrix-and-results.yaml b/examples/v1beta1/pipelineruns/alpha/pipelinerun-with-matrix-and-results.yaml index 2b8c62b96bd..d53ce940186 100644 --- a/examples/v1beta1/pipelineruns/alpha/pipelinerun-with-matrix-and-results.yaml +++ b/examples/v1beta1/pipelineruns/alpha/pipelinerun-with-matrix-and-results.yaml @@ -39,16 +39,13 @@ spec: - name: get-browsers taskSpec: results: - - name: one - - name: two - - name: three + - name: browsers steps: - name: echo image: alpine script: | - printf chrome | tee /tekton/results/one - printf safari | tee /tekton/results/two - printf firefox | tee /tekton/results/three + #!/usr/bin/env bash + echo -n "[\"chrome\",\"safari\",\"firefox\"]" | tee $(results.browsers.path) - name: platforms-and-browsers-dag matrix: params: @@ -58,8 +55,6 @@ spec: - $(tasks.get-platforms.results.two) - $(tasks.get-platforms.results.three) - name: browser - value: - - $(tasks.get-browsers.results.one) - - $(tasks.get-browsers.results.two) + value: $(tasks.get-browsers.results.browsers[*]) taskRef: name: platform-browsers diff --git a/pkg/apis/pipeline/v1beta1/param_types.go b/pkg/apis/pipeline/v1beta1/param_types.go index 959d6b7ca71..f6bab72202b 100644 --- a/pkg/apis/pipeline/v1beta1/param_types.go +++ b/pkg/apis/pipeline/v1beta1/param_types.go @@ -322,7 +322,7 @@ func validatePipelineParametersVariablesInMatrixParameters(matrix []Param, prefi func validateParametersInTaskMatrix(matrix *Matrix) (errs *apis.FieldError) { if matrix != nil { for _, param := range matrix.Params { - if param.Value.Type != ParamTypeArray { + if param.Value.Type == ParamTypeObject || (param.Value.Type == ParamTypeString && !fullArrayVariableSubstitutionRegex.MatchString(param.Value.StringVal)) { errs = errs.Also(apis.ErrInvalidValue("parameters of type array only are allowed in matrix", "").ViaFieldKey("matrix", param.Name)) } } diff --git a/pkg/apis/pipeline/v1beta1/pipeline_validation_test.go b/pkg/apis/pipeline/v1beta1/pipeline_validation_test.go index e1c2d6b42ff..1a2c7ecde3e 100644 --- a/pkg/apis/pipeline/v1beta1/pipeline_validation_test.go +++ b/pkg/apis/pipeline/v1beta1/pipeline_validation_test.go @@ -3396,7 +3396,7 @@ func Test_validateMatrix(t *testing.T) { }}, }}, }, { - name: "parameters in matrix are strings", + name: "parameters in matrix are strings or full array parameters", tasks: PipelineTaskList{{ Name: "a-task", TaskRef: &TaskRef{Name: "a-task"}, @@ -3413,10 +3413,17 @@ func Test_validateMatrix(t *testing.T) { Params: []Param{{ Name: "baz", Value: ParamValue{Type: ParamTypeString, StringVal: "baz"}, }}}, + }, { + Name: "c-task", + TaskRef: &TaskRef{Name: "c-task"}, + Matrix: &Matrix{ + Params: []Param{{ + Name: "faz", Value: ParamValue{Type: ParamTypeString, StringVal: "$(params.apple[1])"}, + }}}, }}, wantErrs: &apis.FieldError{ Message: "invalid value: parameters of type array only are allowed in matrix", - Paths: []string{"[0].matrix[foo]", "[0].matrix[bar]", "[1].matrix[baz]"}, + Paths: []string{"[0].matrix[foo]", "[0].matrix[bar]", "[1].matrix[baz]", "[2].matrix[faz]"}, }, }, { name: "parameters in matrix are arrays", @@ -3446,6 +3453,13 @@ func Test_validateMatrix(t *testing.T) { Params: []Param{{ Name: "b-param", Value: ParamValue{Type: ParamTypeArray, ArrayVal: []string{"$(tasks.bar-task.results.b-result)"}}, }}}, + }, { + Name: "c-task", + TaskRef: &TaskRef{Name: "c-task"}, + Matrix: &Matrix{ + Params: []Param{{ + Name: "c-param", Value: ParamValue{Type: ParamTypeArray, ArrayVal: []string{"$(tasks.bar-task.results.c-result[*])"}}, + }}}, }}, }} for _, tt := range tests { diff --git a/pkg/apis/pipeline/v1beta1/resultref.go b/pkg/apis/pipeline/v1beta1/resultref.go index a82835348d8..c18ba4d4d87 100644 --- a/pkg/apis/pipeline/v1beta1/resultref.go +++ b/pkg/apis/pipeline/v1beta1/resultref.go @@ -46,6 +46,8 @@ const ( // TODO(#2462) use one regex across all substitutions // variableSubstitutionFormat matches format like $result.resultname, $result.resultname[int] and $result.resultname[*] variableSubstitutionFormat = `\$\([_a-zA-Z0-9.-]+(\.[_a-zA-Z0-9.-]+)*(\[([0-9]+|\*)\])?\)` + // fullArrayVariableSubstitutionFormat matches the array replacement format $result.resultname[*] + fullArrayVariableSubstitutionFormat = `\$\([_a-zA-Z0-9.-]+(\.[_a-zA-Z0-9.-]+)*(\[\*\])\)` // exactVariableSubstitutionFormat matches strings that only contain a single reference to result or param variables, but nothing else // i.e. `$(result.resultname)` is a match, but `foo $(result.resultname)` is not. exactVariableSubstitutionFormat = `^\$\([_a-zA-Z0-9.-]+(\.[_a-zA-Z0-9.-]+)*(\[([0-9]+|\*)\])?\)$` @@ -57,6 +59,7 @@ const ( // VariableSubstitutionRegex is a regex to find all result matching substitutions var VariableSubstitutionRegex = regexp.MustCompile(variableSubstitutionFormat) +var fullArrayVariableSubstitutionRegex = regexp.MustCompile(fullArrayVariableSubstitutionFormat) var exactVariableSubstitutionRegex = regexp.MustCompile(exactVariableSubstitutionFormat) var resultNameFormatRegex = regexp.MustCompile(ResultNameFormat) diff --git a/pkg/reconciler/pipelinerun/pipelinerun_test.go b/pkg/reconciler/pipelinerun/pipelinerun_test.go index 30554cd00d3..b16cac228f8 100644 --- a/pkg/reconciler/pipelinerun/pipelinerun_test.go +++ b/pkg/reconciler/pipelinerun/pipelinerun_test.go @@ -9032,20 +9032,18 @@ spec: - name: platform-1 - name: platform-2 - name: platform-3 - - name: browser-1 - - name: browser-2 - - name: browser-3 + - name: browsers + type: array - name: version steps: - name: echo image: alpine script: | + #!/usr/bin/env bash printf linux | tee /tekton/results/platform-1 printf mac | tee /tekton/results/platform-2 printf windows | tee /tekton/results/platform-3 - printf chrome | tee /tekton/results/browser-1 - printf safari | tee /tekton/results/browser-2 - printf firefox | tee /tekton/results/browser-3 + echo -n "[\"chrome\",\"safari\",\"firefox\"]" | tee $(results.browsers.path) printf v0.33.0 | tee /tekton/results/version `) @@ -9254,10 +9252,7 @@ spec: - $(tasks.pt-with-result.results.platform-2) - $(tasks.pt-with-result.results.platform-3) - name: browser - value: - - $(tasks.pt-with-result.results.browser-1) - - $(tasks.pt-with-result.results.browser-2) - - $(tasks.pt-with-result.results.browser-3) + value: $(tasks.pt-with-result.results.browsers[*]) params: - name: version value: $(tasks.pt-with-result.results.version) @@ -9285,12 +9280,8 @@ status: value: mac - name: platform-3 value: windows - - name: browser-1 - value: chrome - - name: browser-2 - value: safari - - name: browser-3 - value: firefox + - name: browsers + value: chrome,safari,firefox - name: version value: v0.33.0 `), @@ -9307,6 +9298,9 @@ spec: name: p-dag status: pipelineSpec: + params: + - name: browsers + type: array tasks: - name: pt-with-result params: @@ -9331,10 +9325,7 @@ status: - $(tasks.pt-with-result.results.platform-2) - $(tasks.pt-with-result.results.platform-3) - name: browser - value: - - $(tasks.pt-with-result.results.browser-1) - - $(tasks.pt-with-result.results.browser-2) - - $(tasks.pt-with-result.results.browser-3) + value: $(tasks.pt-with-result.results.browsers[*]) params: - name: version value: $(tasks.pt-with-result.results.version) @@ -9563,6 +9554,12 @@ metadata: name: pr namespace: foo spec: + params: + - name: browsers + value: + - chrome + - safari + - firefox serviceAccountName: test-sa pipelineRef: name: %s diff --git a/pkg/reconciler/pipelinerun/resources/apply.go b/pkg/reconciler/pipelinerun/resources/apply.go index c43ec9e5dbf..dee0bd542de 100644 --- a/pkg/reconciler/pipelinerun/resources/apply.go +++ b/pkg/reconciler/pipelinerun/resources/apply.go @@ -178,7 +178,7 @@ func ApplyTaskResults(targets PipelineRunState, resolvedResultRefs ResolvedResul pipelineTask := resolvedPipelineRunTask.PipelineTask.DeepCopy() pipelineTask.Params = replaceParamValues(pipelineTask.Params, stringReplacements, arrayReplacements, objectReplacements) if pipelineTask.IsMatrixed() { - pipelineTask.Matrix.Params = replaceParamValues(pipelineTask.Matrix.Params, stringReplacements, nil, nil) + pipelineTask.Matrix.Params = replaceParamValues(pipelineTask.Matrix.Params, stringReplacements, arrayReplacements, nil) } pipelineTask.WhenExpressions = pipelineTask.WhenExpressions.ReplaceWhenExpressionsVariables(stringReplacements, arrayReplacements) if pipelineTask.TaskRef != nil && pipelineTask.TaskRef.Params != nil { diff --git a/pkg/reconciler/pipelinerun/resources/apply_test.go b/pkg/reconciler/pipelinerun/resources/apply_test.go index af3c0503318..1e1dcd89e75 100644 --- a/pkg/reconciler/pipelinerun/resources/apply_test.go +++ b/pkg/reconciler/pipelinerun/resources/apply_test.go @@ -2330,6 +2330,38 @@ func TestApplyTaskResults_MinimalExpression(t *testing.T) { }}}, }, }}, + }, { + name: "Test array result substitution on minimal variable substitution expression - matrix", + resolvedResultRefs: ResolvedResultRefs{{ + Value: *v1beta1.NewStructuredValues("arrayResultValueOne", "arrayResultValueTwo"), + ResultReference: v1beta1.ResultRef{ + PipelineTask: "aTask", + Result: "aResult", + }, + FromTaskRun: "aTaskRun", + }}, + targets: PipelineRunState{{ + PipelineTask: &v1beta1.PipelineTask{ + Name: "bTask", + TaskRef: &v1beta1.TaskRef{Name: "bTask"}, + Matrix: &v1beta1.Matrix{ + Params: []v1beta1.Param{{ + Name: "bParam", + Value: *v1beta1.NewStructuredValues(`$(tasks.aTask.results.aResult[*])`), + }}}, + }, + }}, + want: PipelineRunState{{ + PipelineTask: &v1beta1.PipelineTask{ + Name: "bTask", + TaskRef: &v1beta1.TaskRef{Name: "bTask"}, + Matrix: &v1beta1.Matrix{ + Params: []v1beta1.Param{{ + Name: "bParam", + Value: *v1beta1.NewStructuredValues("arrayResultValueOne", "arrayResultValueTwo"), + }}}, + }, + }}, }, { name: "Test array result substitution on minimal variable substitution expression - when expressions", resolvedResultRefs: ResolvedResultRefs{{