diff --git a/docs/using.md b/docs/using.md index c9bdbeed411..5f5ca2e086d 100644 --- a/docs/using.md +++ b/docs/using.md @@ -55,21 +55,45 @@ To create a Task, you must: Each container image used as a step in a [`Task`](#task) must comply with a specific contract. -* [The `entrypoint` of the image will be ignored](#step-entrypoint) +When containers are run in a `Task`, the `entrypoint` of the container will be +overwritten with a custom binary that redirects the logs to a separate location +for aggregating the log output. As such, it is always recommended to explicitly +specify a command. -For example, in the following Task the images, `gcr.io/cloud-builders/gcloud` -and `gcr.io/cloud-builders/docker` run as steps: +When `command` is not explicitly set, the controller will attempt to lookup the +entrypoint from the remote registry. + +Due to this metadata lookup, if you use a private image as a step inside a +`Task`, the build-pipeline controller needs to be able to access that registry. +The simplest way to accomplish this is to add a `.docker/config.json` at +`$HOME/.docker/config.json`, which will then be used by the controller when +performing the lookup + +For example, in the following Task with the images, `gcr.io/cloud-builders/gcloud` +and `gcr.io/cloud-builders/docker`, the entrypoint would be resolved from the +registry, resulting in the tasks running `gcloud` and `docker` respectively. ```yaml spec: buildSpec: steps: - image: gcr.io/cloud-builders/gcloud - command: ['gcloud'] - ... + command: [gcloud] - image: gcr.io/cloud-builders/docker - command: ['docker'] - ... + command: [docker] +``` + +However, if the steps specified a custom `command`, that is what would be used. + +```yaml +spec: + buildSpec: + steps: + - image: gcr.io/cloud-builders/gcloud + command: + - bash + - -c + - echo "Hello!" ``` You can also provide `args` to the image's `command`: diff --git a/pkg/reconciler/v1alpha1/taskrun/entrypoint/entrypoint.go b/pkg/reconciler/v1alpha1/taskrun/entrypoint/entrypoint.go new file mode 100644 index 00000000000..869bef05e97 --- /dev/null +++ b/pkg/reconciler/v1alpha1/taskrun/entrypoint/entrypoint.go @@ -0,0 +1,162 @@ +/* +Copyright 2018 The Knative 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 entrypoint + +import ( + "encoding/json" + "fmt" + "sync" + + "github.com/google/go-containerregistry/pkg/authn" + "github.com/google/go-containerregistry/pkg/name" + "github.com/google/go-containerregistry/pkg/v1/remote" + "github.com/knative/build/pkg/apis/build/v1alpha1" + + corev1 "k8s.io/api/core/v1" +) + +const ( + // MountName is the name of the pvc being mounted (which + // will contain the entrypoint binary and eventually the logs) + MountName = "tools" + MountPoint = "/tools" + BinaryLocation = MountPoint + "/entrypoint" + JSONConfigEnvVar = "ENTRYPOINT_OPTIONS" + Image = "gcr.io/k8s-prow/entrypoint@sha256:7c7cd8906ce4982ffee326218e9fc75da2d4896d53cabc9833b9cc8d2d6b2b8f" + InitContainerName = "place-tools" + ProcessLogFile = "/tools/process-log.txt" + MarkerFile = "/tools/marker-file.txt" +) + +var toolsMount = corev1.VolumeMount{ + Name: MountName, + MountPath: MountPoint, +} + +// Cache is a simple caching mechanism allowing for caching the results of +// getting the Entrypoint of a container image from a remote registry. It +// is synchronized via a mutex so that we can share a single Cache across +// each worker thread that the reconciler is running. The mutex is necessary +// due to the possibility of a panic if two workers were to attempt to read and +// write to the internal map at the same time. +type Cache struct { + mtx sync.RWMutex + cache map[string][]string +} + +// NewCache is a simple helper function that returns a pointer to a Cache that +// has had the internal cache map initialized. +func NewCache() *Cache { + return &Cache{ + cache: make(map[string][]string), + } +} + +func (c *Cache) get(sha string) ([]string, bool) { + c.mtx.RLock() + ep, ok := c.cache[sha] + c.mtx.RUnlock() + return ep, ok +} + +func (c *Cache) set(sha string, ep []string) { + c.mtx.Lock() + c.cache[sha] = ep + c.mtx.Unlock() +} + +// AddCopyStep will prepend a BuildStep (Container) that will +// copy the entrypoint binary from the entrypoint image into the +// volume mounted at MountPoint, so that it can be mounted by +// subsequent steps and used to capture logs. +func AddCopyStep(b *v1alpha1.BuildSpec) { + cp := corev1.Container{ + Name: InitContainerName, + Image: Image, + Command: []string{"/bin/cp"}, + Args: []string{"/entrypoint", BinaryLocation}, + VolumeMounts: []corev1.VolumeMount{toolsMount}, + } + b.Steps = append([]corev1.Container{cp}, b.Steps...) + +} + +type entrypointArgs struct { + Args []string `json:"args"` + ProcessLog string `json:"process_log"` + MarkerFile string `json:"marker_file"` +} + +func getEnvVar(cmd, args []string) (string, error) { + entrypointArgs := entrypointArgs{ + Args: append(cmd, args...), + ProcessLog: ProcessLogFile, + MarkerFile: MarkerFile, + } + j, err := json.Marshal(entrypointArgs) + if err != nil { + return "", fmt.Errorf("couldn't marshal arguments %q for entrypoint env var: %s", entrypointArgs, err) + } + return string(j), nil +} + +// GetRemoteEntrypoint accepts a cache of image lookups, as well as the image +// to look for. If the cache does not contain the image, it will lookup the +// metadata from the images registry, and then commit that to the cache +func GetRemoteEntrypoint(cache *Cache, image string) ([]string, error) { + if ep, ok := cache.get(image); ok { + return ep, nil + } + // verify the image name, then download the remote config file + ref, err := name.ParseReference(image, name.WeakValidation) + if err != nil { + return nil, fmt.Errorf("couldn't parse image %s: %v", image, err) + } + img, err := remote.Image(ref, remote.WithAuthFromKeychain(authn.DefaultKeychain)) + if err != nil { + return nil, fmt.Errorf("couldn't get container image info from registry %s: %v", image, err) + } + cfg, err := img.ConfigFile() + if err != nil { + return nil, fmt.Errorf("couldn't get config for image %s: %v", image, err) + } + cache.set(image, cfg.ContainerConfig.Entrypoint) + return cfg.ContainerConfig.Entrypoint, nil +} + +// RedirectSteps will modify each of the steps/containers such that +// the binary being run is no longer the one specified by the Command +// and the Args, but is instead the entrypoint binary, which will +// itself invoke the Command and Args, but also capture logs. +func RedirectSteps(steps []corev1.Container) error { + for i := range steps { + step := &steps[i] + e, err := getEnvVar(step.Command, step.Args) + if err != nil { + return fmt.Errorf("couldn't get env var for entrypoint: %s", err) + } + step.Command = []string{BinaryLocation} + step.Args = []string{} + + step.Env = append(step.Env, corev1.EnvVar{ + Name: JSONConfigEnvVar, + Value: e, + }) + step.VolumeMounts = append(step.VolumeMounts, toolsMount) + } + return nil +} diff --git a/pkg/reconciler/v1alpha1/taskrun/entrypoint/entrypoint_test.go b/pkg/reconciler/v1alpha1/taskrun/entrypoint/entrypoint_test.go new file mode 100644 index 00000000000..157b067c29d --- /dev/null +++ b/pkg/reconciler/v1alpha1/taskrun/entrypoint/entrypoint_test.go @@ -0,0 +1,103 @@ +package entrypoint_test + +import ( + "github.com/knative/build-pipeline/pkg/reconciler/v1alpha1/taskrun/entrypoint" + "github.com/knative/build/pkg/apis/build/v1alpha1" + "k8s.io/api/core/v1" + "testing" +) +const ( + kanikoImage = "gcr.io/kaniko-project/executor" + kanikoEntrypoint = "/kaniko/executor" +) + +func TestAddEntrypoint(t *testing.T) { + inputs := []v1.Container{ + { + Image: kanikoImage, + }, + { + Image: kanikoImage, + Args: []string{"abcd"}, + }, + { + Image: kanikoImage, + Command: []string{"abcd"}, + Args: []string{"efgh"}, + }, + } + // The first test case showcases the downloading of the entrypoint for the + // image. The second test shows downloading the image as well as the args + // being passed in. The third command shows a set Command overriding the + // remote one. + envVarStrings := []string{ + `{"args":null,"process_log":"/tools/process-log.txt","marker_file":"/tools/marker-file.txt"}`, + `{"args":["abcd"],"process_log":"/tools/process-log.txt","marker_file":"/tools/marker-file.txt"}`, + `{"args":["abcd","efgh"],"process_log":"/tools/process-log.txt","marker_file":"/tools/marker-file.txt"}`, + + } + err := entrypoint.RedirectSteps(inputs) + if err != nil { + t.Errorf("failed to get resources: %v", err) + } + for i, input := range inputs { + if len(input.Command) == 0 || input.Command[0] != entrypoint.BinaryLocation { + t.Errorf("command incorrectly set: %q", input.Command) + } + if len(input.Args) > 0 { + t.Errorf("containers should have no args") + } + if len(input.Env) == 0 { + t.Error("there should be atleast one envvar") + } + for _, e := range input.Env { + if e.Name == entrypoint.JSONConfigEnvVar && e.Value != envVarStrings[i] { + t.Errorf("envvar \n%s\n does not match \n%s", e.Value, envVarStrings[i]) + } + } + found := false + for _, vm := range input.VolumeMounts { + if vm.Name == entrypoint.MountName { + found = true + break + } + } + if !found { + t.Error("could not find tools volume mount") + } + } +} + +func TestGetRemoteEntrypoint(t *testing.T) { + ep, err := entrypoint.GetRemoteEntrypoint(entrypoint.NewCache(), kanikoImage) + if err != nil { + t.Errorf("couldn't get entrypoint remote: %v", err) + } + if len(ep) != 1 { + t.Errorf("remote entrypoint should only have one item") + } + if ep[0] != kanikoEntrypoint { + t.Errorf("entrypoints do not match: %s should be %s", ep[0], kanikoEntrypoint) + } +} + +func TestAddCopyStep(t *testing.T) { + bs := &v1alpha1.BuildSpec{ + Steps: []v1.Container{ + { + Name: "test", + }, + { + Name: "test", + }, + }, + } + expectedSteps := len(bs.Steps) + 1 + entrypoint.AddCopyStep(bs) + if len(bs.Steps) != 3 { + t.Errorf("BuildSpec has the wrong step count: %d should be %d", len(bs.Steps), expectedSteps) + } + if bs.Steps[0].Name != entrypoint.InitContainerName { + t.Errorf("entrypoint is incorrect: %s should be %s", bs.Steps[0].Name, entrypoint.InitContainerName) + } +} diff --git a/pkg/reconciler/v1alpha1/taskrun/resources/entrypoint.go b/pkg/reconciler/v1alpha1/taskrun/resources/entrypoint.go deleted file mode 100644 index acdbf9222c4..00000000000 --- a/pkg/reconciler/v1alpha1/taskrun/resources/entrypoint.go +++ /dev/null @@ -1,103 +0,0 @@ -/* -Copyright 2018 The Knative 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 ( - "encoding/json" - "fmt" - - corev1 "k8s.io/api/core/v1" -) - -const ( - // MountName is the name of the pvc being mounted (which - // will contain the entrypoint binary and eventually the logs) - MountName = "tools" - - mountPoint = "/tools" - entrypointBin = mountPoint + "/entrypoint" - entrypointJSONConfigEnvVar = "ENTRYPOINT_OPTIONS" - EntrypointImage = "gcr.io/k8s-prow/entrypoint@sha256:7c7cd8906ce4982ffee326218e9fc75da2d4896d53cabc9833b9cc8d2d6b2b8f" -) - -var toolsMount = corev1.VolumeMount{ - Name: MountName, - MountPath: mountPoint, -} - -// GetCopyStep will return a Build Step (Container) that will -// copy the entrypoint binary from the entrypoint image into the -// volume mounted at mountPoint, so that it can be mounted by -// subsequent steps and used to capture logs. -func GetCopyStep() corev1.Container { - return corev1.Container{ - Name: "place-tools", - Image: EntrypointImage, - Command: []string{"/bin/cp"}, - Args: []string{"/entrypoint", entrypointBin}, - VolumeMounts: []corev1.VolumeMount{toolsMount}, - } -} - -type entrypointArgs struct { - Args []string `json:"args"` - ProcessLog string `json:"process_log"` - MarkerFile string `json:"marker_file"` -} - -func getEnvVar(cmd, args []string) (string, error) { - entrypointArgs := entrypointArgs{ - Args: append(cmd, args...), - ProcessLog: "/tools/process-log.txt", - MarkerFile: "/tools/marker-file.txt", - } - j, err := json.Marshal(entrypointArgs) - if err != nil { - return "", fmt.Errorf("couldn't marshal arguments %q for entrypoint env var: %s", entrypointArgs, err) - } - return string(j), nil -} - -// TODO: add more test cases after all, e.g. with existing env -// var and volume mounts - -// AddEntrypoint will modify each of the steps/containers such that -// the binary being run is no longer the one specified by the Command -// and the Args, but is instead the entrypoint binary, which will -// itself invoke the Command and Args, but also capture logs. -// TODO: This will not work when a step uses an image that has its -// own entrypoint, i.e. `Command` is a required field. In later iterations -// we can update the controller to inspect the image's `Entrypoint` -// and use that if required. -func AddEntrypoint(steps []corev1.Container) error { - for i := range steps { - step := &steps[i] - e, err := getEnvVar(step.Command, step.Args) - if err != nil { - return fmt.Errorf("couldn't get env var for entrypoint: %s", err) - } - step.Command = []string{entrypointBin} - step.Args = []string{} - - step.Env = append(step.Env, corev1.EnvVar{ - Name: entrypointJSONConfigEnvVar, - Value: e, - }) - step.VolumeMounts = append(step.VolumeMounts, toolsMount) - } - return nil -} diff --git a/pkg/reconciler/v1alpha1/taskrun/taskrun.go b/pkg/reconciler/v1alpha1/taskrun/taskrun.go index e80533bd6a7..e8471e6fdd6 100644 --- a/pkg/reconciler/v1alpha1/taskrun/taskrun.go +++ b/pkg/reconciler/v1alpha1/taskrun/taskrun.go @@ -25,6 +25,7 @@ import ( "github.com/knative/build-pipeline/pkg/apis/pipeline/v1alpha1" "github.com/knative/build-pipeline/pkg/reconciler" + "github.com/knative/build-pipeline/pkg/reconciler/v1alpha1/taskrun/entrypoint" "github.com/knative/build-pipeline/pkg/reconciler/v1alpha1/taskrun/resources" buildv1alpha1 "github.com/knative/build/pkg/apis/build/v1alpha1" buildinformers "github.com/knative/build/pkg/client/informers/externalversions/build/v1alpha1" @@ -186,7 +187,7 @@ func (c *Reconciler) reconcile(ctx context.Context, tr *v1alpha1.TaskRun) error return fmt.Errorf("Failed to create persistent volume claim %s for task %q: %v", tr.Name, err, tr.Name) } } else if err != nil { - c.Logger.Errorf("Failed to reconcile taskrun: %q, failed to get pvc %q; %v", tr.Name, tr.Name, err) + c.Logger.Errorf("Failed to reconcile taskrun: %q, failed to get pvc %q: %v", tr.Name, tr.Name, err) return err } @@ -300,47 +301,30 @@ func (c *Reconciler) createBuild(tr *v1alpha1.TaskRun, pvcName string) (*buildv1 return nil, fmt.Errorf("task %s has nil BuildSpec", t.Name) } + // For each step with no entrypoint set, try to populate it with the info + // from the remote registry + cache := entrypoint.NewCache() bSpec := t.Spec.BuildSpec.DeepCopy() - bSpec.Volumes = append(bSpec.Volumes, corev1.Volume{ - Name: resources.MountName, - VolumeSource: corev1.VolumeSource{ - PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ - ClaimName: pvcName, - }, - }, - }) - - // Override the entrypoint so that we can use our custom - // entrypoint which copies logs - err = resources.AddEntrypoint(bSpec.Steps) - if err != nil { - return nil, fmt.Errorf("Failed to add entrypoint to steps of Build: %s", err) + for i := range bSpec.Steps { + step := &bSpec.Steps[i] + if len(step.Command) == 0 { + ep, err := entrypoint.GetRemoteEntrypoint(cache, step.Image) + if err != nil { + return nil, fmt.Errorf("could not get entrypoint from registry for %s: %v", step.Image, err) + } + step.Command = ep + } } - // Add the step which will copy the entrypoint into the volume - // we are going to be using, so that all of the steps will have - // access to it. - bSpec.Steps = append([]corev1.Container{resources.GetCopyStep()}, bSpec.Steps...) - - b := &buildv1alpha1.Build{ - ObjectMeta: metav1.ObjectMeta{ - Name: tr.Name, - Namespace: tr.Namespace, - OwnerReferences: []metav1.OwnerReference{ - *metav1.NewControllerRef(tr, groupVersionKind), - }, - // Attach new label and pass taskrun labels to build - Labels: makeLabels(tr), - }, - Spec: *bSpec, + b, err := CreateRedirectedBuild(bSpec, pvcName, tr) + if err != nil { + return nil, fmt.Errorf("couldn't create redirected Build: %v", err) } - // Pass service account name from taskrun to build - // if task specifies service account name override with taskrun SA - b.Spec.ServiceAccountName = tr.Spec.ServiceAccount build, err := resources.AddInputResource(b, t, tr, c.resourceLister, c.Logger) if err != nil { - return nil, fmt.Errorf("failed to create a build for taskrun: %s due to input resource error %v", tr.Name, err) + c.Logger.Errorf("Failed to create a build for taskrun: %s due to input resource error %v", tr.Name, err) + return nil, err } var defaults []v1alpha1.TaskParam @@ -360,17 +344,51 @@ func (c *Reconciler) createBuild(tr *v1alpha1.TaskRun, pvcName string) (*buildv1 return nil, fmt.Errorf("couldnt apply output resource templating: %s", err) } - build.Labels[pipeline.GroupName+pipeline.TaskRunLabelKey] = tr.Name - // Only propagate the pipeline and pipelinerun keys if they are there. If - // the TaskRun was created via PipelineRun these should be there. - if val, ok := tr.Labels[pipeline.PipelineLabelKey]; ok { - build.Labels[pipeline.GroupName+pipeline.PipelineLabelKey] = val + return c.BuildClientSet.BuildV1alpha1().Builds(tr.Namespace).Create(build) +} + +// CreateRedirectedBuild takes a build, a persistent volume claim name, a taskrun and +// an entrypoint cache creates a build where all entrypoints are switched to +// be the entrypoint redirector binary. This function assumes that it receives +// its own copy of the BuildSpec and modifies it freely +func CreateRedirectedBuild(bs *buildv1alpha1.BuildSpec, pvcName string, tr *v1alpha1.TaskRun) (*buildv1alpha1.Build, error) { + bs.ServiceAccountName = tr.Spec.ServiceAccount + // RedirectSteps the entrypoint in each container so that we can use our custom + // entrypoint which copies logs to the volume + err := entrypoint.RedirectSteps(bs.Steps) + if err != nil { + return nil, fmt.Errorf("failed to add entrypoint to steps of TaskRun %s: %v", tr.Name, err) } - if val, ok := tr.Labels[pipeline.PipelineRunLabelKey]; ok { - build.Labels[pipeline.GroupName+pipeline.PipelineRunLabelKey] = val + // Add the step which will copy the entrypoint into the volume + // we are going to be using, so that all of the steps will have + // access to it. + entrypoint.AddCopyStep(bs) + b := &buildv1alpha1.Build{ + ObjectMeta: metav1.ObjectMeta{ + Name: tr.Name, + Namespace: tr.Namespace, + OwnerReferences: []metav1.OwnerReference{ + *metav1.NewControllerRef(tr, groupVersionKind), + }, + // Attach new label and pass taskrun labels to build + Labels: makeLabels(tr), + }, + Spec: *bs, } + // Add the volume used for storing the binary and logs + b.Spec.Volumes = append(b.Spec.Volumes, corev1.Volume{ + Name: entrypoint.MountName, + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: pvcName, + }, + }, + }) + // Pass service account name from taskrun to build + // if task specifies service account name override with taskrun SA + b.Spec.ServiceAccountName = tr.Spec.ServiceAccount - return c.BuildClientSet.BuildV1alpha1().Builds(tr.Namespace).Create(build) + return b, nil } // makeLabels constructs the labels we will apply to TaskRun resources. diff --git a/pkg/reconciler/v1alpha1/taskrun/taskrun_test.go b/pkg/reconciler/v1alpha1/taskrun/taskrun_test.go index 8dfae682a18..13128bfffd4 100644 --- a/pkg/reconciler/v1alpha1/taskrun/taskrun_test.go +++ b/pkg/reconciler/v1alpha1/taskrun/taskrun_test.go @@ -18,13 +18,14 @@ import ( "strings" "testing" + "github.com/knative/build-pipeline/pkg/reconciler/v1alpha1/taskrun/entrypoint" + "fmt" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/knative/build-pipeline/pkg/apis/pipeline/v1alpha1" "github.com/knative/build-pipeline/pkg/reconciler/v1alpha1/taskrun" - "github.com/knative/build-pipeline/pkg/reconciler/v1alpha1/taskrun/resources" "github.com/knative/build-pipeline/test" buildv1alpha1 "github.com/knative/build/pkg/apis/build/v1alpha1" duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" @@ -49,6 +50,7 @@ const ( entrypointLocation = "/tools/entrypoint" toolsMountName = "tools" pvcSizeBytes = 5 * 1024 * 1024 * 1024 // 5 GBs + kanikoImage = "gcr.io/kaniko-project/executor" ) var toolsMount = corev1.VolumeMount{ @@ -58,7 +60,7 @@ var toolsMount = corev1.VolumeMount{ var entrypointCopyStep = corev1.Container{ Name: "place-tools", - Image: resources.EntrypointImage, + Image: entrypoint.Image, Command: []string{"/bin/cp"}, Args: []string{"/entrypoint", entrypointLocation}, VolumeMounts: []corev1.VolumeMount{toolsMount}, @@ -152,9 +154,10 @@ var templatedTask = &v1alpha1.Task{ "--my-additional-arg=${outputs.resources.myimage.url}"}, }, { - Name: "myothercontainer", - Image: "myotherimage", - Args: []string{"--my-other-arg=${inputs.resources.workspace.url}"}, + Name: "myothercontainer", + Image: "myotherimage", + Command: []string{"/mycmd"}, + Args: []string{"--my-other-arg=${inputs.resources.workspace.url}"}, }, }, }, @@ -185,9 +188,10 @@ var defaultTemplatedTask = &v1alpha1.Task{ Args: []string{"--my-arg=${inputs.params.myarg}"}, }, { - Name: "myothercontainer", - Image: "myotherimage", - Args: []string{"--my-other-arg=${inputs.resources.git-resource.url}"}, + Name: "myothercontainer", + Image: "myotherimage", + Command: []string{"/mycmd"}, + Args: []string{"--my-other-arg=${inputs.resources.git-resource.url}"}, }, }, }, @@ -458,7 +462,7 @@ func TestReconcile(t *testing.T) { Env: []corev1.EnvVar{ { Name: "ENTRYPOINT_OPTIONS", - Value: `{"args":["--my-other-arg=https://foo.git"],"process_log":"/tools/process-log.txt","marker_file":"/tools/marker-file.txt"}`, + Value: `{"args":["/mycmd","--my-other-arg=https://foo.git"],"process_log":"/tools/process-log.txt","marker_file":"/tools/marker-file.txt"}`, }, }, VolumeMounts: []corev1.VolumeMount{toolsMount}, @@ -496,7 +500,7 @@ func TestReconcile(t *testing.T) { Env: []corev1.EnvVar{ { Name: "ENTRYPOINT_OPTIONS", - Value: `{"args":["--my-other-arg=https://foo.git"],"process_log":"/tools/process-log.txt","marker_file":"/tools/marker-file.txt"}`, + Value: `{"args":["/mycmd","--my-other-arg=https://foo.git"],"process_log":"/tools/process-log.txt","marker_file":"/tools/marker-file.txt"}`, }, }, VolumeMounts: []corev1.VolumeMount{toolsMount}, @@ -534,7 +538,7 @@ func TestReconcile(t *testing.T) { Env: []corev1.EnvVar{ { Name: "ENTRYPOINT_OPTIONS", - Value: `{"args":["--my-other-arg=https://foo.git"],"process_log":"/tools/process-log.txt","marker_file":"/tools/marker-file.txt"}`, + Value: `{"args":["/mycmd","--my-other-arg=https://foo.git"],"process_log":"/tools/process-log.txt","marker_file":"/tools/marker-file.txt"}`, }, }, VolumeMounts: []corev1.VolumeMount{toolsMount}, @@ -781,3 +785,46 @@ func TestReconcileBuildUpdateStatus(t *testing.T) { t.Errorf("Taskrun Status diff -want, +got: %v", d) } } + +func TestCreateRedirectedBuild(t *testing.T) { + tr := &v1alpha1.TaskRun{ + Spec: v1alpha1.TaskRunSpec{ + ServiceAccount: "sa", + }, + } + tr.Name = "tr" + tr.Namespace = "tr" + + bs := &buildv1alpha1.BuildSpec{ + Steps: []corev1.Container{ + { + Command: []string{"abcd"}, + Args: []string{"efgh"}, + }, + { + Command: []string{"abcd"}, + Args: []string{"efgh"}, + }, + }, + Volumes: []corev1.Volume{{Name: "v"}}, + } + expectedSteps := len(bs.Steps) + 1 + expectedVolumes := len(bs.Volumes) + 1 + + b, err := taskrun.CreateRedirectedBuild(bs, "pvc", tr) + if err != nil { + t.Errorf("expected CreateRedirectedBuild to pass: %v", err) + } + if b.Name != tr.Name { + t.Errorf("names do not match: %s should be %s", b.Name, tr.Name) + } + if len(b.Spec.Steps) != expectedSteps { + t.Errorf("step counts do not match: %d should be %d", len(b.Spec.Steps), expectedSteps) + } + if len(b.Spec.Volumes) != expectedVolumes { + t.Errorf("volumes do not match: %d should be %d", len(b.Spec.Volumes), expectedVolumes) + } + if b.Spec.ServiceAccountName != tr.Spec.ServiceAccount { + t.Errorf("services accounts do not match: %s should be %s", b.Spec.ServiceAccountName, tr.Spec.ServiceAccount) + } +} diff --git a/pkg/reconciler/v1alpha1/taskrun/validate_test.go b/pkg/reconciler/v1alpha1/taskrun/validate_test.go index 908fa4dd0fe..b7d28cdbe9f 100644 --- a/pkg/reconciler/v1alpha1/taskrun/validate_test.go +++ b/pkg/reconciler/v1alpha1/taskrun/validate_test.go @@ -16,8 +16,9 @@ import ( var validBuild = &buildv1alpha1.BuildSpec{ Steps: []corev1.Container{ { - Name: "mystep", - Image: "myimage", + Name: "mystep", + Image: "myimage", + Command: []string{"mycmd"}, }, }, } diff --git a/test/crd.go b/test/crd.go index a378b36301b..e3243f19b9e 100644 --- a/test/crd.go +++ b/test/crd.go @@ -161,3 +161,4 @@ func getBuildOutputFromVolume(logger *logging.BaseLogger, c *clients, namespace, _, err = io.Copy(out, readCloser) return buf.String(), nil } + diff --git a/test/helm_task_test.go b/test/helm_task_test.go index 75a0fbb04e6..b9a14923638 100644 --- a/test/helm_task_test.go +++ b/test/helm_task_test.go @@ -195,7 +195,6 @@ func getCreateImageTask(namespace string, t *testing.T) *v1alpha1.Task { Steps: []corev1.Container{{ Name: "kaniko", Image: "gcr.io/kaniko-project/executor", - Command: []string{"/kaniko/executor"}, Args: []string{"--dockerfile=/workspace/test/gohelloworld/Dockerfile", fmt.Sprintf("--destination=%s", imageName), }, @@ -232,12 +231,10 @@ func getHelmDeployTask(namespace string) *v1alpha1.Task { Steps: []corev1.Container{{ Name: "helm-init", Image: "alpine/helm", - Command: []string{"helm"}, Args: []string{"init", "--wait"}, }, { Name: "helm-deploy", Image: "alpine/helm", - Command: []string{"helm"}, Args: []string{"install", "--debug", "--name=${inputs.params.chartname}", @@ -487,7 +484,6 @@ func removeHelmFromCluster(c *clients, t *testing.T, namespace string, logger *l Steps: []corev1.Container{{ Name: "helm-reset", Image: "alpine/helm", - Command: []string{"helm"}, Args: []string{"reset", "--force"}, }, }, diff --git a/test/kaniko_task_test.go b/test/kaniko_task_test.go index a626b59c1c9..5aae426cd1f 100644 --- a/test/kaniko_task_test.go +++ b/test/kaniko_task_test.go @@ -122,7 +122,6 @@ func getTask(repo, namespace string, withSecretConfig bool) *v1alpha1.Task { step := corev1.Container{ Name: "kaniko", Image: "gcr.io/kaniko-project/executor", - Command: []string{"/kaniko/executor"}, Args: []string{"--dockerfile=/workspace/Dockerfile", fmt.Sprintf("--destination=%s", repo), },