Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

kind/feat: passing artifacts between tasks #7978

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
365 changes: 314 additions & 51 deletions docs/artifacts.md

Large diffs are not rendered by default.

17 changes: 17 additions & 0 deletions docs/pipeline-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1428,6 +1428,9 @@ string
</table>
<h3 id="tekton.dev/v1.Artifacts">Artifacts
</h3>
<p>
(<em>Appears on:</em><a href="#tekton.dev/v1.TaskRunStatusFields">TaskRunStatusFields</a>)
</p>
<div>
<p>Artifacts represents the collection of input and output artifacts associated with
a task run or a similar process. Artifacts in this context are units of data or resources
Expand Down Expand Up @@ -5902,6 +5905,20 @@ All TaskRunStatus stored in RetriesStatus will have no date within the RetriesSt
</tr>
<tr>
<td>
<code>artifacts</code><br/>
<em>
<a href="#tekton.dev/v1.Artifacts">
Artifacts
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>Artifacts are the list of artifacts written out by the task&rsquo;s containers</p>
</td>
</tr>
<tr>
<td>
<code>sidecars</code><br/>
<em>
<a href="#tekton.dev/v1.SidecarState">
Expand Down
109 changes: 56 additions & 53 deletions docs/variables.md

Large diffs are not rendered by default.

60 changes: 60 additions & 0 deletions examples/v1/pipelineruns/alpha/consume-artifacts-from-task.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
generateName: pipelinerun-consume-tasks-artifacts
spec:
pipelineSpec:
tasks:
- name: produce-artifacts-task
taskSpec:
description: |
A simple task that produces artifacts
steps:
- name: produce-artifacts
image: bash:latest
script: |
#!/usr/bin/env bash
cat > $(artifacts.path) << EOF
{
"inputs":[
{
"name":"input-artifacts",
"values":[
{
"uri":"pkg:example.github.com/inputs",
"digest":{
"sha256":"b35cacccfdb1e24dc497d15d553891345fd155713ffe647c281c583269eaaae0"
}
}
]
}
],
"outputs":[
{
"name":"image",
"values":[
{
"uri":"pkg:github/package-url/purl-spec@244fd47e07d1004f0aed9c",
"digest":{
"sha256":"df85b9e3983fe2ce20ef76ad675ecf435cc99fc9350adc54fa230bae8c32ce48",
"sha1":"95588b8f34c31eb7d62c92aaa4e6506639b06ef2"
}
}
]
}
]
}
EOF
- name: consume-artifacts
runAfter:
- produce-artifacts-task
taskSpec:
steps:
- name: artifacts-consumer-python
image: python:latest
script: |
#!/usr/bin/env python3
import json
data = json.loads('$(tasks.produce-artifacts-task.outputs.image)')
if data[0]['uri'] != "pkg:github/package-url/purl-spec@244fd47e07d1004f0aed9c":
exit(1)
3 changes: 1 addition & 2 deletions examples/v1/taskruns/alpha/produce-consume-artifacts.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,11 @@ spec:
- name: artifacts-consumer
image: docker.io/library/bash:latest
script: |
echo $(steps.artifacts-producer.outputs)
echo $(steps.artifacts-producer.inputs.input-artifacts)
- name: artifacts-consumer-python
image: docker.io/library/python:latest
script: |
#!/usr/bin/env python3
import json
data = json.loads('$(steps.artifacts-producer.outputs)')
data = json.loads('$(steps.artifacts-producer.outputs.image)')
print(data[0]['uri'])
44 changes: 44 additions & 0 deletions examples/v1/taskruns/alpha/task-artifacts.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
apiVersion: tekton.dev/v1
kind: TaskRun
metadata:
generateName: task-run-artifacts
spec:
taskSpec:
description: |
A simple task that produces artifacts
steps:
- name: produce-artifacts
image: bash:latest
script: |
#!/usr/bin/env bash
cat > $(artifacts.path) << EOF
{
"inputs":[
{
"name":"input-artifacts",
"values":[
{
"uri":"pkg:example.github.com/inputs",
"digest":{
"sha256":"b35cacccfdb1e24dc497d15d553891345fd155713ffe647c281c583269eaaae0"
}
}
]
}
],
"outputs":[
{
"name":"image",
"values":[
{
"uri":"pkg:github/package-url/purl-spec@244fd47e07d1004f0aed9c",
"digest":{
"sha256":"df85b9e3983fe2ce20ef76ad675ecf435cc99fc9350adc54fa230bae8c32ce48",
"sha1":"95588b8f34c31eb7d62c92aaa4e6506639b06ef2"
}
}
]
}
]
}
EOF
17 changes: 12 additions & 5 deletions internal/artifactref/artifactref.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,17 @@ package artifactref

