You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by lb...@apache.org on 2020/02/24 13:52:47 UTC

[camel-k] 02/14: #1199: add events to all CRD

This is an automated email from the ASF dual-hosted git repository.

lburgazzoli pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel-k.git

commit d1cf15fd13254ab679dcc34d73415a08fef47485
Author: Nicola Ferraro <ni...@gmail.com>
AuthorDate: Mon Jan 27 15:46:27 2020 +0100

    #1199: add events to all CRD
---
 akamel-config.yaml                                 |   3 -
 pkg/apis/camel/v1/build_types_support.go           |  41 +++++
 pkg/apis/camel/v1/common_types.go                  |  10 ++
 pkg/apis/camel/v1/integration_types_support.go     |  41 +++++
 pkg/apis/camel/v1/integrationkit_types_support.go  |  41 +++++
 pkg/apis/camel/v1/integrationplatform_types.go     |   2 +
 .../camel/v1/integrationplatform_types_support.go  |  41 +++++
 pkg/cmd/operator/operator.go                       |   3 +-
 pkg/controller/build/build_controller.go           |  19 ++-
 .../integrationkit/integrationkit_controller.go    |  19 ++-
 .../integrationplatform_controller.go              |  11 +-
 pkg/events/manager.go                              | 173 +++++++++++++++++----
 12 files changed, 358 insertions(+), 46 deletions(-)

diff --git a/akamel-config.yaml b/akamel-config.yaml
deleted file mode 100755
index 1f20f9b..0000000
--- a/akamel-config.yaml
+++ /dev/null
@@ -1,3 +0,0 @@
-kamel:
-  install:
-    maven-repositories: '[https://repository.apache.org/content/repositories/orgapachecamel-1171]'
diff --git a/pkg/apis/camel/v1/build_types_support.go b/pkg/apis/camel/v1/build_types_support.go
index 19ee880..2c8243b 100644
--- a/pkg/apis/camel/v1/build_types_support.go
+++ b/pkg/apis/camel/v1/build_types_support.go
@@ -142,3 +142,44 @@ func (in *BuildStatus) RemoveCondition(condType BuildConditionType) {
 
 	in.Conditions = newConditions
 }
+
+var _ ResourceCondition = BuildCondition{}
+
+// GetConditions --
+func (in *BuildStatus) GetConditions() []ResourceCondition {
+	res := make([]ResourceCondition, 0, len(in.Conditions))
+	for _, c := range in.Conditions {
+		res = append(res, c)
+	}
+	return res
+}
+
+// GetType --
+func (c BuildCondition) GetType() string {
+	return string(c.Type)
+}
+
+// GetStatus --
+func (c BuildCondition) GetStatus() corev1.ConditionStatus {
+	return c.Status
+}
+
+// GetLastUpdateTime --
+func (c BuildCondition) GetLastUpdateTime() metav1.Time {
+	return c.LastUpdateTime
+}
+
+// GetLastTransitionTime --
+func (c BuildCondition) GetLastTransitionTime() metav1.Time {
+	return c.LastTransitionTime
+}
+
+// GetReason --
+func (c BuildCondition) GetReason() string {
+	return c.Reason
+}
+
+// GetMessage --
+func (c BuildCondition) GetMessage() string {
+	return c.Message
+}
diff --git a/pkg/apis/camel/v1/common_types.go b/pkg/apis/camel/v1/common_types.go
index 36df7e7..0bcc485 100644
--- a/pkg/apis/camel/v1/common_types.go
+++ b/pkg/apis/camel/v1/common_types.go
@@ -99,3 +99,13 @@ const (
 	// ServiceTypeUser --
 	ServiceTypeUser = "user"
 )
+
+// ResourceCondition is a common type for all conditions
+type ResourceCondition interface {
+	GetType() string
+	GetStatus() corev1.ConditionStatus
+	GetLastUpdateTime() metav1.Time
+	GetLastTransitionTime() metav1.Time
+	GetReason() string
+	GetMessage() string
+}
diff --git a/pkg/apis/camel/v1/integration_types_support.go b/pkg/apis/camel/v1/integration_types_support.go
index 5516986..d04e1bc 100644
--- a/pkg/apis/camel/v1/integration_types_support.go
+++ b/pkg/apis/camel/v1/integration_types_support.go
@@ -299,3 +299,44 @@ func (in *IntegrationStatus) RemoveCondition(condType IntegrationConditionType)
 
 	in.Conditions = newConditions
 }
