diff --git a/docs/pipelineruns.md b/docs/pipelineruns.md
index 82b22b9f1d2..0af30996966 100644
--- a/docs/pipelineruns.md
+++ b/docs/pipelineruns.md
@@ -15,6 +15,10 @@ weight: 500
- [Remote Pipelines](#remote-pipelines)
- [Specifying Resources
](#specifying-resources)
- [Specifying Parameters
](#specifying-parameters)
+ - [Propagated Parameters](#propagated-parameters)
+ - [Scope and Precedence](#scope-and-precedence)
+ - [Default Values](#default-values)
+ - [Referenced Resources](#referenced-resources)
- [Specifying custom ServiceAccount
credentials](#specifying-custom-serviceaccount-credentials)
- [Mapping ServiceAccount
credentials to Tasks
](#mapping-serviceaccount-credentials-to-tasks)
- [Specifying a Pod
template](#specifying-a-pod-template)
@@ -283,6 +287,348 @@ 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.
+#### Propagated Parameters
+
+**([alpha only](https://github.com/tektoncd/pipeline/blob/main/docs/install.md#alpha-features))**
+
+When using an inlined spec, parameters from the parent `PipelineRun` will be
+propagated to any inlined specs without needing to be explicitly defined. This
+allows authors to simplify specs by automatically propagating top-level
+parameters down to other inlined resources.
+
+```yaml
+apiVersion: tekton.dev/v1beta1
+kind: PipelineRun
+metadata:
+ generateName: pr-echo-
+spec:
+ params:
+ - name: HELLO
+ value: "Hello World!"
+ - name: BYE
+ value: "Bye World!"
+ pipelineSpec:
+ tasks:
+ - name: echo-hello
+ taskSpec:
+ steps:
+ - name: echo
+ image: ubuntu
+ script: |
+ #!/usr/bin/env bash
+ echo "$(params.HELLO)"
+ - name: echo-bye
+ taskSpec:
+ steps:
+ - name: echo
+ image: ubuntu
+ script: |
+ #!/usr/bin/env bash
+ echo "$(params.BYE)"
+```
+
+On executing the pipeline run, the parameters will be interpolated during resolution.
+The specifications are not mutated before storage and so it remains the same.
+The status is updated.
+
+```yaml
+apiVersion: tekton.dev/v1beta1
+kind: PipelineRun
+metadata:
+ name: pr-echo-szzs9
+ ...
+spec:
+ params:
+ - name: HELLO
+ value: Hello World!
+ - name: BYE
+ value: Bye World!
+ pipelineSpec:
+ tasks:
+ - name: echo-hello
+ taskSpec:
+ steps:
+ - image: ubuntu
+ name: echo
+ script: |
+ #!/usr/bin/env bash
+ echo "$(params.HELLO)"
+ - name: echo-bye
+ taskSpec:
+ steps:
+ - image: ubuntu
+ name: echo
+ script: |
+ #!/usr/bin/env bash
+ echo "$(params.BYE)"
+status:
+ conditions:
+ - lastTransitionTime: "2022-04-07T12:34:58Z"
+ message: 'Tasks Completed: 2 (Failed: 0, Canceled 0), Skipped: 0'
+ reason: Succeeded
+ status: "True"
+ type: Succeeded
+ pipelineSpec:
+ ...
+ taskRuns:
+ pr-echo-szzs9-echo-hello:
+ pipelineTaskName: echo-hello
+ status:
+ ...
+ taskSpec:
+ steps:
+ - image: ubuntu
+ name: echo
+ resources: {}
+ script: |
+ #!/usr/bin/env bash
+ echo "Hello World!"
+ pr-echo-szzs9-echo-bye:
+ pipelineTaskName: echo-bye
+ status:
+ ...
+ taskSpec:
+ steps:
+ - image: ubuntu
+ name: echo
+ resources: {}
+ script: |
+ #!/usr/bin/env bash
+ echo "Bye World!"
+```
+
+##### Scope and Precedence
+
+When Parameters names conflict, the inner scope would take precedence as shown in this example:
+
+```yaml
+apiVersion: tekton.dev/v1beta1
+kind: PipelineRun
+metadata:
+ generateName: pr-echo-
+spec:
+ params:
+ - name: HELLO
+ value: "Hello World!"
+ - name: BYE
+ value: "Bye World!"
+ pipelineSpec:
+ tasks:
+ - name: echo-hello
+ params:
+ - name: HELLO
+ value: "Sasa World!"
+ taskSpec:
+ params:
+ - name: HELLO
+ type: string
+ steps:
+ - name: echo
+ image: ubuntu
+ script: |
+ #!/usr/bin/env bash
+ echo "$(params.HELLO)"
+ ...
+```
+
+resolves to
+
+```yaml
+# Successful execution of the above PipelineRun
+apiVersion: tekton.dev/v1beta1
+kind: PipelineRun
+metadata:
+ name: pr-echo-szzs9
+ ...
+spec:
+ ...
+status:
+ conditions:
+ - lastTransitionTime: "2022-04-07T12:34:58Z"
+ message: 'Tasks Completed: 2 (Failed: 0, Canceled 0), Skipped: 0'
+ reason: Succeeded
+ status: "True"
+ type: Succeeded
+ ...
+ taskRuns:
+ pr-echo-szzs9-echo-hello:
+ pipelineTaskName: echo-hello
+ status:
+ conditions:
+ - lastTransitionTime: "2022-04-07T12:34:57Z"
+ message: All Steps have completed executing
+ reason: Succeeded
+ status: "True"
+ type: Succeeded
+ taskSpec:
+ steps:
+ - image: ubuntu
+ name: echo
+ resources: {}
+ script: |
+ #!/usr/bin/env bash
+ echo "Sasa World!"
+ ...
+```
+
+##### Default Values
+
+When `Parameter` specifications have default values, the `Parameter` value provided at runtime would take precedence to give users control, as shown in this example:
+
+```yaml
+apiVersion: tekton.dev/v1beta1
+kind: PipelineRun
+metadata:
+ generateName: pr-echo-
+spec:
+ params:
+ - name: HELLO
+ value: "Hello World!"
+ - name: BYE
+ value: "Bye World!"
+ pipelineSpec:
+ tasks:
+ - name: echo-hello
+ taskSpec:
+ params:
+ - name: HELLO
+ type: string
+ default: "Sasa World!"
+ steps:
+ - name: echo
+ image: ubuntu
+ script: |
+ #!/usr/bin/env bash
+ echo "$(params.HELLO)"
+ ...
+```
+
+resolves to
+
+```yaml
+# Successful execution of the above PipelineRun
+apiVersion: tekton.dev/v1beta1
+kind: PipelineRun
+metadata:
+ name: pr-echo-szzs9
+ ...
+spec:
+ ...
+status:
+ conditions:
+ - lastTransitionTime: "2022-04-07T12:34:58Z"
+ message: 'Tasks Completed: 2 (Failed: 0, Canceled 0), Skipped: 0'
+ reason: Succeeded
+ status: "True"
+ type: Succeeded
+ ...
+ taskRuns:
+ pr-echo-szzs9-echo-hello:
+ pipelineTaskName: echo-hello
+ status:
+ conditions:
+ - lastTransitionTime: "2022-04-07T12:34:57Z"
+ message: All Steps have completed executing
+ reason: Succeeded
+ status: "True"
+ type: Succeeded
+ taskSpec:
+ steps:
+ - image: ubuntu
+ name: echo
+ resources: {}
+ script: |
+ #!/usr/bin/env bash
+ echo "Hello World!"
+ ...
+```
+
+##### Referenced Resources
+
+When a PipelineRun definition has referenced specifications but does not explicitly pass Parameters, the PipelineRun will be created but the execution will fail because of missing Parameters.
+
+```yaml
+# Invalid PipelineRun attempting to propagate Parameters to referenced Tasks
+apiVersion: tekton.dev/v1beta1
+kind: PipelineRun
+metadata:
+ generateName: pr-echo-
+spec:
+ params:
+ - name: HELLO
+ value: "Hello World!"
+ - name: BYE
+ value: "Bye World!"
+ pipelineSpec:
+ tasks:
+ - name: echo-hello
+ taskRef:
+ name: echo-hello
+ - name: echo-bye
+ taskRef:
+ name: echo-bye
+---
+apiVersion: tekton.dev/v1beta1
+kind: Task
+metadata:
+ name: echo-hello
+spec:
+ steps:
+ - name: echo
+ image: ubuntu
+ script: |
+ #!/usr/bin/env bash
+ echo "$(params.HELLO)"
+---
+apiVersion: tekton.dev/v1beta1
+kind: Task
+metadata:
+ name: echo-bye
+spec:
+ steps:
+ - name: echo
+ image: ubuntu
+ script: |
+ #!/usr/bin/env bash
+ echo "$(params.BYE)"
+```
+
+Fails as follows:
+
+```yaml
+# Failed execution of the above PipelineRun
+apiVersion: tekton.dev/v1beta1
+kind: PipelineRun
+metadata:
+ name: pr-echo-24lmf
+ ...
+spec:
+ params:
+ - name: HELLO
+ value: Hello World!
+ - name: BYE
+ value: Bye World!
+ pipelineSpec:
+ tasks:
+ - name: echo-hello
+ taskRef:
+ kind: Task
+ name: echo-hello
+ - name: echo-bye
+ taskRef:
+ kind: Task
+ name: echo-bye
+status:
+ conditions:
+ - lastTransitionTime: "2022-04-07T20:24:51Z"
+ message: 'invalid input params for task echo-hello: missing values for
+ these params which have no default values: [HELLO]'
+ reason: PipelineValidationFailed
+ status: "False"
+ type: Succeeded
+ ...
+```
+
### Specifying custom `ServiceAccount` credentials
You can execute the `Pipeline` in your `PipelineRun` with a specific set of credentials by
diff --git a/docs/taskruns.md b/docs/taskruns.md
index 1b0a60ed4b9..5da4f98a1af 100644
--- a/docs/taskruns.md
+++ b/docs/taskruns.md
@@ -14,6 +14,7 @@ weight: 300
- [Tekton Bundles](#tekton-bundles)
- [Remote Tasks](#remote-tasks)
- [Specifying `Parameters`](#specifying-parameters)
+ - [Propagated Parameters](#propagated-parameters)
- [Extra Parameters](#extra-parameters)
- [Specifying `Resources`](#specifying-resources)
- [Specifying `Resource` limits](#specifying-resource-limits)
@@ -197,6 +198,73 @@ spec:
**Note:** If a parameter does not have an implicit default value, you must explicitly set its value.
+#### Propagated Parameters
+
+**([alpha only](https://github.com/tektoncd/pipeline/blob/main/docs/install.md#alpha-features))**
+
+When using an inlined `taskSpec`, parameters from the parent `TaskRun` will be
+available to the `Task` without needing to be explicitly defined.
+
+```yaml
+apiVersion: tekton.dev/v1beta1
+kind: TaskRun
+metadata:
+ generateName: hello-
+spec:
+ params:
+ - name: message
+ value: "hello world!"
+ taskSpec:
+ # There are no explicit params defined here.
+ # They are derived from the TaskRun params above.
+ steps:
+ - name: default
+ image: ubuntu
+ script: |
+ echo $(params.message)
+```
+
+On executing the task run, the parameters will be interpolated during resolution.
+The specifications are not mutated before storage and so it remains the same.
+The status is updated.
+
+```yaml
+kind: TaskRun
+metadata:
+ name: hello-dlqm9
+ ...
+spec:
+ params:
+ - name: message
+ value: hello world!
+ serviceAccountName: default
+ taskSpec:
+ steps:
+ - image: ubuntu
+ name: default
+ resources: {}
+ script: |
+ echo $(params.message)
+status:
+ conditions:
+ - lastTransitionTime: "2022-05-20T15:24:41Z"
+ message: All Steps have completed executing
+ reason: Succeeded
+ status: "True"
+ type: Succeeded
+ ...
+ steps:
+ - container: step-default
+ ...
+ taskSpec:
+ steps:
+ - image: ubuntu
+ name: default
+ resources: {}
+ script: |
+ echo "hello world!"
+```
+
#### Extra Parameters
**([alpha only](https://github.com/tektoncd/pipeline/blob/main/docs/install.md#alpha-features))**
diff --git a/examples/v1beta1/pipelineruns/alpha/propagating_params_implicit_parameters.yaml b/examples/v1beta1/pipelineruns/alpha/propagating_params_implicit_parameters.yaml
new file mode 100644
index 00000000000..9cf03eea189
--- /dev/null
+++ b/examples/v1beta1/pipelineruns/alpha/propagating_params_implicit_parameters.yaml
@@ -0,0 +1,18 @@
+apiVersion: tekton.dev/v1beta1
+kind: PipelineRun
+metadata:
+ generateName: pr-echo-
+spec:
+ params:
+ - name: HELLO
+ value: "Pipeline Hello World!"
+ pipelineSpec:
+ tasks:
+ - name: echo-hello
+ taskSpec:
+ steps:
+ - name: echo
+ image: ubuntu
+ script: |
+ #!/usr/bin/env bash
+ echo "$(params.HELLO)"
diff --git a/examples/v1beta1/pipelineruns/alpha/propagating_params_with_scope_precedence.yaml b/examples/v1beta1/pipelineruns/alpha/propagating_params_with_scope_precedence.yaml
new file mode 100644
index 00000000000..0791e27ad41
--- /dev/null
+++ b/examples/v1beta1/pipelineruns/alpha/propagating_params_with_scope_precedence.yaml
@@ -0,0 +1,21 @@
+apiVersion: tekton.dev/v1beta1
+kind: PipelineRun
+metadata:
+ generateName: pr-echo-
+spec:
+ params:
+ - name: HELLO
+ value: "Pipeline Hello World!"
+ pipelineSpec:
+ tasks:
+ - name: echo-hello
+ params:
+ - name: HELLO
+ value: "Task Hello World!"
+ taskSpec:
+ steps:
+ - name: echo
+ image: ubuntu
+ script: |
+ #!/usr/bin/env bash
+ echo "$(params.HELLO)"
diff --git a/examples/v1beta1/pipelineruns/alpha/propagating_params_with_scope_precedence_default_only.yaml b/examples/v1beta1/pipelineruns/alpha/propagating_params_with_scope_precedence_default_only.yaml
new file mode 100644
index 00000000000..3d5af4a40c0
--- /dev/null
+++ b/examples/v1beta1/pipelineruns/alpha/propagating_params_with_scope_precedence_default_only.yaml
@@ -0,0 +1,21 @@
+apiVersion: tekton.dev/v1beta1
+kind: PipelineRun
+metadata:
+ generateName: pr-echo-
+spec:
+ params:
+ - name: HELLO
+ value: "Pipeline Hello World!"
+ pipelineSpec:
+ tasks:
+ - name: echo-hello
+ taskSpec:
+ params:
+ - name: HELLO
+ default: "Default Hello World!"
+ steps:
+ - name: echo
+ image: ubuntu
+ script: |
+ #!/usr/bin/env bash
+ echo "$(params.HELLO)"
diff --git a/examples/v1beta1/taskruns/alpha/propagating_params_implicit.yaml b/examples/v1beta1/taskruns/alpha/propagating_params_implicit.yaml
new file mode 100644
index 00000000000..d0a47a8eacc
--- /dev/null
+++ b/examples/v1beta1/taskruns/alpha/propagating_params_implicit.yaml
@@ -0,0 +1,15 @@
+apiVersion: tekton.dev/v1beta1
+kind: TaskRun
+metadata:
+ generateName: hello-
+spec:
+ params:
+ - name: message
+ value: "hello world!"
+ taskSpec:
+ # There are no explicit params defined here. They are derived from the TaskRun.
+ steps:
+ - name: default
+ image: ubuntu
+ script: |
+ echo $(params.message)
diff --git a/pkg/apis/pipeline/v1alpha1/task_validation.go b/pkg/apis/pipeline/v1alpha1/task_validation.go
index 8941ebff560..a80b5658f6e 100644
--- a/pkg/apis/pipeline/v1alpha1/task_validation.go
+++ b/pkg/apis/pipeline/v1alpha1/task_validation.go
@@ -130,7 +130,7 @@ func (ts *TaskSpec) Validate(ctx context.Context) *apis.FieldError {
}
}
- if err := v1beta1.ValidateParameterVariables(ts.Steps, ts.Params); err != nil {
+ if err := v1beta1.ValidateParameterVariables(ctx, ts.Steps, ts.Params); err != nil {
return err
}
// Deprecated
@@ -138,7 +138,7 @@ func (ts *TaskSpec) Validate(ctx context.Context) *apis.FieldError {
return err
}
- if err := v1beta1.ValidateResourcesVariables(ts.Steps, ts.Resources); err != nil {
+ if err := v1beta1.ValidateResourcesVariables(ctx, ts.Steps, ts.Resources); err != nil {
return err
}
// Deprecated
diff --git a/pkg/apis/pipeline/v1beta1/task_validation.go b/pkg/apis/pipeline/v1beta1/task_validation.go
index acea12ba00d..771aaeed74c 100644
--- a/pkg/apis/pipeline/v1beta1/task_validation.go
+++ b/pkg/apis/pipeline/v1beta1/task_validation.go
@@ -76,9 +76,9 @@ func (ts *TaskSpec) Validate(ctx context.Context) (errs *apis.FieldError) {
errs = errs.Also(validateSteps(ctx, mergedSteps).ViaField("steps"))
errs = errs.Also(ts.Resources.Validate(ctx).ViaField("resources"))
errs = errs.Also(ValidateParameterTypes(ctx, ts.Params).ViaField("params"))
- errs = errs.Also(ValidateParameterVariables(ts.Steps, ts.Params))
- errs = errs.Also(ValidateResourcesVariables(ts.Steps, ts.Resources))
- errs = errs.Also(validateTaskContextVariables(ts.Steps))
+ errs = errs.Also(ValidateParameterVariables(ctx, ts.Steps, ts.Params))
+ errs = errs.Also(ValidateResourcesVariables(ctx, ts.Steps, ts.Resources))
+ errs = errs.Also(validateTaskContextVariables(ctx, ts.Steps))
errs = errs.Also(validateResults(ctx, ts.Results).ViaField("results"))
return errs
}
@@ -317,7 +317,7 @@ func (p ParamSpec) ValidateObjectType() *apis.FieldError {
}
// ValidateParameterVariables validates all variables within a slice of ParamSpecs against a slice of Steps
-func ValidateParameterVariables(steps []Step, params []ParamSpec) *apis.FieldError {
+func ValidateParameterVariables(ctx context.Context, steps []Step, params []ParamSpec) *apis.FieldError {
parameterNames := sets.NewString()
arrayParameterNames := sets.NewString()
objectParamSpecs := []ParamSpec{}
@@ -336,12 +336,12 @@ func ValidateParameterVariables(steps []Step, params []ParamSpec) *apis.FieldErr
}
}
- errs = errs.Also(validateVariables(steps, "params", parameterNames))
+ errs = errs.Also(validateVariables(ctx, steps, "params", parameterNames))
errs = errs.Also(validateArrayUsage(steps, "params", arrayParameterNames))
- return errs.Also(validateObjectUsage(steps, objectParamSpecs))
+ return errs.Also(validateObjectUsage(ctx, steps, objectParamSpecs))
}
-func validateTaskContextVariables(steps []Step) *apis.FieldError {
+func validateTaskContextVariables(ctx context.Context, steps []Step) *apis.FieldError {
taskRunContextNames := sets.NewString().Insert(
"name",
"namespace",
@@ -351,12 +351,12 @@ func validateTaskContextVariables(steps []Step) *apis.FieldError {
"name",
"retry-count",
)
- errs := validateVariables(steps, "context\\.taskRun", taskRunContextNames)
- return errs.Also(validateVariables(steps, "context\\.task", taskContextNames))
+ errs := validateVariables(ctx, steps, "context\\.taskRun", taskRunContextNames)
+ return errs.Also(validateVariables(ctx, steps, "context\\.task", taskContextNames))
}
// ValidateResourcesVariables validates all variables within a TaskResources against a slice of Steps
-func ValidateResourcesVariables(steps []Step, resources *TaskResources) *apis.FieldError {
+func ValidateResourcesVariables(ctx context.Context, steps []Step, resources *TaskResources) *apis.FieldError {
if resources == nil {
return nil
}
@@ -371,7 +371,7 @@ func ValidateResourcesVariables(steps []Step, resources *TaskResources) *apis.Fi
resourceNames.Insert(r.Name)
}
}
- return validateVariables(steps, "resources.(?:inputs|outputs)", resourceNames)
+ return validateVariables(ctx, steps, "resources.(?:inputs|outputs)", resourceNames)
}
// TODO (@chuangw6): Make sure an object param is not used as a whole when providing values for strings.
@@ -379,7 +379,7 @@ func ValidateResourcesVariables(steps []Step, resources *TaskResources) *apis.Fi
// "When providing values for strings, Task and Pipeline authors can access
// individual attributes of an object param; they cannot access the object
// as whole (we could add support for this later)."
-func validateObjectUsage(steps []Step, params []ParamSpec) (errs *apis.FieldError) {
+func validateObjectUsage(ctx context.Context, steps []Step, params []ParamSpec) (errs *apis.FieldError) {
objectParameterNames := sets.NewString()
for _, p := range params {
// collect all names of object type params
@@ -396,7 +396,7 @@ func validateObjectUsage(steps []Step, params []ParamSpec) (errs *apis.FieldErro
}
// check if the object's key names are referenced correctly i.e. param.objectParam.key1
- errs = errs.Also(validateVariables(steps, fmt.Sprintf("params\\.%s", p.Name), objectKeys))
+ errs = errs.Also(validateVariables(ctx, steps, fmt.Sprintf("params\\.%s", p.Name), objectKeys))
}
return errs
@@ -450,7 +450,7 @@ func validateStepArrayUsage(step Step, prefix string, vars sets.String) *apis.Fi
return errs
}
-func validateVariables(steps []Step, prefix string, vars sets.String) (errs *apis.FieldError) {
+func validateVariables(ctx context.Context, steps []Step, prefix string, vars sets.String) (errs *apis.FieldError) {
// validate that the variable name format follows the rules
// - Must only contain alphanumeric characters, hyphens (-), underscores (_), and dots (.)
// - Must begin with a letter or an underscore (_)
@@ -474,16 +474,18 @@ func validateVariables(steps []Step, prefix string, vars sets.String) (errs *api
// We've checked param name format. Now, we want to check if param names are referenced correctly in each step
for idx, step := range steps {
- errs = errs.Also(validateStepVariables(step, prefix, vars).ViaFieldIndex("steps", idx))
+ errs = errs.Also(validateStepVariables(ctx, step, prefix, vars).ViaFieldIndex("steps", idx))
}
return errs
}
-func validateStepVariables(step Step, prefix string, vars sets.String) *apis.FieldError {
+func validateStepVariables(ctx context.Context, step Step, prefix string, vars sets.String) *apis.FieldError {
errs := validateTaskVariable(step.Name, prefix, vars).ViaField("name")
errs = errs.Also(validateTaskVariable(step.Image, prefix, vars).ViaField("image"))
errs = errs.Also(validateTaskVariable(step.WorkingDir, prefix, vars).ViaField("workingDir"))
- errs = errs.Also(validateTaskVariable(step.Script, prefix, vars).ViaField("script"))
+ if !(config.FromContextOrDefaults(ctx).FeatureFlags.EnableAPIFields == "alpha" && prefix == "params") {
+ errs = errs.Also(validateTaskVariable(step.Script, prefix, vars).ViaField("script"))
+ }
for i, cmd := range step.Command {
errs = errs.Also(validateTaskVariable(cmd, prefix, vars).ViaFieldIndex("command", i))
}
diff --git a/pkg/reconciler/pipelinerun/pipelinerun.go b/pkg/reconciler/pipelinerun/pipelinerun.go
index a6dfd810f18..f6faa371f32 100644
--- a/pkg/reconciler/pipelinerun/pipelinerun.go
+++ b/pkg/reconciler/pipelinerun/pipelinerun.go
@@ -473,9 +473,9 @@ func (c *Reconciler) reconcile(ctx context.Context, pr *v1beta1.PipelineRun, get
}
// Apply parameter substitution from the PipelineRun
- pipelineSpec = resources.ApplyParameters(pipelineSpec, pr)
- pipelineSpec = resources.ApplyContexts(pipelineSpec, pipelineMeta.Name, pr)
- pipelineSpec = resources.ApplyWorkspaces(pipelineSpec, pr)
+ pipelineSpec = resources.ApplyParameters(ctx, pipelineSpec, pr)
+ pipelineSpec = resources.ApplyContexts(ctx, pipelineSpec, pipelineMeta.Name, pr)
+ pipelineSpec = resources.ApplyWorkspaces(ctx, pipelineSpec, pr)
// pipelineState holds a list of pipeline tasks after resolving conditions and pipeline resources
// pipelineState also holds a taskRun for each pipeline task after the taskRun is created
diff --git a/pkg/reconciler/pipelinerun/resources/apply.go b/pkg/reconciler/pipelinerun/resources/apply.go
index 089b5fc7bf8..9ed5a944830 100644
--- a/pkg/reconciler/pipelinerun/resources/apply.go
+++ b/pkg/reconciler/pipelinerun/resources/apply.go
@@ -17,18 +17,21 @@ limitations under the License.
package resources
import (
+ "context"
"fmt"
"strconv"
"strings"
+ "github.com/tektoncd/pipeline/pkg/apis/config"
"github.com/tektoncd/pipeline/pkg/apis/run/v1alpha1"
+ "github.com/tektoncd/pipeline/pkg/reconciler/taskrun/resources"
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
"github.com/tektoncd/pipeline/pkg/substitution"
)
// ApplyParameters applies the params from a PipelineRun.Params to a PipelineSpec.
-func ApplyParameters(p *v1beta1.PipelineSpec, pr *v1beta1.PipelineRun) *v1beta1.PipelineSpec {
+func ApplyParameters(ctx context.Context, p *v1beta1.PipelineSpec, pr *v1beta1.PipelineRun) *v1beta1.PipelineSpec {
// This assumes that the PipelineRun inputs have been validated against what the Pipeline requests.
// stringReplacements is used for standard single-string stringReplacements, while arrayReplacements contains arrays
@@ -69,19 +72,19 @@ func ApplyParameters(p *v1beta1.PipelineSpec, pr *v1beta1.PipelineRun) *v1beta1.
}
}
- return ApplyReplacements(p, stringReplacements, arrayReplacements)
+ return ApplyReplacements(ctx, p, stringReplacements, arrayReplacements)
}
// ApplyContexts applies the substitution from $(context.(pipelineRun|pipeline).*) with the specified values.
// Currently supports only name substitution. Uses "" as a default if name is not specified.
-func ApplyContexts(spec *v1beta1.PipelineSpec, pipelineName string, pr *v1beta1.PipelineRun) *v1beta1.PipelineSpec {
+func ApplyContexts(ctx context.Context, spec *v1beta1.PipelineSpec, pipelineName string, pr *v1beta1.PipelineRun) *v1beta1.PipelineSpec {
replacements := map[string]string{
"context.pipelineRun.name": pr.Name,
"context.pipeline.name": pipelineName,
"context.pipelineRun.namespace": pr.Namespace,
"context.pipelineRun.uid": string(pr.ObjectMeta.UID),
}
- return ApplyReplacements(spec, replacements, map[string][]string{})
+ return ApplyReplacements(ctx, spec, replacements, map[string][]string{})
}
// ApplyPipelineTaskContexts applies the substitution from $(context.pipelineTask.*) with the specified values.
@@ -129,7 +132,7 @@ func ApplyPipelineTaskStateContext(state PipelineRunState, replacements map[stri
// ApplyWorkspaces replaces workspace variables in the given pipeline spec with their
// concrete values.
-func ApplyWorkspaces(p *v1beta1.PipelineSpec, pr *v1beta1.PipelineRun) *v1beta1.PipelineSpec {
+func ApplyWorkspaces(ctx context.Context, p *v1beta1.PipelineSpec, pr *v1beta1.PipelineRun) *v1beta1.PipelineSpec {
p = p.DeepCopy()
replacements := map[string]string{}
for _, declaredWorkspace := range p.Workspaces {
@@ -140,11 +143,11 @@ func ApplyWorkspaces(p *v1beta1.PipelineSpec, pr *v1beta1.PipelineRun) *v1beta1.
key := fmt.Sprintf("workspaces.%s.bound", boundWorkspace.Name)
replacements[key] = "true"
}
- return ApplyReplacements(p, replacements, map[string][]string{})
+ return ApplyReplacements(ctx, p, replacements, map[string][]string{})
}
// ApplyReplacements replaces placeholders for declared parameters with the specified replacements.
-func ApplyReplacements(p *v1beta1.PipelineSpec, replacements map[string]string, arrayReplacements map[string][]string) *v1beta1.PipelineSpec {
+func ApplyReplacements(ctx context.Context, p *v1beta1.PipelineSpec, replacements map[string]string, arrayReplacements map[string][]string) *v1beta1.PipelineSpec {
p = p.DeepCopy()
for i := range p.Tasks {
@@ -158,6 +161,7 @@ func ApplyReplacements(p *v1beta1.PipelineSpec, replacements map[string]string,
c.Params = replaceParamValues(c.Params, replacements, arrayReplacements)
}
p.Tasks[i].WhenExpressions = p.Tasks[i].WhenExpressions.ReplaceWhenExpressionsVariables(replacements, arrayReplacements)
+ p.Tasks[i], replacements, arrayReplacements = propagateParams(ctx, p.Tasks[i], replacements, arrayReplacements)
}
for i := range p.Finally {
@@ -169,6 +173,33 @@ func ApplyReplacements(p *v1beta1.PipelineSpec, replacements map[string]string,
return p
}
+func propagateParams(ctx context.Context, t v1beta1.PipelineTask, replacements map[string]string, arrayReplacements map[string][]string) (v1beta1.PipelineTask, map[string]string, map[string][]string) {
+ if t.TaskSpec != nil && config.FromContextOrDefaults(ctx).FeatureFlags.EnableAPIFields == "alpha" {
+ patterns := []string{
+ "params.%s",
+ "params[%q]",
+ "params['%s']",
+ }
+ // check if there are task parameters defined that match the params at pipeline level
+ if len(t.Params) > 0 {
+ for _, par := range t.Params {
+ for _, pattern := range patterns {
+ checkName := fmt.Sprintf(pattern, par.Name)
+ // Scoping. Task Params will replace Pipeline Params
+ if _, ok := replacements[checkName]; ok {
+ replacements[checkName] = par.Value.StringVal
+ }
+ if _, ok := arrayReplacements[checkName]; ok {
+ arrayReplacements[checkName] = par.Value.ArrayVal
+ }
+ }
+ }
+ }
+ t.TaskSpec.TaskSpec = *resources.ApplyReplacements(&t.TaskSpec.TaskSpec, replacements, arrayReplacements)
+ }
+ return t, replacements, arrayReplacements
+}
+
func replaceParamValues(params []v1beta1.Param, stringReplacements map[string]string, arrayReplacements map[string][]string) []v1beta1.Param {
for i := range params {
params[i].Value.ApplyReplacements(stringReplacements, arrayReplacements)
diff --git a/pkg/reconciler/pipelinerun/resources/apply_test.go b/pkg/reconciler/pipelinerun/resources/apply_test.go
index 495f39b9483..5e5ab9605bb 100644
--- a/pkg/reconciler/pipelinerun/resources/apply_test.go
+++ b/pkg/reconciler/pipelinerun/resources/apply_test.go
@@ -17,10 +17,12 @@ limitations under the License.
package resources
import (
+ "context"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
+ "github.com/tektoncd/pipeline/pkg/apis/config"
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1"
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
resourcev1alpha1 "github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1"
@@ -35,6 +37,7 @@ func TestApplyParameters(t *testing.T) {
original v1beta1.PipelineSpec
params []v1beta1.Param
expected v1beta1.PipelineSpec
+ alpha bool
}{{
name: "single parameter",
original: v1beta1.PipelineSpec{
@@ -64,6 +67,230 @@ func TestApplyParameters(t *testing.T) {
},
}},
},
+ }, {
+ name: "parameter propagation string no task or task default winner pipeline",
+ original: v1beta1.PipelineSpec{
+ Tasks: []v1beta1.PipelineTask{{
+ TaskSpec: &v1beta1.EmbeddedTask{
+ TaskSpec: v1beta1.TaskSpec{
+ Steps: []v1beta1.Step{{
+ Name: "step1",
+ Image: "ubuntu",
+ Script: `#!/usr/bin/env bash\necho "$(params.HELLO)"`,
+ }},
+ },
+ },
+ }},
+ },
+ params: []v1beta1.Param{{Name: "HELLO", Value: *v1beta1.NewArrayOrString("hello param!")}},
+ expected: v1beta1.PipelineSpec{
+ Tasks: []v1beta1.PipelineTask{{
+ TaskSpec: &v1beta1.EmbeddedTask{
+ TaskSpec: v1beta1.TaskSpec{
+ Steps: []v1beta1.Step{{
+ Name: "step1",
+ Image: "ubuntu",
+ Script: `#!/usr/bin/env bash\necho "hello param!"`,
+ }},
+ },
+ },
+ }},
+ },
+ alpha: true,
+ }, {
+ name: "parameter propagation array no task or task default winner pipeline",
+ original: v1beta1.PipelineSpec{
+ Tasks: []v1beta1.PipelineTask{{
+ TaskSpec: &v1beta1.EmbeddedTask{
+ TaskSpec: v1beta1.TaskSpec{
+ Steps: []v1beta1.Step{{
+ Name: "step1",
+ Image: "ubuntu",
+ Args: []string{"#!/usr/bin/env bash\n", "echo", "$(params.HELLO[*])"},
+ }},
+ },
+ },
+ }},
+ },
+ params: []v1beta1.Param{{Name: "HELLO", Value: *v1beta1.NewArrayOrString("hello", "param", "!!!")}},
+ expected: v1beta1.PipelineSpec{
+ Tasks: []v1beta1.PipelineTask{{
+ TaskSpec: &v1beta1.EmbeddedTask{
+ TaskSpec: v1beta1.TaskSpec{
+ Steps: []v1beta1.Step{{
+ Name: "step1",
+ Image: "ubuntu",
+ Args: []string{"#!/usr/bin/env bash\n", "echo", "hello", "param", "!!!"},
+ }},
+ },
+ },
+ }},
+ },
+ alpha: true,
+ }, {
+ name: "parameter propagation with task default but no task winner pipeline",
+ original: v1beta1.PipelineSpec{
+ Tasks: []v1beta1.PipelineTask{{
+ TaskSpec: &v1beta1.EmbeddedTask{
+ TaskSpec: v1beta1.TaskSpec{
+ Params: []v1beta1.ParamSpec{{
+ Name: "HELLO",
+ Default: v1beta1.NewArrayOrString("default param!"),
+ }},
+ Steps: []v1beta1.Step{{
+ Name: "step1",
+ Image: "ubuntu",
+ Script: `#!/usr/bin/env bash\necho "$(params.HELLO)"`,
+ }},
+ },
+ },
+ }},
+ },
+ params: []v1beta1.Param{{Name: "HELLO", Value: *v1beta1.NewArrayOrString("pipeline param!")}},
+ expected: v1beta1.PipelineSpec{
+ Tasks: []v1beta1.PipelineTask{{
+ TaskSpec: &v1beta1.EmbeddedTask{
+ TaskSpec: v1beta1.TaskSpec{
+ Params: []v1beta1.ParamSpec{{
+ Name: "HELLO",
+ Default: v1beta1.NewArrayOrString("default param!"),
+ }},
+ Steps: []v1beta1.Step{{
+ Name: "step1",
+ Image: "ubuntu",
+ Script: `#!/usr/bin/env bash\necho "pipeline param!"`,
+ }},
+ },
+ },
+ }},
+ },
+ alpha: true,
+ }, {
+ name: "parameter propagation array with task default but no task winner pipeline",
+ original: v1beta1.PipelineSpec{
+ Tasks: []v1beta1.PipelineTask{{
+ TaskSpec: &v1beta1.EmbeddedTask{
+ TaskSpec: v1beta1.TaskSpec{
+ Params: []v1beta1.ParamSpec{{
+ Name: "HELLO",
+ Default: v1beta1.NewArrayOrString("default", "param!"),
+ }},
+ Steps: []v1beta1.Step{{
+ Name: "step1",
+ Image: "ubuntu",
+ Args: []string{"#!/usr/bin/env bash\n", "echo", "$(params.HELLO)"},
+ }},
+ },
+ },
+ }},
+ },
+ params: []v1beta1.Param{{Name: "HELLO", Value: *v1beta1.NewArrayOrString("pipeline", "param!")}},
+ expected: v1beta1.PipelineSpec{
+ Tasks: []v1beta1.PipelineTask{{
+ TaskSpec: &v1beta1.EmbeddedTask{
+ TaskSpec: v1beta1.TaskSpec{
+ Params: []v1beta1.ParamSpec{{
+ Name: "HELLO",
+ Default: v1beta1.NewArrayOrString("default", "param!"),
+ }},
+ Steps: []v1beta1.Step{{
+ Name: "step1",
+ Image: "ubuntu",
+ Args: []string{"#!/usr/bin/env bash\n", "echo", "pipeline", "param!"},
+ }},
+ },
+ },
+ }},
+ },
+ alpha: true,
+ }, {
+ name: "parameter propagation array with task default and task winner task",
+ original: v1beta1.PipelineSpec{
+ Tasks: []v1beta1.PipelineTask{{
+ Params: []v1beta1.Param{
+ {Name: "HELLO", Value: *v1beta1.NewArrayOrString("task", "param!")},
+ },
+ TaskSpec: &v1beta1.EmbeddedTask{
+ TaskSpec: v1beta1.TaskSpec{
+ Params: []v1beta1.ParamSpec{{
+ Name: "HELLO",
+ Default: v1beta1.NewArrayOrString("default", "param!"),
+ }},
+ Steps: []v1beta1.Step{{
+ Name: "step1",
+ Image: "ubuntu",
+ Args: []string{"#!/usr/bin/env bash\n", "echo", "$(params.HELLO)"},
+ }},
+ },
+ },
+ }},
+ },
+ params: []v1beta1.Param{{Name: "HELLO", Value: *v1beta1.NewArrayOrString("pipeline", "param!")}},
+ expected: v1beta1.PipelineSpec{
+ Tasks: []v1beta1.PipelineTask{{
+ Params: []v1beta1.Param{
+ {Name: "HELLO", Value: *v1beta1.NewArrayOrString("task", "param!")},
+ },
+ TaskSpec: &v1beta1.EmbeddedTask{
+ TaskSpec: v1beta1.TaskSpec{
+ Params: []v1beta1.ParamSpec{{
+ Name: "HELLO",
+ Default: v1beta1.NewArrayOrString("default", "param!"),
+ }},
+ Steps: []v1beta1.Step{{
+ Name: "step1",
+ Image: "ubuntu",
+ Args: []string{"#!/usr/bin/env bash\n", "echo", "task", "param!"},
+ }},
+ },
+ },
+ }},
+ },
+ alpha: true,
+ }, {
+ name: "parameter propagation with task default and task winner task",
+ original: v1beta1.PipelineSpec{
+ Tasks: []v1beta1.PipelineTask{{
+ Params: []v1beta1.Param{
+ {Name: "HELLO", Value: *v1beta1.NewArrayOrString("task param!")},
+ },
+ TaskSpec: &v1beta1.EmbeddedTask{
+ TaskSpec: v1beta1.TaskSpec{
+ Params: []v1beta1.ParamSpec{{
+ Name: "HELLO",
+ Default: v1beta1.NewArrayOrString("default param!"),
+ }},
+ Steps: []v1beta1.Step{{
+ Name: "step1",
+ Image: "ubuntu",
+ Script: `#!/usr/bin/env bash\necho "$(params.HELLO)"`,
+ }},
+ },
+ },
+ }},
+ },
+ params: []v1beta1.Param{{Name: "HELLO", Value: *v1beta1.NewArrayOrString("pipeline param!")}},
+ expected: v1beta1.PipelineSpec{
+ Tasks: []v1beta1.PipelineTask{{
+ Params: []v1beta1.Param{
+ {Name: "HELLO", Value: *v1beta1.NewArrayOrString("task param!")},
+ },
+ TaskSpec: &v1beta1.EmbeddedTask{
+ TaskSpec: v1beta1.TaskSpec{
+ Params: []v1beta1.ParamSpec{{
+ Name: "HELLO",
+ Default: v1beta1.NewArrayOrString("default param!"),
+ }},
+ Steps: []v1beta1.Step{{
+ Name: "step1",
+ Image: "ubuntu",
+ Script: `#!/usr/bin/env bash\necho "task param!"`,
+ }},
+ },
+ },
+ }},
+ },
+ alpha: true,
}, {
name: "single parameter with when expression",
original: v1beta1.PipelineSpec{
@@ -349,6 +576,12 @@ func TestApplyParameters(t *testing.T) {
},
},
} {
+ ctx := context.Background()
+ if tt.alpha {
+ cfg := config.FromContextOrDefaults(ctx)
+ cfg.FeatureFlags = &config.FeatureFlags{EnableAPIFields: "alpha"}
+ ctx = config.ToContext(ctx, cfg)
+ }
tt := tt // capture range variable
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
@@ -357,7 +590,7 @@ func TestApplyParameters(t *testing.T) {
Params: tt.params,
},
}
- got := ApplyParameters(&tt.original, run)
+ got := ApplyParameters(ctx, &tt.original, run)
if d := cmp.Diff(&tt.expected, got); d != "" {
t.Errorf("ApplyParameters() got diff %s", diff.PrintWantGot(d))
}
@@ -590,6 +823,7 @@ func TestApplyTaskResults_Conditions(t *testing.T) {
}
func TestContext(t *testing.T) {
+ ctx := context.Background()
for _, tc := range []struct {
description string
pr *v1beta1.PipelineRun
@@ -655,7 +889,7 @@ func TestContext(t *testing.T) {
}},
},
}
- got := ApplyContexts(&orig.Spec, orig.Name, tc.pr)
+ got := ApplyContexts(ctx, &orig.Spec, orig.Name, tc.pr)
if d := cmp.Diff(tc.expected, got.Tasks[0].Params[0]); d != "" {
t.Errorf(diff.PrintWantGot(d))
}
@@ -728,6 +962,7 @@ func TestApplyPipelineTaskContexts(t *testing.T) {
}
func TestApplyWorkspaces(t *testing.T) {
+ ctx := context.Background()
for _, tc := range []struct {
description string
declarations []v1beta1.PipelineWorkspaceDeclaration
@@ -769,7 +1004,7 @@ func TestApplyWorkspaces(t *testing.T) {
Workspaces: tc.bindings,
},
}
- p2 := ApplyWorkspaces(&p1, pr)
+ p2 := ApplyWorkspaces(ctx, &p1, pr)
str := p2.Tasks[0].Params[0].Value.StringVal
if str != tc.expectedReplacement {
t.Errorf("expected %q, received %q", tc.expectedReplacement, str)