Skip to content

Commit

Permalink
add support for reserved default object param
Browse files Browse the repository at this point in the history
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]
  • Loading branch information
Yongxuanzhang committed Dec 12, 2023
1 parent 7c040de commit 1f81f21
Show file tree
Hide file tree
Showing 12 changed files with 528 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
name: echo-message-pipeline
spec:
params:
- name: hello
properties:
project_id:
type: string
type: object
tasks:
- name: task1
params:
- name: foo
value:
project_id: $(params.hello.project_id)
taskSpec:
params:
- name: foo
type: object
properties:
project_id:
type: string
steps:
- name: echo
image: ubuntu
script: |
#!/bin/bash
echo $(params.TBD.project_id)
echo $(params.foo.project_id)
---
kind: PipelineRun
apiVersion: tekton.dev/v1
metadata:
name: test-pipelinerun
spec:
params:
- name: TBD
value:
project_id: foo
- name: hello
value:
project_id: foo
pipelineRef:
name: echo-message-pipeline
37 changes: 37 additions & 0 deletions examples/v1/taskruns/alpha/remote-task-ref-reserved-param.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
kind: Task
apiVersion: tekton.dev/v1
metadata:
name: example-task
spec:
params:
- name: foo
type: object
properties:
project_id:
type: string
digest:
type: string
steps:
- image: ubuntu
name: echo
script: |
echo -n $(params.foo.project_id)
echo -n $(params.TBD.project_id)
---
kind: TaskRun
apiVersion: tekton.dev/v1
metadata:
name: example-tr
spec:
params:
- name: foo
value:
project_id: foo
digest: bar
- name: TBD
value:
project_id: foo
digest: bar
taskRef:
name: example-task
kind: task
10 changes: 6 additions & 4 deletions pkg/apis/pipeline/v1/pipeline_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,11 @@ var _ apis.Validatable = (*Pipeline)(nil)
var _ resourcesemantics.VerbLimited = (*Pipeline)(nil)

const (
taskRef = "taskRef"
taskSpec = "taskSpec"
pipelineRef = "pipelineRef"
pipelineSpec = "pipelineSpec"
taskRef = "taskRef"
taskSpec = "taskSpec"
pipelineRef = "pipelineRef"
pipelineSpec = "pipelineSpec"
ReservedParamName = "TBD"
)