+
+var _ ResourceCondition = IntegrationCondition{}
+
+// GetConditions --
+func (in *IntegrationStatus) GetConditions() []ResourceCondition {
+	res := make([]ResourceCondition, 0, len(in.Conditions))
+	for _, c := range in.Conditions {
+		res = append(res, c)
+	}
+	return res
+}
+
+// GetType --
+func (c IntegrationCondition) GetType() string {
+	return string(c.Type)
+}
+
+// GetStatus --
+func (c IntegrationCondition) GetStatus() corev1.ConditionStatus {
+	return c.Status
+}
+
+// GetLastUpdateTime --
+func (c IntegrationCondition) GetLastUpdateTime() metav1.Time {
+	return c.LastUpdateTime
+}
+
+// GetLastTransitionTime --
+func (c IntegrationCondition) GetLastTransitionTime() metav1.Time {
+	return c.LastTransitionTime
+}
+
+// GetReason --
+func (c IntegrationCondition) GetReason() string {
+	return c.Reason
+}
+
+// GetMessage --
+func (c IntegrationCondition) GetMessage() string {
+	return c.Message
+}
diff --git a/pkg/apis/camel/v1/integrationkit_types_support.go b/pkg/apis/camel/v1/integrationkit_types_support.go
index df3a95d..fb310df 100644
--- a/pkg/apis/camel/v1/integrationkit_types_support.go
+++ b/pkg/apis/camel/v1/integrationkit_types_support.go
@@ -155,3 +155,44 @@ func (in *IntegrationKitStatus) RemoveCondition(condType IntegrationKitCondition
 
 	in.Conditions = newConditions
 }
+
+var _ ResourceCondition = IntegrationKitCondition{}
+
+// GetConditions --
+func (in *IntegrationKitStatus) GetConditions() []ResourceCondition {
+	res := make([]ResourceCondition, 0, len(in.Conditions))
+	for _, c := range in.Conditions {
+		res = append(res, c)
+	}
+	return res
+}
+
+// GetType --
+func (c IntegrationKitCondition) GetType() string {
+	return string(c.Type)
+}
+
+// GetStatus --
+func (c IntegrationKitCondition) GetStatus() corev1.ConditionStatus {
+	return c.Status
+}
+
+// GetLastUpdateTime --
+func (c IntegrationKitCondition) GetLastUpdateTime() metav1.Time {
+	return c.LastUpdateTime
+}
+
+// GetLastTransitionTime --
+func (c IntegrationKitCondition) GetLastTransitionTime() metav1.Time {
+	return c.LastTransitionTime
+}
+
+// GetReason --
+func (c IntegrationKitCondition) GetReason() string {
+	return c.Reason
+}
+
+// GetMessage --
+func (c IntegrationKitCondition) GetMessage() string {
+	return c.Message
+}
diff --git a/pkg/apis/camel/v1/integrationplatform_types.go b/pkg/apis/camel/v1/integrationplatform_types.go
index ef72193..ff9afbb 100644
--- a/pkg/apis/camel/v1/integrationplatform_types.go
+++ b/pkg/apis/camel/v1/integrationplatform_types.go
@@ -156,6 +156,8 @@ const (
 	// IntegrationPlatformKind --
 	IntegrationPlatformKind string = "IntegrationPlatform"
 
+	// IntegrationPlatformPhaseNone --
+	IntegrationPlatformPhaseNone IntegrationPlatformPhase = ""
 	// IntegrationPlatformPhaseCreating --
 	IntegrationPlatformPhaseCreating IntegrationPlatformPhase = "Creating"
 	// IntegrationPlatformPhaseWarming --
diff --git a/pkg/apis/camel/v1/integrationplatform_types_support.go b/pkg/apis/camel/v1/integrationplatform_types_support.go
index 8a2a806..ce33114 100644
--- a/pkg/apis/camel/v1/integrationplatform_types_support.go
+++ b/pkg/apis/camel/v1/integrationplatform_types_support.go
@@ -202,3 +202,44 @@ func (m MavenSpec) GetTimeout() metav1.Duration {
 	}
 	return *m.Timeout
 }
