Skip to content

Commit 1f81f21

Browse files
committed
add support for reserved default object param
This commit adds support for reserved default object param.Users can reference to default object param values without needing to declaring them in remote Task&Pipeline. Validation err will be returned if referencing to non-existent object key. Signed-off-by: Yongxuan Zhang [email protected]
1 parent 7c040de commit 1f81f21

File tree

12 files changed

+528
-12
lines changed

12 files changed

+528
-12
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
apiVersion: tekton.dev/v1
2+
kind: Pipeline
3+
metadata:
4+
name: echo-message-pipeline
5+
spec:
6+
params:
7+
- name: hello
8+
properties:
9+
project_id:
10+
type: string
11+
type: object
12+
tasks:
13+
- name: task1
14+
params:
15+
- name: foo
16+
value:
17+
project_id: $(params.hello.project_id)
18+
taskSpec:
19+
params:
20+
- name: foo
21+
type: object
22+
properties:
23+
project_id:
24+
type: string
25+
steps:
26+
- name: echo
27+
image: ubuntu
28+
script: |
29+
#!/bin/bash
30+
echo $(params.TBD.project_id)
31+
echo $(params.foo.project_id)
32+
---
33+
kind: PipelineRun
34+
apiVersion: tekton.dev/v1
35+
metadata:
36+
name: test-pipelinerun
37+
spec:
38+
params:
39+
- name: TBD
40+
value:
41+
project_id: foo
42+
- name: hello
43+
value:
44+
project_id: foo
45+
pipelineRef:
46+
name: echo-message-pipeline
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
kind: Task
2+
apiVersion: tekton.dev/v1
3+
metadata:
4+
name: example-task
5+
spec:
6+
params:
7+
- name: foo
8+
type: object
9+
properties:
10+
project_id:
11+
type: string
12+
digest:
13+
type: string
14+
steps:
15+
- image: ubuntu
16+
name: echo
17+
script: |
18+
echo -n $(params.foo.project_id)
19+
echo -n $(params.TBD.project_id)
20+
---
21+
kind: TaskRun
22+
apiVersion: tekton.dev/v1
23+
metadata:
24+
name: example-tr
25+
spec:
26+
params:
27+
- name: foo
28+
value:
29+
project_id: foo
30+
digest: bar
31+
- name: TBD
32+
value:
33+
project_id: foo
34+
digest: bar
35+
taskRef:
36+
name: example-task
37+
kind: task

pkg/apis/pipeline/v1/pipeline_validation.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,11 @@ var _ apis.Validatable = (*Pipeline)(nil)
3737
var _ resourcesemantics.VerbLimited = (*Pipeline)(nil)
3838

3939
const (
40-
taskRef = "taskRef"
41-
taskSpec = "taskSpec"
42-
pipelineRef = "pipelineRef"
43-
pipelineSpec = "pipelineSpec"
40+
taskRef = "taskRef"
41+
taskSpec = "taskSpec"
42+
pipelineRef = "pipelineRef"
43+
pipelineSpec = "pipelineSpec"
44+
ReservedParamName = "TBD"
4445
)
4546

