From c85cf2fb45be6658ad1fcdda701301c437d89660 Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Fri, 25 Jun 2021 16:06:44 -0400 Subject: [PATCH] Emit events for BuildRun objects Enhance the BuildRun controller to emit k8s events when build runs start and complete. If a BuildRun fails, a Warning event is issued. Includes updates to the BuildRun documentation. --- deploy/200-role.yaml | 5 ++ docs/buildrun.md | 7 +++ pkg/reconciler/buildrun/buildrun.go | 69 +++++++++++++++------ pkg/reconciler/buildrun/buildrun_test.go | 48 ++++++++++++-- pkg/reconciler/buildrun/resources/errors.go | 6 +- test/catalog.go | 13 +++- test/e2e/validators_test.go | 9 +++ test/utils/environment.go | 17 +++++ test/utils/events.go | 14 +++++ 9 files changed, 159 insertions(+), 29 deletions(-) create mode 100644 test/utils/events.go diff --git a/deploy/200-role.yaml b/deploy/200-role.yaml index a583ebea91..b12368fb21 100644 --- a/deploy/200-role.yaml +++ b/deploy/200-role.yaml @@ -74,3 +74,8 @@ rules: - apiGroups: [''] resources: ['serviceaccounts'] verbs: ['get', 'list', 'watch', 'create', 'update', 'delete'] + +# Allow events to be created in any namespace related to BuildRun objects +- apiGroups: [''] + resources: ['events'] + verbs: ['create'] diff --git a/docs/buildrun.md b/docs/buildrun.md index c1a0d08960..4ddaefc6a9 100644 --- a/docs/buildrun.md +++ b/docs/buildrun.md @@ -15,6 +15,8 @@ SPDX-License-Identifier: Apache-2.0 - [BuildRun Status](#buildrun-status) - [Understanding the state of a BuildRun](#understanding-the-state-of-a-BuildRun) - [Understanding failed BuildRuns](#understanding-failed-buildruns) + - [Build Snapshot](#build-snapshot) + - [Events](#events) - [Relationship with Tekton Tasks](#relationship-with-tekton-tasks) ## Overview @@ -193,6 +195,11 @@ In addition, the `Status.Conditions` will host under the `Message` field a compa For every BuildRun controller reconciliation, the `buildSpec` in the Status of the `BuildRun` is updated if an existing owned `TaskRun` is present. During this update, a `Build` resource snapshot is generated and embedded into the `status.buildSpec` path of the `BuildRun`. A `buildSpec` is just a copy of the original `Build` spec, from where the `BuildRun` executed a particular image build. The snapshot approach allows developers to see the original `Build` configuration. +### Events + +The BuildRun controller will emit Kubernetes events when a BuildRun starts and completes. +If the BuildRun ends in a failure, a `Warning` event is emitted; otherwise a `Normal` event is emitted. + ## Relationship with Tekton Tasks The `BuildRun` resource abstracts the image construction by delegating this work to the Tekton Pipeline [TaskRun](https://github.com/tektoncd/pipeline/blob/main/docs/taskruns.md). Compared to a Tekton Pipeline [Task](https://github.com/tektoncd/pipeline/blob/main/docs/tasks.md), a `TaskRun` runs all `steps` until completion of the `Task` or until a failure occurs in the `Task`. diff --git a/pkg/reconciler/buildrun/buildrun.go b/pkg/reconciler/buildrun/buildrun.go index 3693422f01..d6395cffc1 100644 --- a/pkg/reconciler/buildrun/buildrun.go +++ b/pkg/reconciler/buildrun/buildrun.go @@ -17,6 +17,7 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/tools/record" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/reconcile" @@ -46,6 +47,7 @@ type ReconcileBuildRun struct { client client.Client scheme *runtime.Scheme setOwnerReferenceFunc setOwnerReferenceFunc + eventRecorder record.EventRecorder } // NewReconciler returns a new reconcile.Reconciler @@ -56,6 +58,7 @@ func NewReconciler(ctx context.Context, c *config.Config, mgr manager.Manager, o client: mgr.GetClient(), scheme: mgr.GetScheme(), setOwnerReferenceFunc: ownerRef, + eventRecorder: mgr.GetEventRecorderFor("buildruns"), } } @@ -111,11 +114,8 @@ func (r *ReconcileBuildRun) Reconcile(request reconcile.Request) (reconcile.Resu // stop reconciling and mark the BuildRun as Failed // we only reconcile again if the status.Update call fails message := fmt.Sprintf("the Build is not registered correctly, build: %s, registered status: %s, reason: %s", build.Name, build.Status.Registered, build.Status.Reason) - if updateErr := resources.UpdateConditionWithFalseStatus(ctx, r.client, buildRun, message, resources.ConditionBuildRegistrationFailed); updateErr != nil { - return reconcile.Result{}, updateErr - } - - return reconcile.Result{}, nil + updateErr := resources.UpdateConditionWithFalseStatus(ctx, r.client, buildRun, message, resources.ConditionBuildRegistrationFailed) + return r.handleError(buildRun, updateErr) } // Ensure the build-related labels on the BuildRun @@ -126,6 +126,7 @@ func (r *ReconcileBuildRun) Reconcile(request reconcile.Request) (reconcile.Resu // Set OwnerReference for Build and BuildRun only when build.shipwright.io/build-run-deletion is set "true" if build.GetAnnotations()[buildv1alpha1.AnnotationBuildRunDeletion] == "true" && !resources.IsOwnedByBuild(build, buildRun.OwnerReferences) { if err := r.setOwnerReferenceFunc(build, buildRun, r.scheme); err != nil { + // TODO - why are we setting this on the Build object instead of the BuildRun object? build.Status.Reason = buildv1alpha1.SetOwnerReferenceFailed build.Status.Message = fmt.Sprintf("unexpected error when trying to set the ownerreference: %v", err) if err := r.client.Status().Update(ctx, build); err != nil { @@ -161,30 +162,18 @@ func (r *ReconcileBuildRun) Reconcile(request reconcile.Request) (reconcile.Resu // Choose a service account to use svcAccount, err := resources.RetrieveServiceAccount(ctx, r.client, build, buildRun) if err != nil { - if !resources.IsClientStatusUpdateError(err) && buildRun.Status.IsFailed(buildv1alpha1.Succeeded) { - return reconcile.Result{}, nil - } - // system call failure, reconcile again - return reconcile.Result{}, err + return r.handleError(buildRun, err) } strategy, err := r.getReferencedStrategy(ctx, build, buildRun) if err != nil { - if !resources.IsClientStatusUpdateError(err) && buildRun.Status.IsFailed(buildv1alpha1.Succeeded) { - return reconcile.Result{}, nil - } - return reconcile.Result{}, err + return r.handleError(buildRun, err) } // Create the TaskRun, this needs to be the last step in this block to be idempotent generatedTaskRun, err := r.createTaskRun(ctx, svcAccount, strategy, build, buildRun) if err != nil { - if !resources.IsClientStatusUpdateError(err) && buildRun.Status.IsFailed(buildv1alpha1.Succeeded) { - ctxlog.Info(ctx, "taskRun generation failed", namespace, request.Namespace, name, request.Name) - return reconcile.Result{}, nil - } - // system call failure, reconcile again - return reconcile.Result{}, err + return r.handleError(buildRun, err) } ctxlog.Info(ctx, "creating TaskRun from BuildRun", namespace, request.Namespace, name, generatedTaskRun.GenerateName, "BuildRun", buildRun.Name) @@ -267,6 +256,7 @@ func (r *ReconcileBuildRun) Reconcile(request reconcile.Request) (reconcile.Resu if buildRun.Status.StartTime == nil && lastTaskRun.Status.StartTime != nil { buildRun.Status.StartTime = lastTaskRun.Status.StartTime + r.recordBuildRunStarted(buildRun) // Report the buildrun established duration (time between the creation of the buildrun and the start of the buildrun) buildmetrics.BuildRunEstablishObserve( buildRun.Status.BuildSpec.StrategyName(), @@ -280,6 +270,7 @@ func (r *ReconcileBuildRun) Reconcile(request reconcile.Request) (reconcile.Resu if lastTaskRun.Status.CompletionTime != nil && buildRun.Status.CompletionTime == nil { buildRun.Status.CompletionTime = lastTaskRun.Status.CompletionTime + r.recordBuildRunCompleted(buildRun) // buildrun completion duration (total time between the creation of the buildrun and the buildrun completion) buildmetrics.BuildRunCompletionObserve( buildRun.Status.BuildSpec.StrategyName(), @@ -318,6 +309,7 @@ func (r *ReconcileBuildRun) Reconcile(request reconcile.Request) (reconcile.Resu pod.CreationTimestamp.Time.Sub(lastTaskRun.CreationTimestamp.Time), ) } + } ctxlog.Info(ctx, "updating buildRun status", namespace, request.Namespace, name, request.Name) @@ -332,6 +324,43 @@ func (r *ReconcileBuildRun) Reconcile(request reconcile.Request) (reconcile.Resu return reconcile.Result{}, nil } +// handleError determines the appropriate reconciliation result for the provided error. +// It also ensures the build success/failure event is recorded. +func (r *ReconcileBuildRun) handleError(buildRun *buildv1alpha1.BuildRun, err error) (reconcile.Result, error) { + if resources.IsClientStatusUpdateError(err) { + return reconcile.Result{}, err + } + if !buildRun.Status.IsFailed(buildv1alpha1.Succeeded) { + return reconcile.Result{}, err + } + r.recordBuildRunCompleted(buildRun) + return reconcile.Result{}, nil +} + +// recordBuildRunCompleted records the appropriate event if the provided BuildRun has completed. +func (r *ReconcileBuildRun) recordBuildRunCompleted(buildRun *buildv1alpha1.BuildRun) { + if buildRun.Status.CompletionTime == nil { + return + } + successStatus := buildRun.Status.GetCondition(buildv1alpha1.Succeeded) + if successStatus == nil { + return + } + if successStatus.Status == corev1.ConditionTrue { + r.eventRecorder.Event(buildRun, "Normal", "Succeeded", "Completed with no errors") + } + if successStatus.Status == corev1.ConditionFalse { + r.eventRecorder.Eventf(buildRun, "Warning", "Failed", "Failure reason: %s", successStatus.Reason) + } +} + +func (r *ReconcileBuildRun) recordBuildRunStarted(buildRun *buildv1alpha1.BuildRun) { + if buildRun.Status.StartTime == nil { + return + } + r.eventRecorder.Event(buildRun, "Normal", "Started", "") +} + // GetBuildRunObject retrieves an existing BuildRun based on a name and namespace func (r *ReconcileBuildRun) GetBuildRunObject(ctx context.Context, objectName string, objectNS string, buildRun *buildv1alpha1.BuildRun) error { if err := r.client.Get(ctx, types.NamespacedName{Name: objectName, Namespace: objectNS}, buildRun); err != nil { diff --git a/pkg/reconciler/buildrun/buildrun_test.go b/pkg/reconciler/buildrun/buildrun_test.go index 9cf54d4413..f567e2a63d 100644 --- a/pkg/reconciler/buildrun/buildrun_test.go +++ b/pkg/reconciler/buildrun/buildrun_test.go @@ -19,6 +19,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/tools/record" "k8s.io/utils/pointer" knativeapi "knative.dev/pkg/apis" knativev1beta1 "knative.dev/pkg/apis/duck/v1beta1" @@ -47,6 +48,7 @@ var _ = Describe("Reconcile BuildRun", func() { buildRunSample *build.BuildRun taskRunSample *v1beta1.TaskRun statusWriter *fakes.FakeStatusWriter + eventRecorder *record.FakeRecorder taskRunName, buildRunName, buildName, strategyName, ns string ) @@ -101,6 +103,9 @@ var _ = Describe("Reconcile BuildRun", func() { client.StatusCalls(func() crc.StatusWriter { return statusWriter }) manager.GetClientReturns(client) + eventRecorder = record.NewFakeRecorder(10) + manager.GetEventRecorderForReturns(eventRecorder) + // init the Build resource, this never change throughout this test suite buildSample = ctl.DefaultBuild(buildName, strategyName, build.ClusterBuildStrategyKind) @@ -139,7 +144,7 @@ var _ = Describe("Reconcile BuildRun", func() { result, err := reconciler.Reconcile(taskRunRequest) Expect(err).ToNot(HaveOccurred()) Expect(reconcile.Result{}).To(Equal(result)) - Expect(client.GetCallCount()).To(Equal(2)) + Expect(client.GetCallCount()).To(Equal(3)) }) It("does not fail when the BuildRun does not exist", func() { @@ -163,7 +168,7 @@ var _ = Describe("Reconcile BuildRun", func() { result, err := reconciler.Reconcile(taskRunRequest) Expect(err).ToNot(HaveOccurred()) Expect(reconcile.Result{}).To(Equal(result)) - Expect(client.GetCallCount()).To(Equal(2)) + Expect(client.GetCallCount()).To(Equal(3)) }) It("updates the BuildRun status", func() { @@ -189,7 +194,7 @@ var _ = Describe("Reconcile BuildRun", func() { result, err := reconciler.Reconcile(taskRunRequest) Expect(err).ToNot(HaveOccurred()) Expect(reconcile.Result{}).To(Equal(result)) - Expect(client.GetCallCount()).To(Equal(2)) + Expect(client.GetCallCount()).To(Equal(3)) Expect(client.StatusCallCount()).To(Equal(1)) }) @@ -300,6 +305,8 @@ var _ = Describe("Reconcile BuildRun", func() { Expect(reconcile.Result{}).To(Equal(result)) Expect(client.GetCallCount()).To(Equal(2)) Expect(client.StatusCallCount()).To(Equal(1)) + // Build started event should have been emitted + Expect(len(eventRecorder.Events)).To(Equal(1)) }) It("updates the BuildRun status with a RUNNING reason", func() { @@ -327,6 +334,8 @@ var _ = Describe("Reconcile BuildRun", func() { Expect(reconcile.Result{}).To(Equal(result)) Expect(client.GetCallCount()).To(Equal(2)) Expect(client.StatusCallCount()).To(Equal(1)) + // Build started event should have been emitted + Expect(len(eventRecorder.Events)).To(Equal(1)) }) It("updates the BuildRun status with a SUCCEEDED reason", func() { @@ -352,8 +361,13 @@ var _ = Describe("Reconcile BuildRun", func() { result, err := reconciler.Reconcile(taskRunRequest) Expect(err).ToNot(HaveOccurred()) Expect(reconcile.Result{}).To(Equal(result)) - Expect(client.GetCallCount()).To(Equal(2)) + Expect(client.GetCallCount()).To(Equal(3)) Expect(client.StatusCallCount()).To(Equal(1)) + + // Two events should be fired in this reconcile loop + // 1. Build started + // 2. Build succeeded + Expect(len(eventRecorder.Events)).To(Equal(2)) }) It("updates the BuildRun status when a FALSE status occurs", func() { @@ -380,6 +394,11 @@ var _ = Describe("Reconcile BuildRun", func() { result, err := reconciler.Reconcile(taskRunRequest) Expect(err).ToNot(HaveOccurred()) Expect(reconcile.Result{}).To(Equal(result)) + + // Two events should be fired in this reconcile loop + // 1. Build started + // 2. Build failed + Expect(len(eventRecorder.Events)).To(Equal(2)) }) It("does not break the reconcile when a taskrun pod initcontainers are not ready", func() { @@ -574,6 +593,9 @@ var _ = Describe("Reconcile BuildRun", func() { Expect(err).ToNot(HaveOccurred()) Expect(client.GetCallCount()).To(Equal(4)) Expect(client.StatusCallCount()).To(Equal(2)) + + // Updating the status to "Succeeded = false" should fire a failure event + Expect(len(eventRecorder.Events)).To(Equal(1)) }) It("fails on a TaskRun creation due to issues when retrieving the service account", func() { @@ -636,6 +658,9 @@ var _ = Describe("Reconcile BuildRun", func() { _, err := reconciler.Reconcile(buildRunRequest) // we mark the BuildRun as Failed and do not reconcile again Expect(err).ToNot(HaveOccurred()) + + // Updating the status to "Succeeded = false" should fire a failure event + Expect(len(eventRecorder.Events)).To(Equal(1)) }) It("fails on a TaskRun creation due to issues when retrieving the buildstrategy", func() { @@ -704,6 +729,9 @@ var _ = Describe("Reconcile BuildRun", func() { _, err := reconciler.Reconcile(buildRunRequest) // we mark the BuildRun as Failed and do not reconcile again Expect(err).ToNot(HaveOccurred()) + + // Updating the status to "Succeeded false" should fire a failure event + Expect(len(eventRecorder.Events)).To(Equal(1)) }) It("fails on a TaskRun creation due to issues when retrieving the clusterbuildstrategy", func() { @@ -771,6 +799,9 @@ var _ = Describe("Reconcile BuildRun", func() { Expect(err).ToNot(HaveOccurred()) Expect(client.GetCallCount()).To(Equal(4)) Expect(client.StatusCallCount()).To(Equal(2)) + + // Updating the status to "Succeeded" should fire a failure event + Expect(len(eventRecorder.Events)).To(Equal(1)) }) It("defaults to a namespaced strategy if strategy kind is not set", func() { @@ -829,6 +860,9 @@ var _ = Describe("Reconcile BuildRun", func() { Expect(err).ToNot(HaveOccurred()) Expect(client.GetCallCount()).To(Equal(5)) Expect(client.StatusCallCount()).To(Equal(2)) + + // Updating the status to "Succeeded = false" should fire an event + Expect(len(eventRecorder.Events)).To(Equal(1)) }) It("fails on a TaskRun creation due to owner references errors", func() { @@ -871,6 +905,9 @@ var _ = Describe("Reconcile BuildRun", func() { _, err := reconciler.Reconcile(buildRunRequest) Expect(err).ToNot(HaveOccurred()) Expect(client.StatusCallCount()).To(Equal(2)) + + // Updating the status to "Succeeded = false" should fire an event + Expect(len(eventRecorder.Events)).To(Equal(1)) }) It("succeeds creating a TaskRun from a namespaced buildstrategy", func() { @@ -965,6 +1002,9 @@ var _ = Describe("Reconcile BuildRun", func() { _, err := reconciler.Reconcile(buildRunRequest) Expect(err).ToNot(HaveOccurred()) Expect(client.StatusCallCount()).To(Equal(1)) + + // Updating the status to "Succeeded = false" should fire an event + Expect(len(eventRecorder.Events)).To(Equal(1)) }) It("delays creation if the registered status of the build is not yet set", func() { diff --git a/pkg/reconciler/buildrun/resources/errors.go b/pkg/reconciler/buildrun/resources/errors.go index c697c0a78b..db2f942e88 100644 --- a/pkg/reconciler/buildrun/resources/errors.go +++ b/pkg/reconciler/buildrun/resources/errors.go @@ -21,10 +21,13 @@ func (e ClientStatusUpdateError) Error() string { // IsClientStatusUpdateError checks whether the given error is of type ClientStatusUpdateError or // in case this is a list of errors, that it contains at least one error of type ClientStatusUpdateError func IsClientStatusUpdateError(err error) bool { + if err == nil { + return false + } + switch terr := err.(type) { case *ClientStatusUpdateError, ClientStatusUpdateError: return true - case Errors: for _, e := range terr.errors { if IsClientStatusUpdateError(e) { @@ -32,7 +35,6 @@ func IsClientStatusUpdateError(err error) bool { } } } - return false } diff --git a/test/catalog.go b/test/catalog.go index a56d1d3b62..7ec907a5b9 100644 --- a/test/catalog.go +++ b/test/catalog.go @@ -515,7 +515,8 @@ func (c *Catalog) TaskRunWithStatus(trName string, ns string) *v1beta1.TaskRun { // DefaultTaskRunWithStatus returns a minimal tekton TaskRun with an Status func (c *Catalog) DefaultTaskRunWithStatus(trName string, buildRunName string, ns string, status corev1.ConditionStatus, reason string) *v1beta1.TaskRun { - return &v1beta1.TaskRun{ + now := metav1.Now() + taskRun := &v1beta1.TaskRun{ ObjectMeta: metav1.ObjectMeta{ Name: trName, Namespace: ns, @@ -534,11 +535,15 @@ func (c *Catalog) DefaultTaskRunWithStatus(trName string, buildRunName string, n }, TaskRunStatusFields: v1beta1.TaskRunStatusFields{ StartTime: &metav1.Time{ - Time: time.Now(), + Time: now.Add(-5 * time.Minute), }, }, }, } + if status == corev1.ConditionTrue || status == corev1.ConditionFalse { + taskRun.Status.CompletionTime = &now + } + return taskRun } // TaskRunWithCompletionAndStartTime provides a TaskRun object with a @@ -577,6 +582,7 @@ func (c *Catalog) TaskRunWithCompletionAndStartTime(trName string, buildRunName // DefaultTaskRunWithFalseStatus returns a minimal tektont TaskRun with a FALSE status func (c *Catalog) DefaultTaskRunWithFalseStatus(trName string, buildRunName string, ns string) *v1beta1.TaskRun { + now := metav1.Now() return &v1beta1.TaskRun{ ObjectMeta: metav1.ObjectMeta{ Name: trName, @@ -597,8 +603,9 @@ func (c *Catalog) DefaultTaskRunWithFalseStatus(trName string, buildRunName stri }, TaskRunStatusFields: v1beta1.TaskRunStatusFields{ StartTime: &metav1.Time{ - Time: time.Now(), + Time: now.Add(-5 * time.Minute), }, + CompletionTime: &now, }, }, } diff --git a/test/e2e/validators_test.go b/test/e2e/validators_test.go index cb3af419ed..a2a419260a 100644 --- a/test/e2e/validators_test.go +++ b/test/e2e/validators_test.go @@ -129,6 +129,15 @@ func validateBuildRunToSucceed(testBuild *utils.TestBuild, testBuildRun *buildv1 // Verify that the BuildSpec is still available in the status Expect(testBuildRun.Status.BuildSpec).ToNot(BeNil(), "BuildSpec is not available in the status") + // Verify that a succeeded event was fired + events, err := testBuild.GetEventsForObject(testBuildRun.Namespace, testBuildRun) + Expect(err).NotTo(HaveOccurred()) + Expect(len(events)).To(Equal(2)) + for _, event := range events { + Expect(event.Type).To(Equal("Normal")) + Expect(event.Reason).To(Or(Equal("Started"), Equal("Succeeded"))) + } + Logf("Test build '%s' is completed after %v !", testBuildRun.GetName(), testBuildRun.Status.CompletionTime.Time.Sub(testBuildRun.Status.StartTime.Time)) } diff --git a/test/utils/environment.go b/test/utils/environment.go index 9bc1fbba3b..7317fe66db 100644 --- a/test/utils/environment.go +++ b/test/utils/environment.go @@ -16,11 +16,14 @@ import ( gomegaConfig "github.com/onsi/ginkgo/config" tektonClient "github.com/tektoncd/pipeline/pkg/client/clientset/versioned" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes" _ "k8s.io/client-go/plugin/pkg/client/auth/oidc" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" + buildapis "github.com/shipwright-io/build/pkg/apis" buildClient "github.com/shipwright-io/build/pkg/client/clientset/versioned" "github.com/shipwright-io/build/pkg/ctxlog" "github.com/shipwright-io/build/test" @@ -49,6 +52,7 @@ type TestBuild struct { Catalog test.Catalog Context context.Context BuildControllerLogBuffer *bytes.Buffer + Scheme *runtime.Scheme } // NewTestBuild returns an initialized instance of TestBuild @@ -56,6 +60,18 @@ func NewTestBuild() (*TestBuild, error) { namespaceID := gomegaConfig.GinkgoConfig.ParallelNode*200 + int(atomic.AddInt32(&namespaceCounter, 1)) testNamespace := "test-build-" + strconv.Itoa(namespaceID) + // Scheme needed to search events by object + // Add additional APIs here if tests need to search events for other resource types + scheme := runtime.NewScheme() + err := corev1.AddToScheme(scheme) + if err != nil { + return nil, err + } + err = buildapis.AddToScheme(scheme) + if err != nil { + return nil, err + } + logBuffer := &bytes.Buffer{} l := ctxlog.NewLoggerTo(logBuffer, testNamespace) @@ -89,6 +105,7 @@ func NewTestBuild() (*TestBuild, error) { PipelineClientSet: pipelineClientSet, Context: ctx, BuildControllerLogBuffer: logBuffer, + Scheme: scheme, }, nil } diff --git a/test/utils/events.go b/test/utils/events.go new file mode 100644 index 0000000000..04edcdd4a3 --- /dev/null +++ b/test/utils/events.go @@ -0,0 +1,14 @@ +package utils + +import ( + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +func (t *TestBuild) GetEventsForObject(namespace string, obj runtime.Object) ([]corev1.Event, error) { + events, err := t.Clientset.CoreV1().Events(namespace).Search(t.Scheme, obj) + if err != nil { + return nil, err + } + return events.Items, nil +}