import "regexp"

// case 1: steps.<step-name>.inputs
// case 2: steps.<step-name>.outputs
// case 3: steps.<step-name>.inputs.<artifact-category-name>
// case 4: steps.<step-name>.outputs.<artifact-category-name>
const stepArtifactUsagePattern = `\$\(steps\.([^.]+)\.(?:inputs|outputs)(?:\.([^.^\)]+))?\)`
// case 1: steps.<step-name>.inputs.<artifact-category-name>
// case 2: steps.<step-name>.outputs.<artifact-category-name>
const stepArtifactUsagePattern = `\$\(steps\.([^.]+)\.(?:inputs|outputs)\.([^.)]+)\)`

// case 1: tasks.<task-name>.inputs.<artifact-category-name>
// case 2: tasks.<task-name>.outputs.<artifact-category-name>
const taskArtifactUsagePattern = `\$\(tasks\.([^.]+)\.(?:inputs|outputs)\.([^.)]+)\)`

const StepArtifactPathPattern = `step.artifacts.path`

const TaskArtifactPathPattern = `artifacts.path`

var StepArtifactRegex = regexp.MustCompile(stepArtifactUsagePattern)
var TaskArtifactRegex = regexp.MustCompile(taskArtifactUsagePattern)
3 changes: 3 additions & 0 deletions internal/sidecarlogresults/sidecarlogresults.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ const (
stepResultType SidecarLogResultType = "step"

stepArtifactType SidecarLogResultType = "stepArtifact"
taskArtifactType SidecarLogResultType = "taskArtifact"
sidecarResultNameSeparator string = "."
)