4647
// SupportedVerbs returns the operations that validation should be called for
@@ -395,6 +396,7 @@ func (ps *PipelineSpec) validatePipelineParameterUsage(ctx context.Context) (err
395396
// validatePipelineTaskParameterUsage validates that parameters referenced in the Pipeline Tasks are declared by the Pipeline
396397
func validatePipelineTaskParameterUsage(tasks []PipelineTask, params ParamSpecs) (errs *apis.FieldError) {
397398
allParamNames := sets.NewString(params.GetNames()...)
399+
allParamNames.Insert(ReservedParamName)
398400
_, arrayParams, objectParams := params.SortByType()
399401
arrayParamNames := sets.NewString(arrayParams.GetNames()...)
400402
objectParameterNameKeys := map[string][]string{}

pkg/apis/pipeline/v1/pipeline_validation_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package v1
1818

1919
import (
2020
"context"
21+
"fmt"
2122
"testing"
2223

2324
"github.com/google/go-cmp/cmp"
@@ -114,6 +115,24 @@ func TestPipeline_Validate_Success(t *testing.T) {
114115
},
115116
},
116117
},
118+
}, {
119+
name: "valid reserved param",
120+
wc: cfgtesting.EnableAlphaAPIFields,
121+
p: &Pipeline{
122+
ObjectMeta: metav1.ObjectMeta{Name: "pipeline"},
123+
Spec: PipelineSpec{
124+
Tasks: []PipelineTask{{
125+
Name: "pipeline-task-use-reserved-param",
126+
TaskSpec: &EmbeddedTask{TaskSpec: TaskSpec{
127+
Steps: []Step{{
128+
Name: "some-step",
129+
Image: "some-image",
130+
Script: fmt.Sprintf("echo $(params.%s.foo)", ReservedParamName),
131+
}},
132+
}},
133+
}},
134+
},
135+
},
117136
}}
118137
for _, tt := range tests {
119138
t.Run(tt.name, func(t *testing.T) {

pkg/apis/pipeline/v1/pipelinerun_types.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,8 @@ const (
414414
PipelineRunReasonCELEvaluationFailed PipelineRunReason = "CELEvaluationFailed"
415415
// PipelineRunReasonInvalidParamValue indicates that the PipelineRun Param input value is not allowed.
416416
PipelineRunReasonInvalidParamValue PipelineRunReason = "InvalidParamValue"
417+
// PipelineRunReasonParamKeyNotExistent indicates that the default object param doesn't have the key which the param reference requires
418+
PipelineRunReasonParamKeyNotExistent PipelineRunReason = "ParamKeyNotExistent"
417419
)
418420

419421
// PipelineTaskOnErrorAnnotation is used to pass the failure strategy to TaskRun pods from PipelineTask OnError field

pkg/apis/pipeline/v1/task_validation.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ func ValidateUsageOfDeclaredParameters(ctx context.Context, steps []Step, params
132132
var errs *apis.FieldError
133133
_, _, objectParams := params.SortByType()
134134
allParameterNames := sets.NewString(params.GetNames()...)
135+
allParameterNames.Insert(ReservedParamName)
135136
errs = errs.Also(validateVariables(ctx, steps, "params", allParameterNames))
136137
errs = errs.Also(validateObjectUsage(ctx, steps, objectParams))
137138
errs = errs.Also(ValidateObjectParamsHaveProperties(ctx, params))
@@ -643,6 +644,27 @@ func validateVariables(ctx context.Context, steps []Step, prefix string, vars se
643644
return errs
644645
}
645646

647+
// Reserved Param doesn't have properties, so we need to check all the references if they are using a non-existent key from the param
648+
func ValidateReservedParamReferenceMissingKeys(ctx context.Context, steps []Step, params Params) error {
649+
objectKeys := sets.NewString()
650+
// collect all the existent keys from the object reserved param.
651+
for _, p := range params {
652+
if p.Name == ReservedParamName && p.Value.Type == ParamTypeObject {
653+
for key := range p.Value.ObjectVal {
654+
objectKeys.Insert(key)
655+
}
656+
}
657+
}
658+
if len(objectKeys) > 0 {
659+
// check if the object's key names are referenced correctly i.e. param.objectParam.key1
660+
err := validateVariables(ctx, steps, fmt.Sprintf("params\\.%s", ReservedParamName), objectKeys)
661+
if err != nil {
662+
return err
663+
}
664+
}
665+
return nil
666+
}
667+
646668
// ValidateNameFormat validates that the name format of all param types follows the rules
647669
func ValidateNameFormat(stringAndArrayParams sets.String, objectParams []ParamSpec) (errs *apis.FieldError) {
648670
// checking string or array name format

pkg/apis/pipeline/v1/task_validation_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,19 @@ func TestTaskValidate(t *testing.T) {
6161
}},
6262
},
6363
},
64+
}, {
65+
name: "valid reserved param",
66+
wc: cfgtesting.EnableAlphaAPIFields,
67+
t: &v1.Task{
68+
ObjectMeta: metav1.ObjectMeta{Name: "task"},
69+
Spec: v1.TaskSpec{
70+
Steps: []v1.Step{{
71+
Name: "some-step",
72+
Image: "some-image",
73+
Script: fmt.Sprintf("echo $(params.%s.foo)", v1.ReservedParamName),
74+
}},
75+
},
76+
},
6477
}}
6578
for _, tt := range tests {
6679
t.Run(tt.name, func(t *testing.T) {

pkg/apis/pipeline/v1beta1/pipeline_validation.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,11 @@ var _ apis.Validatable = (*Pipeline)(nil)
3838
var _ resourcesemantics.VerbLimited = (*Pipeline)(nil)
3939

4040
const (
41-
taskRef = "taskRef"
42-
taskSpec = "taskSpec"
43-
pipelineRef = "pipelineRef"
44-
pipelineSpec = "pipelineSpec"
41+
taskRef = "taskRef"
42+
taskSpec = "taskSpec"
43+
pipelineRef = "pipelineRef"
44+
pipelineSpec = "pipelineSpec"
45+
ReservedParamName = "TBD"
4546
)
4647

4748
// SupportedVerbs returns the operations that validation should be called for
@@ -414,6 +415,7 @@ func (ps *PipelineSpec) validatePipelineParameterUsage(ctx context.Context) (err
414415
// validatePipelineTaskParameterUsage validates that parameters referenced in the Pipeline Tasks are declared by the Pipeline
415416
func validatePipelineTaskParameterUsage(tasks []PipelineTask, params ParamSpecs) (errs *apis.FieldError) {
416417
allParamNames := sets.NewString(params.getNames()...)
418+
allParamNames.Insert(ReservedParamName)
417419
_, arrayParams, objectParams := params.sortByType()
418420
arrayParamNames := sets.NewString(arrayParams.getNames()...)
419421
objectParameterNameKeys := map[string][]string{}

pkg/reconciler/pipelinerun/pipelinerun.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,20 @@ func (c *Reconciler) reconcile(ctx context.Context, pr *v1.PipelineRun, getPipel
671671
}
672672

673673
if pipelineRunFacts.State.IsBeforeFirstTaskRun() {
674+
// check if the object's key names are referenced correctly i.e. param.objectParam.key1
675+
for _, prs := range pipelineRunState {
676+
if prs.ResolvedTask == nil {
677+
continue
678+
}
679+
if validateErr := v1.ValidateReservedParamReferenceMissingKeys(ctx, prs.ResolvedTask.TaskSpec.Steps, pr.Spec.Params); validateErr != nil {
680+
logger.Errorf("PipelineRun %s params references error %v", pr.Name, validateErr)
681+
pr.Status.MarkFailed(v1.PipelineRunReasonParamKeyNotExistent.String(),
682+
"PipelineRun %s/%s doesn't has the references key: %s",
683+
pr.Namespace, pr.Name, err)
684+
return controller.NewPermanentError(validateErr)
685+
}
686+
}
687+
674688
if err := resources.ValidatePipelineTaskResults(pipelineRunFacts.State); err != nil {
675689
logger.Errorf("Failed to resolve task result reference for %q with error %v", pr.Name, err)
676690
pr.Status.MarkFailed(v1.PipelineRunReasonInvalidTaskResultReference.String(), err.Error())

pkg/reconciler/pipelinerun/pipelinerun_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4627,18 +4627,18 @@ spec:
46274627
steps:
46284628
- name: s1
46294629
image: alpine
4630-
script: |
4630+
script: |
46314631
echo $(params.version) + $(params.tag)
46324632
- name: b-task
4633-
params:
4633+
params:
46344634
- name: ref-p1
46354635
value: $(params.version)
46364636
- name: ref-p2
46374637
value: "v3"
46384638
taskRef:
46394639
name: ref-task
46404640
- name: c-task-matrixed
4641-
matrix:
4641+
matrix:
46424642
params:
46434643
- name: ref-p1
46444644
value: [v1, v2]
@@ -4717,7 +4717,7 @@ spec:
47174717
steps:
47184718
- name: s1
47194719
image: alpine
4720-
script: |
4720+
script: |
47214721
echo $(params.version)
47224722
`)}
47234723
prs := []*v1.PipelineRun{parse.MustParseV1PipelineRun(t, `

0 commit comments

Comments
 (0)