+
+var _ ResourceCondition = IntegrationPlatformCondition{}
+
+// GetConditions --
+func (in *IntegrationPlatformStatus) GetConditions() []ResourceCondition {
+	res := make([]ResourceCondition, 0, len(in.Conditions))
+	for _, c := range in.Conditions {
+		res = append(res, c)
+	}
+	return res
+}
+
+// GetType --
+func (c IntegrationPlatformCondition) GetType() string {
+	return string(c.Type)
+}
+
+// GetStatus --
+func (c IntegrationPlatformCondition) GetStatus() corev1.ConditionStatus {
+	return c.Status
+}
+
+// GetLastUpdateTime --
+func (c IntegrationPlatformCondition) GetLastUpdateTime() metav1.Time {
+	return c.LastUpdateTime
+}
+
+// GetLastTransitionTime --
+func (c IntegrationPlatformCondition) GetLastTransitionTime() metav1.Time {
+	return c.LastTransitionTime
+}
+
+// GetReason --
+func (c IntegrationPlatformCondition) GetReason() string {
+	return c.Reason
+}
+
+// GetMessage --
+func (c IntegrationPlatformCondition) GetMessage() string {
+	return c.Message
+}
diff --git a/pkg/cmd/operator/operator.go b/pkg/cmd/operator/operator.go
index 92229df..012179f 100644
--- a/pkg/cmd/operator/operator.go
+++ b/pkg/cmd/operator/operator.go
@@ -27,7 +27,6 @@ import (
 	"time"
 
 	"github.com/apache/camel-k/pkg/client"
-	camellog "github.com/apache/camel-k/pkg/util/log"
 	"github.com/operator-framework/operator-sdk/pkg/k8sutil"
 	"github.com/operator-framework/operator-sdk/pkg/leader"
 	"github.com/operator-framework/operator-sdk/pkg/ready"
@@ -111,7 +110,7 @@ func Run() {
 		os.Exit(1)
 	}
 	eventBroadcaster := record.NewBroadcaster()
-	eventBroadcaster.StartLogging(camellog.WithName("events").Infof)
+	//eventBroadcaster.StartLogging(camellog.WithName("events").Infof)
 	eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: c.CoreV1().Events(namespace)})
 
 	// Create a new Cmd to provide shared dependencies and start components