// SupportedVerbs returns the operations that validation should be called for
Expand Down Expand Up @@ -395,6 +396,7 @@ func (ps *PipelineSpec) validatePipelineParameterUsage(ctx context.Context) (err
// validatePipelineTaskParameterUsage validates that parameters referenced in the Pipeline Tasks are declared by the Pipeline
func validatePipelineTaskParameterUsage(tasks []PipelineTask, params ParamSpecs) (errs *apis.FieldError) {
allParamNames := sets.NewString(params.GetNames()...)
allParamNames.Insert(ReservedParamName)
_, arrayParams, objectParams := params.SortByType()
arrayParamNames := sets.NewString(arrayParams.GetNames()...)
objectParameterNameKeys := map[string][]string{}
Expand Down
19 changes: 19 additions & 0 deletions pkg/apis/pipeline/v1/pipeline_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package v1

import (
"context"
"fmt"
"testing"

"github.com/google/go-cmp/cmp"
Expand Down Expand Up @@ -114,6 +115,24 @@ func TestPipeline_Validate_Success(t *testing.T) {
},
},
},
}, {
name: "valid reserved param",
wc: cfgtesting.EnableAlphaAPIFields,
p: &Pipeline{
ObjectMeta: metav1.ObjectMeta{Name: "pipeline"},
Spec: PipelineSpec{
Tasks: []PipelineTask{{
Name: "pipeline-task-use-reserved-param",
TaskSpec: &EmbeddedTask{TaskSpec: TaskSpec{
Steps: []Step{{
Name: "some-step",
Image: "some-image",
Script: fmt.Sprintf("echo $(params.%s.foo)", ReservedParamName),
}},
}},
}},
},
},
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
2 changes: 2 additions & 0 deletions pkg/apis/pipeline/v1/pipelinerun_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,8 @@ const (
PipelineRunReasonCELEvaluationFailed PipelineRunReason = "CELEvaluationFailed"
// PipelineRunReasonInvalidParamValue indicates that the PipelineRun Param input value is not allowed.
PipelineRunReasonInvalidParamValue PipelineRunReason = "InvalidParamValue"
// PipelineRunReasonParamKeyNotExistent indicates that the default object param doesn't have the key which the param reference requires
PipelineRunReasonParamKeyNotExistent PipelineRunReason = "ParamKeyNotExistent"
)

// PipelineTaskOnErrorAnnotation is used to pass the failure strategy to TaskRun pods from PipelineTask OnError field
Expand Down
22 changes: 22 additions & 0 deletions pkg/apis/pipeline/v1/task_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ func ValidateUsageOfDeclaredParameters(ctx context.Context, steps []Step, params
var errs *apis.FieldError
_, _, objectParams := params.SortByType()
allParameterNames := sets.NewString(params.GetNames()...)
allParameterNames.Insert(ReservedParamName)
errs = errs.Also(validateVariables(ctx, steps, "params", allParameterNames))
errs = errs.Also(validateObjectUsage(ctx, steps, objectParams))
errs = errs.Also(ValidateObjectParamsHaveProperties(ctx, params))
Expand Down Expand Up @@ -643,6 +644,27 @@ func validateVariables(ctx context.Context, steps []Step, prefix string, vars se
return errs
}

// 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
func ValidateReservedParamReferenceMissingKeys(ctx context.Context, steps []Step, params Params) error {
objectKeys := sets.NewString()
// collect all the existent keys from the object reserved param.
for _, p := range params {
if p.Name == ReservedParamName && p.Value.Type == ParamTypeObject {
for key := range p.Value.ObjectVal {
objectKeys.Insert(key)
}
}
}
if len(objectKeys) > 0 {
// check if the object's key names are referenced correctly i.e. param.objectParam.key1
err := validateVariables(ctx, steps, fmt.Sprintf("params\\.%s", ReservedParamName), objectKeys)
if err != nil {
return err
}
}
return nil
}

// ValidateNameFormat validates that the name format of all param types follows the rules
func ValidateNameFormat(stringAndArrayParams sets.String, objectParams []ParamSpec) (errs *apis.FieldError) {
// checking string or array name format
Expand Down
13 changes: 13 additions & 0 deletions pkg/apis/pipeline/v1/task_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,19 @@ func TestTaskValidate(t *testing.T) {
}},
},
},
}, {
name: "valid reserved param",
wc: cfgtesting.EnableAlphaAPIFields,
t: &v1.Task{
ObjectMeta: metav1.ObjectMeta{Name: "task"},
Spec: v1.TaskSpec{
Steps: []v1.Step{{
Name: "some-step",
Image: "some-image",
Script: fmt.Sprintf("echo $(params.%s.foo)", v1.ReservedParamName),
}},
},
},
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
10 changes: 6 additions & 4 deletions pkg/apis/pipeline/v1beta1/pipeline_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,11 @@ var _ apis.Validatable = (*Pipeline)(nil)
var _ resourcesemantics.VerbLimited = (*Pipeline)(nil)

const (
taskRef = "taskRef"
taskSpec = "taskSpec"
pipelineRef = "pipelineRef"
pipelineSpec = "pipelineSpec"
taskRef = "taskRef"
taskSpec = "taskSpec"
pipelineRef = "pipelineRef"
pipelineSpec = "pipelineSpec"
ReservedParamName = "TBD"
)

// SupportedVerbs returns the operations that validation should be called for
Expand Down Expand Up @@ -414,6 +415,7 @@ func (ps *PipelineSpec) validatePipelineParameterUsage(ctx context.Context) (err
// validatePipelineTaskParameterUsage validates that parameters referenced in the Pipeline Tasks are declared by the Pipeline
func validatePipelineTaskParameterUsage(tasks []PipelineTask, params ParamSpecs) (errs *apis.FieldError) {
allParamNames := sets.NewString(params.getNames()...)
allParamNames.Insert(ReservedParamName)
_, arrayParams, objectParams := params.sortByType()
arrayParamNames := sets.NewString(arrayParams.getNames()...)
objectParameterNameKeys := map[string][]string{}
Expand Down
14 changes: 14 additions & 0 deletions pkg/reconciler/pipelinerun/pipelinerun.go
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,20 @@ func (c *Reconciler) reconcile(ctx context.Context, pr *v1.PipelineRun, getPipel
}

if pipelineRunFacts.State.IsBeforeFirstTaskRun() {
// check if the object's key names are referenced correctly i.e. param.objectParam.key1
for _, prs := range pipelineRunState {
if prs.ResolvedTask == nil {
continue
}
if validateErr := v1.ValidateReservedParamReferenceMissingKeys(ctx, prs.ResolvedTask.TaskSpec.Steps, pr.Spec.Params); validateErr != nil {
logger.Errorf("PipelineRun %s params references error %v", pr.Name, validateErr)
pr.Status.MarkFailed(v1.PipelineRunReasonParamKeyNotExistent.String(),
"PipelineRun %s/%s doesn't has the references key: %s",
pr.Namespace, pr.Name, err)
return controller.NewPermanentError(validateErr)
}
}

if err := resources.ValidatePipelineTaskResults(pipelineRunFacts.State); err != nil {
logger.Errorf("Failed to resolve task result reference for %q with error %v", pr.Name, err)
pr.Status.MarkFailed(v1.PipelineRunReasonInvalidTaskResultReference.String(), err.Error())
Expand Down
8 changes: 4 additions & 4 deletions pkg/reconciler/pipelinerun/pipelinerun_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4627,18 +4627,18 @@ spec:
steps:
- name: s1
image: alpine
script: |
script: |
echo $(params.version) + $(params.tag)
- name: b-task
params:
params:
- name: ref-p1
value: $(params.version)
- name: ref-p2
value: "v3"
taskRef:
name: ref-task
- name: c-task-matrixed
matrix:
matrix:
params:
- name: ref-p1
value: [v1, v2]
Expand Down Expand Up @@ -4717,7 +4717,7 @@ spec:
steps:
- name: s1
image: alpine
script: |
script: |
echo $(params.version)
`)}
prs := []*v1.PipelineRun{parse.MustParseV1PipelineRun(t, `
Expand Down
6 changes: 6 additions & 0 deletions pkg/reconciler/taskrun/taskrun.go
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,12 @@ func (c *Reconciler) prepare(ctx context.Context, tr *v1.TaskRun) (*v1.TaskSpec,
Kind: resources.GetTaskKind(tr),
}

if validateErr := v1.ValidateReservedParamReferenceMissingKeys(ctx, taskSpec.Steps, tr.Spec.Params); validateErr != nil {
logger.Errorf("TaskRun %s params references error %v", tr.Name, validateErr)
tr.Status.MarkResourceFailed(v1.TaskRunReason(v1.PipelineRunReasonParamKeyNotExistent.String()), validateErr)
return nil, nil, controller.NewPermanentError(validateErr)
}

if err := validateTaskSpecRequestResources(taskSpec); err != nil {
logger.Errorf("TaskRun %s taskSpec request resources are invalid: %v", tr.Name, err)
tr.Status.MarkResourceFailed(v1.TaskRunReasonFailedValidation, err)
Expand Down
Loading

0 comments on commit 1f81f21

Please sign in to comment.