Skip to content

Commit 67a34d9

Browse files
[TEP-0144] Validate PipelineRun for Param Enum
Part of [tektoncd#7270][tektoncd#7270]. In [TEP-0144][tep-0144] we proposed a new `enum` field to support built-in param input validation. This commit adds validation logic for PipelineRun against Param Enum /kind feature [tektoncd#7270]: tektoncd#7270 [tep-0144]: https://github.com/tektoncd/community/blob/main/teps/0144-param-enum.md
1 parent 1bcb057 commit 67a34d9

13 files changed

+710
-13
lines changed

config/config-feature-flags.yaml

-1
Original file line numberDiff line numberDiff line change
@@ -127,5 +127,4 @@ data:
127127
# This feature is in preview mode and not implemented yet. Please check #7259 for updates.
128128
enable-step-actions: "false"
129129
# Setting this flag to "true" will enable the built-in param input validation via param enum.
130-
# NOTE (#7270): this feature is still under development and not yet functional.
131130
enable-param-enum: "false"

docs/additional-configs.md

+1
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@ Features currently in "alpha" are:
317317
| [Coschedule](./affinityassistants.md) | [TEP-0135](https://github.com/tektoncd/community/blob/main/teps/0135-coscheduling-pipelinerun-pods.md) | N/A |`coschedule` |
318318
| [keep pod on cancel](./taskruns.md#cancelling-a-taskrun) | N/A | v0.52 | keep-pod-on-cancel |
319319
| [CEL in WhenExpression](./taskruns.md#cancelling-a-taskrun) | [TEP-0145](https://github.com/tektoncd/community/blob/main/teps/0145-cel-in-whenexpression.md) | N/A | enable-cel-in-whenexpression |
320+
| [Param Enum](./taskruns.md#parameter-enums) | [TEP-0144](https://github.com/tektoncd/community/blob/main/teps/0144-param-enum.md) | N/A | `enable-param-enum` |
320321

321322
### Beta Features
322323

docs/pipeline-api.md

+3
Original file line numberDiff line numberDiff line change
@@ -1997,6 +1997,9 @@ associated Pipeline is an invalid graph (a.k.a wrong order, cycle, …)</p>
19971997
</tr><tr><td><p>&#34;InvalidMatrixParameterTypes&#34;</p></td>
19981998
<td><p>ReasonInvalidMatrixParameterTypes indicates a matrix contains invalid parameter types</p>
19991999
</td>
2000+
</tr><tr><td><p>&#34;InvalidParamValue&#34;</p></td>
2001+
<td><p>PipelineRunReasonInvalidParamValue indicates that the PipelineRun Param input value is not allowed.</p>
2002+
</td>
20002003
</tr><tr><td><p>&#34;InvalidTaskResultReference&#34;</p></td>
20012004
<td><p>ReasonInvalidTaskResultReference indicates a task result was declared
20022005
but was not initialized by that task</p>

docs/pipelineruns.md

+12
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,18 @@ case is when your CI system autogenerates `PipelineRuns` and it has `Parameters`
271271
provide to all `PipelineRuns`. Because you can pass in extra `Parameters`, you don't have to
272272
go through the complexity of checking each `Pipeline` and providing only the required params.
273273

274+
#### Parameter Enums
275+
276+
> :seedling: **`enum` is an [alpha](additional-configs.md#alpha-features) feature.** The `enable-param-enum` feature flag must be set to `"true"` to enable this feature.
277+
278+
If a `Parameter` is guarded by `Enum` in the `Pipeline`, you can only provide `Parameter` values in the `PipelineRun` that are predefined in the `Param.Enum` in the `Pipeline`. The `PipelineRun` will fail with reason `InvalidParamValue` otherwise.
279+
280+
Tekton will also the validate the `param` values passed to any referenced `Tasks` (via `taskRef`) if `Enum` is specified for the `Task`. The `PipelineRun` will fail with reason `InvalidParamValue` if `Enum` validation is failed for any of the `PipelineTask`.
281+
282+
You can also specify `Enum` in an embedded `Pipeline` in a `PipelineRun`. The same `Param` validation will be executed in this scenario.
283+
284+
See more details in [Param.Enum](./pipelines.md#param-enum).
285+
274286
#### Propagated Parameters
275287

276288
When using an inlined spec, parameters from the parent `PipelineRun` will be

docs/pipelines.md

+69-3
Original file line numberDiff line numberDiff line change
@@ -272,11 +272,77 @@ spec:
272272
```
273273

274274
#### Param enum
275-
> :seedling: **Specifying `enum` is an [alpha](additional-configs.md#alpha-features) feature.** The `enable-param-enum` feature flag must be set to `"true"` to enable this feature.
275+
> :seedling: **`enum` is an [alpha](additional-configs.md#alpha-features) feature.** The `enable-param-enum` feature flag must be set to `"true"` to enable this feature.
276276

277-
> :seedling: This feature is WIP and not yet supported/implemented. Documentation to be completed.
277+
Parameter declarations can include `enum` which is a predefine set of valid values that can be accepted by the `Pipeline` `Param`. If a `Param` has both `enum` and default value, the default value must be in the `enum` set. For example, the valid/allowed values for `Param` "message" is bounded to `v1` and `v2`:
278278

279-
Parameter declarations can include `enum` which is a predefine set of valid values that can be accepted by the `Pipeline`.
279+
``` yaml
280+
apiVersion: tekton.dev/v1
281+
kind: Pipeline
282+
metadata:
283+
name: pipeline-param-enum
284+
spec:
285+
params:
286+
- name: message
287+
enum: ["v1", "v2"]
288+
default: "v1"
289+
tasks:
290+
- name: task1
291+
params:
292+
- name: message
293+
value: $(params.message)
294+
steps:
295+
- name: build
296+
image: bash:3.2
297+
script: |
298+
echo "$(params.message)"
299+
```
300+
301+
If the `Param` value passed in by `PipelineRun` is **NOT** in the predefined `enum` list, the `PipelineRun` will fail with reason `InvalidParamValue`.
302+
303+
If a `PipelineTask` references a `Task` with `enum`, the `enums` specified in the Pipeline `spec.params` (pipeline-level `enum`) must be
304+
a **subset** of the `enums` specified in the referenced `Task` (task-level `enum`). Note that an empty pipeline-level `enum` is invalid
305+
in this scenario since an empty `enum` set indicates a "universal set" which allows all possible values. In the below example, the referenced `Task` accepts `v1` and `v2` as valid values, the `Pipeline` further restricts the valid values to `v1`.
306+
307+
``` yaml
308+
apiVersion: tekton.dev/v1
309+
kind: Task
310+
metadata:
311+
name: param-enum-demo
312+
spec:
313+
params:
314+
- name: message
315+
type: string
316+
enum: ["v1", "v2"]
317+
steps:
318+
- name: build
319+
image: bash:latest
320+
script: |
321+
echo "$(params.message)"
322+
```
323+
324+
``` yaml
325+
apiVersion: tekton.dev/v1
326+
kind: Pipeline
327+
metadata:
328+
name: pipeline-param-enum
329+
spec:
330+
params:
331+
- name: message
332+
enum: ["v1"] # note that an empty enum set is invalid
333+
tasks:
334+
- name: task1
335+
params:
336+
- name: message
337+
value: $(params.message)
338+
taskRef:
339+
name: param-enum-demo
340+
```
341+
342+
Tekton validates user-provided values in a `PipelineRun` against the `enum` specified in the `PipelineSpec.params`. Tekton also validates
343+
any resolved `param` value against the `enum` specified in each `PipelineTask` before creating the `TaskRun`.
344+
345+
See usage in this [example](../examples/v1/pipelineruns/alpha/param-enum.yaml)
280346

281347
## Adding `Tasks` to the `Pipeline`
282348

docs/taskruns.md

+1-3
Original file line numberDiff line numberDiff line change
@@ -404,9 +404,7 @@ go through the complexity of checking each `Task` and providing only the require
404404

405405
#### Parameter Enums
406406

407-
> :seedling: **Specifying `enum` is an [alpha](additional-configs.md#alpha-features) feature.** The `enable-param-enum` feature flag must be set to `"true"` to enable this feature.
408-
409-
> :seedling: This feature is WIP and not yet supported/implemented. Documentation to be completed.
407+
> :seedling: **`enum` is an [alpha](additional-configs.md#alpha-features) feature.** The `enable-param-enum` feature flag must be set to `"true"` to enable this feature.
410408

411409
If a `Parameter` is guarded by `Enum` in the `Task`, you can only provide `Parameter` values in the `TaskRun` that are predefined in the `Param.Enum` in the `Task`. The `TaskRun` will fail with reason `InvalidParamValue` otherwise.
412410

docs/tasks.md

+3-4
Original file line numberDiff line numberDiff line change
@@ -713,11 +713,9 @@ spec:
713713
```
714714
715715
#### Param enum
716-
> :seedling: **Specifying `enum` is an [alpha](additional-configs.md#alpha-features) feature.** The `enable-param-enum` feature flag must be set to `"true"` to enable this feature.
716+
> :seedling: **`enum` is an [alpha](additional-configs.md#alpha-features) feature.** The `enable-param-enum` feature flag must be set to `"true"` to enable this feature.
717717

718-
> :seedling: This feature is WIP and not yet supported/implemented. Documentation to be completed.
719-
720-
Parameter declarations can include `enum` which is a predefine set of valid values that can be accepted by the `Param`. For example, the valid/allowed values for `Param` "message" is bounded to `v1`, `v2` and `v3`:
718+
Parameter declarations can include `enum` which is a predefine set of valid values that can be accepted by the `Param`. If a `Param` has both `enum` and default value, the default value must be in the `enum` set. For example, the valid/allowed values for `Param` "message" is bounded to `v1`, `v2` and `v3`:
721719

722720
``` yaml
723721
apiVersion: tekton.dev/v1
@@ -729,6 +727,7 @@ spec:
729727
- name: message
730728
type: string
731729
enum: ["v1", "v2", "v3"]
730+
default: "v1"
732731
steps:
733732
- name: build
734733
image: bash:latest
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
apiVersion: tekton.dev/v1
2+
kind: Task
3+
metadata:
4+
name: task-param-enum
5+
spec:
6+
params:
7+
- name: message
8+
type: string
9+
enum: ["v1", "v2", "v3"]
10+
steps:
11+
- name: build
12+
image: bash:latest
13+
script: |
14+
echo "$(params.message)"
15+
---
16+
apiVersion: tekton.dev/v1
17+
kind: Pipeline
18+
metadata:
19+
name: pipeline-param-enum
20+
spec:
21+
params:
22+
- name: message
23+
enum: ["v1", "v2"]
24+
default: "v1"
25+
tasks:
26+
- name: task1
27+
params:
28+
- name: message
29+
value: $(params.message)
30+
taskRef:
31+
name: task-param-enum
32+
---
33+
apiVersion: tekton.dev/v1
34+
kind: PipelineRun
35+
metadata:
36+
name: pipelinerun-param-enum
37+
spec:
38+
pipelineRef:
39+
name: pipeline-param-enum
40+
params:
41+
- name: message
42+
value: "v1"

pkg/apis/pipeline/v1/pipelinerun_types.go

+2
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,8 @@ const (
408408
PipelineRunReasonCreateRunFailed PipelineRunReason = "CreateRunFailed"
409409
// ReasonCELEvaluationFailed indicates the pipeline fails the CEL evaluation
410410
PipelineRunReasonCELEvaluationFailed PipelineRunReason = "CELEvaluationFailed"
411+
// PipelineRunReasonInvalidParamValue indicates that the PipelineRun Param input value is not allowed.
412+
PipelineRunReasonInvalidParamValue PipelineRunReason = "InvalidParamValue"
411413
)
412414

413415
func (t PipelineRunReason) String() string {

pkg/reconciler/pipelinerun/pipelinerun.go

+40-2
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,16 @@ func (c *Reconciler) reconcile(ctx context.Context, pr *v1.PipelineRun, getPipel
487487
return controller.NewPermanentError(err)
488488
}
489489

490+
if config.FromContextOrDefaults(ctx).FeatureFlags.EnableParamEnum {
491+
if err := taskrun.ValidateEnumParam(ctx, pr.Spec.Params, pipelineSpec.Params); err != nil {
492+
logger.Errorf("PipelineRun %q Param Enum validation failed: %v", pr.Name, err)
493+
pr.Status.MarkFailed(v1.PipelineRunReasonInvalidParamValue.String(),
494+
"PipelineRun %s/%s parameters have invalid value: %s",
495+
pr.Namespace, pr.Name, err)
496+
return controller.NewPermanentError(err)
497+
}
498+
}
499+
490500
// Ensure that the keys of an object param declared in PipelineSpec are not missed in the PipelineRunSpec
491501
if err = resources.ValidateObjectParamRequiredKeys(pipelineSpec.Params, pr.Spec.Params); err != nil {
492502
// This Run has failed, so we need to mark it as failed and stop reconciling it
@@ -521,6 +531,12 @@ func (c *Reconciler) reconcile(ctx context.Context, pr *v1.PipelineRun, getPipel
521531
return controller.NewPermanentError(err)
522532
}
523533

534+
// Make a deep copy of the Pipeline and its Tasks before value substution.
535+
// This is used to find referenced pipeline-level params at each PipelineTask when validate param enum subset requirement
536+
originalPipeline := pipelineSpec.DeepCopy()
537+
originalTasks := originalPipeline.Tasks
538+
originalTasks = append(originalTasks, originalPipeline.Finally...)
539+
524540
// Apply parameter substitution from the PipelineRun
525541
pipelineSpec = resources.ApplyParameters(ctx, pipelineSpec, pr)
526542
pipelineSpec = resources.ApplyContexts(pipelineSpec, pipelineMeta.Name, pr)
@@ -615,14 +631,22 @@ func (c *Reconciler) reconcile(ctx context.Context, pr *v1.PipelineRun, getPipel
615631
pipelineRunFacts.TimeoutsState.PipelineTimeout = &pipelineTimeout
616632
}
617633

618-
for _, rpt := range pipelineRunFacts.State {
634+
for i, rpt := range pipelineRunFacts.State {
619635
if !rpt.IsCustomTask() {
620636
err := taskrun.ValidateResolvedTask(ctx, rpt.PipelineTask.Params, rpt.PipelineTask.Matrix, rpt.ResolvedTask)
621637
if err != nil {
622638
logger.Errorf("Failed to validate pipelinerun %q with error %v", pr.Name, err)
623639
pr.Status.MarkFailed(v1.PipelineRunReasonFailedValidation.String(), err.Error())
624640
return controller.NewPermanentError(err)
625641
}
642+
643+
if config.FromContextOrDefaults(ctx).FeatureFlags.EnableParamEnum {
644+
if err := resources.ValidateParamEnumSubset(originalTasks[i].Params, pipelineSpec.Params, rpt.ResolvedTask); err != nil {
645+
logger.Errorf("Failed to validate pipelinerun %q with error %v", pr.Name, err)
646+
pr.Status.MarkFailed(v1.PipelineRunReasonFailedValidation.String(), err.Error())
647+
return controller.NewPermanentError(err)
648+
}
649+
}
626650
}
627651
}
628652

@@ -864,10 +888,24 @@ func (c *Reconciler) createTaskRuns(ctx context.Context, rpt *resources.Resolved
864888
defer span.End()
865889
var taskRuns []*v1.TaskRun
866890
var matrixCombinations []v1.Params
867-
868891
if rpt.PipelineTask.IsMatrixed() {
869892
matrixCombinations = rpt.PipelineTask.Matrix.FanOut()
870893
}
894+
// validate the param values meet resolved Task Param Enum requirements before creating TaskRuns
895+
if config.FromContextOrDefaults(ctx).FeatureFlags.EnableParamEnum {
896+
for i := range rpt.TaskRunNames {
897+
var params v1.Params
898+
if len(matrixCombinations) > i {
899+
params = matrixCombinations[i]
900+
}
901+
params = append(params, rpt.PipelineTask.Params...)
902+
if err := taskrun.ValidateEnumParam(ctx, params, rpt.ResolvedTask.TaskSpec.Params); err != nil {
903+
err = fmt.Errorf("Invalid param value from PipelineTask \"%s\": %w", rpt.PipelineTask.Name, err)
904+
pr.Status.MarkFailed(v1.PipelineRunReasonInvalidParamValue.String(), err.Error())
905+
return nil, controller.NewPermanentError(err)
906+
}
907+
}
908+
}
871909
for i, taskRunName := range rpt.TaskRunNames {
872910
var params v1.Params
873911
if len(matrixCombinations) > i {

0 commit comments

Comments
 (0)