Skip to content

Commit

Permalink
TEP-140: Produce Results in Matrix
Browse files Browse the repository at this point in the history
This commit enables producing Results from Matrixed PipelineTasks so that they can be used in subsequent PipelineTasks. A Pipeline author can now declare a matrixed taskRun that emits results of type string that are fanned out over multiple taskRuns and aggregated into an array of results that can then be consumed by another pipelineTask using the syntax `$(tasks.<pipelineTaskName>.results.<resultName>[*])`.

This commit also introduces 2 context variables to 1) Access Matrix Combinations Length using `tasks.<pipelineTaskName>.matrix.length` and 2) Access Aggregated Results Length using `tasks.<pipelineTaskName>.matrix.<resultName>.length`

Note: Currently, we don't support consuming a single instance/combinations of results. Authors must consume the entire aggregated results array.
  • Loading branch information
EmmaMunley committed Oct 4, 2023
1 parent cbabe7f commit 8100f01
Show file tree
Hide file tree
Showing 23 changed files with 3,331 additions and 201 deletions.
84 changes: 82 additions & 2 deletions docs/matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ weight: 406
- [Parameters in Matrix.Include.Params](#parameters-in-matrixincludeparams)
- [Specifying both `params` and `matrix` in a `PipelineTask`](#specifying-both-params-and-matrix-in-a-pipelinetask)
- [Context Variables](#context-variables)
- [Access Matrix Combinations Length](#access-matrix-combinations-length)
- [Access Aggregated Results Length](#access-aggregated-results-length)
- [Results](#results)
- [Specifying Results in a Matrix](#specifying-results-in-a-matrix)
- [Results in Matrix.Params](#results-in-matrixparams)
Expand Down Expand Up @@ -291,6 +293,38 @@ Similarly to the `Parameters` in the `Params` field, the `Parameters` in the `Ma
* `Pipeline` name
* `PipelineTask` retries


The following `context` variables allow users to access the `matrix` runtime data. Note: In order to create an ordering dependency, use `runAfter` or `taskResult` consumption as part of the same pipelineTask.

#### Access Matrix Combinations Length

The pipeline authors can access the total number of instances created as part of the `matrix` using the syntax: `tasks.<pipelineTaskName>.matrix.length`.

```yaml
- name: matrixed-echo-length
runAfter:
- matrix-emitting-results
params:
- name: matrixlength
value: $(tasks.matrix-emitting-results.matrix.length)
```

#### Access Aggregated Results Length

The pipeline authors can access the length of the array of aggregated results that were
actually produced using the syntax: `tasks.<pipelineTaskName>.matrix.<resultName>.length`. This will allow users to loop over the results produced.

```yaml
- name: matrixed-echo-results-length
runAfter:
- matrix-emitting-results
params:
- name: matrixlength
value: $(tasks.matrix-emitting-results.matrix.a-result.length)
```

See the full example here: [pr-with-matrix-context-variables]

## Results

### Specifying Results in a Matrix
Expand Down Expand Up @@ -360,8 +394,51 @@ tasks:

### Results from fanned out Matrixed PipelineTasks

Emitting `Results` from fanned out `PipelineTasks` is not currently supported.
We plan to support emitting `Results` from fanned out `PipelineTasks` in the near future.
Emitting `Results` from fanned out `PipelineTasks` is now supported. Each fanned out
`TaskRun` that produces `Result` of type `string` will be aggregated into an `array`
of `Results` during reconciliation, in which the whole `array` of `Results` can be consumed by another `pipelineTask` using the star notion [*].
Note: A known limitation is not being able to consume a singular result or specific
combinations of results produced by a previous fanned out `PipelineTask`.

| Result Type in `taskRef` or `taskSpec` | Parameter Type of Consumer | Specification |
|----------------------------------------|----------------------------|-------------------------------------------------------|
| string | array | `$(tasks.<pipelineTaskName>.results.<resultName>[*])` |
| array | Not Supported | Not Supported |
| object | Not Supported | Not Supported |

```yaml
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: platform-browser-tests
spec:
tasks:
- name: matrix-emitting-results
matrix:
params:
- name: platform
value:
- linux
- mac
- windows
- name: browser
value:
- chrome
- safari
- firefox
taskRef:
name: taskwithresults
kind: Task
- name: task-consuming-results
taskRef:
name: echoarrayurl
kind: Task
params:
- name: url
value: $(tasks.matrix-emitting-results.results.report-url[*])
...
```
See the full example [pr-with-matrix-emitting-results]


## Retries
Expand Down Expand Up @@ -851,4 +928,7 @@ status:
[cel]: https://github.com/tektoncd/experimental/tree/1609827ea81d05c8d00f8933c5c9d6150cd36989/cel
[pr-with-matrix]: ../examples/v1/pipelineruns/alpha/pipelinerun-with-matrix.yaml
[pr-with-matrix-and-results]: ../examples/v1/pipelineruns/alpha/pipelinerun-with-matrix-and-results.yaml
[pr-with-matrix-context-variables]: ../examples/v1/pipelineruns/alpha/pipelinerun-with-matrix-context-variables.yaml
[pr-with-matrix-emitting-results]: ../examples/v1/pipelineruns/alpha/pipelinerun-with-matrix-emitting-results.yaml

[retries]: pipelines.md#using-the-retries-field
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: echomatrixlength
spec:
params:
- name: matrixlength
type: string
steps:
- name: echo
image: alpine
script: echo $(params.matrixlength)
---
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: echomatrixresultslength
spec:
params:
- name: matrixresultslength
type: string
steps:
- name: echo
image: alpine
script: echo $(params.matrixresultslength)
---
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: taskwithresults
spec:
params:
- name: IMAGE
- name: DIGEST
default: ""
results:
- name: IMAGE-DIGEST
steps:
- name: produce-results
image: bash:latest
script: |
#!/usr/bin/env bash
echo "Building image for $(params.IMAGE)"
echo -n "$(params.DIGEST)" | sha256sum | tee $(results.IMAGE-DIGEST.path)
---
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
generateName: matrix-emitting-results
spec:
serviceAccountName: "default"
pipelineSpec:
tasks:
- name: matrix-emitting-results
matrix:
include:
- name: build-1
params:
- name: IMAGE
value: image-1
- name: DOCKERFILE
value: path/to/Dockerfile1
- name: build-2
params:
- name: IMAGE
value: image-2
- name: DOCKERFILE
value: path/to/Dockerfile2
- name: build-3
params:
- name: IMAGE
value: image-3
- name: DOCKERFILE
value: path/to/Dockerfile3
taskRef:
name: taskwithresults
kind: Task
- name: matrixed-echo-length
runAfter:
- matrix-emitting-results
params:
- name: matrixlength
value: $(tasks.matrix-emitting-results.matrix.length)
taskRef:
name: echomatrixlength
kind: Task
- name: matrixed-echo-results-length
runAfter:
- matrix-emitting-results
params:
- name: matrixresultslength
value: $(tasks.matrix-emitting-results.matrix.IMAGE-DIGEST.length)
taskRef:
name: echomatrixresultslength
kind: Task
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: echostringurl
spec:
params:
- name: url
type: string
steps:
- name: echo
image: alpine
script: |
echo "$(params.url)"
---
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: echoarrayurl
spec:
params:
- name: url
type: array
steps:
- name: use-environments
image: bash:latest
args: ["$(params.url[*])"]
script: |
for arg in "$@"; do
echo "URL: $arg"
done
---
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: taskwithresults
spec:
params:
- name: platform
default: ""
- name: browser
default: ""
results:
- name: report-url
type: string
steps:
- name: produce-report-url
image: alpine
script: |
echo "Running tests on $(params.platform)-$(params.browser)"
echo -n "https://api.example/get-report/$(params.platform)-$(params.browser)" | tee $(results.report-url.path)
---
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
generateName: platforms-with-results
spec:
serviceAccountName: "default"
pipelineSpec:
tasks:
- name: matrix-emitting-results
matrix:
params:
- name: platform
value:
- linux
- mac
- windows
- name: browser
value:
- chrome
- safari
- firefox
taskRef:
name: taskwithresults
kind: Task
- name: task-consuming-results
taskRef:
name: echoarrayurl
kind: Task
params:
- name: url
value: $(tasks.matrix-emitting-results.results.report-url[*])
- name: matrix-consuming-results
taskRef:
name: echostringurl
kind: Task
matrix:
params:
- name: url
value: $(tasks.matrix-emitting-results.results.report-url[*])
22 changes: 22 additions & 0 deletions pkg/apis/pipeline/v1/param_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,28 @@ func (ps Params) extractParamMapArrVals() map[string][]string {
return paramsMap
}

// ParseTaskandResultName parses "task name", "result name" from a Matrix Context Variable
// Valid Example 1:
// - Input: tasks.myTask.matrix.length
// - Output: "myTask", ""
// Valid Example 2:
// - Input: tasks.myTask.matrix.ResultName.length
// - Output: "myTask", "ResultName"
func (p Param) ParseTaskandResultName() (string, string) {
if expressions, ok := p.GetVarSubstitutionExpressions(); ok {
for _, expression := range expressions {
subExpressions := strings.Split(expression, ".")
pipelineTaskName := subExpressions[1]
if len(subExpressions) == 4 {
return pipelineTaskName, ""
} else if len(subExpressions) == 5 {
return pipelineTaskName, subExpressions[3]
}
}
}
return "", ""
}

// Params is a list of Param
type Params []Param

Expand Down
30 changes: 30 additions & 0 deletions pkg/apis/pipeline/v1/param_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -653,3 +653,33 @@ func TestExtractDefaultParamArrayLengths(t *testing.T) {
})
}
}

func TestParseTaskandResultName(t *testing.T) {
tcs := []struct {
name string
param v1.Param
pipelineTaskName string
resultName string
}{{
name: "matrix length context var",
param: v1.Param{Name: "foo", Value: v1.ParamValue{StringVal: "$(tasks.matrix-emitting-results.matrix.length)", Type: v1.ParamTypeString}},
pipelineTaskName: "matrix-emitting-results",
}, {
name: "matrix results length context var",
param: v1.Param{Name: "foo", Value: v1.ParamValue{StringVal: "$(tasks.myTask.matrix.ResultName.length)", Type: v1.ParamTypeString}},
pipelineTaskName: "myTask",
resultName: "ResultName",
}}
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
pipelineTaskName, resultName := tc.param.ParseTaskandResultName()

if d := cmp.Diff(tc.pipelineTaskName, pipelineTaskName); d != "" {
t.Errorf(diff.PrintWantGot(d))
}
if d := cmp.Diff(tc.resultName, resultName); d != "" {
t.Errorf(diff.PrintWantGot(d))
}
})
}
}
Loading

0 comments on commit 8100f01

Please sign in to comment.