Skip to content

Commit

Permalink
[TEP-0144] Validate PipelineRun for Param Enum
Browse files Browse the repository at this point in the history
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
  • Loading branch information
QuanZhang-William committed Nov 8, 2023
1 parent a106110 commit d9f2e56
Show file tree
Hide file tree
Showing 11 changed files with 319 additions and 7 deletions.
1 change: 0 additions & 1 deletion config/config-feature-flags.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -127,5 +127,4 @@ data:
# This feature is in preview mode and not implemented yet. Please check #7259 for updates.
enable-step-actions: "false"
# Setting this flag to "true" will enable the built-in param input validation via param enum.
# NOTE (#7270): this feature is still under development and not yet functional.
enable-param-enum: "false"
1 change: 1 addition & 0 deletions docs/additional-configs.md
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ Features currently in "alpha" are:
| [Coschedule](./affinityassistants.md) | [TEP-0135](https://github.com/tektoncd/community/blob/main/teps/0135-coscheduling-pipelinerun-pods.md) | N/A |`coschedule` |
| [keep pod on cancel](./taskruns.md#cancelling-a-taskrun) | N/A | v0.52 | keep-pod-on-cancel |
| [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 |
| [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` |

### Beta Features

Expand Down
3 changes: 3 additions & 0 deletions docs/pipeline-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1997,6 +1997,9 @@ associated Pipeline is an invalid graph (a.k.a wrong order, cycle, …)</p>
</tr><tr><td><p>&#34;InvalidMatrixParameterTypes&#34;</p></td>
<td><p>ReasonInvalidMatrixParameterTypes indicates a matrix contains invalid parameter types</p>
</td>
</tr><tr><td><p>&#34;InvalidParamValue&#34;</p></td>
<td><p>PipelineRunReasonInvalidParamValue indicates that the PipelineRun Param input value is not allowed.</p>
</td>
</tr><tr><td><p>&#34;InvalidTaskResultReference&#34;</p></td>
<td><p>ReasonInvalidTaskResultReference indicates a task result was declared
but was not initialized by that task</p>
Expand Down
12 changes: 12 additions & 0 deletions docs/pipelineruns.md
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,18 @@ case is when your CI system autogenerates `PipelineRuns` and it has `Parameters`
provide to all `PipelineRuns`. Because you can pass in extra `Parameters`, you don't have to
go through the complexity of checking each `Pipeline` and providing only the required params.

#### Parameter Enums

> :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.

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.

Tekton will also the validate the `param` values passed to any referenced `Tasks` (vis `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`.

You can also specify `Enum` for `PipelineRun` with an embedded `Pipeline`. The same param validation will be executed in this scenario.

See more details in [Param.Enum](./pipelines.md#param-enum).

#### Propagated Parameters

When using an inlined spec, parameters from the parent `PipelineRun` will be
Expand Down
65 changes: 63 additions & 2 deletions docs/pipelines.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,9 +274,70 @@ spec:
#### Param enum
> :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.

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

Parameter declarations can include `enum` which is a predefine set of valid values that can be accepted by the `Pipeline`.
``` yaml
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
name: pipeline-param-enum
spec:
params:
- name: message
enum: ["v1", "v2"]
default: "v1"
tasks:
- name: task1
params:
- name: message
value: $(params.message)
steps:
- name: build
image: bash:3.2
script: |
echo "$(params.message)"
```

If the `Param` value passed in by `PipelineRun` is **NOT** in the predefined `enum` list, the `PipelineRun` will fail with reason `InvalidParamValue`.

If a `PipelineTask` references a `Task` with `enum`, Tekton validates the **intersection** of enum specified in the referenced `Task` and the enum specified in the Pipeline `spec.params`. In the example below, the referenced `Task` accepts `v1` and `v2` as valid values, and the `Pipeline` accepts `v2` and `v3` as valid values. Only passing `v2` in the `PipelineRun` will lead to a sucessful execution.

``` yaml
apiVersion: tekton.dev/v1
kind: Task
metadata:
name: param-enum-demo
spec:
params:
- name: message
type: string
enum: ["v1", "v2"]
steps:
- name: build
image: bash:latest
script: |
echo "$(params.message)"
```

``` yaml
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
name: pipeline-param-enum
spec:
params:
- name: message
enum: ["v2", "v3"]
tasks:
- name: task1
params:
- name: message
value: $(params.message)
taskRef:
name: param-enum-demo
```

See usage in this [example](../examples/v1/pipelineruns/alpha/param-enum.yaml)

## Adding `Tasks` to the `Pipeline`

Expand Down
2 changes: 0 additions & 2 deletions docs/taskruns.md
Original file line number Diff line number Diff line change
Expand Up @@ -406,8 +406,6 @@ go through the complexity of checking each `Task` and providing only the require

> :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.

> :seedling: This feature is WIP and not yet supported/implemented. Documentation to be completed.

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.

You can also specify `Enum` for [`TaskRun` with an embedded `Task`](#example-taskrun-with-an-embedded-task). The same param validation will be executed in this scenario.
Expand Down
2 changes: 0 additions & 2 deletions docs/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -715,8 +715,6 @@ spec:
#### Param enum
> :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.

> :seedling: This feature is WIP and not yet supported/implemented. Documentation to be completed.

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`:

``` yaml
Expand Down
33 changes: 33 additions & 0 deletions examples/v1/pipelineruns/alpha/param-enum.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
name: pipeline-param-enum
spec:
params:
- name: message
enum: ["v1", "v2", "v3"]
default: "v1"
tasks:
- name: task1
params:
- name: message
value: $(params.message)
taskSpec:
params:
- name: message
steps:
- name: build
image: bash:3.2
script: |
echo "$(params.message)"
---
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
name: pipelinerun-param-enum
spec:
pipelineRef:
name: pipeline-param-enum
params:
- name: message
value: "v2"
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 @@ -409,6 +409,8 @@ const (
PipelineRunReasonCreateRunFailed PipelineRunReason = "CreateRunFailed"
// ReasonCELEvaluationFailed indicates the pipeline fails the CEL evaluation
PipelineRunReasonCELEvaluationFailed PipelineRunReason = "CELEvaluationFailed"
// PipelineRunReasonInvalidParamValue indicates that the PipelineRun Param input value is not allowed.
PipelineRunReasonInvalidParamValue PipelineRunReason = "InvalidParamValue"
)

func (t PipelineRunReason) String() string {
Expand Down
23 changes: 23 additions & 0 deletions pkg/reconciler/pipelinerun/pipelinerun.go
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,19 @@ func (c *Reconciler) resolvePipelineState(
return nil, controller.NewPermanentError(err)
}
}

if config.FromContextOrDefaults(ctx).FeatureFlags.EnableParamEnum {
for _, tr := range resolvedTask.TaskRuns {
if len(tr.Status.Conditions) > 0 {
cond := resolvedTask.TaskRuns[0].Status.Conditions[0]
if cond.Status == corev1.ConditionFalse && cond.Reason == v1.TaskRunReasonInvalidParamValue {
pr.Status.MarkFailed(v1.PipelineRunReasonInvalidParamValue.String(),
"Invalid param value in the referenced Task from PipelineTask \"%s\": %s", resolvedTask.PipelineTask.Name, cond.Message)
return nil, controller.NewPermanentError(err)
}
}
}
}
pst = append(pst, resolvedTask)
}
return pst, nil
Expand Down Expand Up @@ -487,6 +500,16 @@ func (c *Reconciler) reconcile(ctx context.Context, pr *v1.PipelineRun, getPipel
return controller.NewPermanentError(err)
}

if config.FromContextOrDefaults(ctx).FeatureFlags.EnableParamEnum {
if err := taskrun.ValidateEnumParam(ctx, pr.Spec.Params, pipelineSpec.Params); err != nil {
logger.Errorf("PipelineRun %q Param Enum validation failed: %v", pr.Name, err)
pr.Status.MarkFailed(v1.PipelineRunReasonInvalidParamValue.String(),
"PipelineRun %s/%s parameters have invalid value: %s",
pr.Namespace, pr.Name, err)
return controller.NewPermanentError(err)
}
}

// Ensure that the keys of an object param declared in PipelineSpec are not missed in the PipelineRunSpec
if err = resources.ValidateObjectParamRequiredKeys(pipelineSpec.Params, pr.Spec.Params); err != nil {
// This Run has failed, so we need to mark it as failed and stop reconciling it
Expand Down
Loading

0 comments on commit d9f2e56

Please sign in to comment.