Expand Down Expand Up @@ -285,6 +286,8 @@ func parseResults(resultBytes []byte, maxResultLimit int) (result.RunResult, err
resultType = result.StepResultType
case stepArtifactType:
resultType = result.StepArtifactsResultType
case taskArtifactType:
resultType = result.TaskRunArtifactsResultType
default:
return result.RunResult{}, fmt.Errorf("invalid sidecar result type %v. Must be %v or %v or %v", res.Type, taskResultType, stepResultType, stepArtifactType)
}
Expand Down
65 changes: 65 additions & 0 deletions internal/sidecarlogresults/sidecarlogresults_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,39 @@ func TestParseResults(t *testing.T) {
}`,
Type: "stepArtifact",
},
{
Name: "task-run-artifacts-result",
Value: `{
"inputs":[
{
"name":"input-artifacts",
"values":[
{
"uri":"pkg:example.github.com/inputs",
"digest":{
"sha256":"b35cacccfdb1e24dc497d15d553891345fd155713ffe647c281c583269eaaae0"
}
}
]
}
],
"outputs":[
{
"name":"image",
"values":[
{
"uri":"pkg:github/package-url/purl-spec@244fd47e07d1004f0aed9c",
"digest":{
"sha256":"df85b9e3983fe2ce20ef76ad675ecf435cc99fc9350adc54fa230bae8c32ce48",
"sha1":"95588b8f34c31eb7d62c92aaa4e6506639b06ef2"
}
}
]
}
]
}`,
Type: "taskArtifact",
},
}
podLogs := []string{}
for _, r := range results {
Expand Down Expand Up @@ -400,6 +433,38 @@ func TestParseResults(t *testing.T) {
]
}`,
ResultType: result.StepArtifactsResultType,
}, {
Key: "task-run-artifacts-result",
Value: `{
"inputs":[
{
"name":"input-artifacts",
"values":[
{
"uri":"pkg:example.github.com/inputs",
"digest":{
"sha256":"b35cacccfdb1e24dc497d15d553891345fd155713ffe647c281c583269eaaae0"
}
}
]
}
],
"outputs":[
{
"name":"image",
"values":[
{
"uri":"pkg:github/package-url/purl-spec@244fd47e07d1004f0aed9c",
"digest":{
"sha256":"df85b9e3983fe2ce20ef76ad675ecf435cc99fc9350adc54fa230bae8c32ce48",
"sha1":"95588b8f34c31eb7d62c92aaa4e6506639b06ef2"
}
}
]
}
]
}`,
ResultType: result.TaskRunArtifactsResultType,
}}
stepResults := []result.RunResult{}
for _, plog := range podLogs {
Expand Down
2 changes: 2 additions & 0 deletions pkg/apis/pipeline/paths.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,6 @@ const (
StepsDir = "/tekton/steps"

ScriptDir = "/tekton/scripts"

ArtifactsDir = "/tekton/artifacts"
)
89 changes: 89 additions & 0 deletions pkg/apis/pipeline/v1/artifact_types.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
/*
Copyright 2024 The Tekton Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1

import (
"github.com/google/go-cmp/cmp"
)

// Algorithm Standard cryptographic hash algorithm
type Algorithm string

Expand Down Expand Up @@ -27,3 +47,72 @@ type Artifacts struct {
Inputs []Artifact `json:"inputs,omitempty"`
Outputs []Artifact `json:"outputs,omitempty"`
}

func (a *Artifacts) Merge(another Artifacts) {
inputMap := make(map[string][]ArtifactValue)
var newInputs []Artifact

for _, v := range a.Inputs {
inputMap[v.Name] = v.Values
}

for _, v := range another.Inputs {
_, ok := inputMap[v.Name]
if !ok {
inputMap[v.Name] = []ArtifactValue{}
}
for _, vv := range v.Values {
exists := false
for _, av := range inputMap[v.Name] {
if cmp.Equal(vv, av) {
exists = true
break
}
}
if !exists {
inputMap[v.Name] = append(inputMap[v.Name], vv)
}
}
}

for k, v := range inputMap {
newInputs = append(newInputs, Artifact{
Name: k,
Values: v,
})
}

outputMap := make(map[string][]ArtifactValue)
var newOutputs []Artifact
for _, v := range a.Outputs {
outputMap[v.Name] = v.Values
}

for _, v := range another.Outputs {
_, ok := outputMap[v.Name]
if !ok {
outputMap[v.Name] = []ArtifactValue{}
}
for _, vv := range v.Values {
exists := false
for _, av := range outputMap[v.Name] {
if cmp.Equal(vv, av) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason to use cmp.Equal here ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

vv, av are instances of ArtifactValue struct, this struct contains a map, we so cannot use == operator to compare them, that's why cmp.Equal is used here.

type ArtifactValue struct {
	Digest map[Algorithm]string `json:"digest,omitempty"` // Algorithm-specific digests for verifying the content (e.g., SHA256)
	Uri    string               `json:"uri,omitempty"`    // Location where the artifact value can be retrieved
}

exists = true
break
}
}
if !exists {
outputMap[v.Name] = append(outputMap[v.Name], vv)
}
}
}

for k, v := range outputMap {
newOutputs = append(newOutputs, Artifact{
Name: k,
Values: v,
})
}
a.Inputs = newInputs
a.Outputs = newOutputs
}
Loading