diff --git a/cmd/controller/main.go b/cmd/controller/main.go index 4febb20e64c..db8822cdf0c 100644 --- a/cmd/controller/main.go +++ b/cmd/controller/main.go @@ -36,9 +36,8 @@ const ( var ( entrypointImage = flag.String("entrypoint-image", "override-with-entrypoint:latest", "The container image containing our entrypoint binary.") - nopImage = flag.String("nop-image", "tianon/true", "The container image used to stop sidecars") - affinityAssistantImage = flag.String("affinity-assistant-image", "nginx", "The container image used for the Affinity Assistant") - gitImage = flag.String("git-image", "override-with-git:latest", + nopImage = flag.String("nop-image", "override-with-nop:latest", "The container image used to stop sidecars") + gitImage = flag.String("git-image", "override-with-git:latest", "The container image containing our Git binary.") credsImage = flag.String("creds-image", "override-with-creds:latest", "The container image for preparing our Build's credentials.") @@ -61,7 +60,6 @@ func main() { images := pipeline.Images{ EntrypointImage: *entrypointImage, NopImage: *nopImage, - AffinityAssistantImage: *affinityAssistantImage, GitImage: *gitImage, CredsImage: *credsImage, KubeconfigWriterImage: *kubeconfigWriterImage, diff --git a/cmd/nop/README.md b/cmd/nop/README.md new file mode 100644 index 00000000000..eb679829b96 --- /dev/null +++ b/cmd/nop/README.md @@ -0,0 +1,38 @@ +# `nop` Image + +This image is responsible for two internal functions of Tekton: + +1. Stopping sidecar containers[#stopping-sidecar-containers] +1. Affinity Assistant StatefulSet[#affinity-assistant-statefulset] + +The `nop` image satisfies these two functions with a minimally small image, +both to optimize image pull latency and to present a minimal surface for a +potential attacker. + +## Stopping sidecar containers + +When all steps in a TaskRun are complete, Tekton attempts to gracefully stop +any running sidecar containers, by replacing their `image` with an image that +exits immediately, regardless of any `args` passed to the container. + +When the `nop` image is run with any args (except one unique string, described +[below](#affinity-assistant-statefulset)), it will exit with the exit code zero +immediately. + +* **NB:** If the sidecar container has its `command` specified, the `nop` + binary will not be invoked, and may exit with a non-zero exit code. Tekton + will not interpret this as a TaskRun failure, but it may result in noisy + logs/metrics being emitted. + +## Affinity Assistant StatefulSet + +The Affinity Assistant, which powers [workspaces](docs/workspaces.md), works +by running a +[`StatefulSet`](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/) +with an container that runs indefinitely. This container doesn't need to _do_ +anything, it just needs to exist. + +When the `nop` image is passed the string `tekton_run_indefinitely` (a unique, +Tekton-identified string), it will run indefinitely until it receives a signal +to terminate. The affinity assistant StatefulSet passes this arg to ensure its +container runs indefinitely. diff --git a/cmd/nop/main.go b/cmd/nop/main.go new file mode 100644 index 00000000000..890457fed52 --- /dev/null +++ b/cmd/nop/main.go @@ -0,0 +1,36 @@ +/* +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 main + +import ( + "log" + "os" + "os/signal" + "syscall" +) + +func main() { + if len(os.Args) >= 2 && os.Args[1] == "tekton_run_indefinitely" { + log.Println("Waiting indefinitely...") + ch := make(chan os.Signal) + signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM) + log.Println("received signal:", <-ch) + } + + log.Println("Exiting...") + os.Exit(0) +} diff --git a/config/controller.yaml b/config/controller.yaml index 5ffde8cbfd7..3c87c1e5fdb 100644 --- a/config/controller.yaml +++ b/config/controller.yaml @@ -62,18 +62,11 @@ spec: "-creds-image", "ko://github.com/tektoncd/pipeline/cmd/creds-init", "-git-image", "ko://github.com/tektoncd/pipeline/cmd/git-init", "-entrypoint-image", "ko://github.com/tektoncd/pipeline/cmd/entrypoint", + "-nop-image", "ko://github.com/tektoncd/pipeline/cmd/nop", "-imagedigest-exporter-image", "ko://github.com/tektoncd/pipeline/cmd/imagedigestexporter", "-pr-image", "ko://github.com/tektoncd/pipeline/cmd/pullrequest-init", "-build-gcs-fetcher-image", "ko://github.com/tektoncd/pipeline/vendor/github.com/GoogleCloudPlatform/cloud-builders/gcs-fetcher/cmd/gcs-fetcher", - # This image is used as a placeholder pod, the Affinity Assistant - # TODO(#2640) We may want to create a custom, minimal binary - # As of June 8, 2020, tag 1.19.0 - "-affinity-assistant-image", "nginx@sha256:c870bf53de0357813af37b9500cb1c2ff9fb4c00120d5fe1d75c21591293c34d", - - # These images are pulled from Dockerhub, by digest, as of May 19, 2020. - # As of May 29, 2020 new sha for nop image - "-nop-image", "tianon/true@sha256:009cce421096698832595ce039aa13fa44327d96beedb84282a69d3dbcf5a81b", # This is google/cloud-sdk:293.0.0-slim "-gsutil-image", "google/cloud-sdk@sha256:37654ada9b7afbc32828b537030e85de672a9dd468ac5c92a36da1e203a98def", # The shell image must be root in order to create directories and copy files to PVCs. diff --git a/pkg/apis/pipeline/images.go b/pkg/apis/pipeline/images.go index 731dbca0dfa..899256e47cd 100644 --- a/pkg/apis/pipeline/images.go +++ b/pkg/apis/pipeline/images.go @@ -23,8 +23,6 @@ type Images struct { EntrypointImage string // NopImage is the container image used to kill sidecars. NopImage string - // AffinityAssistantImage is the container image used for the Affinity Assistant. - AffinityAssistantImage string // GitImage is the container image with Git that we use to implement the Git source step. GitImage string // CredsImage is the container image used to initialize credentials before the build runs. diff --git a/pkg/apis/resource/v1alpha1/storage/build_gcs_test.go b/pkg/apis/resource/v1alpha1/storage/build_gcs_test.go index 28c26c6b0cc..242b9011b40 100644 --- a/pkg/apis/resource/v1alpha1/storage/build_gcs_test.go +++ b/pkg/apis/resource/v1alpha1/storage/build_gcs_test.go @@ -32,8 +32,7 @@ import ( var images = pipeline.Images{ EntrypointImage: "override-with-entrypoint:latest", - NopImage: "tianon/true", - AffinityAssistantImage: "nginx", + NopImage: "override-with-nop:latest", GitImage: "override-with-git:latest", CredsImage: "override-with-creds:latest", KubeconfigWriterImage: "override-with-kubeconfig-writer:latest", diff --git a/pkg/artifacts/artifact_storage_test.go b/pkg/artifacts/artifact_storage_test.go index 99dd9099af4..9a1bf708df1 100644 --- a/pkg/artifacts/artifact_storage_test.go +++ b/pkg/artifacts/artifact_storage_test.go @@ -38,8 +38,7 @@ import ( var ( images = pipeline.Images{ EntrypointImage: "override-with-entrypoint:latest", - NopImage: "tianon/true", - AffinityAssistantImage: "nginx", + NopImage: "override-with-nop:latest", GitImage: "override-with-git:latest", CredsImage: "override-with-creds:latest", KubeconfigWriterImage: "override-with-kubeconfig-writer:latest", diff --git a/pkg/reconciler/pipelinerun/affinity_assistant.go b/pkg/reconciler/pipelinerun/affinity_assistant.go index 920b66e12a5..659426839fe 100644 --- a/pkg/reconciler/pipelinerun/affinity_assistant.go +++ b/pkg/reconciler/pipelinerun/affinity_assistant.go @@ -58,7 +58,7 @@ func (c *Reconciler) createAffinityAssistants(ctx context.Context, wb []v1alpha1 claimName := getClaimName(w, pr.GetOwnerReference()) switch { case apierrors.IsNotFound(err): - affinityAssistantStatefulSet := affinityAssistantStatefulSet(affinityAssistantName, pr, claimName, c.Images.AffinityAssistantImage) + affinityAssistantStatefulSet := affinityAssistantStatefulSet(affinityAssistantName, pr, claimName, c.Images.NopImage) _, err := c.KubeClientSet.AppsV1().StatefulSets(namespace).Create(affinityAssistantStatefulSet) if err != nil { errs = append(errs, fmt.Errorf("failed to create StatefulSet %s: %s", affinityAssistantName, err)) @@ -137,6 +137,7 @@ func affinityAssistantStatefulSet(name string, pr *v1beta1.PipelineRun, claimNam containers := []corev1.Container{{ Name: "affinity-assistant", Image: affinityAssistantImage, + Args: []string{"tekton_run_indefinitely"}, // Set requests == limits to get QoS class _Guaranteed_. // See https://kubernetes.io/docs/tasks/configure-pod-container/quality-service-pod/#create-a-pod-that-gets-assigned-a-qos-class-of-guaranteed diff --git a/pkg/reconciler/pipelinerun/pipelinerun_test.go b/pkg/reconciler/pipelinerun/pipelinerun_test.go index 6738b2752a6..902d85f9943 100644 --- a/pkg/reconciler/pipelinerun/pipelinerun_test.go +++ b/pkg/reconciler/pipelinerun/pipelinerun_test.go @@ -64,8 +64,7 @@ var ( ignoreLastTransitionTime = cmpopts.IgnoreTypes(apis.Condition{}.LastTransitionTime.Inner.Time) images = pipeline.Images{ EntrypointImage: "override-with-entrypoint:latest", - NopImage: "tianon/true", - AffinityAssistantImage: "nginx", + NopImage: "override-with-nop:latest", GitImage: "override-with-git:latest", CredsImage: "override-with-creds:latest", KubeconfigWriterImage: "override-with-kubeconfig-writer:latest", diff --git a/pkg/reconciler/taskrun/resources/apply_test.go b/pkg/reconciler/taskrun/resources/apply_test.go index e30587b0013..afbdf46f934 100644 --- a/pkg/reconciler/taskrun/resources/apply_test.go +++ b/pkg/reconciler/taskrun/resources/apply_test.go @@ -35,8 +35,7 @@ import ( var ( images = pipeline.Images{ EntrypointImage: "override-with-entrypoint:latest", - NopImage: "tianon/true", - AffinityAssistantImage: "nginx", + NopImage: "override-with-nop:latest", GitImage: "override-with-git:latest", CredsImage: "override-with-creds:latest", KubeconfigWriterImage: "override-with-kubeconfig-writer-image:latest", diff --git a/pkg/reconciler/taskrun/resources/input_resource_test.go b/pkg/reconciler/taskrun/resources/input_resource_test.go index 962979d0acb..5a5fdbb07f0 100644 --- a/pkg/reconciler/taskrun/resources/input_resource_test.go +++ b/pkg/reconciler/taskrun/resources/input_resource_test.go @@ -37,8 +37,7 @@ import ( var ( images = pipeline.Images{ EntrypointImage: "override-with-entrypoint:latest", - NopImage: "tianon/true", - AffinityAssistantImage: "nginx", + NopImage: "override-with-nop:latest", GitImage: "override-with-git:latest", CredsImage: "override-with-creds:latest", KubeconfigWriterImage: "override-with-kubeconfig-writer:latest", diff --git a/pkg/reconciler/taskrun/taskrun_test.go b/pkg/reconciler/taskrun/taskrun_test.go index 1bf6d3c9da5..d2adf189573 100644 --- a/pkg/reconciler/taskrun/taskrun_test.go +++ b/pkg/reconciler/taskrun/taskrun_test.go @@ -72,8 +72,7 @@ var ( namespace = "" // all namespaces images = pipeline.Images{ EntrypointImage: "override-with-entrypoint:latest", - NopImage: "tianon/true", - AffinityAssistantImage: "nginx", + NopImage: "override-with-nop:latest", GitImage: "override-with-git:latest", CredsImage: "override-with-creds:latest", KubeconfigWriterImage: "override-with-kubeconfig-writer:latest", diff --git a/tekton/publish.yaml b/tekton/publish.yaml index 8af5c9e5589..ad28bb58a73 100644 --- a/tekton/publish.yaml +++ b/tekton/publish.yaml @@ -28,6 +28,8 @@ spec: type: image - name: builtEntrypointImage type: image + - name: builtNopImage + type: image - name: builtKubeconfigWriterImage type: image - name: builtCredsInitImage @@ -182,6 +184,7 @@ spec: REGIONS=(us eu asia) IMAGES=( $(inputs.params.imageRegistry)/$(inputs.params.pathToProject)/$(outputs.resources.builtEntrypointImage.url):$(inputs.params.versionTag) + $(inputs.params.imageRegistry)/$(inputs.params.pathToProject)/$(outputs.resources.builtNopImage.url):$(inputs.params.versionTag) $(inputs.params.imageRegistry)/$(inputs.params.pathToProject)/$(outputs.resources.builtKubeconfigWriterImage.url):$(inputs.params.versionTag) $(inputs.params.imageRegistry)/$(inputs.params.pathToProject)/$(outputs.resources.builtCredsInitImage.url):$(inputs.params.versionTag) $(inputs.params.imageRegistry)/$(inputs.params.pathToProject)/$(outputs.resources.builtGitInitImage.url):$(inputs.params.versionTag) diff --git a/tekton/release-cheat-sheet.md b/tekton/release-cheat-sheet.md index 4f30cbda156..6937e5a04bf 100644 --- a/tekton/release-cheat-sheet.md +++ b/tekton/release-cheat-sheet.md @@ -61,6 +61,7 @@ the pipelines repo, a terminal window and a text editor. --resource=bucket=pipeline-tekton-bucket \ --resource=builtBaseImage=base-image \ --resource=builtEntrypointImage=entrypoint-image \ + --resource=builtNopImage=nop-image \ --resource=builtKubeconfigWriterImage=kubeconfigwriter-image \ --resource=builtCredsInitImage=creds-init-image \ --resource=builtGitInitImage=git-init-image \