Skip to content

[Controller] Implement Events to communicate errors in CR configuration

Summary

To follow suit with Kubernetes common practice, we can implement Events to surface information about the progress of a release. For example, a Secret defined for use in a Redis subqueue may not have actually been created in the cluster. We could post a Warning Event with extra details like:

  • What's wrong (like the name of the Secret missing)
  • How to fix it (perhaps with links to docs, etc)

As suggested by @pursultani in this thread, this gives end users a better experience when debugging the application compared to parsing through many log entries in the controller pod.

Acceptance criteria

  • Events are created in the Reconcile loop when the configuration provided in the CR does not result in a successful Helm template

References

Example implementation

Events

Below is an example implementation of Events for reference:

diff --git a/controllers/gitlab_controller.go b/controllers/gitlab_controller.go
index 0655632..47df96e 100644
--- a/controllers/gitlab_controller.go
+++ b/controllers/gitlab_controller.go
@@ -27,6 +27,7 @@ import (
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/types"
+	"k8s.io/client-go/tools/record"
 	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
@@ -51,8 +52,9 @@ import (
 type GitLabReconciler struct {
 	client.Client
 
-	Log    logr.Logger
-	Scheme *runtime.Scheme
+	Recorder record.EventRecorder
+	Log      logr.Logger
+	Scheme   *runtime.Scheme
 }
 
 // +kubebuilder:rbac:groups=apps.gitlab.com,resources=gitlabs,verbs=get;list;watch;create;update;patch;delete
@@ -497,6 +499,8 @@ func (r *GitLabReconciler) reconcileStatefulSets(ctx context.Context, adapter gi
 		if _, err := r.createOrPatch(ctx, statefulset, adapter); err != nil {
+     r.Recorder.Event(statefulset, "Warning", "NotCreated", fmt.Sprintf("Failed to create StatefulSet %s/%s: %v", statefulset.Namespace, statefulset.Name, err))
 			return err
 		}
+
+		r.Recorder.Event(statefulset, "Normal", "Created", fmt.Sprintf("Created StatefulSet %s/%s", statefulset.Namespace, statefulset.Name))
 	}
 
 	return nil
diff --git a/main.go b/main.go
index 8a32112..a354c29 100644
--- a/main.go
+++ b/main.go
@@ -109,9 +109,10 @@ func main() {
 	mgr.AddReadyzCheck("readyz", settings.ReadyzCheck)
 
 	if err = (&controllers.GitLabReconciler{
-		Client: mgr.GetClient(),
-		Log:    ctrl.Log.WithName("controllers").WithName("GitLab"),
-		Scheme: mgr.GetScheme(),
+		Client:   mgr.GetClient(),
+		Log:      ctrl.Log.WithName("controllers").WithName("GitLab"),
+		Scheme:   mgr.GetScheme(),
+		Recorder: mgr.GetEventRecorderFor("gitlab-controller"),
 	}).SetupWithManager(mgr); err != nil {
 		setupLog.Error(err, "unable to create controller", "controller", "GitLab")
 		os.Exit(1)

These events can be seen by running:

$ kubectl get events -n gitlab-system
LAST SEEN   TYPE      REASON                         OBJECT                                                       MESSAGE
3m47s       Normal    Created                        statefulset/gitlab-postgresql                                Created StatefulSet gitlab-system/gitlab-postgresql
3m47s       Normal    Created                        statefulset/gitlab-redis-master                              Created StatefulSet gitlab-system/gitlab-redis-master
Edited by Mitchell Nielsen