diff --git a/pkg/controller/build/build_controller.go b/pkg/controller/build/build_controller.go
index b69b4a6..ddf5da7 100644
--- a/pkg/controller/build/build_controller.go
+++ b/pkg/controller/build/build_controller.go
@@ -22,9 +22,11 @@ import (
 	"sync"
 	"time"
 
+	"github.com/apache/camel-k/pkg/events"
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/client-go/tools/record"
 
 	k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/controller"
@@ -54,10 +56,11 @@ func Add(mgr manager.Manager) error {
 // newReconciler returns a new reconcile.Reconciler
 func newReconciler(mgr manager.Manager, c client.Client) reconcile.Reconciler {
 	return &ReconcileBuild{
-		client:  c,
-		reader:  mgr.GetAPIReader(),
-		scheme:  mgr.GetScheme(),
-		builder: builder.New(c),
+		client:   c,
+		reader:   mgr.GetAPIReader(),
+		scheme:   mgr.GetScheme(),
+		builder:  builder.New(c),
+		recorder: mgr.GetEventRecorderFor("camel-k-build-controller"),
 	}
 }
 
@@ -121,6 +124,7 @@ type ReconcileBuild struct {
 	scheme   *runtime.Scheme
 	builder  builder.Builder
 	routines sync.Map
+	recorder record.EventRecorder
 }
 
 // Reconcile reads that state of the cluster for a Build object and makes changes based on the state read
@@ -204,12 +208,14 @@ func (r *ReconcileBuild) Reconcile(request reconcile.Request) (reconcile.Result,
 
 			newTarget, err := a.Handle(ctx, target)
 			if err != nil {
+				events.NotifyBuildError(r.recorder, &instance, newTarget, err)
 				return reconcile.Result{}, err
 			}
 
 			if newTarget != nil {
-				if r, err := r.update(ctx, &instance, newTarget); err != nil {
-					return r, err
+				if res, err := r.update(ctx, &instance, newTarget); err != nil {
+					events.NotifyBuildError(r.recorder, &instance, newTarget, err)
+					return res, err
 				}
 
 				if newTarget.Status.Phase != target.Status.Phase {
@@ -225,6 +231,7 @@ func (r *ReconcileBuild) Reconcile(request reconcile.Request) (reconcile.Result,
 
 			// handle one action at time so the resource
 			// is always at its latest state
+			events.NotifyBuildUpdated(r.recorder, &instance, newTarget)
 			break
 		}
 	}
diff --git a/pkg/controller/integrationkit/integrationkit_controller.go b/pkg/controller/integrationkit/integrationkit_controller.go
index d9144aa..fc87130 100644
--- a/pkg/controller/integrationkit/integrationkit_controller.go
+++ b/pkg/controller/integrationkit/integrationkit_controller.go
@@ -20,7 +20,9 @@ package integrationkit
 import (
 	"context"
 
+	"github.com/apache/camel-k/pkg/events"
 	"github.com/apache/camel-k/pkg/platform"
+	"k8s.io/client-go/tools/record"
 
 	"github.com/apache/camel-k/pkg/util/digest"
 	"github.com/apache/camel-k/pkg/util/log"
@@ -55,8 +57,9 @@ func Add(mgr manager.Manager) error {
 // newReconciler returns a new reconcile.Reconciler
 func newReconciler(mgr manager.Manager, c client.Client) reconcile.Reconciler {
 	return &ReconcileIntegrationKit{
-		client: c,
-		scheme: mgr.GetScheme(),
+		client:   c,
+		scheme:   mgr.GetScheme(),
+		recorder: mgr.GetEventRecorderFor("camel-k-integration-kit-controller"),
 	}
 }
 
@@ -152,8 +155,9 @@ var _ reconcile.Reconciler = &ReconcileIntegrationKit{}
 type ReconcileIntegrationKit struct {
 	// This client, initialized using mgr.Client() above, is a split client
 	// that reads objects from the cache and writes to the apiserver
-	client client.Client
-	scheme *runtime.Scheme
+	client   client.Client
+	scheme   *runtime.Scheme
+	recorder record.EventRecorder
 }
 
 // Reconcile reads that state of the cluster for a IntegrationKit object and makes changes based on the state read
@@ -223,12 +227,14 @@ func (r *ReconcileIntegrationKit) Reconcile(request reconcile.Request) (reconcil
 
 			newTarget, err := a.Handle(ctx, target)
 			if err != nil {
+				events.NotifyIntegrationKitError(r.recorder, &instance, newTarget, err)
 				return reconcile.Result{}, err
 			}
 
 			if newTarget != nil {
-				if r, err := r.update(ctx, &instance, newTarget); err != nil {
-					return r, err
+				if res, err := r.update(ctx, &instance, newTarget); err != nil {
+					events.NotifyIntegrationKitError(r.recorder, &instance, newTarget, err)
+					return res, err
 				}
 
 				if newTarget.Status.Phase != target.Status.Phase {
@@ -242,6 +248,7 @@ func (r *ReconcileIntegrationKit) Reconcile(request reconcile.Request) (reconcil
 
 			// handle one action at time so the resource
 			// is always at its latest state
+			events.NotifyIntegrationKitUpdated(r.recorder, &instance, newTarget)
 			break
 		}
 	}
diff --git a/pkg/controller/integrationplatform/integrationplatform_controller.go b/pkg/controller/integrationplatform/integrationplatform_controller.go
index 5b77310..37fd592 100644
--- a/pkg/controller/integrationplatform/integrationplatform_controller.go
+++ b/pkg/controller/integrationplatform/integrationplatform_controller.go
@@ -21,8 +21,10 @@ import (
 	"context"
 	"time"
 
+	"github.com/apache/camel-k/pkg/events"
 	"k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/client-go/tools/record"
 
 	k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/controller"
@@ -52,6 +54,7 @@ func newReconciler(mgr manager.Manager, c client.Client) reconcile.Reconciler {
 	return &ReconcileIntegrationPlatform{
 		client: c,
 		scheme: mgr.GetScheme(),
+		recorder: mgr.GetEventRecorderFor("camel-k-integration-platform-controller"),
 	}
 }
 
@@ -92,8 +95,9 @@ var _ reconcile.Reconciler = &ReconcileIntegrationPlatform{}
 type ReconcileIntegrationPlatform struct {
 	// This client, initialized using mgr.Client() above, is a split client
 	// that reads objects from the cache and writes to the apiserver
-	client client.Client
-	scheme *runtime.Scheme
+	client   client.Client
+	scheme   *runtime.Scheme
+	recorder record.EventRecorder
 }
 
 // Reconcile reads that state of the cluster for a IntegrationPlatform object and makes changes based
@@ -147,11 +151,13 @@ func (r *ReconcileIntegrationPlatform) Reconcile(request reconcile.Request) (rec
 
 			target, err = a.Handle(ctx, target)
 			if err != nil {
+				events.NotifyIntegrationPlatformError(r.recorder, &instance, target, err)
 				return reconcile.Result{}, err
 			}
 
 			if target != nil {
 				if err := r.client.Status().Patch(ctx, target, k8sclient.MergeFrom(&instance)); err != nil {
+					events.NotifyIntegrationPlatformError(r.recorder, &instance, target, err)
 					return reconcile.Result{}, err
 				}
 
@@ -168,6 +174,7 @@ func (r *ReconcileIntegrationPlatform) Reconcile(request reconcile.Request) (rec
 
 			// handle one action at time so the resource
 			// is always at its latest state
+			events.NotifyIntegrationPlatformUpdated(r.recorder, &instance, target)
 			break
 		}
 	}
diff --git a/pkg/events/manager.go b/pkg/events/manager.go
index 5cd9a70..2a792eb 100644
--- a/pkg/events/manager.go
+++ b/pkg/events/manager.go
@@ -22,6 +22,7 @@ import (
 
 	v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
 	corev1 "k8s.io/api/core/v1"
+	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/client-go/tools/record"
 )
 
@@ -30,8 +31,29 @@ const (
 	ReasonIntegrationPhaseUpdated = "IntegrationPhaseUpdated"
 	// ReasonIntegrationConditionChanged --
 	ReasonIntegrationConditionChanged = "IntegrationConditionChanged"
-	// ReasonIntegrationError
+	// ReasonIntegrationError --
 	ReasonIntegrationError = "IntegrationError"
+
+	// ReasonIntegrationKitPhaseUpdated --
+	ReasonIntegrationKitPhaseUpdated = "IntegrationKitPhaseUpdated"
+	// ReasonIntegrationKitConditionChanged --
+	ReasonIntegrationKitConditionChanged = "IntegrationKitConditionChanged"
+	// ReasonIntegrationKitError --
+	ReasonIntegrationKitError = "IntegrationKitError"
+
+	// ReasonIntegrationPlatformPhaseUpdated --
+	ReasonIntegrationPlatformPhaseUpdated = "IntegrationPlatformPhaseUpdated"
+	// ReasonIntegrationPlatformConditionChanged --
+	ReasonIntegrationPlatformConditionChanged = "IntegrationPlatformConditionChanged"
+	// ReasonIntegrationPlatformError --
+	ReasonIntegrationPlatformError = "IntegrationPlatformError"
+
+	// ReasonBuildPhaseUpdated --
+	ReasonBuildPhaseUpdated = "BuildPhaseUpdated"
+	// ReasonBuildConditionChanged --
+	ReasonBuildConditionChanged = "BuildConditionChanged"
+	// ReasonBuildError --
+	ReasonBuildError = "BuildError"
 )
 
 // NotifyIntegrationError automatically generates error events when the integration reconcile cycle phase has an error
@@ -43,7 +65,7 @@ func NotifyIntegrationError(recorder record.EventRecorder, old, new *v1.Integrat
 	if it == nil {
 		return
 	}
-	recorder.Eventf(it, corev1.EventTypeWarning, ReasonIntegrationError, "Cannot reconcile integration %s: %v", it.Name, err)
+	recorder.Eventf(it, corev1.EventTypeWarning, ReasonIntegrationError, "Cannot reconcile Integration %s: %v", it.Name, err)
 }
 
 // NotifyIntegrationUpdated automatically generates events when the integration changes
@@ -51,43 +73,140 @@ func NotifyIntegrationUpdated(recorder record.EventRecorder, old, new *v1.Integr
 	if new == nil {
 		return
 	}
+	oldPhase := ""
+	var oldConditions []v1.ResourceCondition
+	if old != nil {
+		oldPhase = string(old.Status.Phase)
+		oldConditions = old.Status.GetConditions()
+	}
+	notifyIfPhaseUpdated(recorder, new, oldPhase, string(new.Status.Phase), "Integration", new.Name, ReasonIntegrationPhaseUpdated)
+	if new.Status.Phase != v1.IntegrationPhaseNone {
+		notifyIfConditionUpdated(recorder, new, oldConditions, new.Status.GetConditions(), "Integration", new.Name, ReasonIntegrationConditionChanged)
+	}
+}
+
+// NotifyIntegrationKitUpdated automatically generates events when an integration kit changes
+func NotifyIntegrationKitUpdated(recorder record.EventRecorder, old, new *v1.IntegrationKit) {
+	if new == nil {
+		return
+	}
+	oldPhase := ""
+	var oldConditions []v1.ResourceCondition
+	if old != nil {
+		oldPhase = string(old.Status.Phase)
+		oldConditions = old.Status.GetConditions()
+	}
+	notifyIfPhaseUpdated(recorder, new, oldPhase, string(new.Status.Phase), "Integration Kit", new.Name, ReasonIntegrationKitPhaseUpdated)
+	if new.Status.Phase != v1.IntegrationKitPhaseNone {
+		notifyIfConditionUpdated(recorder, new, oldConditions, new.Status.GetConditions(), "Integration Kit", new.Name, ReasonIntegrationKitConditionChanged)
+	}
+}
+
+// NotifyIntegrationKitError automatically generates error events when the integration kit reconcile cycle phase has an error
+func NotifyIntegrationKitError(recorder record.EventRecorder, old, new *v1.IntegrationKit, err error) {
+	kit := old
+	if new != nil {
+		kit = new
+	}
+	if kit == nil {
+		return
+	}
+	recorder.Eventf(kit, corev1.EventTypeWarning, ReasonIntegrationKitError, "Cannot reconcile Integration Kit %s: %v", kit.Name, err)
+}
+
+// NotifyIntegrationPlatformUpdated automatically generates events when an integration platform changes
+func NotifyIntegrationPlatformUpdated(recorder record.EventRecorder, old, new *v1.IntegrationPlatform) {
+	if new == nil {
+		return
+	}
+	oldPhase := ""
+	var oldConditions []v1.ResourceCondition
+	if old != nil {
+		oldPhase = string(old.Status.Phase)
+		oldConditions = old.Status.GetConditions()
+	}
+	notifyIfPhaseUpdated(recorder, new, oldPhase, string(new.Status.Phase), "Integration Platform", new.Name, ReasonIntegrationPlatformPhaseUpdated)
+	if new.Status.Phase != v1.IntegrationPlatformPhaseNone {
+		notifyIfConditionUpdated(recorder, new, oldConditions, new.Status.GetConditions(), "Integration Platform", new.Name, ReasonIntegrationPlatformConditionChanged)
+	}
+}
 
+// NotifyIntegrationPlatformError automatically generates error events when the integration Platform reconcile cycle phase has an error
+func NotifyIntegrationPlatformError(recorder record.EventRecorder, old, new *v1.IntegrationPlatform, err error) {
+	p := old
+	if new != nil {
+		p = new
+	}
+	if p == nil {
+		return
+	}
+	recorder.Eventf(p, corev1.EventTypeWarning, ReasonIntegrationPlatformError, "Cannot reconcile Integration Platform %s: %v", p.Name, err)
+}
+
+// NotifyBuildUpdated automatically generates events when a build changes
+func NotifyBuildUpdated(recorder record.EventRecorder, old, new *v1.Build) {
+	if new == nil {
+		return
+	}
+	oldPhase := ""
+	var oldConditions []v1.ResourceCondition
+	if old != nil {
+		oldPhase = string(old.Status.Phase)
+		oldConditions = old.Status.GetConditions()
+	}
+	notifyIfPhaseUpdated(recorder, new, oldPhase, string(new.Status.Phase), "Build", new.Name, ReasonBuildPhaseUpdated)
+	if new.Status.Phase != v1.BuildPhaseNone {
+		notifyIfConditionUpdated(recorder, new, oldConditions, new.Status.GetConditions(), "Build", new.Name, ReasonBuildConditionChanged)
+	}
+}
+
+// NotifyBuildError automatically generates error events when the build reconcile cycle phase has an error
+func NotifyBuildError(recorder record.EventRecorder, old, new *v1.Build, err error) {
+	p := old
+	if new != nil {
+		p = new
+	}
+	if p == nil {
+		return
+	}
+	recorder.Eventf(p, corev1.EventTypeWarning, ReasonBuildError, "Cannot reconcile Build %s: %v", p.Name, err)
+}
+
+func notifyIfPhaseUpdated(recorder record.EventRecorder, new runtime.Object, oldPhase, newPhase string, resourceType, name, reason string) {
 	// Update information about phase changes
-	if old == nil || old.Status.Phase != new.Status.Phase {
-		phase := new.Status.Phase
-		if phase == v1.IntegrationPhaseNone {
+	if oldPhase != newPhase {
+		phase := newPhase
+		if phase == "" {
 			phase = "[none]"
 		}
-		recorder.Eventf(new, corev1.EventTypeNormal, ReasonIntegrationPhaseUpdated, "Integration %s in phase %s", new.Name, phase)
+		recorder.Eventf(new, corev1.EventTypeNormal, reason, "%s %s in phase %s", resourceType, name, phase)
 	}
+}
 
+func notifyIfConditionUpdated(recorder record.EventRecorder, new runtime.Object, oldConditions, newConditions []v1.ResourceCondition, resourceType, name, reason string) {
 	// Update information about changes in conditions
-	if new.Status.Phase != v1.IntegrationPhaseNone {
-		for _, cond := range getChangedConditions(old, new) {
-			head := ""
-			if cond.Status == corev1.ConditionFalse {
-				head = "No "
-			}
-			tail := ""
-			if cond.Message != "" {
-				tail = fmt.Sprintf(": %s", cond.Message)
-			}
-			recorder.Eventf(new, corev1.EventTypeNormal, ReasonIntegrationConditionChanged, "%s%s for integration %s%s", head, cond.Type, new.Name, tail)
+	for _, cond := range getCommonChangedConditions(oldConditions, newConditions) {
+		head := ""
+		if cond.GetStatus() == corev1.ConditionFalse {
+			head = "No "
+		}
+		tail := ""
+		if cond.GetMessage() != "" {
+			tail = fmt.Sprintf(": %s", cond.GetMessage())
 		}
+		recorder.Eventf(new, corev1.EventTypeNormal, reason, "%s%s for %s %s%s", head, cond.GetType(), resourceType, name, tail)
 	}
-
 }
 
-func getChangedConditions(old, new *v1.Integration) (res []v1.IntegrationCondition) {
-	if old == nil {
-		old = &v1.Integration{}
+func getCommonChangedConditions(old, new []v1.ResourceCondition) (res []v1.ResourceCondition) {
+	oldState := make(map[string]v1.ResourceCondition)
+	for _, c := range old {
+		oldState[c.GetType()] = c
 	}
-	if new == nil {
-		new = &v1.Integration{}
-	}
-	for _, newCond := range new.Status.Conditions {
-		oldCond := old.Status.GetCondition(newCond.Type)
-		if oldCond == nil || oldCond.Status != newCond.Status || oldCond.Message != newCond.Message {
+
+	for _, newCond := range new {
+		oldCond := oldState[newCond.GetType()]
+		if oldCond == nil || oldCond.GetStatus() != newCond.GetStatus() || oldCond.GetMessage() != newCond.GetMessage() {
 			res = append(res, newCond)
 		}
 	}