From 7abf5455931961daee7e4f5d73141d821ef84a4b Mon Sep 17 00:00:00 2001 From: Wendy Dembowski Date: Fri, 26 Jun 2020 20:05:38 +0000 Subject: [PATCH] Add expansion of context.(pipeline|pipelinerun|task|taskRun).name and add tests. --- docs/variables.md | 5 + pkg/reconciler/pipelinerun/pipelinerun.go | 1 + .../pipelinerun/pipelinerun_test.go | 19 ++- pkg/reconciler/pipelinerun/resources/apply.go | 12 ++ .../pipelinerun/resources/apply_test.go | 68 +++++++++ pkg/reconciler/taskrun/resources/apply.go | 12 ++ .../taskrun/resources/apply_test.go | 133 ++++++++++++++++++ pkg/reconciler/taskrun/taskrun.go | 3 + pkg/reconciler/taskrun/taskrun_test.go | 5 +- 9 files changed, 251 insertions(+), 7 deletions(-) diff --git a/docs/variables.md b/docs/variables.md index 1bf67785296..e30a40aeab3 100644 --- a/docs/variables.md +++ b/docs/variables.md @@ -14,6 +14,9 @@ This page documents the variable substitions supported by `Tasks` and `Pipelines | -------- | ----------- | | `params.` | The value of the parameter at runtime. | | `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.pipeline.name` | The name of this `Pipeline` . | + ## Variables available in a `Task` @@ -27,6 +30,8 @@ This page documents the variable substitions supported by `Tasks` and `Pipelines | `workspaces..claim` | The name of the `PersistentVolumeClaim` specified as a volume source for the `Workspace`. Empty string for other volume types. | | `workspaces..volume` | The name of the volume populating the `Workspace`. | | `credentials.path` | The path to the credentials written by the `creds-init` init container. | +| `context.taskRun.name` | The name 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/pkg/reconciler/pipelinerun/pipelinerun.go b/pkg/reconciler/pipelinerun/pipelinerun.go index 1730afbe7d0..4facc569f87 100644 --- a/pkg/reconciler/pipelinerun/pipelinerun.go +++ b/pkg/reconciler/pipelinerun/pipelinerun.go @@ -376,6 +376,7 @@ func (c *Reconciler) reconcile(ctx context.Context, pr *v1beta1.PipelineRun) err // Apply parameter substitution from the PipelineRun pipelineSpec = resources.ApplyParameters(pipelineSpec, pr) + pipelineSpec = resources.ApplyContexts(pipelineSpec, pipelineMeta.Name, 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/pipelinerun_test.go b/pkg/reconciler/pipelinerun/pipelinerun_test.go index 8ebe3c1c110..751ddf41f27 100644 --- a/pkg/reconciler/pipelinerun/pipelinerun_test.go +++ b/pkg/reconciler/pipelinerun/pipelinerun_test.go @@ -137,9 +137,9 @@ func TestReconcile(t *testing.T) { // TestReconcile runs "Reconcile" on a PipelineRun with one Task that has not been started yet. // It verifies that the TaskRun is created, it checks the resulting API actions, status and events. names.TestingSeed() - + const pipelineRunName = "test-pipeline-run-success" prs := []*v1beta1.PipelineRun{ - tb.PipelineRun("test-pipeline-run-success", + tb.PipelineRun(pipelineRunName, tb.PipelineRunNamespace("foo"), tb.PipelineRunSpec("test-pipeline", tb.PipelineRunServiceAccountName("test-sa"), @@ -160,8 +160,11 @@ func TestReconcile(t *testing.T) { funParam := tb.PipelineTaskParam("foo", "somethingfun") moreFunParam := tb.PipelineTaskParam("bar", "$(params.bar)") templatedParam := tb.PipelineTaskParam("templatedparam", "$(inputs.workspace.$(params.rev-param))") + contextRunParam := tb.PipelineTaskParam("contextRunParam", "$(context.pipelineRun.name)") + contextPipelineParam := tb.PipelineTaskParam("contextPipelineParam", "$(context.pipeline.name)") + const pipelineName = "test-pipeline" ps := []*v1beta1.Pipeline{ - tb.Pipeline("test-pipeline", + tb.Pipeline(pipelineName, tb.PipelineNamespace("foo"), tb.PipelineSpec( tb.PipelineDeclaredResource("git-repo", "git"), @@ -171,7 +174,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, + funParam, moreFunParam, templatedParam, contextRunParam, contextPipelineParam, tb.RunAfter("unit-test-2"), tb.PipelineTaskInputResource("workspace", "git-repo"), tb.PipelineTaskOutputResource("image-to-use", "best-image"), @@ -179,7 +182,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, + funParam, moreFunParam, templatedParam, contextRunParam, contextPipelineParam, tb.PipelineTaskInputResource("workspace", "git-repo"), tb.PipelineTaskOutputResource("image-to-use", "best-image"), tb.PipelineTaskOutputResource("workspace", "git-repo"), @@ -191,7 +194,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, + funParam, moreFunParam, templatedParam, contextRunParam, contextPipelineParam, tb.PipelineTaskInputResource("workspace", "git-repo"), tb.PipelineTaskOutputResource("image-to-use", "best-image"), tb.PipelineTaskOutputResource("workspace", "git-repo"), @@ -202,6 +205,7 @@ 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.TaskResources( tb.TaskResourcesInput("workspace", resourcev1alpha1.PipelineResourceTypeGit), tb.TaskResourcesOutput("image-to-use", resourcev1alpha1.PipelineResourceTypeImage), @@ -215,6 +219,7 @@ 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.TaskResources( tb.TaskResourcesInput("workspace", resourcev1alpha1.PipelineResourceTypeGit), tb.TaskResourcesOutput("image-to-use", resourcev1alpha1.PipelineResourceTypeImage), @@ -283,6 +288,8 @@ func TestReconcile(t *testing.T) { tb.TaskRunParam("foo", "somethingfun"), tb.TaskRunParam("bar", "somethingmorefun"), tb.TaskRunParam("templatedparam", "$(inputs.workspace.revision)"), + tb.TaskRunParam("contextRunParam", pipelineRunName), + tb.TaskRunParam("contextPipelineParam", pipelineName), tb.TaskRunResources( tb.TaskRunResourcesInput("workspace", tb.TaskResourceBindingRef("some-repo")), tb.TaskRunResourcesOutput("image-to-use", diff --git a/pkg/reconciler/pipelinerun/resources/apply.go b/pkg/reconciler/pipelinerun/resources/apply.go index 92c46d4cfbc..db6388e254e 100644 --- a/pkg/reconciler/pipelinerun/resources/apply.go +++ b/pkg/reconciler/pipelinerun/resources/apply.go @@ -53,6 +53,18 @@ func ApplyParameters(p *v1beta1.PipelineSpec, pr *v1beta1.PipelineRun) *v1beta1. return ApplyReplacements(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 { + stringReplacements := map[string]string{} + stringReplacements["context.pipelineRun.name"] = pr.Name + stringReplacements["context.pipeline.name"] = pipelineName + + return ApplyReplacements(spec, + map[string]string{"context.pipelineRun.name": pr.Name, "context.pipeline.name": pipelineName}, + map[string][]string{}) +} + // ApplyTaskResults applies the ResolvedResultRef to each PipelineTask.Params in targets func ApplyTaskResults(targets PipelineRunState, resolvedResultRefs ResolvedResultRefs) { stringReplacements := map[string]string{} diff --git a/pkg/reconciler/pipelinerun/resources/apply_test.go b/pkg/reconciler/pipelinerun/resources/apply_test.go index ab92a183131..4cf0ac5ef9b 100644 --- a/pkg/reconciler/pipelinerun/resources/apply_test.go +++ b/pkg/reconciler/pipelinerun/resources/apply_test.go @@ -384,3 +384,71 @@ func TestApplyTaskResults_Conditions(t *testing.T) { }) } } + +func TestContext(t *testing.T) { + for _, tc := range []struct { + description string + pr *v1beta1.PipelineRun + original *v1beta1.Pipeline + expected *v1beta1.Pipeline + }{{ + description: "context pipeline name replacement without pipelineRun in spec", + original: tb.Pipeline("test-pipeline", + tb.PipelineSpec( + tb.PipelineTask("first-task-1", "first-task", + tb.PipelineTaskParam("first-task-first-param", "$(context.pipeline.name)-1"), + ))), + expected: tb.Pipeline("test-pipeline", + tb.PipelineSpec( + tb.PipelineTask("first-task-1", "first-task", + tb.PipelineTaskParam("first-task-first-param", "test-pipeline-1"), + ))), + pr: &v1beta1.PipelineRun{}, + }, { + description: "context pipeline name replacement with pipelineRun in spec", + pr: tb.PipelineRun("pipelineRunName"), + original: tb.Pipeline("test-pipeline", + tb.PipelineSpec( + tb.PipelineTask("first-task-1", "first-task", + tb.PipelineTaskParam("first-task-first-param", "$(context.pipeline.name)-1"), + ))), + expected: tb.Pipeline("test-pipeline", + tb.PipelineSpec( + tb.PipelineTask("first-task-1", "first-task", + tb.PipelineTaskParam("first-task-first-param", "test-pipeline-1"), + ))), + }, { + description: "context pipelineRunName replacement with defined pipelineRun in spec", + pr: tb.PipelineRun("pipelineRunName"), + original: tb.Pipeline("test-pipeline", + tb.PipelineSpec( + tb.PipelineTask("first-task-1", "first-task", + tb.PipelineTaskParam("first-task-first-param", "$(context.pipelineRun.name)-1"), + ))), + expected: tb.Pipeline("test-pipeline", + tb.PipelineSpec( + tb.PipelineTask("first-task-1", "first-task", + tb.PipelineTaskParam("first-task-first-param", "pipelineRunName-1"), + ))), + }, { + description: "context pipelineRunName replacement with no defined pipeline in spec", + pr: &v1beta1.PipelineRun{}, + original: tb.Pipeline("test-pipeline", + tb.PipelineSpec( + tb.PipelineTask("first-task-1", "first-task", + tb.PipelineTaskParam("first-task-first-param", "$(context.pipelineRun.name)-1"), + ))), + expected: tb.Pipeline("test-pipeline", + tb.PipelineSpec( + tb.PipelineTask("first-task-1", "first-task", + tb.PipelineTaskParam("first-task-first-param", "-1"), + ))), + }} { + t.Run(tc.description, func(t *testing.T) { + got := ApplyContexts(&tc.original.Spec, tc.original.Name, tc.pr) + if d := cmp.Diff(tc.expected.Spec, *got); d != "" { + t.Errorf(diff.PrintWantGot(d)) + } + }) + } +} diff --git a/pkg/reconciler/taskrun/resources/apply.go b/pkg/reconciler/taskrun/resources/apply.go index dfbf6ece3da..89a9edb1693 100644 --- a/pkg/reconciler/taskrun/resources/apply.go +++ b/pkg/reconciler/taskrun/resources/apply.go @@ -96,6 +96,18 @@ func ApplyResources(spec *v1beta1.TaskSpec, resolvedResources map[string]v1beta1 return ApplyReplacements(spec, replacements, map[string][]string{}) } +// 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. +func ApplyContexts(spec *v1beta1.TaskSpec, rtr *ResolvedTaskResources, tr *v1beta1.TaskRun) *v1beta1.TaskSpec { + stringReplacements := map[string]string{} + stringReplacements["context.taskRun.name"] = tr.Name + stringReplacements["context.task.name"] = rtr.TaskName + + return ApplyReplacements(spec, + map[string]string{"context.taskRun.name": tr.Name, "context.task.name": rtr.TaskName}, + map[string][]string{}) +} + // ApplyWorkspaces applies the substitution from paths that the workspaces in w are mounted to, the // volumes that wb are realized with in the task spec ts and the PersistentVolumeClaim names for the // workspaces. diff --git a/pkg/reconciler/taskrun/resources/apply_test.go b/pkg/reconciler/taskrun/resources/apply_test.go index 8bee8005d11..4629470e3ab 100644 --- a/pkg/reconciler/taskrun/resources/apply_test.go +++ b/pkg/reconciler/taskrun/resources/apply_test.go @@ -743,6 +743,139 @@ func TestApplyWorkspaces(t *testing.T) { } } +func TestContext(t *testing.T) { + for _, tc := range []struct { + description string + rtr resources.ResolvedTaskResources + tr v1beta1.TaskRun + spec v1beta1.TaskSpec + want 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", + }, + }}, + }, + want: v1beta1.TaskSpec{ + Steps: []v1beta1.Step{{ + Container: corev1.Container{ + Name: "ImageName", + Image: "Task1-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", + }, + }}, + }, + want: v1beta1.TaskSpec{ + Steps: []v1beta1.Step{{ + Container: corev1.Container{ + Name: "ImageName", + Image: "Task1-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", + }, + }}, + }, + want: v1beta1.TaskSpec{ + Steps: []v1beta1.Step{{ + Container: corev1.Container{ + Name: "ImageName", + Image: "taskrunName-1", + }, + }}, + }, + }, { + description: "context taskRunName replacement with no defined 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.taskRun.name)-1", + }, + }}, + }, + want: v1beta1.TaskSpec{ + Steps: []v1beta1.Step{{ + Container: corev1.Container{ + Name: "ImageName", + Image: "-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", + }, + }}, + }, + want: v1beta1.TaskSpec{ + Steps: []v1beta1.Step{{ + Container: corev1.Container{ + Name: "ImageName", + Image: "-1", + }, + }}, + }, + }} { + t.Run(tc.description, func(t *testing.T) { + got := resources.ApplyContexts(&tc.spec, &tc.rtr, &tc.tr) + if d := cmp.Diff(&tc.want, got); d != "" { + t.Errorf(diff.PrintWantGot(d)) + } + }) + } +} + func TestTaskResults(t *testing.T) { names.TestingSeed() ts := &v1beta1.TaskSpec{ diff --git a/pkg/reconciler/taskrun/taskrun.go b/pkg/reconciler/taskrun/taskrun.go index 06650cef615..fda987dce03 100644 --- a/pkg/reconciler/taskrun/taskrun.go +++ b/pkg/reconciler/taskrun/taskrun.go @@ -520,6 +520,9 @@ func (c *Reconciler) createPod(ctx context.Context, tr *v1beta1.TaskRun, rtr *re // Apply parameter substitution from the taskrun. ts = resources.ApplyParameters(ts, tr, defaults...) + // Apply context substitution from the taskrun + ts = resources.ApplyContexts(ts, rtr, tr) + // Apply bound resource substitution from the taskrun. ts = resources.ApplyResources(ts, inputResources, "inputs") ts = resources.ApplyResources(ts, outputResources, "outputs") diff --git a/pkg/reconciler/taskrun/taskrun_test.go b/pkg/reconciler/taskrun/taskrun_test.go index a11db4f5217..63e0574fd07 100644 --- a/pkg/reconciler/taskrun/taskrun_test.go +++ b/pkg/reconciler/taskrun/taskrun_test.go @@ -140,6 +140,8 @@ var ( "--my-arg-with-default=$(inputs.params.myarghasdefault)", "--my-arg-with-default2=$(inputs.params.myarghasdefault2)", "--my-additional-arg=$(outputs.resources.myimage.url)", + "--my-taskname-arg=$(context.task.name)", + "--my-taskrun-arg=$(context.taskRun.name)", )), tb.Step("myotherimage", tb.StepName("myothercontainer"), tb.StepCommand("/mycmd"), tb.StepArgs( "--my-other-arg=$(inputs.resources.workspace.url)", @@ -955,7 +957,8 @@ func TestReconcile(t *testing.T) { tb.Command(entrypointLocation), tb.Args("-wait_file", "/tekton/tools/1", "-post_file", "/tekton/tools/2", "-termination_path", "/tekton/termination", "-entrypoint", "/mycmd", "--", "--my-arg=foo", "--my-arg-with-default=bar", - "--my-arg-with-default2=thedefault", "--my-additional-arg=gcr.io/kristoff/sven"), + "--my-arg-with-default2=thedefault", "--my-additional-arg=gcr.io/kristoff/sven", "--my-taskname-arg=test-task-with-substitution", + "--my-taskrun-arg=test-taskrun-substitution"), tb.WorkingDir(workspaceDir), tb.EnvVar("HOME", "/tekton/home"), tb.VolumeMount("tekton-internal-tools", "/tekton/tools"),