From 2b81f15f5fd0b6c48bfdcd04b08438d9595c1019 Mon Sep 17 00:00:00 2001 From: Aaron Prindle Date: Mon, 12 Nov 2018 15:59:58 -0800 Subject: [PATCH] no init containers --- pkg/reconciler/build/resources/pod.go | 145 ++++++++-- pkg/reconciler/build/resources/pod_test.go | 292 +++++++++++---------- test/e2e/simple_test.go | 1 - 3 files changed, 289 insertions(+), 149 deletions(-) diff --git a/pkg/reconciler/build/resources/pod.go b/pkg/reconciler/build/resources/pod.go index 5fc1bb26..ae136856 100644 --- a/pkg/reconciler/build/resources/pod.go +++ b/pkg/reconciler/build/resources/pod.go @@ -28,7 +28,11 @@ import ( "io/ioutil" "path/filepath" "strconv" + "sync" + "github.com/google/go-containerregistry/pkg/authn" + "github.com/google/go-containerregistry/pkg/name" + "github.com/google/go-containerregistry/pkg/v1/remote" v1alpha1 "github.com/knative/build/pkg/apis/build/v1alpha1" "github.com/knative/build/pkg/credentials" "github.com/knative/build/pkg/credentials/dockercreds" @@ -258,8 +262,9 @@ func MakePod(build *v1alpha1.Build, kubeclient kubernetes.Interface) (*corev1.Po var sources []v1alpha1.SourceSpec // if source is present convert into sources - // NOTES(aaron-prindle) adds custom steps outside of user Steps for git, logs, etc - podContainers := []corev1.Container{*cred} + initContainers := []corev1.Container{*cred} + podContainers := []corev1.Container{} + if source := build.Spec.Source; source != nil { sources = []v1alpha1.SourceSpec{*source} } @@ -275,26 +280,37 @@ func MakePod(build *v1alpha1.Build, kubeclient kubernetes.Interface) (*corev1.Po if err != nil { return nil, err } - podContainers = append(podContainers, *git) + initContainers = append(initContainers, *git) case source.GCS != nil: gcs, err := gcsToContainer(source, i) if err != nil { return nil, err } - podContainers = append(podContainers, *gcs) + initContainers = append(initContainers, *gcs) case source.Custom != nil: cust, err := customToContainer(source.Custom, source.Name) if err != nil { return nil, err } // Prepend the custom container to the steps, to be augmented later with env, volume mounts, etc. + build.Spec.Steps = append([]corev1.Container{*cust}, build.Spec.Steps...) } // webhook validation checks that only one source has subPath defined workspaceSubPath = source.SubPath } - // NOTES(aaron-prindle) setup volume mounts for steps + // init container that copies entrypoint binary into shared volume + // to be used by all other containers w/ entrypoint rewriting + initContainers = append(initContainers, + corev1.Container{ + Name: InitContainerName, + Image: DefaultEntrypointImage, + Command: []string{"/bin/cp"}, + Args: []string{"/entrypoint", BinaryLocation}, + VolumeMounts: []corev1.VolumeMount{toolsMount}, + }) + for i, step := range build.Spec.Steps { step.Env = append(implicitEnvVars, step.Env...) // TODO(mattmoor): Check that volumeMounts match volumes. @@ -332,6 +348,7 @@ func MakePod(build *v1alpha1.Build, kubeclient kubernetes.Interface) (*corev1.Po // declared user volumes. volumes := append(build.Spec.Volumes, implicitVolumes...) volumes = append(volumes, secrets...) + volumes = append(volumes, toolsVolume) if err := v1alpha1.ValidateVolumes(volumes); err != nil { return nil, err } @@ -342,7 +359,7 @@ func MakePod(build *v1alpha1.Build, kubeclient kubernetes.Interface) (*corev1.Po return nil, err } gibberish := hex.EncodeToString(b) - // entrypoint.RedirectSteps(podContainers) + RedirectSteps(podContainers) return &corev1.Pod{ @@ -373,6 +390,7 @@ func MakePod(build *v1alpha1.Build, kubeclient kubernetes.Interface) (*corev1.Po Spec: corev1.PodSpec{ // If the build fails, don't restart it. RestartPolicy: corev1.RestartPolicyNever, + InitContainers: initContainers, Containers: podContainers, ServiceAccountName: build.Spec.ServiceAccountName, Volumes: volumes, @@ -497,8 +515,15 @@ const ( BinaryLocation = MountPoint + "/entrypoint" JSONConfigEnvVar = "ENTRYPOINT_OPTIONS" InitContainerName = "place-tools" - ProcessLogFile = "/tools/process-log.txt" - MarkerFile = "/tools/marker-file.txt" + // TODO(aaron-prindle) change this to wherever is sensible + DefaultEntrypointImage = "gcr.io/aprindle-vm-test/entrypoint:latest" + + ProcessLogFile = "/tools/process-log.txt" + MarkerFile = "/tools/marker-file.txt" + ShouldWaitForPrevStep = false + PreRunFile = "0" + ShouldRunPostRun = true + PostRunFile = "0" ) var toolsMount = corev1.VolumeMount{ @@ -506,23 +531,104 @@ var toolsMount = corev1.VolumeMount{ MountPath: MountPoint, } +var toolsVolume = corev1.Volume{ + Name: MountName, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, +} + type entrypointArgs struct { Args []string `json:"args"` ProcessLog string `json:"process_log"` MarkerFile string `json:"marker_file"` + + ShouldWaitForPrevStep bool `json:"shouldWaitForPrevStep"` + PreRunFile string `json:"preRunFile"` + ShouldRunPostRun bool `json:"shouldRunPostRun"` + PostRunFile string `json:"postRunFile"` +} + +// 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() } +// 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 +} + +// TODO(aaron-prindle) setup the cache properly +var cache = NewCache() + // 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 each step with no entrypoint set, try to populate it with the info + // from the remote registry for i := range steps { step := &steps[i] - e, err := getEnvVar(step.Command, step.Args) + if len(step.Command) == 0 { + ep, err := GetRemoteEntrypoint(cache, step.Image) + if err != nil { + return fmt.Errorf("could not get entrypoint from registry for %s: %v", step.Image, err) + } + step.Command = ep + } + e, err := getEnvVar(step.Command, step.Args, i) if err != nil { return fmt.Errorf("couldn't get env var for entrypoint: %s", err) } + step.Command = []string{BinaryLocation} step.Args = []string{} @@ -535,16 +641,25 @@ func RedirectSteps(steps []corev1.Container) error { return nil } -func getEnvVar(cmd, args []string) (string, error) { +func getEnvVar(cmd, args []string, stepNumber int) (string, error) { + shouldWaitForPrevStep := ShouldWaitForPrevStep + // TODO(aaron-prindle) modify ShouldRunPostRun to not run on last step + if stepNumber != 0 { + shouldWaitForPrevStep = true + } + entrypointArgs := entrypointArgs{ - Args: append(cmd, args...), - ProcessLog: ProcessLogFile, - MarkerFile: MarkerFile, - // TODO(aaron-prindle) add the new options here + Args: append(cmd, args...), + ProcessLog: ProcessLogFile, + MarkerFile: MarkerFile, + ShouldWaitForPrevStep: shouldWaitForPrevStep, + PreRunFile: filepath.Join(MountPoint, strconv.Itoa(stepNumber)), + ShouldRunPostRun: ShouldRunPostRun, + PostRunFile: filepath.Join(MountPoint, strconv.Itoa(stepNumber+1)), } j, err := json.Marshal(entrypointArgs) if err != nil { - return "", fmt.Errorf("couldn't marshal arguments %q for entrypoint env var: %s", entrypointArgs, err) + return "", fmt.Errorf("couldn't marshal arguments %v for entrypoint env var: %s", entrypointArgs, err) } return string(j), nil } diff --git a/pkg/reconciler/build/resources/pod_test.go b/pkg/reconciler/build/resources/pod_test.go index 40343bf4..2b29dc03 100644 --- a/pkg/reconciler/build/resources/pod_test.go +++ b/pkg/reconciler/build/resources/pod_test.go @@ -18,6 +18,7 @@ package resources import ( "crypto/rand" + "fmt" "strings" "testing" @@ -37,12 +38,16 @@ var ( ignorePrivateResourceFields = cmpopts.IgnoreUnexported(resource.Quantity{}) ignoreVolatileTime = cmp.Comparer(func(_, _ apis.VolatileTime) bool { return true }) ignoreVolatileTimePtr = cmp.Comparer(func(_, _ *apis.VolatileTime) bool { return true }) - nopContainer = corev1.Container{ - Name: "nop", - Image: *nopImage, - } ) +var entrypointContainer = corev1.Container{ + Name: InitContainerName, + Image: DefaultEntrypointImage, + Command: []string{"/bin/cp"}, + Args: []string{"/entrypoint", BinaryLocation}, + VolumeMounts: []corev1.VolumeMount{toolsMount}, +} + func TestMakePod(t *testing.T) { subPath := "subpath" implicitVolumeMountsWithSubPath := []corev1.VolumeMount{} @@ -80,7 +85,7 @@ func TestMakePod(t *testing.T) { b: v1alpha1.BuildSpec{ Steps: []corev1.Container{{ Name: "name", - Image: "image", + Image: "gcr.io/kaniko-project/executor", }}, }, want: &corev1.PodSpec{ @@ -92,15 +97,15 @@ func TestMakePod(t *testing.T) { Env: implicitEnvVars, VolumeMounts: implicitVolumeMounts, WorkingDir: workspaceDir, - }, { + }}, + Containers: []corev1.Container{{ Name: "build-step-name", - Image: "image", + Image: "gcr.io/kaniko-project/executor", Env: implicitEnvVars, VolumeMounts: implicitVolumeMounts, WorkingDir: workspaceDir, }}, - Containers: []corev1.Container{nopContainer}, - Volumes: implicitVolumes, + Volumes: implicitVolumes, }, }, { desc: "source", @@ -113,7 +118,7 @@ func TestMakePod(t *testing.T) { }, Steps: []corev1.Container{{ Name: "name", - Image: "image", + Image: "gcr.io/kaniko-project/executor", }}, }, want: &corev1.PodSpec{ @@ -132,15 +137,15 @@ func TestMakePod(t *testing.T) { Env: implicitEnvVars, VolumeMounts: implicitVolumeMounts, WorkingDir: workspaceDir, - }, { + }}, + Containers: []corev1.Container{{ Name: "build-step-name", - Image: "image", + Image: "gcr.io/kaniko-project/executor", Env: implicitEnvVars, VolumeMounts: implicitVolumeMounts, WorkingDir: workspaceDir, }}, - Containers: []corev1.Container{nopContainer}, - Volumes: implicitVolumes, + Volumes: implicitVolumes, }, }, { desc: "sources", @@ -160,7 +165,7 @@ func TestMakePod(t *testing.T) { }}, Steps: []corev1.Container{{ Name: "name", - Image: "image", + Image: "gcr.io/kaniko-project/executor", }}, }, want: &corev1.PodSpec{ @@ -186,15 +191,15 @@ func TestMakePod(t *testing.T) { Env: implicitEnvVars, VolumeMounts: implicitVolumeMounts, WorkingDir: workspaceDir, - }, { + }}, + Containers: []corev1.Container{{ Name: "build-step-name", - Image: "image", + Image: "gcr.io/kaniko-project/executor", Env: implicitEnvVars, VolumeMounts: implicitVolumeMounts, WorkingDir: workspaceDir, }}, - Containers: []corev1.Container{nopContainer}, - Volumes: implicitVolumes, + Volumes: implicitVolumes, }, }, { desc: "git-source-with-subpath", @@ -208,7 +213,7 @@ func TestMakePod(t *testing.T) { }, Steps: []corev1.Container{{ Name: "name", - Image: "image", + Image: "gcr.io/kaniko-project/executor", }}, }, want: &corev1.PodSpec{ @@ -227,15 +232,15 @@ func TestMakePod(t *testing.T) { Env: implicitEnvVars, VolumeMounts: implicitVolumeMounts, // without subpath WorkingDir: workspaceDir, - }, { + }}, + Containers: []corev1.Container{{ Name: "build-step-name", - Image: "image", + Image: "gcr.io/kaniko-project/executor", Env: implicitEnvVars, VolumeMounts: implicitVolumeMountsWithSubPath, WorkingDir: workspaceDir, }}, - Containers: []corev1.Container{nopContainer}, - Volumes: implicitVolumes, + Volumes: implicitVolumes, }, }, { desc: "git-sources-with-subpath", @@ -257,7 +262,7 @@ func TestMakePod(t *testing.T) { }}, Steps: []corev1.Container{{ Name: "name", - Image: "image", + Image: "gcr.io/kaniko-project/executor", }}, }, want: &corev1.PodSpec{ @@ -283,15 +288,15 @@ func TestMakePod(t *testing.T) { Env: implicitEnvVars, VolumeMounts: implicitVolumeMounts, // without subpath WorkingDir: workspaceDir, - }, { + }}, + Containers: []corev1.Container{{ Name: "build-step-name", - Image: "image", + Image: "gcr.io/kaniko-project/executor", Env: implicitEnvVars, VolumeMounts: implicitVolumeMountsWithSubPath, WorkingDir: workspaceDir, }}, - Containers: []corev1.Container{nopContainer}, - Volumes: implicitVolumes, + Volumes: implicitVolumes, }, }, { desc: "gcs-source-with-subpath", @@ -305,7 +310,7 @@ func TestMakePod(t *testing.T) { }, Steps: []corev1.Container{{ Name: "name", - Image: "image", + Image: "gcr.io/kaniko-project/executor", }}, }, want: &corev1.PodSpec{ @@ -324,121 +329,124 @@ func TestMakePod(t *testing.T) { Env: implicitEnvVars, VolumeMounts: implicitVolumeMounts, // without subpath WorkingDir: workspaceDir, - }, { + }}, + Containers: []corev1.Container{{ Name: "build-step-name", - Image: "image", + Image: "gcr.io/kaniko-project/executor", Env: implicitEnvVars, VolumeMounts: implicitVolumeMountsWithSubPath, WorkingDir: workspaceDir, }}, - Containers: []corev1.Container{nopContainer}, - Volumes: implicitVolumes, + Volumes: implicitVolumes, }, - }, { - desc: "gcs-source-with-targetPath", - b: v1alpha1.BuildSpec{ - Source: &v1alpha1.SourceSpec{ - GCS: &v1alpha1.GCSSourceSpec{ - Type: v1alpha1.GCSManifest, - Location: "gs://foo/bar", + }, + { + desc: "gcs-source-with-targetPath", + b: v1alpha1.BuildSpec{ + Source: &v1alpha1.SourceSpec{ + GCS: &v1alpha1.GCSSourceSpec{ + Type: v1alpha1.GCSManifest, + Location: "gs://foo/bar", + }, + TargetPath: "path/foo", }, - TargetPath: "path/foo", }, - }, - want: &corev1.PodSpec{ - RestartPolicy: corev1.RestartPolicyNever, - InitContainers: []corev1.Container{{ - Name: initContainerPrefix + credsInit, - Image: *credsImage, - Args: []string{}, - Env: implicitEnvVars, - VolumeMounts: implicitVolumeMounts, // without subpath - WorkingDir: workspaceDir, - }, { - Name: initContainerPrefix + gcsSource + "-0", - Image: *gcsFetcherImage, - Args: []string{"--type", "Manifest", "--location", "gs://foo/bar", "--dest_dir", "/workspace/path/foo"}, - Env: implicitEnvVars, - VolumeMounts: implicitVolumeMounts, // without subpath - WorkingDir: workspaceDir, - }}, - Containers: []corev1.Container{nopContainer}, - Volumes: implicitVolumes, - }, - }, { - desc: "custom-source-with-subpath", - b: v1alpha1.BuildSpec{ - Source: &v1alpha1.SourceSpec{ - Custom: &corev1.Container{ - Image: "image", - }, - SubPath: subPath, + want: &corev1.PodSpec{ + RestartPolicy: corev1.RestartPolicyNever, + InitContainers: []corev1.Container{{ + Name: initContainerPrefix + credsInit, + Image: *credsImage, + Args: []string{}, + Env: implicitEnvVars, + VolumeMounts: implicitVolumeMounts, // without subpath + WorkingDir: workspaceDir, + }, { + Name: initContainerPrefix + gcsSource + "-0", + Image: *gcsFetcherImage, + Args: []string{"--type", "Manifest", "--location", "gs://foo/bar", "--dest_dir", "/workspace/path/foo"}, + Env: implicitEnvVars, + VolumeMounts: implicitVolumeMounts, // without subpath + WorkingDir: workspaceDir, + }}, + Containers: []corev1.Container{}, + Volumes: implicitVolumes, }, - Steps: []corev1.Container{{ - Name: "name", - Image: "image", - }}, - }, - want: &corev1.PodSpec{ - RestartPolicy: corev1.RestartPolicyNever, - InitContainers: []corev1.Container{{ - Name: initContainerPrefix + credsInit, - Image: *credsImage, - Args: []string{}, - Env: implicitEnvVars, - VolumeMounts: implicitVolumeMounts, // without subpath - WorkingDir: workspaceDir, - }, { - Name: initContainerPrefix + customSource, - Image: "image", - Env: implicitEnvVars, - VolumeMounts: implicitVolumeMountsWithSubPath, // *with* subpath - WorkingDir: workspaceDir, - }, { - Name: "build-step-name", - Image: "image", - Env: implicitEnvVars, - VolumeMounts: implicitVolumeMountsWithSubPath, - WorkingDir: workspaceDir, - }}, - Containers: []corev1.Container{nopContainer}, - Volumes: implicitVolumes, - }, - }, { - desc: "with-service-account", - b: v1alpha1.BuildSpec{ - ServiceAccountName: "service-account", - Steps: []corev1.Container{{ - Name: "name", - Image: "image", - }}, }, - want: &corev1.PodSpec{ - ServiceAccountName: "service-account", - RestartPolicy: corev1.RestartPolicyNever, - InitContainers: []corev1.Container{{ - Name: initContainerPrefix + credsInit, - Image: *credsImage, - Args: []string{ - "-basic-docker=multi-creds=https://docker.io", - "-basic-docker=multi-creds=https://us.gcr.io", - "-basic-git=multi-creds=github.com", - "-basic-git=multi-creds=gitlab.com", + { + desc: "custom-source-with-subpath", + b: v1alpha1.BuildSpec{ + Source: &v1alpha1.SourceSpec{ + Custom: &corev1.Container{ + Image: "gcr.io/kaniko-project/executor", + }, + SubPath: subPath, }, - Env: implicitEnvVars, - VolumeMounts: implicitVolumeMountsWithSecrets, - WorkingDir: workspaceDir, - }, { - Name: "build-step-name", - Image: "image", - Env: implicitEnvVars, - VolumeMounts: implicitVolumeMounts, - WorkingDir: workspaceDir, - }}, - Containers: []corev1.Container{nopContainer}, - Volumes: implicitVolumesWithSecrets, + Steps: []corev1.Container{{ + Name: "name", + Image: "gcr.io/kaniko-project/executor", + }}, + }, + want: &corev1.PodSpec{ + RestartPolicy: corev1.RestartPolicyNever, + InitContainers: []corev1.Container{{ + Name: initContainerPrefix + credsInit, + Image: *credsImage, + Args: []string{}, + Env: implicitEnvVars, + VolumeMounts: implicitVolumeMounts, // without subpath + WorkingDir: workspaceDir, + }}, + Containers: []corev1.Container{{ + Name: initContainerPrefix + customSource, + Image: "gcr.io/kaniko-project/executor", + Env: implicitEnvVars, + VolumeMounts: implicitVolumeMountsWithSubPath, // *with* subpath + WorkingDir: workspaceDir, + }, { + Name: "build-step-name", + Image: "gcr.io/kaniko-project/executor", + Env: implicitEnvVars, + VolumeMounts: implicitVolumeMountsWithSubPath, + WorkingDir: workspaceDir, + }}, + Volumes: implicitVolumes, + }, }, - }} { + { + desc: "with-service-account", + b: v1alpha1.BuildSpec{ + ServiceAccountName: "service-account", + Steps: []corev1.Container{{ + Name: "name", + Image: "gcr.io/kaniko-project/executor", + }}, + }, + want: &corev1.PodSpec{ + ServiceAccountName: "service-account", + RestartPolicy: corev1.RestartPolicyNever, + InitContainers: []corev1.Container{{ + Name: initContainerPrefix + credsInit, + Image: *credsImage, + Args: []string{ + "-basic-docker=multi-creds=https://docker.io", + "-basic-docker=multi-creds=https://us.gcr.io", + "-basic-git=multi-creds=github.com", + "-basic-git=multi-creds=gitlab.com", + }, + Env: implicitEnvVars, + VolumeMounts: implicitVolumeMountsWithSecrets, + WorkingDir: workspaceDir, + }}, + Containers: []corev1.Container{{ + Name: "build-step-name", + Image: "gcr.io/kaniko-project/executor", + Env: implicitEnvVars, + VolumeMounts: implicitVolumeMounts, + WorkingDir: workspaceDir, + }}, + Volumes: implicitVolumesWithSecrets, + }, + }} { t.Run(c.desc, func(t *testing.T) { cs := fakek8s.NewSimpleClientset( &corev1.ServiceAccount{ObjectMeta: metav1.ObjectMeta{Name: "default"}}, @@ -472,6 +480,24 @@ func TestMakePod(t *testing.T) { if err != c.wantErr { t.Fatalf("MakePod: %v", err) } + c.want.InitContainers = append(c.want.InitContainers, entrypointContainer) + c.want.Volumes = append(c.want.Volumes, toolsVolume) + + for i := range c.want.Containers { + c.want.Containers[i].Command = []string{"/tools/entrypoint"} + c.want.Containers[i].VolumeMounts = append( + c.want.Containers[i].VolumeMounts, toolsMount) + shouldWaitForPrevStep := false + if i > 0 { + shouldWaitForPrevStep = true + } + c.want.Containers[i].Env = append( + c.want.Containers[i].Env, corev1.EnvVar{Name: "ENTRYPOINT_OPTIONS", + Value: fmt.Sprintf(`{"args":["/kaniko/executor"],"process_log":"/tools/process-log.txt","marker_file":"/tools/marker-file.txt","shouldWaitForPrevStep":%t,"preRunFile":"/tools/%d","shouldRunPostRun":true,"postRunFile":"/tools/%d"}`, + shouldWaitForPrevStep, i, i+1)}, + ) + c.want.Containers[i].Args = []string{} + } // Generated name from hexlifying a stream of 'a's. wantName := "build-name-pod-616161" diff --git a/test/e2e/simple_test.go b/test/e2e/simple_test.go index 8465f376..2c5d0c03 100644 --- a/test/e2e/simple_test.go +++ b/test/e2e/simple_test.go @@ -279,7 +279,6 @@ func TestBuildLowTimeout(t *testing.T) { b, err := clients.buildClient.watchBuild(buildName) if err != errBuildFailed { t.Fatalf("watchBuild got %v, want %v (build status: %+v)", err, errBuildFailed, b.Status) - } if d := cmp.Diff(b.Status.GetCondition(duckv1alpha1.ConditionSucceeded), &duckv1alpha1.Condition{ Type: duckv1alpha1.ConditionSucceeded,