diff --git a/docs/variables.md b/docs/variables.md index 974c6642c6a..1e9a5599538 100644 --- a/docs/variables.md +++ b/docs/variables.md @@ -16,6 +16,7 @@ This page documents the variable substitions supported by `Tasks` and `Pipelines | `tasks..results.` | The value of the `Task's` result. Can alter `Task` execution order within a `Pipeline`.) | | `context.pipelineRun.name` | The name of the `PipelineRun` that this `Pipeline` is running in. | | `context.pipelineRun.namespace` | The namespace of the `PipelineRun` that this `Pipeline` is running in. | +| `context.pipelineRun.uid` | The uid of the `PipelineRun` that this `Pipeline` is running in. | | `context.pipeline.name` | The name of this `Pipeline` . | @@ -33,6 +34,7 @@ This page documents the variable substitions supported by `Tasks` and `Pipelines | `credentials.path` | The path to credentials injected from Secrets with matching annotations. | | `context.taskRun.name` | The name of the `TaskRun` that this `Task` is running in. | | `context.taskRun.namespace` | The namespace of the `TaskRun` that this `Task` is running in. | +| `context.taskRun.uid` | The uid of the `TaskRun` that this `Task` is running in. | | `context.task.name` | The name of this `Task`. | ### `PipelineResource` variables available in a `Task` diff --git a/examples/v1beta1/pipelineruns/using_context_variables.yaml b/examples/v1beta1/pipelineruns/using_context_variables.yaml new file mode 100644 index 00000000000..d05cf49b3a5 --- /dev/null +++ b/examples/v1beta1/pipelineruns/using_context_variables.yaml @@ -0,0 +1,34 @@ +kind: PipelineRun +apiVersion: tekton.dev/v1beta1 +metadata: + generateName: test-pipelinerun- +spec: + serviceAccountName: 'default' + pipelineSpec: + tasks: + - name: task1 + params: + - name: pipeline-uid + value: "$(context.pipelineRun.uid)" + - name: pipeline-name + value: "$(context.pipeline.name)" + - name: pipelineRun-name + value: "$(context.pipelineRun.name)" + taskSpec: + params: + - name: pipeline-uid + - name: pipeline-name + - name: pipelineRun-name + steps: + - image: ubuntu + name: print-uid + script: | + echo "TaskRun UID: $(context.taskRun.uid)" + echo "PipelineRun UID from params: $(params.pipeline-uid)" + - image: ubuntu + name: print-names + script: | + echo "Task name: $(context.task.name)" + echo "TaskRun name: $(context.taskRun.name)" + echo "Pipeline name from params: $(params.pipeline-name)" + echo "PipelineRun name from params: $(params.pipelineRun-name)" \ No newline at end of file diff --git a/examples/v1beta1/taskruns/using_context_variables.yaml b/examples/v1beta1/taskruns/using_context_variables.yaml new file mode 100644 index 00000000000..46f1a3af01e --- /dev/null +++ b/examples/v1beta1/taskruns/using_context_variables.yaml @@ -0,0 +1,16 @@ +kind: TaskRun +apiVersion: tekton.dev/v1beta1 +metadata: + generateName: test-taskrun- +spec: + taskSpec: + steps: + - image: ubuntu + name: print-uid + script: | + echo "TaskRunUID name: $(context.taskRun.uid)" + - image: ubuntu + name: print-names + script: | + echo "Task name: $(context.task.name)" + echo "TaskRun name: $(context.taskRun.name)" diff --git a/internal/builder/v1beta1/pipeline.go b/internal/builder/v1beta1/pipeline.go index 54f0a1ca50c..cb84e115938 100644 --- a/internal/builder/v1beta1/pipeline.go +++ b/internal/builder/v1beta1/pipeline.go @@ -24,6 +24,7 @@ import ( resource "github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" "knative.dev/pkg/apis" ) @@ -354,6 +355,13 @@ func PipelineRun(name string, ops ...PipelineRunOp) *v1beta1.PipelineRun { return pr } +// PipelineRunUID sets the namespace on a PipelineRun +func PipelineRunUID(uid string) PipelineRunOp { + return func(t *v1beta1.PipelineRun) { + t.ObjectMeta.UID = types.UID(uid) + } +} + // PipelineRunNamespace sets the namespace on a PipelineRun func PipelineRunNamespace(namespace string) PipelineRunOp { return func(t *v1beta1.PipelineRun) { diff --git a/internal/builder/v1beta1/pipeline_test.go b/internal/builder/v1beta1/pipeline_test.go index c2637f247bf..a32d66b84bd 100644 --- a/internal/builder/v1beta1/pipeline_test.go +++ b/internal/builder/v1beta1/pipeline_test.go @@ -158,7 +158,7 @@ func TestPipelineRun(t *testing.T) { startTime := time.Now() completedTime := startTime.Add(5 * time.Minute) - pipelineRun := tb.PipelineRun("pear", tb.PipelineRunNamespace("foo"), tb.PipelineRunSpec( + pipelineRun := tb.PipelineRun("pear", tb.PipelineRunUID("uid"), tb.PipelineRunNamespace("foo"), tb.PipelineRunSpec( "tomatoes", tb.PipelineRunServiceAccountName("sa"), tb.PipelineRunParam("first-param-string", "first-value"), tb.PipelineRunParam("second-param-array", "some", "array"), @@ -176,6 +176,7 @@ func TestPipelineRun(t *testing.T) { expectedPipelineRun := &v1beta1.PipelineRun{ ObjectMeta: metav1.ObjectMeta{ Name: "pear", + UID: "uid", Namespace: "foo", Labels: map[string]string{ "label-key": "label-value", @@ -222,7 +223,7 @@ func TestPipelineRunWithPodTemplate(t *testing.T) { startTime := time.Now() completedTime := startTime.Add(5 * time.Minute) - pipelineRun := tb.PipelineRun("pear", tb.PipelineRunNamespace("foo"), tb.PipelineRunSpec( + pipelineRun := tb.PipelineRun("pear", tb.PipelineRunUID("uid"), tb.PipelineRunNamespace("foo"), tb.PipelineRunSpec( "tomatoes", tb.PipelineRunServiceAccountName("sa"), tb.PipelineRunParam("first-param-string", "first-value"), tb.PipelineRunParam("second-param-array", "some", "array"), @@ -243,6 +244,7 @@ func TestPipelineRunWithPodTemplate(t *testing.T) { expectedPipelineRun := &v1beta1.PipelineRun{ ObjectMeta: metav1.ObjectMeta{ Name: "pear", + UID: "uid", Namespace: "foo", Labels: map[string]string{ "label-key": "label-value", @@ -294,7 +296,7 @@ func TestPipelineRunWithResourceSpec(t *testing.T) { startTime := time.Now() completedTime := startTime.Add(5 * time.Minute) - pipelineRun := tb.PipelineRun("pear", tb.PipelineRunNamespace("foo"), tb.PipelineRunSpec( + pipelineRun := tb.PipelineRun("pear", tb.PipelineRunUID("uid"), tb.PipelineRunNamespace("foo"), tb.PipelineRunSpec( "tomatoes", tb.PipelineRunServiceAccountName("sa"), tb.PipelineRunParam("first-param-string", "first-value"), tb.PipelineRunParam("second-param-array", "some", "array"), @@ -318,6 +320,7 @@ func TestPipelineRunWithResourceSpec(t *testing.T) { expectedPipelineRun := &v1beta1.PipelineRun{ ObjectMeta: metav1.ObjectMeta{ Name: "pear", + UID: "uid", Namespace: "foo", Labels: map[string]string{ "label-key": "label-value", @@ -366,7 +369,7 @@ func TestPipelineRunWithResourceSpec(t *testing.T) { } func TestPipelineRunWithPipelineSpec(t *testing.T) { - pipelineRun := tb.PipelineRun("pear", tb.PipelineRunNamespace("foo"), tb.PipelineRunSpec("", tb.PipelineRunPipelineSpec( + pipelineRun := tb.PipelineRun("pear", tb.PipelineRunUID("uid"), tb.PipelineRunNamespace("foo"), tb.PipelineRunSpec("", tb.PipelineRunPipelineSpec( tb.PipelineTask("a-task", "some-task")), tb.PipelineRunServiceAccountName("sa"), )) @@ -374,6 +377,7 @@ func TestPipelineRunWithPipelineSpec(t *testing.T) { expectedPipelineRun := &v1beta1.PipelineRun{ ObjectMeta: metav1.ObjectMeta{ Name: "pear", + UID: "uid", Namespace: "foo", }, Spec: v1beta1.PipelineRunSpec{ @@ -395,7 +399,7 @@ func TestPipelineRunWithPipelineSpec(t *testing.T) { } func TestPipelineRunWithFinalTask(t *testing.T) { - pipelineRun := tb.PipelineRun("pear", tb.PipelineRunNamespace("foo"), tb.PipelineRunSpec("", tb.PipelineRunPipelineSpec( + pipelineRun := tb.PipelineRun("pear", tb.PipelineRunUID("uid"), tb.PipelineRunNamespace("foo"), tb.PipelineRunSpec("", tb.PipelineRunPipelineSpec( tb.PipelineTask("dag-task", "some-task"), tb.FinalPipelineTask("final-task", "some-task")), tb.PipelineRunServiceAccountName("sa"), @@ -404,6 +408,7 @@ func TestPipelineRunWithFinalTask(t *testing.T) { expectedPipelineRun := &v1beta1.PipelineRun{ ObjectMeta: metav1.ObjectMeta{ Name: "pear", + UID: "uid", Namespace: "foo", }, Spec: v1beta1.PipelineRunSpec{ diff --git a/internal/builder/v1beta1/task.go b/internal/builder/v1beta1/task.go index f5605f1259a..002c1657aa9 100644 --- a/internal/builder/v1beta1/task.go +++ b/internal/builder/v1beta1/task.go @@ -24,6 +24,7 @@ import ( resource "github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" "knative.dev/pkg/apis" ) @@ -356,6 +357,13 @@ func TaskRunNamespace(namespace string) TaskRunOp { } } +// TaskRunUID sets the uid for the TaskRun. +func TaskRunUID(uid string) TaskRunOp { + return func(t *v1beta1.TaskRun) { + t.ObjectMeta.UID = types.UID(uid) + } +} + // TaskRunStatus sets the TaskRunStatus to tshe TaskRun func TaskRunStatus(ops ...TaskRunStatusOp) TaskRunOp { return func(tr *v1beta1.TaskRun) { @@ -541,11 +549,12 @@ func SetStepStateWaiting(waiting corev1.ContainerStateWaiting) StepStateOp { } // TaskRunOwnerReference sets the OwnerReference, with specified kind and name, to the TaskRun. -func TaskRunOwnerReference(kind, name string, ops ...OwnerReferenceOp) TaskRunOp { +func TaskRunOwnerReference(kind, name string, uid string, ops ...OwnerReferenceOp) TaskRunOp { return func(tr *v1beta1.TaskRun) { o := &metav1.OwnerReference{ Kind: kind, Name: name, + UID: types.UID(uid), } for _, op := range ops { op(o) diff --git a/internal/builder/v1beta1/task_test.go b/internal/builder/v1beta1/task_test.go index 28ef7c2e13f..ba7d068aa8b 100644 --- a/internal/builder/v1beta1/task_test.go +++ b/internal/builder/v1beta1/task_test.go @@ -172,8 +172,9 @@ func TestTaskRunWithTaskRef(t *testing.T) { terminatedState := corev1.ContainerStateTerminated{Reason: "Completed"} taskRun := tb.TaskRun("test-taskrun", + tb.TaskRunUID("uid"), tb.TaskRunNamespace("foo"), - tb.TaskRunOwnerReference("PipelineRun", "test", + tb.TaskRunOwnerReference("PipelineRun", "test", "uid", tb.OwnerReferenceAPIVersion("a1"), tb.Controller, tb.BlockOwnerDeletion, ), @@ -219,11 +220,14 @@ func TestTaskRunWithTaskRef(t *testing.T) { ) expectedTaskRun := &v1beta1.TaskRun{ ObjectMeta: metav1.ObjectMeta{ - Name: "test-taskrun", Namespace: "foo", + Name: "test-taskrun", + UID: "uid", + Namespace: "foo", OwnerReferences: []metav1.OwnerReference{{ Name: "test", Kind: "PipelineRun", APIVersion: "a1", + UID: "uid", Controller: &trueB, BlockOwnerDeletion: &trueB, }}, @@ -319,6 +323,7 @@ func TestTaskRunWithTaskRef(t *testing.T) { func TestTaskRunWithTaskSpec(t *testing.T) { taskRun := tb.TaskRun("test-taskrun", + tb.TaskRunUID("uid"), tb.TaskRunNamespace("foo"), tb.TaskRunSpec( tb.TaskRunTaskSpec( @@ -333,7 +338,9 @@ func TestTaskRunWithTaskSpec(t *testing.T) { )) expectedTaskRun := &v1beta1.TaskRun{ ObjectMeta: metav1.ObjectMeta{ - Name: "test-taskrun", Namespace: "foo", + Name: "test-taskrun", + UID: "uid", + Namespace: "foo", Annotations: map[string]string{}, }, Spec: v1beta1.TaskRunSpec{ @@ -364,6 +371,7 @@ func TestTaskRunWithTaskSpec(t *testing.T) { func TestTaskRunWithPodTemplate(t *testing.T) { taskRun := tb.TaskRun("test-taskrun", + tb.TaskRunUID("uid"), tb.TaskRunNamespace("foo"), tb.TaskRunSpec( tb.TaskRunTaskSpec( @@ -381,7 +389,9 @@ func TestTaskRunWithPodTemplate(t *testing.T) { )) expectedTaskRun := &v1beta1.TaskRun{ ObjectMeta: metav1.ObjectMeta{ - Name: "test-taskrun", Namespace: "foo", + Name: "test-taskrun", + UID: "uid", + Namespace: "foo", Annotations: map[string]string{}, }, Spec: v1beta1.TaskRunSpec{ diff --git a/pkg/apis/pipeline/v1beta1/task_validation.go b/pkg/apis/pipeline/v1beta1/task_validation.go index 846faf315d8..19a3c47b461 100644 --- a/pkg/apis/pipeline/v1beta1/task_validation.go +++ b/pkg/apis/pipeline/v1beta1/task_validation.go @@ -242,7 +242,7 @@ func ValidateParameterVariables(steps []Step, params []ParamSpec) *apis.FieldErr } } - if err := validateVariables(steps, "params", parameterNames); err != nil { + if err := ValidateVariables(steps, "params", parameterNames); err != nil { return err } return validateArrayUsage(steps, "params", arrayParameterNames) @@ -263,7 +263,7 @@ func ValidateResourcesVariables(steps []Step, resources *TaskResources) *apis.Fi resourceNames.Insert(r.Name) } } - return validateVariables(steps, "resources.(?:inputs|outputs)", resourceNames) + return ValidateVariables(steps, "resources.(?:inputs|outputs)", resourceNames) } func validateArrayUsage(steps []Step, prefix string, vars sets.String) *apis.FieldError { @@ -310,7 +310,7 @@ func validateArrayUsage(steps []Step, prefix string, vars sets.String) *apis.Fie return nil } -func validateVariables(steps []Step, prefix string, vars sets.String) *apis.FieldError { +func ValidateVariables(steps []Step, prefix string, vars sets.String) *apis.FieldError { for _, step := range steps { if err := validateTaskVariable("name", step.Name, prefix, vars); err != nil { return err diff --git a/pkg/reconciler/pipelinerun/pipelinerun.go b/pkg/reconciler/pipelinerun/pipelinerun.go index ac1b5772730..85082b9f00e 100644 --- a/pkg/reconciler/pipelinerun/pipelinerun.go +++ b/pkg/reconciler/pipelinerun/pipelinerun.go @@ -98,6 +98,9 @@ const ( // ReasonCouldntCancel indicates that a PipelineRun was cancelled but attempting to update // all of the running TaskRuns as cancelled failed. ReasonCouldntCancel = "PipelineRunCouldntCancel" + // ReasonFailedContextValidation indicates that the reason for failure is that the PipelineRun + // failed to resolve the contexts. + ReasonFailedContextValidation = "ReasonFailedContextValidation" ) // Reconciler implements controller.Reconciler for Configuration resources. @@ -366,6 +369,14 @@ func (c *Reconciler) reconcile(ctx context.Context, pr *v1beta1.PipelineRun) err return controller.NewPermanentError(err) } + // Ensure that the context variables in the PipelineRun parameters are valid. + if err := resources.ValidateContextVariables(pipelineSpec, pr); err != nil { + pr.Status.MarkFailed(ReasonFailedContextValidation, + "PipelineRun %s/%s parameters has invalid context variable", + pr.Namespace, pr.Name) + return controller.NewPermanentError(err) + } + // Ensure that the ServiceAccountNames defined correct. if err := resources.ValidateServiceaccountMapping(pipelineSpec, pr); err != nil { pr.Status.MarkFailed(ReasonInvalidServiceAccountMapping, diff --git a/pkg/reconciler/pipelinerun/pipelinerun_test.go b/pkg/reconciler/pipelinerun/pipelinerun_test.go index aba01b12b1e..0df67760900 100644 --- a/pkg/reconciler/pipelinerun/pipelinerun_test.go +++ b/pkg/reconciler/pipelinerun/pipelinerun_test.go @@ -193,6 +193,7 @@ func TestReconcile(t *testing.T) { const pipelineRunName = "test-pipeline-run-success" prs := []*v1beta1.PipelineRun{ tb.PipelineRun(pipelineRunName, + tb.PipelineRunUID("uid-1"), tb.PipelineRunNamespace("foo"), tb.PipelineRunSpec("test-pipeline", tb.PipelineRunServiceAccountName("test-sa"), @@ -215,6 +216,7 @@ func TestReconcile(t *testing.T) { templatedParam := tb.PipelineTaskParam("templatedparam", "$(inputs.workspace.$(params.rev-param))") contextRunParam := tb.PipelineTaskParam("contextRunParam", "$(context.pipelineRun.name)") contextPipelineParam := tb.PipelineTaskParam("contextPipelineParam", "$(context.pipeline.name)") + contextRunUID := tb.PipelineTaskParam("contextRunUID", "$(context.pipelineRun.uid)") const pipelineName = "test-pipeline" ps := []*v1beta1.Pipeline{ tb.Pipeline(pipelineName, @@ -227,7 +229,7 @@ func TestReconcile(t *testing.T) { tb.PipelineParamSpec("bar", v1beta1.ParamTypeString), // unit-test-3 uses runAfter to indicate it should run last tb.PipelineTask("unit-test-3", "unit-test-task", - funParam, moreFunParam, templatedParam, contextRunParam, contextPipelineParam, + funParam, moreFunParam, templatedParam, contextRunParam, contextPipelineParam, contextRunUID, tb.RunAfter("unit-test-2"), tb.PipelineTaskInputResource("workspace", "git-repo"), tb.PipelineTaskOutputResource("image-to-use", "best-image"), @@ -235,7 +237,7 @@ func TestReconcile(t *testing.T) { ), // unit-test-1 can run right away because it has no dependencies tb.PipelineTask("unit-test-1", "unit-test-task", - funParam, moreFunParam, templatedParam, contextRunParam, contextPipelineParam, + funParam, moreFunParam, templatedParam, contextRunParam, contextPipelineParam, contextRunUID, tb.PipelineTaskInputResource("workspace", "git-repo"), tb.PipelineTaskOutputResource("image-to-use", "best-image"), tb.PipelineTaskOutputResource("workspace", "git-repo"), @@ -247,7 +249,7 @@ func TestReconcile(t *testing.T) { // unit-test-cluster-task can run right away because it has no dependencies tb.PipelineTask("unit-test-cluster-task", "unit-test-cluster-task", tb.PipelineTaskRefKind(v1beta1.ClusterTaskKind), - funParam, moreFunParam, templatedParam, contextRunParam, contextPipelineParam, + funParam, moreFunParam, templatedParam, contextRunParam, contextPipelineParam, contextRunUID, tb.PipelineTaskInputResource("workspace", "git-repo"), tb.PipelineTaskOutputResource("image-to-use", "best-image"), tb.PipelineTaskOutputResource("workspace", "git-repo"), @@ -257,8 +259,9 @@ func TestReconcile(t *testing.T) { } ts := []*v1beta1.Task{ tb.Task("unit-test-task", tb.TaskSpec( - tb.TaskParam("foo", v1beta1.ParamTypeString), tb.TaskParam("bar", v1beta1.ParamTypeString), tb.TaskParam("templatedparam", v1beta1.ParamTypeString), - tb.TaskParam("contextRunParam", v1beta1.ParamTypeString), tb.TaskParam("contextPipelineParam", v1beta1.ParamTypeString), + tb.TaskParam("foo", v1beta1.ParamTypeString), tb.TaskParam("bar", v1beta1.ParamTypeString), + tb.TaskParam("templatedparam", v1beta1.ParamTypeString), tb.TaskParam("contextRunParam", v1beta1.ParamTypeString), + tb.TaskParam("contextPipelineParam", v1beta1.ParamTypeString), tb.TaskParam("contextRunUID", v1beta1.ParamTypeString), tb.TaskResources( tb.TaskResourcesInput("workspace", resourcev1alpha1.PipelineResourceTypeGit), tb.TaskResourcesOutput("image-to-use", resourcev1alpha1.PipelineResourceTypeImage), @@ -271,8 +274,9 @@ func TestReconcile(t *testing.T) { } clusterTasks := []*v1beta1.ClusterTask{ tb.ClusterTask("unit-test-cluster-task", tb.ClusterTaskSpec( - tb.TaskParam("foo", v1beta1.ParamTypeString), tb.TaskParam("bar", v1beta1.ParamTypeString), tb.TaskParam("templatedparam", v1beta1.ParamTypeString), - tb.TaskParam("contextRunParam", v1beta1.ParamTypeString), tb.TaskParam("contextPipelineParam", v1beta1.ParamTypeString), + tb.TaskParam("foo", v1beta1.ParamTypeString), tb.TaskParam("bar", v1beta1.ParamTypeString), + tb.TaskParam("templatedparam", v1beta1.ParamTypeString), tb.TaskParam("contextRunParam", v1beta1.ParamTypeString), + tb.TaskParam("contextPipelineParam", v1beta1.ParamTypeString), tb.TaskParam("contextRunUID", v1beta1.ParamTypeString), tb.TaskResources( tb.TaskResourcesInput("workspace", resourcev1alpha1.PipelineResourceTypeGit), tb.TaskResourcesOutput("image-to-use", resourcev1alpha1.PipelineResourceTypeImage), @@ -328,7 +332,7 @@ func TestReconcile(t *testing.T) { actual := clients.Pipeline.Actions()[1].(ktesting.CreateAction).GetObject() expectedTaskRun := tb.TaskRun("test-pipeline-run-success-unit-test-1-mz4c7", tb.TaskRunNamespace("foo"), - tb.TaskRunOwnerReference("PipelineRun", "test-pipeline-run-success", + tb.TaskRunOwnerReference("PipelineRun", "test-pipeline-run-success", "uid-1", tb.OwnerReferenceAPIVersion("tekton.dev/v1beta1"), tb.Controller, tb.BlockOwnerDeletion, ), @@ -343,6 +347,7 @@ func TestReconcile(t *testing.T) { tb.TaskRunParam("templatedparam", "$(inputs.workspace.revision)"), tb.TaskRunParam("contextRunParam", pipelineRunName), tb.TaskRunParam("contextPipelineParam", pipelineName), + tb.TaskRunParam("contextRunUID", "uid-1"), tb.TaskRunResources( tb.TaskRunResourcesInput("workspace", tb.TaskResourceBindingRef("some-repo")), tb.TaskRunResourcesOutput("image-to-use", @@ -412,6 +417,7 @@ func TestReconcile_PipelineSpecTaskSpec(t *testing.T) { prs := []*v1beta1.PipelineRun{ tb.PipelineRun("test-pipeline-run-success", + tb.PipelineRunUID("uid-1"), tb.PipelineRunNamespace("foo"), tb.PipelineRunSpec("test-pipeline"), ), @@ -462,7 +468,7 @@ func TestReconcile_PipelineSpecTaskSpec(t *testing.T) { actual := clients.Pipeline.Actions()[1].(ktesting.CreateAction).GetObject() expectedTaskRun := tb.TaskRun("test-pipeline-run-success-unit-test-task-spec-9l9zj", tb.TaskRunNamespace("foo"), - tb.TaskRunOwnerReference("PipelineRun", "test-pipeline-run-success", + tb.TaskRunOwnerReference("PipelineRun", "test-pipeline-run-success", "uid-1", tb.OwnerReferenceAPIVersion("tekton.dev/v1beta1"), tb.Controller, tb.BlockOwnerDeletion, ), @@ -533,7 +539,8 @@ func TestReconcile_InvalidPipelineRuns(t *testing.T) { tb.Pipeline("a-pipeline-with-array-params", tb.PipelineNamespace("foo"), tb.PipelineSpec( tb.PipelineParamSpec("some-param", v1beta1.ParamTypeArray), tb.PipelineTask("some-task", "a-task-that-needs-array-params"))), - tb.Pipeline("a-pipeline-with-missing-conditions", tb.PipelineNamespace("foo"), tb.PipelineSpec(tb.PipelineTask("some-task", "a-task-that-exists", tb.PipelineTaskCondition("condition-does-not-exist")))), + tb.Pipeline("a-pipeline-with-missing-conditions", tb.PipelineNamespace("foo"), tb.PipelineSpec( + tb.PipelineTask("some-task", "a-task-that-exists", tb.PipelineTaskCondition("condition-does-not-exist")))), } prs := []*v1beta1.PipelineRun{ tb.PipelineRun("invalid-pipeline", tb.PipelineRunNamespace("foo"), tb.PipelineRunSpec("pipeline-not-exist")), @@ -568,6 +575,11 @@ func TestReconcile_InvalidPipelineRuns(t *testing.T) { tb.PipelineTask("dag-task-1", "taskName"), tb.FinalPipelineTask("final-task-1", "taskName"), tb.FinalPipelineTask("final-task-1", "taskName")))), + tb.PipelineRun("pipeline-invalid-missing-contexts", tb.PipelineRunNamespace("foo"), tb.PipelineRunSpec("", tb.PipelineRunPipelineSpec( + tb.PipelineParamSpec("some-param", v1beta1.ParamTypeString), + tb.PipelineTask("some-task", "a-task-that-needs-params")), + tb.PipelineRunParam("some-param", "$(context.pipelineRun.missing)"), + )), } d := test.Data{ @@ -710,6 +722,15 @@ func TestReconcile_InvalidPipelineRuns(t *testing.T) { "Normal Started", "Warning Failed PipelineRun foo's Pipeline DAG is invalid for finally clause", }, + }, { + name: "invalid-pipeline-with-missing-contexts", + pipelineRun: prs[14], + reason: ReasonFailedContextValidation, + permanentError: true, + wantEvents: []string{ + "Normal Started", + "Warning Failed PipelineRun foo/pipeline-invalid-missing-contexts parameters has invalid context variable", + }, }, } @@ -1020,6 +1041,7 @@ func TestReconcileOnCompletedPipelineRun(t *testing.T) { // a successful event is triggered taskRunName := "test-pipeline-run-completed-hello-world" prs := []*v1beta1.PipelineRun{tb.PipelineRun("test-pipeline-run-completed", + tb.PipelineRunUID("uid-1"), tb.PipelineRunNamespace("foo"), tb.PipelineRunSpec("test-pipeline", tb.PipelineRunServiceAccountName("test-sa")), tb.PipelineRunStatus(tb.PipelineRunStatusCondition(apis.Condition{ @@ -1040,7 +1062,7 @@ func TestReconcileOnCompletedPipelineRun(t *testing.T) { trs := []*v1beta1.TaskRun{ tb.TaskRun(taskRunName, tb.TaskRunNamespace("foo"), - tb.TaskRunOwnerReference("kind", "name"), + tb.TaskRunOwnerReference("kind", "name", "uid-1"), tb.TaskRunLabel(pipeline.GroupName+pipeline.PipelineLabelKey, "test-pipeline-run-completed"), tb.TaskRunLabel(pipeline.GroupName+pipeline.PipelineRunLabelKey, "test-pipeline"), tb.TaskRunSpec(tb.TaskRunTaskRef("hello-world")), @@ -1128,6 +1150,7 @@ func TestReconcileOnCancelledPipelineRun(t *testing.T) { // TestReconcileOnCancelledPipelineRun runs "Reconcile" on a PipelineRun that has been cancelled. // It verifies that reconcile is successful, the pipeline status updated and events generated. prs := []*v1beta1.PipelineRun{tb.PipelineRun("test-pipeline-run-cancelled", + tb.PipelineRunUID("uid-1"), tb.PipelineRunNamespace("foo"), tb.PipelineRunSpec("test-pipeline", tb.PipelineRunServiceAccountName("test-sa"), tb.PipelineRunCancelled, @@ -1141,7 +1164,7 @@ func TestReconcileOnCancelledPipelineRun(t *testing.T) { trs := []*v1beta1.TaskRun{ tb.TaskRun("test-pipeline-run-cancelled-hello-world", tb.TaskRunNamespace("foo"), - tb.TaskRunOwnerReference("kind", "name"), + tb.TaskRunOwnerReference("kind", "name", "uid-1"), tb.TaskRunLabel(pipeline.GroupName+pipeline.PipelineLabelKey, "test-pipeline-run-cancelled"), tb.TaskRunLabel(pipeline.GroupName+pipeline.PipelineRunLabelKey, "test-pipeline"), tb.TaskRunSpec(tb.TaskRunTaskRef("hello-world"), @@ -1198,6 +1221,7 @@ func TestReconcileWithTimeout(t *testing.T) { tb.PipelineTask("hello-world-1", "hello-world"), ))} prs := []*v1beta1.PipelineRun{tb.PipelineRun("test-pipeline-run-with-timeout", + tb.PipelineRunUID("uid-1"), tb.PipelineRunNamespace("foo"), tb.PipelineRunSpec("test-pipeline", tb.PipelineRunServiceAccountName("test-sa"), @@ -1266,7 +1290,7 @@ func TestReconcileWithoutPVC(t *testing.T) { tb.PipelineTask("hello-world-2", "hello-world"), ))} - prs := []*v1beta1.PipelineRun{tb.PipelineRun("test-pipeline-run", tb.PipelineRunNamespace("foo"), + prs := []*v1beta1.PipelineRun{tb.PipelineRun("test-pipeline-run", tb.PipelineRunUID("uid-1"), tb.PipelineRunNamespace("foo"), tb.PipelineRunSpec("test-pipeline")), } ts := []*v1beta1.Task{tb.Task("hello-world", tb.TaskNamespace("foo"))} @@ -1449,7 +1473,7 @@ func TestReconcilePropagateLabels(t *testing.T) { ps := []*v1beta1.Pipeline{tb.Pipeline("test-pipeline", tb.PipelineNamespace("foo"), tb.PipelineSpec( tb.PipelineTask(taskName, "hello-world"), ))} - prs := []*v1beta1.PipelineRun{tb.PipelineRun("test-pipeline-run-with-labels", tb.PipelineRunNamespace("foo"), + prs := []*v1beta1.PipelineRun{tb.PipelineRun("test-pipeline-run-with-labels", tb.PipelineRunUID("uid-1"), tb.PipelineRunNamespace("foo"), tb.PipelineRunLabel("PipelineRunLabel", "PipelineRunValue"), tb.PipelineRunLabel("tekton.dev/pipeline", "WillNotBeUsed"), tb.PipelineRunSpec("test-pipeline", @@ -1466,7 +1490,7 @@ func TestReconcilePropagateLabels(t *testing.T) { expected := tb.TaskRun("test-pipeline-run-with-labels-hello-world-1-9l9zj", tb.TaskRunNamespace("foo"), - tb.TaskRunOwnerReference("PipelineRun", "test-pipeline-run-with-labels", + tb.TaskRunOwnerReference("PipelineRun", "test-pipeline-run-with-labels", "uid-1", tb.OwnerReferenceAPIVersion("tekton.dev/v1beta1"), tb.Controller, tb.BlockOwnerDeletion, ), @@ -1514,7 +1538,7 @@ func TestReconcileWithDifferentServiceAccounts(t *testing.T) { tb.PipelineTask("hello-world-0", "hello-world-task"), tb.PipelineTask("hello-world-1", "hello-world-task"), ))} - prs := []*v1beta1.PipelineRun{tb.PipelineRun("test-pipeline-run-different-service-accs", tb.PipelineRunNamespace("foo"), + prs := []*v1beta1.PipelineRun{tb.PipelineRun("test-pipeline-run-different-service-accs", tb.PipelineRunUID("uid-1"), tb.PipelineRunNamespace("foo"), tb.PipelineRunSpec("test-pipeline", tb.PipelineRunServiceAccountName("test-sa-0"), tb.PipelineRunServiceAccountNameTask("hello-world-1", "test-sa-1"), @@ -1551,7 +1575,7 @@ func TestReconcileWithDifferentServiceAccounts(t *testing.T) { expectedTaskRuns := []*v1beta1.TaskRun{ tb.TaskRun(taskRunNames[0], tb.TaskRunNamespace("foo"), - tb.TaskRunOwnerReference("PipelineRun", "test-pipeline-run-different-service-accs", + tb.TaskRunOwnerReference("PipelineRun", "test-pipeline-run-different-service-accs", "uid-1", tb.OwnerReferenceAPIVersion("tekton.dev/v1beta1"), tb.Controller, tb.BlockOwnerDeletion, ), @@ -1565,7 +1589,7 @@ func TestReconcileWithDifferentServiceAccounts(t *testing.T) { ), tb.TaskRun(taskRunNames[1], tb.TaskRunNamespace("foo"), - tb.TaskRunOwnerReference("PipelineRun", "test-pipeline-run-different-service-accs", + tb.TaskRunOwnerReference("PipelineRun", "test-pipeline-run-different-service-accs", "uid-1", tb.OwnerReferenceAPIVersion("tekton.dev/v1beta1"), tb.Controller, tb.BlockOwnerDeletion, ), @@ -1710,7 +1734,7 @@ func TestReconcilePropagateAnnotations(t *testing.T) { ps := []*v1beta1.Pipeline{tb.Pipeline("test-pipeline", tb.PipelineNamespace("foo"), tb.PipelineSpec( tb.PipelineTask("hello-world-1", "hello-world"), ))} - prs := []*v1beta1.PipelineRun{tb.PipelineRun("test-pipeline-run-with-annotations", tb.PipelineRunNamespace("foo"), + prs := []*v1beta1.PipelineRun{tb.PipelineRun("test-pipeline-run-with-annotations", tb.PipelineRunUID("uid-1"), tb.PipelineRunNamespace("foo"), tb.PipelineRunAnnotation("PipelineRunAnnotation", "PipelineRunValue"), tb.PipelineRunSpec("test-pipeline", tb.PipelineRunServiceAccountName("test-sa"), @@ -1747,7 +1771,7 @@ func TestReconcilePropagateAnnotations(t *testing.T) { } expectedTaskRun := tb.TaskRun("test-pipeline-run-with-annotations-hello-world-1-9l9zj", tb.TaskRunNamespace("foo"), - tb.TaskRunOwnerReference("PipelineRun", "test-pipeline-run-with-annotations", + tb.TaskRunOwnerReference("PipelineRun", "test-pipeline-run-with-annotations", "uid-1", tb.OwnerReferenceAPIVersion("tekton.dev/v1beta1"), tb.Controller, tb.BlockOwnerDeletion, ), @@ -1866,7 +1890,7 @@ func TestReconcileAndPropagateCustomPipelineTaskRunSpec(t *testing.T) { ps := []*v1beta1.Pipeline{tb.Pipeline("test-pipeline", tb.PipelineNamespace("foo"), tb.PipelineSpec( tb.PipelineTask("hello-world-1", "hello-world"), ))} - prs := []*v1beta1.PipelineRun{tb.PipelineRun(prName, tb.PipelineRunNamespace("foo"), + prs := []*v1beta1.PipelineRun{tb.PipelineRun(prName, tb.PipelineRunUID("uid-1"), tb.PipelineRunNamespace("foo"), tb.PipelineRunAnnotation("PipelineRunAnnotation", "PipelineRunValue"), tb.PipelineRunSpec("test-pipeline", tb.PipelineRunServiceAccountName("test-sa"), @@ -1912,7 +1936,7 @@ func TestReconcileAndPropagateCustomPipelineTaskRunSpec(t *testing.T) { } expectedTaskRun := tb.TaskRun("test-pipeline-run-hello-world-1-9l9zj", tb.TaskRunNamespace("foo"), - tb.TaskRunOwnerReference("PipelineRun", "test-pipeline-run", + tb.TaskRunOwnerReference("PipelineRun", "test-pipeline-run", "uid-1", tb.OwnerReferenceAPIVersion("tekton.dev/v1beta1"), tb.Controller, tb.BlockOwnerDeletion, ), @@ -1973,7 +1997,7 @@ func TestReconcileWithConditionChecks(t *testing.T) { tb.PipelineTaskCondition("cond-1"), tb.PipelineTaskCondition("cond-2")), ))} - prs := []*v1beta1.PipelineRun{tb.PipelineRun(prName, tb.PipelineRunNamespace("foo"), + prs := []*v1beta1.PipelineRun{tb.PipelineRun(prName, tb.PipelineRunUID("uid-1"), tb.PipelineRunNamespace("foo"), tb.PipelineRunAnnotation("PipelineRunAnnotation", "PipelineRunValue"), tb.PipelineRunSpec("test-pipeline", tb.PipelineRunServiceAccountName("test-sa"), @@ -2059,7 +2083,7 @@ func TestReconcileWithFailingConditionChecks(t *testing.T) { tb.PipelineTask("task-3", "hello-world", tb.RunAfter("task-1")), ))} - prs := []*v1beta1.PipelineRun{tb.PipelineRun("test-pipeline-run-with-conditions", tb.PipelineRunNamespace("foo"), + prs := []*v1beta1.PipelineRun{tb.PipelineRun("test-pipeline-run-with-conditions", tb.PipelineRunUID("uid-1"), tb.PipelineRunNamespace("foo"), tb.PipelineRunAnnotation("PipelineRunAnnotation", "PipelineRunValue"), tb.PipelineRunSpec("test-pipeline", tb.PipelineRunServiceAccountName("test-sa"), @@ -2083,7 +2107,7 @@ func TestReconcileWithFailingConditionChecks(t *testing.T) { trs := []*v1beta1.TaskRun{ tb.TaskRun(pipelineRunName+"task-1", tb.TaskRunNamespace("foo"), - tb.TaskRunOwnerReference("kind", "name"), + tb.TaskRunOwnerReference("kind", "name", "uid-1"), tb.TaskRunLabel(pipeline.GroupName+pipeline.PipelineLabelKey, "test-pipeline-run-with-conditions"), tb.TaskRunLabel(pipeline.GroupName+pipeline.PipelineRunLabelKey, "test-pipeline"), tb.TaskRunSpec(tb.TaskRunTaskRef("hello-world")), @@ -2095,7 +2119,7 @@ func TestReconcileWithFailingConditionChecks(t *testing.T) { ), tb.TaskRun(conditionCheckName, tb.TaskRunNamespace("foo"), - tb.TaskRunOwnerReference("kind", "name"), + tb.TaskRunOwnerReference("kind", "name", "uid-1"), tb.TaskRunLabel(pipeline.GroupName+pipeline.PipelineLabelKey, "test-pipeline-run-with-conditions"), tb.TaskRunLabel(pipeline.GroupName+pipeline.PipelineRunLabelKey, "test-pipeline"), tb.TaskRunLabel(pipeline.GroupName+pipeline.ConditionCheckKey, conditionCheckName), @@ -2139,7 +2163,7 @@ func TestReconcileWithFailingConditionChecks(t *testing.T) { } expectedTaskRun := tb.TaskRun("test-pipeline-run-with-conditions-task-3-9l9zj", tb.TaskRunNamespace("foo"), - tb.TaskRunOwnerReference("PipelineRun", "test-pipeline-run-with-conditions", + tb.TaskRunOwnerReference("PipelineRun", "test-pipeline-run-with-conditions", "uid-1", tb.OwnerReferenceAPIVersion("tekton.dev/v1beta1"), tb.Controller, tb.BlockOwnerDeletion, ), @@ -2170,7 +2194,7 @@ func TestReconcileWithFailingConditionChecks(t *testing.T) { func makeExpectedTr(condName, ccName string, labels, annotations map[string]string) *v1beta1.TaskRun { return tb.TaskRun(ccName, tb.TaskRunNamespace("foo"), - tb.TaskRunOwnerReference("PipelineRun", "test-pipeline-run", + tb.TaskRunOwnerReference("PipelineRun", "test-pipeline-run", "uid-1", tb.OwnerReferenceAPIVersion("tekton.dev/v1beta1"), tb.Controller, tb.BlockOwnerDeletion, ), @@ -2522,7 +2546,7 @@ func TestReconcileWithTaskResults(t *testing.T) { tb.PipelineTaskParam("bParam", "$(tasks.a-task.results.aResult)"), ), ))} - prs := []*v1beta1.PipelineRun{tb.PipelineRun("test-pipeline-run-different-service-accs", tb.PipelineRunNamespace("foo"), + prs := []*v1beta1.PipelineRun{tb.PipelineRun("test-pipeline-run-different-service-accs", tb.PipelineRunUID("uid-1"), tb.PipelineRunNamespace("foo"), tb.PipelineRunSpec("test-pipeline", tb.PipelineRunServiceAccountName("test-sa-0"), ), @@ -2538,7 +2562,7 @@ func TestReconcileWithTaskResults(t *testing.T) { trs := []*v1beta1.TaskRun{ tb.TaskRun("test-pipeline-run-different-service-accs-a-task-xxyyy", tb.TaskRunNamespace("foo"), - tb.TaskRunOwnerReference("PipelineRun", "test-pipeline-run-different-service-accs", + tb.TaskRunOwnerReference("PipelineRun", "test-pipeline-run-different-service-accs", "uid-1", tb.OwnerReferenceAPIVersion("tekton.dev/v1beta1"), tb.Controller, tb.BlockOwnerDeletion, ), @@ -2582,7 +2606,7 @@ func TestReconcileWithTaskResults(t *testing.T) { expectedTaskRunName := "test-pipeline-run-different-service-accs-b-task-9l9zj" expectedTaskRun := tb.TaskRun(expectedTaskRunName, tb.TaskRunNamespace("foo"), - tb.TaskRunOwnerReference("PipelineRun", "test-pipeline-run-different-service-accs", + tb.TaskRunOwnerReference("PipelineRun", "test-pipeline-run-different-service-accs", "uid-1", tb.OwnerReferenceAPIVersion("tekton.dev/v1beta1"), tb.Controller, tb.BlockOwnerDeletion, ), @@ -2615,7 +2639,7 @@ func TestReconcileWithTaskResults(t *testing.T) { func TestReconcileWithTaskResultsEmbeddedNoneStarted(t *testing.T) { names.TestingSeed() - prs := []*v1beta1.PipelineRun{tb.PipelineRun("test-pipeline-run-different-service-accs", tb.PipelineRunNamespace("foo"), + prs := []*v1beta1.PipelineRun{tb.PipelineRun("test-pipeline-run-different-service-accs", tb.PipelineRunUID("uid-1"), tb.PipelineRunNamespace("foo"), tb.PipelineRunSpec("test-pipeline", tb.PipelineRunPipelineSpec( tb.PipelineParamSpec("foo", v1beta1.ParamTypeString), @@ -2666,7 +2690,7 @@ func TestReconcileWithTaskResultsEmbeddedNoneStarted(t *testing.T) { expectedTaskRunName := "test-pipeline-run-different-service-accs-a-task-9l9zj" expectedTaskRun := tb.TaskRun(expectedTaskRunName, tb.TaskRunNamespace("foo"), - tb.TaskRunOwnerReference("PipelineRun", "test-pipeline-run-different-service-accs", + tb.TaskRunOwnerReference("PipelineRun", "test-pipeline-run-different-service-accs", "uid-1", tb.OwnerReferenceAPIVersion("tekton.dev/v1beta1"), tb.Controller, tb.BlockOwnerDeletion, ), @@ -2704,7 +2728,7 @@ func TestReconcileWithPipelineResults(t *testing.T) { trs := []*v1beta1.TaskRun{ tb.TaskRun("test-pipeline-run-different-service-accs-a-task-9l9zj", tb.TaskRunNamespace("foo"), - tb.TaskRunOwnerReference("PipelineRun", "test-pipeline-run-different-service-accs", + tb.TaskRunOwnerReference("PipelineRun", "test-pipeline-run-different-service-accs", "uid-1", tb.OwnerReferenceAPIVersion("tekton.dev/v1beta1"), tb.Controller, tb.BlockOwnerDeletion, ), @@ -2726,7 +2750,7 @@ func TestReconcileWithPipelineResults(t *testing.T) { ), ), } - prs := []*v1beta1.PipelineRun{tb.PipelineRun("test-pipeline-run-different-service-accs", tb.PipelineRunNamespace("foo"), + prs := []*v1beta1.PipelineRun{tb.PipelineRun("test-pipeline-run-different-service-accs", tb.PipelineRunUID("uid-1"), tb.PipelineRunNamespace("foo"), tb.PipelineRunSpec("test-pipeline", tb.PipelineRunServiceAccountName("test-sa-0"), ), @@ -2854,7 +2878,7 @@ func TestReconcileOutOfSyncPipelineRun(t *testing.T) { // This taskrun is in the pipelinerun status. It completed successfully. taskRunDone := tb.TaskRun("test-pipeline-run-out-of-sync-hello-world-1", tb.TaskRunNamespace("foo"), - tb.TaskRunOwnerReference("PipelineRun", prOutOfSyncName), + tb.TaskRunOwnerReference("PipelineRun", prOutOfSyncName, "uid-1"), tb.TaskRunLabel(pipeline.GroupName+pipeline.PipelineLabelKey, testPipeline.Name), tb.TaskRunLabel(pipeline.GroupName+pipeline.PipelineRunLabelKey, prOutOfSyncName), tb.TaskRunLabel(pipeline.GroupName+pipeline.PipelineTaskLabelKey, "hello-world-1"), @@ -2870,7 +2894,7 @@ func TestReconcileOutOfSyncPipelineRun(t *testing.T) { // This taskrun is *not* in the pipelinerun status. It's still running. taskRunOrphaned := tb.TaskRun("test-pipeline-run-out-of-sync-hello-world-2", tb.TaskRunNamespace("foo"), - tb.TaskRunOwnerReference("PipelineRun", prOutOfSyncName), + tb.TaskRunOwnerReference("PipelineRun", prOutOfSyncName, "uid-1"), tb.TaskRunLabel(pipeline.GroupName+pipeline.PipelineLabelKey, testPipeline.Name), tb.TaskRunLabel(pipeline.GroupName+pipeline.PipelineRunLabelKey, prOutOfSyncName), tb.TaskRunLabel(pipeline.GroupName+pipeline.PipelineTaskLabelKey, "hello-world-2"), @@ -2887,7 +2911,7 @@ func TestReconcileOutOfSyncPipelineRun(t *testing.T) { // itself is *not* in the pipelinerun status. It's still running. taskRunWithCondition := tb.TaskRun("test-pipeline-run-out-of-sync-hello-world-3", tb.TaskRunNamespace("foo"), - tb.TaskRunOwnerReference("PipelineRun", prOutOfSyncName), + tb.TaskRunOwnerReference("PipelineRun", prOutOfSyncName, "uid-1"), tb.TaskRunLabel(pipeline.GroupName+pipeline.PipelineLabelKey, testPipeline.Name), tb.TaskRunLabel(pipeline.GroupName+pipeline.PipelineRunLabelKey, prOutOfSyncName), tb.TaskRunLabel(pipeline.GroupName+pipeline.PipelineTaskLabelKey, "hello-world-3"), @@ -2902,7 +2926,7 @@ func TestReconcileOutOfSyncPipelineRun(t *testing.T) { taskRunForConditionOfOrphanedTaskRun := tb.TaskRun(conditionCheckName3, tb.TaskRunNamespace("foo"), - tb.TaskRunOwnerReference("PipelineRun", prOutOfSyncName), + tb.TaskRunOwnerReference("PipelineRun", prOutOfSyncName, "uid-1"), tb.TaskRunLabel(pipeline.GroupName+pipeline.PipelineLabelKey, testPipeline.Name), tb.TaskRunLabel(pipeline.GroupName+pipeline.PipelineRunLabelKey, prOutOfSyncName), tb.TaskRunLabel(pipeline.GroupName+pipeline.PipelineTaskLabelKey, "hello-world-3"), @@ -2923,7 +2947,7 @@ func TestReconcileOutOfSyncPipelineRun(t *testing.T) { taskRunForOrphanedCondition := tb.TaskRun(conditionCheckName4, tb.TaskRunNamespace("foo"), - tb.TaskRunOwnerReference("PipelineRun", prOutOfSyncName), + tb.TaskRunOwnerReference("PipelineRun", prOutOfSyncName, "uid-1"), tb.TaskRunLabel(pipeline.GroupName+pipeline.PipelineLabelKey, testPipeline.Name), tb.TaskRunLabel(pipeline.GroupName+pipeline.PipelineRunLabelKey, prOutOfSyncName), tb.TaskRunLabel(pipeline.GroupName+pipeline.PipelineTaskLabelKey, "hello-world-4"), @@ -3794,7 +3818,7 @@ func getPipeline(p string, t []tb.PipelineSpecOp) []*v1beta1.Pipeline { func getTaskRun(tr, pr, p, t string, status corev1.ConditionStatus) *v1beta1.TaskRun { return tb.TaskRun(tr, tb.TaskRunNamespace("foo"), - tb.TaskRunOwnerReference("pipelineRun", pr), + tb.TaskRunOwnerReference("pipelineRun", pr, "uid-1"), tb.TaskRunLabel(pipeline.GroupName+pipeline.PipelineLabelKey, p), tb.TaskRunLabel(pipeline.GroupName+pipeline.PipelineRunLabelKey, pr), tb.TaskRunLabel(pipeline.GroupName+pipeline.PipelineTaskLabelKey, t), diff --git a/pkg/reconciler/pipelinerun/resources/apply.go b/pkg/reconciler/pipelinerun/resources/apply.go index 4962f727b05..dd12c092151 100644 --- a/pkg/reconciler/pipelinerun/resources/apply.go +++ b/pkg/reconciler/pipelinerun/resources/apply.go @@ -56,11 +56,13 @@ func ApplyParameters(p *v1beta1.PipelineSpec, pr *v1beta1.PipelineRun) *v1beta1. // 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 { - return ApplyReplacements(spec, - map[string]string{"context.pipelineRun.name": pr.Name, - "context.pipeline.name": pipelineName, - "context.pipelineRun.namespace": pr.Namespace}, - map[string][]string{}) + 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{}) } // ApplyTaskResults applies the ResolvedResultRef to each PipelineTask.Params in targets diff --git a/pkg/reconciler/pipelinerun/resources/apply_test.go b/pkg/reconciler/pipelinerun/resources/apply_test.go index 003b5beef6b..3be1f59fa05 100644 --- a/pkg/reconciler/pipelinerun/resources/apply_test.go +++ b/pkg/reconciler/pipelinerun/resources/apply_test.go @@ -490,6 +490,23 @@ func TestContext(t *testing.T) { tb.PipelineTask("first-task-1", "first-task", tb.PipelineTaskParam("first-task-first-param", "-1"), ))), + }, { + description: "context pipeline name replacement with pipelinerun uid", + pr: &v1beta1.PipelineRun{ + ObjectMeta: metav1.ObjectMeta{ + UID: "UID-1", + }, + }, + original: tb.Pipeline("test-pipeline", + tb.PipelineSpec( + tb.PipelineTask("first-task-1", "first-task", + tb.PipelineTaskParam("first-task-first-param", "$(context.pipelineRun.uid)"), + ))), + expected: tb.Pipeline("test-pipeline", + tb.PipelineSpec( + tb.PipelineTask("first-task-1", "first-task", + tb.PipelineTaskParam("first-task-first-param", "UID-1"), + ))), }} { t.Run(tc.description, func(t *testing.T) { got := ApplyContexts(&tc.original.Spec, tc.original.Name, tc.pr) diff --git a/pkg/reconciler/pipelinerun/resources/validate_contexts.go b/pkg/reconciler/pipelinerun/resources/validate_contexts.go new file mode 100644 index 00000000000..e08dc6b76fb --- /dev/null +++ b/pkg/reconciler/pipelinerun/resources/validate_contexts.go @@ -0,0 +1,86 @@ +/* +Copyright 2020 The Tekton Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package resources + +import ( + "fmt" + + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + "github.com/tektoncd/pipeline/pkg/substitution" + "k8s.io/apimachinery/pkg/util/sets" + "knative.dev/pkg/apis" +) + +// ValidateContextVariables validate that context variables used in Pipelines and PipelineRuns are valid +func ValidateContextVariables(ps *v1beta1.PipelineSpec, pr *v1beta1.PipelineRun) *apis.FieldError { + var paramValues []string + + for _, param := range pr.Spec.Params { + newParamValues := getParameterValues(param) + paramValues = append(paramValues, newParamValues...) + } + + if err := validatePipelineRunContextVariables(paramValues); err != nil { + return err + } + if err := validatePipelineContextVariables(¶mValues); err != nil { + return err + } + return nil +} + +func validatePipelineRunContextVariables(paramValues []string) *apis.FieldError { + pipelineRunContextNames := sets.NewString().Insert( + "name", + "namespace", + "uid", + ) + + for _, paramValue := range paramValues { + if err := substitution.ValidateVariable(fmt.Sprintf("param[%s]", paramValue), paramValue, "context\\.pipelineRun", "params", "pipelinespec.params", pipelineRunContextNames); err != nil { + return err + } + } + + return nil +} + +func validatePipelineContextVariables(paramValues *[]string) *apis.FieldError { + pipelineContextNames := sets.NewString().Insert( + "name", + ) + + for _, paramValue := range *paramValues { + if err := substitution.ValidateVariable(fmt.Sprintf("param[%s]", paramValue), paramValue, "context\\.pipeline", "params", "pipelinespec.params", pipelineContextNames); err != nil { + return err + } + } + + return nil +} + +func getParameterValues(param v1beta1.Param) []string { + var paramValues []string + + if param.Value.Type == v1beta1.ParamTypeString { + paramValues = append(paramValues, param.Value.StringVal) + } else { + paramValues = append(paramValues, param.Value.ArrayVal...) + } + + return paramValues +} diff --git a/pkg/reconciler/pipelinerun/resources/validate_contexts_test.go b/pkg/reconciler/pipelinerun/resources/validate_contexts_test.go new file mode 100644 index 00000000000..153f2fd6670 --- /dev/null +++ b/pkg/reconciler/pipelinerun/resources/validate_contexts_test.go @@ -0,0 +1,110 @@ +/* +Copyright 2020 The Tekton Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package resources + +import ( + "testing" + + tb "github.com/tektoncd/pipeline/internal/builder/v1beta1" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" +) + +func TestContextValid(t *testing.T) { + tcs := []struct { + name string + p *v1beta1.Pipeline + original *v1beta1.PipelineRun + }{{ + name: "param pipeline name context variable", + p: tb.Pipeline("a-pipeline", tb.PipelineSpec( + tb.PipelineParamSpec("pipeline-name", v1beta1.ParamTypeString))), + original: tb.PipelineRun("a-pipelinerun", tb.PipelineRunSpec( + "test-pipeline", + tb.PipelineRunParam("pipeline-name", "$(context.pipeline.name)"))), + }, { + name: "param pipelinerun name context variable", + p: tb.Pipeline("a-pipeline", tb.PipelineSpec( + tb.PipelineParamSpec("pipelinerun-name", v1beta1.ParamTypeString))), + original: tb.PipelineRun("a-pipelinerun", tb.PipelineRunSpec( + "test-pipeline", + tb.PipelineRunParam("pipelinerun-name", "$(context.pipelineRun.name)"))), + }, { + name: "param pipelinerun uid context variable", + p: tb.Pipeline("a-pipeline", tb.PipelineSpec( + tb.PipelineParamSpec("pipelinerun-uid", v1beta1.ParamTypeString))), + original: tb.PipelineRun("a-pipelinerun", tb.PipelineRunSpec( + "test-pipeline", + tb.PipelineRunParam("pipelinerun-uid", "$(context.pipelineRun.uid)"))), + }, { + name: "param pipelinerun name, uid context variable", + p: tb.Pipeline("a-pipeline", tb.PipelineSpec( + tb.PipelineParamSpec("pipelinerun-name-uid", v1beta1.ParamTypeString))), + original: tb.PipelineRun("a-pipelinerun", tb.PipelineRunSpec( + "test-pipeline", + tb.PipelineRunParam("pipelinerun-name-uid", "$(context.pipelineRun.name)", "$(context.pipelineRun.uid)"))), + }} + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + if err := ValidateContextVariables(&tc.p.Spec, tc.original); err != nil { + t.Errorf("Did not expect to see error when validating valid contexts but saw %v", err) + } + }) + } +} + +func TestContextInvalid(t *testing.T) { + tcs := []struct { + name string + p *v1beta1.Pipeline + pr *v1beta1.PipelineRun + }{{ + name: "param pipeline name context variable", + p: tb.Pipeline("a-pipeline", tb.PipelineSpec( + tb.PipelineParamSpec("pipeline-name", v1beta1.ParamTypeString))), + pr: tb.PipelineRun("a-pipelinerun", tb.PipelineRunSpec( + "test-pipeline", + tb.PipelineRunParam("pipeline-name", "$(context.pipeline.missing)"))), + }, { + name: "param pipelinerun name context variable", + p: tb.Pipeline("a-pipeline", tb.PipelineSpec( + tb.PipelineParamSpec("pipelinerun-name", v1beta1.ParamTypeString))), + pr: tb.PipelineRun("a-pipelinerun", tb.PipelineRunSpec( + "test-pipeline", + tb.PipelineRunParam("pipelinerun-name", "$(context.pipelineRun.missing)"))), + }, { + name: "param pipelinerun uid context variable", + p: tb.Pipeline("a-pipeline", tb.PipelineSpec( + tb.PipelineParamSpec("pipelinerun-uid", v1beta1.ParamTypeString))), + pr: tb.PipelineRun("a-pipelinerun", tb.PipelineRunSpec( + "test-pipeline", + tb.PipelineRunParam("pipelinerun-uid", "$(context.pipelineRun.missing)"))), + }, { + name: "param pipelinerun name, uid context variable", + p: tb.Pipeline("a-pipeline", tb.PipelineSpec( + tb.PipelineParamSpec("pipelinerun-name-uid", v1beta1.ParamTypeString))), + pr: tb.PipelineRun("a-pipelinerun", tb.PipelineRunSpec( + "test-pipeline", + tb.PipelineRunParam("pipelinerun-name-uid", "$(context.pipelineRun.missing)", "$(context.pipelineRun.missing)"))), + }} + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + if err := ValidateContextVariables(&tc.p.Spec, tc.pr); err == nil { + t.Errorf("expected to see error when validating invalid contexts") + } + }) + } +} diff --git a/pkg/reconciler/taskrun/resources/apply.go b/pkg/reconciler/taskrun/resources/apply.go index e8706acfb69..6893a5a481d 100644 --- a/pkg/reconciler/taskrun/resources/apply.go +++ b/pkg/reconciler/taskrun/resources/apply.go @@ -97,11 +97,15 @@ func ApplyResources(spec *v1beta1.TaskSpec, resolvedResources map[string]v1beta1 } // ApplyContexts applies the substitution from $(context.(taskRun|task).*) with the specified values. -// Currently supports only name substitution. Uses "" as a default if name is not specified. +// Uses "" as a default if a value is not available. func ApplyContexts(spec *v1beta1.TaskSpec, rtr *ResolvedTaskResources, tr *v1beta1.TaskRun) *v1beta1.TaskSpec { - return ApplyReplacements(spec, - map[string]string{"context.taskRun.name": tr.Name, "context.task.name": rtr.TaskName, "context.taskRun.namespace": tr.Namespace}, - map[string][]string{}) + replacements := map[string]string{ + "context.taskRun.name": tr.Name, + "context.task.name": rtr.TaskName, + "context.taskRun.namespace": tr.Namespace, + "context.taskRun.uid": string(tr.ObjectMeta.UID), + } + return ApplyReplacements(spec, replacements, map[string][]string{}) } // ApplyWorkspaces applies the substitution from paths that the workspaces in w are mounted to, the diff --git a/pkg/reconciler/taskrun/resources/apply_test.go b/pkg/reconciler/taskrun/resources/apply_test.go index afbdf46f934..40625a752c7 100644 --- a/pkg/reconciler/taskrun/resources/apply_test.go +++ b/pkg/reconciler/taskrun/resources/apply_test.go @@ -928,6 +928,32 @@ func TestContext(t *testing.T) { }, }}, }, + }, { + description: "context UID replacement", + rtr: resources.ResolvedTaskResources{ + TaskName: "Task1", + }, + tr: v1beta1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{ + UID: "UID-1", + }, + }, + spec: v1beta1.TaskSpec{ + Steps: []v1beta1.Step{{ + Container: corev1.Container{ + Name: "ImageName", + Image: "$(context.taskRun.uid)", + }, + }}, + }, + want: v1beta1.TaskSpec{ + Steps: []v1beta1.Step{{ + Container: corev1.Container{ + Name: "ImageName", + Image: "UID-1", + }, + }}, + }, }} { t.Run(tc.description, func(t *testing.T) { got := resources.ApplyContexts(&tc.spec, &tc.rtr, &tc.tr) diff --git a/pkg/reconciler/taskrun/taskrun.go b/pkg/reconciler/taskrun/taskrun.go index 690abcf6dd9..7d9a17e5371 100644 --- a/pkg/reconciler/taskrun/taskrun.go +++ b/pkg/reconciler/taskrun/taskrun.go @@ -300,6 +300,12 @@ func (c *Reconciler) prepare(ctx context.Context, tr *v1beta1.TaskRun) (*v1beta1 return nil, nil, controller.NewPermanentError(err) } + if err := validateContextVariables(taskSpec, tr); err != nil { + logger.Errorf("TaskRun %q context is invalid: %v", tr.Name, err) + tr.Status.MarkResourceFailed(podconvert.ReasonFailedValidation, err) + return nil, nil, controller.NewPermanentError(err) + } + // Initialize the cloud events if at least a CloudEventResource is defined // and they have not been initialized yet. // FIXME(afrittoli) This resource specific logic will have to be replaced diff --git a/pkg/reconciler/taskrun/taskrun_test.go b/pkg/reconciler/taskrun/taskrun_test.go index d2757a9c6e5..3ea2e501c7e 100644 --- a/pkg/reconciler/taskrun/taskrun_test.go +++ b/pkg/reconciler/taskrun/taskrun_test.go @@ -795,7 +795,7 @@ func TestReconcile(t *testing.T) { )) taskRunInputOutput := tb.TaskRun("test-taskrun-input-output", tb.TaskRunNamespace("foo"), - tb.TaskRunOwnerReference("PipelineRun", "test"), + tb.TaskRunOwnerReference("PipelineRun", "test", "uid-1"), tb.TaskRunSpec( tb.TaskRunTaskRef(outputTask.Name), tb.TaskRunResources( @@ -1636,7 +1636,11 @@ func TestReconcileInvalidTaskRuns(t *testing.T) { withWrongRef := tb.TaskRun("taskrun-with-wrong-ref", tb.TaskRunNamespace("foo"), tb.TaskRunSpec( tb.TaskRunTaskRef("taskrun-with-wrong-ref", tb.TaskRefKind(v1beta1.ClusterTaskKind)), )) - taskRuns := []*v1beta1.TaskRun{noTaskRun, withWrongRef} + withWrongContext := tb.TaskRun("taskrun-with-wrong-context", tb.TaskRunNamespace("foo"), tb.TaskRunSpec( + tb.TaskRunTaskRef(simpleTask.Name), + tb.TaskRunParam("taskRun-uid", "$(contexts.taskRun.missing)"), + )) + taskRuns := []*v1beta1.TaskRun{noTaskRun, withWrongRef, withWrongContext} tasks := []*v1beta1.Task{simpleTask} d := test.Data{ @@ -1667,6 +1671,15 @@ func TestReconcileInvalidTaskRuns(t *testing.T) { "Warning Failed", "Warning InternalError", }, + }, { + name: "task run with wrong context", + taskRun: withWrongContext, + reason: podconvert.ReasonFailedValidation, + wantEvents: []string{ + "Normal Started", + "Warning Failed", + "Warning InternalError", + }, }} for _, tc := range testcases { diff --git a/pkg/reconciler/taskrun/validate_contexts.go b/pkg/reconciler/taskrun/validate_contexts.go new file mode 100644 index 00000000000..92fdb95445c --- /dev/null +++ b/pkg/reconciler/taskrun/validate_contexts.go @@ -0,0 +1,40 @@ +/* +Copyright 2020 The Tekton Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package taskrun + +import ( + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + "k8s.io/apimachinery/pkg/util/sets" + "knative.dev/pkg/apis" +) + +func validateContextVariables(ts *v1beta1.TaskSpec, tr *v1beta1.TaskRun) *apis.FieldError { + taskRunContextNames := sets.NewString().Insert( + "name", + "namespace", + "uid", + ) + taskContextNames := sets.NewString().Insert( + "name", + ) + if err := v1beta1.ValidateVariables(ts.Steps, "context\\.taskRun", taskRunContextNames); err != nil { + return err + } + + return v1beta1.ValidateVariables(ts.Steps, "context\\.task", taskContextNames) + +} diff --git a/pkg/reconciler/taskrun/validate_contexts_test.go b/pkg/reconciler/taskrun/validate_contexts_test.go new file mode 100644 index 00000000000..6856f3ebba6 --- /dev/null +++ b/pkg/reconciler/taskrun/validate_contexts_test.go @@ -0,0 +1,211 @@ +/* +Copyright 2020 The Tekton Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package taskrun + +import ( + "testing" + + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + "github.com/tektoncd/pipeline/pkg/reconciler/taskrun/resources" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestContextValid(t *testing.T) { + for _, tc := range []struct { + description string + rtr resources.ResolvedTaskResources + tr v1beta1.TaskRun + spec v1beta1.TaskSpec + }{{ + description: "context taskName replacement without taskRun in spec container", + rtr: resources.ResolvedTaskResources{ + TaskName: "Task1", + }, + tr: v1beta1.TaskRun{}, + spec: v1beta1.TaskSpec{ + Steps: []v1beta1.Step{{ + Container: corev1.Container{ + Name: "ImageName", + Image: "$(context.task.name)-1", + }, + }}, + }, + }, { + description: "context taskName replacement with taskRun in spec container", + rtr: resources.ResolvedTaskResources{ + TaskName: "Task1", + }, + tr: v1beta1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: "taskrunName", + }, + }, + spec: v1beta1.TaskSpec{ + Steps: []v1beta1.Step{{ + Container: corev1.Container{ + Name: "ImageName", + Image: "$(context.task.name)-1", + }, + }}, + }, + }, { + description: "context taskRunName replacement with defined taskRun in spec container", + rtr: resources.ResolvedTaskResources{ + TaskName: "Task1", + }, + tr: v1beta1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: "taskrunName", + }, + }, + spec: v1beta1.TaskSpec{ + Steps: []v1beta1.Step{{ + Container: corev1.Container{ + Name: "ImageName", + Image: "$(context.taskRun.name)-1", + }, + }}, + }, + }, { + description: "context taskRunName replacement with no defined taskRun name in spec container", + rtr: resources.ResolvedTaskResources{ + TaskName: "Task1", + }, + tr: v1beta1.TaskRun{}, + spec: v1beta1.TaskSpec{ + Steps: []v1beta1.Step{{ + Container: corev1.Container{ + Name: "ImageName", + Image: "$(context.taskRun.name)-1", + }, + }}, + }, + }, { + description: "context taskRun namespace replacement with no defined namespace in spec container", + rtr: resources.ResolvedTaskResources{ + TaskName: "Task1", + }, + tr: v1beta1.TaskRun{}, + spec: v1beta1.TaskSpec{ + Steps: []v1beta1.Step{{ + Container: corev1.Container{ + Name: "ImageName", + Image: "$(context.taskRun.namespace)-1", + }, + }}, + }, + }, { + description: "context taskRun namespace replacement with defined namespace in spec container", + rtr: resources.ResolvedTaskResources{ + TaskName: "Task1", + }, + tr: v1beta1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: "taskrunName", + Namespace: "trNamespace", + }, + }, + spec: v1beta1.TaskSpec{ + Steps: []v1beta1.Step{{ + Container: corev1.Container{ + Name: "ImageName", + Image: "$(context.taskRun.namespace)-1", + }, + }}, + }, + }, { + description: "context taskRunName replacement with no defined taskName in spec container", + rtr: resources.ResolvedTaskResources{}, + tr: v1beta1.TaskRun{}, + spec: v1beta1.TaskSpec{ + Steps: []v1beta1.Step{{ + Container: corev1.Container{ + Name: "ImageName", + Image: "$(context.task.name)-1", + }, + }}, + }, + }, { + description: "context UID replacement valid", + rtr: resources.ResolvedTaskResources{ + TaskName: "Task1", + }, + tr: v1beta1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{ + UID: "UID-1", + }, + }, + spec: v1beta1.TaskSpec{ + Steps: []v1beta1.Step{{ + Container: corev1.Container{ + Name: "ImageName", + Image: "$(context.taskRun.uid)", + }, + }}, + }, + }} { + t.Run(tc.description, func(t *testing.T) { + if err := validateContextVariables(&tc.spec, &tc.tr); err != nil { + t.Fatalf("Did not expect to see error when validating valid contexts but saw %v", err) + } + }) + } +} + +func TestContextInvalid(t *testing.T) { + for _, tc := range []struct { + description string + rtr resources.ResolvedTaskResources + tr v1beta1.TaskRun + spec v1beta1.TaskSpec + }{{ + description: "context taskRun replacement invalid", + rtr: resources.ResolvedTaskResources{ + TaskName: "Task1", + }, + tr: v1beta1.TaskRun{}, + spec: v1beta1.TaskSpec{ + Steps: []v1beta1.Step{{ + Container: corev1.Container{ + Name: "ImageName", + Image: "$(context.taskRun.missing)", + }, + }}, + }, + }, { + description: "context task replacement invalid", + rtr: resources.ResolvedTaskResources{ + TaskName: "Task1", + }, + tr: v1beta1.TaskRun{}, + spec: v1beta1.TaskSpec{ + Steps: []v1beta1.Step{{ + Container: corev1.Container{ + Name: "ImageName", + Image: "$(context.task.missing)", + }, + }}, + }, + }} { + t.Run(tc.description, func(t *testing.T) { + if err := validateContextVariables(&tc.spec, &tc.tr); err == nil { + t.Fatalf("expected to see error when validating invalid contexts but saw %v", err) + } + }) + } +} diff --git a/pkg/substitution/substitution.go b/pkg/substitution/substitution.go index 30d027074a1..06d5b96c0a5 100644 --- a/pkg/substitution/substitution.go +++ b/pkg/substitution/substitution.go @@ -27,7 +27,7 @@ import ( const parameterSubstitution = `[_a-zA-Z][_a-zA-Z0-9.-]*(\[\*\])?` -const braceMatchingRegex = "(\\$(\\(%s.(?P%s)\\)))" +const braceMatchingRegex = "(\\$(\\(%s\\.(?P%s)\\)))" func ValidateVariable(name, value, prefix, locationName, path string, vars sets.String) *apis.FieldError { if vs, present := extractVariablesFromString(value, prefix); present { diff --git a/pkg/substitution/substitution_test.go b/pkg/substitution/substitution_test.go index c218fda0612..1c7f497b579 100644 --- a/pkg/substitution/substitution_test.go +++ b/pkg/substitution/substitution_test.go @@ -49,6 +49,16 @@ func TestValidateVariables(t *testing.T) { vars: sets.NewString("baz"), }, expectedError: nil, + }, { + name: "valid variable uid", + args: args{ + input: "--flag=$(context.taskRun.uid)", + prefix: "context.taskRun", + locationName: "step", + path: "taskspec.steps", + vars: sets.NewString("uid"), + }, + expectedError: nil, }, { name: "multiple variables", args: args{