diff --git a/docs/tasks.md b/docs/tasks.md index afc50853244..9fcb65e2380 100644 --- a/docs/tasks.md +++ b/docs/tasks.md @@ -765,6 +765,33 @@ spec: key: bot-token ``` +#### Using a sidecar + +```yaml +apiVersion: tekton.dev/v1alpha1 +kind: Task +metadata: + name: with-sidecar-task +spec: + inputs: + params: + - name: sidecar-image + type: string + description: Image name of the sidecar container + - name: sidecar-env + type: string + description: Environment variable value + sidecars: + - name: sidecar + image: $(inputs.params.sidecar-image) + env: + - name: SIDECAR_ENV + value: $(inputs.params.sidecar-env) + steps: + - name: test + image: hello-world +``` + Except as otherwise noted, the content of this page is licensed under the [Creative Commons Attribution 4.0 License](https://creativecommons.org/licenses/by/4.0/), and code samples are licensed under the diff --git a/examples/taskruns/sidecar-interp.yaml b/examples/taskruns/sidecar-interp.yaml new file mode 100644 index 00000000000..9f367bc9a7a --- /dev/null +++ b/examples/taskruns/sidecar-interp.yaml @@ -0,0 +1,34 @@ +apiVersion: tekton.dev/v1alpha1 +kind: TaskRun +metadata: + generateName: sidecar-interp- +spec: + taskSpec: + inputs: + params: + - name: some-input + default: foo + volumes: + - name: shared + emptyDir: {} + sidecars: + - name: value-sidecar + image: ubuntu + command: + - /bin/bash + args: + - -c + - "echo $(inputs.params.some-input) > /shared/value && sleep infinity" + volumeMounts: + - name: shared + mountPath: /shared + steps: + - name: check-value + image: ubuntu + script: | + #!/bin/bash + VALUE=$(cat /shared/value) + [[ $VALUE == 'foo' ]] + volumeMounts: + - name: shared + mountPath: /shared diff --git a/pkg/apis/pipeline/v1alpha1/container_replacements.go b/pkg/apis/pipeline/v1alpha1/container_replacements.go new file mode 100644 index 00000000000..c8ee090ba6b --- /dev/null +++ b/pkg/apis/pipeline/v1alpha1/container_replacements.go @@ -0,0 +1,72 @@ +/* + Copyright 2019 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 v1alpha1 + +import ( + "github.com/tektoncd/pipeline/pkg/substitution" + corev1 "k8s.io/api/core/v1" +) + +func ApplyContainerReplacements(step *corev1.Container, stringReplacements map[string]string, arrayReplacements map[string][]string) { + step.Name = substitution.ApplyReplacements(step.Name, stringReplacements) + step.Image = substitution.ApplyReplacements(step.Image, stringReplacements) + + // Use ApplyArrayReplacements here, as additional args may be added via an array parameter. + var newArgs []string + for _, a := range step.Args { + newArgs = append(newArgs, substitution.ApplyArrayReplacements(a, stringReplacements, arrayReplacements)...) + } + step.Args = newArgs + + for ie, e := range step.Env { + step.Env[ie].Value = substitution.ApplyReplacements(e.Value, stringReplacements) + if step.Env[ie].ValueFrom != nil { + if e.ValueFrom.SecretKeyRef != nil { + step.Env[ie].ValueFrom.SecretKeyRef.LocalObjectReference.Name = substitution.ApplyReplacements(e.ValueFrom.SecretKeyRef.LocalObjectReference.Name, stringReplacements) + step.Env[ie].ValueFrom.SecretKeyRef.Key = substitution.ApplyReplacements(e.ValueFrom.SecretKeyRef.Key, stringReplacements) + } + if e.ValueFrom.ConfigMapKeyRef != nil { + step.Env[ie].ValueFrom.ConfigMapKeyRef.LocalObjectReference.Name = substitution.ApplyReplacements(e.ValueFrom.ConfigMapKeyRef.LocalObjectReference.Name, stringReplacements) + step.Env[ie].ValueFrom.ConfigMapKeyRef.Key = substitution.ApplyReplacements(e.ValueFrom.ConfigMapKeyRef.Key, stringReplacements) + } + } + } + + for ie, e := range step.EnvFrom { + step.EnvFrom[ie].Prefix = substitution.ApplyReplacements(e.Prefix, stringReplacements) + if e.ConfigMapRef != nil { + step.EnvFrom[ie].ConfigMapRef.LocalObjectReference.Name = substitution.ApplyReplacements(e.ConfigMapRef.LocalObjectReference.Name, stringReplacements) + } + if e.SecretRef != nil { + step.EnvFrom[ie].SecretRef.LocalObjectReference.Name = substitution.ApplyReplacements(e.SecretRef.LocalObjectReference.Name, stringReplacements) + } + } + step.WorkingDir = substitution.ApplyReplacements(step.WorkingDir, stringReplacements) + + // Use ApplyArrayReplacements here, as additional commands may be added via an array parameter. + var newCommand []string + for _, c := range step.Command { + newCommand = append(newCommand, substitution.ApplyArrayReplacements(c, stringReplacements, arrayReplacements)...) + } + step.Command = newCommand + + for iv, v := range step.VolumeMounts { + step.VolumeMounts[iv].Name = substitution.ApplyReplacements(v.Name, stringReplacements) + step.VolumeMounts[iv].MountPath = substitution.ApplyReplacements(v.MountPath, stringReplacements) + step.VolumeMounts[iv].SubPath = substitution.ApplyReplacements(v.SubPath, stringReplacements) + } +} diff --git a/pkg/apis/pipeline/v1alpha1/container_replacements_test.go b/pkg/apis/pipeline/v1alpha1/container_replacements_test.go new file mode 100644 index 00000000000..22d85ea82f2 --- /dev/null +++ b/pkg/apis/pipeline/v1alpha1/container_replacements_test.go @@ -0,0 +1,147 @@ +/* + Copyright 2019 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 v1alpha1_test + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + corev1 "k8s.io/api/core/v1" +) + +func TestApplyContainerReplacements(t *testing.T) { + replacements := map[string]string{ + "replace.me": "replaced!", + } + + arrayReplacements := map[string][]string{ + "array.replace.me": {"val1", "val2"}, + } + + s := corev1.Container{ + Name: "$(replace.me)", + Image: "$(replace.me)", + Command: []string{"$(array.replace.me)"}, + Args: []string{"$(array.replace.me)"}, + WorkingDir: "$(replace.me)", + EnvFrom: []corev1.EnvFromSource{{ + ConfigMapRef: &corev1.ConfigMapEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "$(replace.me)", + }, + }, + SecretRef: &corev1.SecretEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "$(replace.me)", + }, + }, + }}, + Env: []corev1.EnvVar{{ + Name: "not_me", + Value: "$(replace.me)", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "$(replace.me)", + }, + Key: "$(replace.me)", + }, + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "$(replace.me)", + }, + Key: "$(replace.me)", + }, + }, + }}, + VolumeMounts: []corev1.VolumeMount{{ + Name: "$(replace.me)", + MountPath: "$(replace.me)", + SubPath: "$(replace.me)", + }}, + } + + expected := corev1.Container{ + Name: "replaced!", + Image: "replaced!", + Command: []string{"val1", "val2"}, + Args: []string{"val1", "val2"}, + WorkingDir: "replaced!", + EnvFrom: []corev1.EnvFromSource{{ + ConfigMapRef: &corev1.ConfigMapEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "replaced!", + }, + }, + SecretRef: &corev1.SecretEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "replaced!", + }, + }, + }}, + Env: []corev1.EnvVar{{ + Name: "not_me", + Value: "replaced!", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "replaced!", + }, + Key: "replaced!", + }, + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "replaced!", + }, + Key: "replaced!", + }, + }, + }}, + VolumeMounts: []corev1.VolumeMount{{ + Name: "replaced!", + MountPath: "replaced!", + SubPath: "replaced!", + }}, + } + + v1alpha1.ApplyContainerReplacements(&s, replacements, arrayReplacements) + if d := cmp.Diff(s, expected); d != "" { + t.Errorf("Container replacements failed: %s", d) + } +} + +func TestApplyContainerReplacements_NotDefined(t *testing.T) { + s := corev1.Container{ + Name: "$(params.not.defined)", + } + replacements := map[string]string{ + "replace.me": "replaced!", + } + + arrayReplacements := map[string][]string{ + "array.replace.me": {"val1", "val2"}, + } + + expected := corev1.Container{ + Name: "$(params.not.defined)", + } + v1alpha1.ApplyContainerReplacements(&s, replacements, arrayReplacements) + if d := cmp.Diff(s, expected); d != "" { + t.Errorf("Unexpected container replacement: %s", d) + } +} diff --git a/pkg/apis/pipeline/v1alpha1/step_replacements.go b/pkg/apis/pipeline/v1alpha1/step_replacements.go index d9d0180f445..2507a7b933a 100644 --- a/pkg/apis/pipeline/v1alpha1/step_replacements.go +++ b/pkg/apis/pipeline/v1alpha1/step_replacements.go @@ -21,52 +21,6 @@ import ( ) func ApplyStepReplacements(step *Step, stringReplacements map[string]string, arrayReplacements map[string][]string) { - step.Name = substitution.ApplyReplacements(step.Name, stringReplacements) - step.Image = substitution.ApplyReplacements(step.Image, stringReplacements) step.Script = substitution.ApplyReplacements(step.Script, stringReplacements) - - // Use ApplyArrayReplacements here, as additional args may be added via an array parameter. - var newArgs []string - for _, a := range step.Args { - newArgs = append(newArgs, substitution.ApplyArrayReplacements(a, stringReplacements, arrayReplacements)...) - } - step.Args = newArgs - - for ie, e := range step.Env { - step.Env[ie].Value = substitution.ApplyReplacements(e.Value, stringReplacements) - if step.Env[ie].ValueFrom != nil { - if e.ValueFrom.SecretKeyRef != nil { - step.Env[ie].ValueFrom.SecretKeyRef.LocalObjectReference.Name = substitution.ApplyReplacements(e.ValueFrom.SecretKeyRef.LocalObjectReference.Name, stringReplacements) - step.Env[ie].ValueFrom.SecretKeyRef.Key = substitution.ApplyReplacements(e.ValueFrom.SecretKeyRef.Key, stringReplacements) - } - if e.ValueFrom.ConfigMapKeyRef != nil { - step.Env[ie].ValueFrom.ConfigMapKeyRef.LocalObjectReference.Name = substitution.ApplyReplacements(e.ValueFrom.ConfigMapKeyRef.LocalObjectReference.Name, stringReplacements) - step.Env[ie].ValueFrom.ConfigMapKeyRef.Key = substitution.ApplyReplacements(e.ValueFrom.ConfigMapKeyRef.Key, stringReplacements) - } - } - } - - for ie, e := range step.EnvFrom { - step.EnvFrom[ie].Prefix = substitution.ApplyReplacements(e.Prefix, stringReplacements) - if e.ConfigMapRef != nil { - step.EnvFrom[ie].ConfigMapRef.LocalObjectReference.Name = substitution.ApplyReplacements(e.ConfigMapRef.LocalObjectReference.Name, stringReplacements) - } - if e.SecretRef != nil { - step.EnvFrom[ie].SecretRef.LocalObjectReference.Name = substitution.ApplyReplacements(e.SecretRef.LocalObjectReference.Name, stringReplacements) - } - } - step.WorkingDir = substitution.ApplyReplacements(step.WorkingDir, stringReplacements) - - // Use ApplyArrayReplacements here, as additional commands may be added via an array parameter. - var newCommand []string - for _, c := range step.Command { - newCommand = append(newCommand, substitution.ApplyArrayReplacements(c, stringReplacements, arrayReplacements)...) - } - step.Command = newCommand - - for iv, v := range step.VolumeMounts { - step.VolumeMounts[iv].Name = substitution.ApplyReplacements(v.Name, stringReplacements) - step.VolumeMounts[iv].MountPath = substitution.ApplyReplacements(v.MountPath, stringReplacements) - step.VolumeMounts[iv].SubPath = substitution.ApplyReplacements(v.SubPath, stringReplacements) - } + ApplyContainerReplacements(&step.Container, stringReplacements, arrayReplacements) } diff --git a/pkg/apis/pipeline/v1alpha1/step_replacements_test.go b/pkg/apis/pipeline/v1alpha1/step_replacements_test.go index 097b9fe2969..9ee6dc40caa 100644 --- a/pkg/apis/pipeline/v1alpha1/step_replacements_test.go +++ b/pkg/apis/pipeline/v1alpha1/step_replacements_test.go @@ -129,24 +129,3 @@ func TestApplyStepReplacements(t *testing.T) { t.Errorf("Container replacements failed: %s", d) } } - -func TestApplyStepReplacements_NotDefined(t *testing.T) { - s := v1alpha1.Step{Container: corev1.Container{ - Name: "$(params.not.defined)", - }} - replacements := map[string]string{ - "replace.me": "replaced!", - } - - arrayReplacements := map[string][]string{ - "array.replace.me": {"val1", "val2"}, - } - - expected := v1alpha1.Step{Container: corev1.Container{ - Name: "$(params.not.defined)", - }} - v1alpha1.ApplyStepReplacements(&s, replacements, arrayReplacements) - if d := cmp.Diff(s, expected); d != "" { - t.Errorf("Unexpected container replacement: %s", d) - } -} diff --git a/pkg/reconciler/taskrun/resources/apply.go b/pkg/reconciler/taskrun/resources/apply.go index de15d1fb678..8ab3fe8bb8c 100644 --- a/pkg/reconciler/taskrun/resources/apply.go +++ b/pkg/reconciler/taskrun/resources/apply.go @@ -125,5 +125,11 @@ func ApplyReplacements(spec *v1alpha1.TaskSpec, stringReplacements map[string]st } } + // Apply variable substitution to the sidecar definitions + sidecars := spec.Sidecars + for i := range sidecars { + v1alpha1.ApplyContainerReplacements(&sidecars[i], stringReplacements, arrayReplacements) + } + return spec } diff --git a/pkg/reconciler/taskrun/resources/apply_test.go b/pkg/reconciler/taskrun/resources/apply_test.go index 2809ffcac29..287a5133d01 100644 --- a/pkg/reconciler/taskrun/resources/apply_test.go +++ b/pkg/reconciler/taskrun/resources/apply_test.go @@ -58,6 +58,14 @@ var ( }, }}, }, + Sidecars: []corev1.Container{{ + Name: "foo", + Image: "$(inputs.params.myimage)", + Env: []corev1.EnvVar{{ + Name: "foo", + Value: "$(inputs.params.FOO)", + }}, + }}, StepTemplate: &corev1.Container{ Env: []corev1.EnvVar{{ Name: "template-var", @@ -504,6 +512,9 @@ func TestApplyParameters(t *testing.T) { spec.Volumes[0].VolumeSource.ConfigMap.LocalObjectReference.Name = "world" spec.Volumes[1].VolumeSource.Secret.SecretName = "world" spec.Volumes[2].VolumeSource.PersistentVolumeClaim.ClaimName = "world" + + spec.Sidecars[0].Image = "bar" + spec.Sidecars[0].Env[0].Value = "world" }) got := resources.ApplyParameters(simpleTaskSpec, tr, dp...) if d := cmp.Diff(got, want); d != "" {