From ec248f2d422cf5524d49ef245a189e0d4cd012dc Mon Sep 17 00:00:00 2001 From: Maciej Olesinski Date: Tue, 22 Jan 2019 00:45:02 +0100 Subject: [PATCH] Add events --- cmd/manager/main.go | 9 ++- pkg/controller/jenkins/jenkins_controller.go | 38 +++++++---- pkg/event/event.go | 71 ++++++++++++++++++++ 3 files changed, 105 insertions(+), 13 deletions(-) create mode 100644 pkg/event/event.go diff --git a/cmd/manager/main.go b/cmd/manager/main.go index 942a0802..99ec5844 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -9,6 +9,7 @@ import ( "github.com/VirtusLab/jenkins-operator/pkg/apis" "github.com/VirtusLab/jenkins-operator/pkg/controller/jenkins" + "github.com/VirtusLab/jenkins-operator/pkg/event" "github.com/VirtusLab/jenkins-operator/pkg/log" "github.com/VirtusLab/jenkins-operator/version" @@ -79,8 +80,14 @@ func main() { fatal(err, "failed to setup scheme") } + // setup events + events, err := event.New(cfg) + if err != nil { + fatal(err, "failed to create manager") + } + // setup Jenkins controller - if err := jenkins.Add(mgr, *local, *minikube); err != nil { + if err := jenkins.Add(mgr, *local, *minikube, events); err != nil { fatal(err, "failed to setup controllers") } diff --git a/pkg/controller/jenkins/jenkins_controller.go b/pkg/controller/jenkins/jenkins_controller.go index 090780e6..cfc7953b 100644 --- a/pkg/controller/jenkins/jenkins_controller.go +++ b/pkg/controller/jenkins/jenkins_controller.go @@ -9,11 +9,13 @@ import ( "github.com/VirtusLab/jenkins-operator/pkg/controller/jenkins/configuration/user" "github.com/VirtusLab/jenkins-operator/pkg/controller/jenkins/constants" "github.com/VirtusLab/jenkins-operator/pkg/controller/jenkins/plugins" + "github.com/VirtusLab/jenkins-operator/pkg/event" "github.com/VirtusLab/jenkins-operator/pkg/log" "github.com/go-logr/logr" + "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" + apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -25,19 +27,26 @@ import ( "sigs.k8s.io/controller-runtime/pkg/source" ) +const ( + ReasonBaseConfigurationSuccess event.Reason = "BaseConfigurationSuccess" + ReasonBaseConfigurationFailure event.Reason = "BaseConfigurationFailure" + ReasonCRValidationFailure event.Reason = "CRValidationFailure" +) + // Add creates a new Jenkins Controller and adds it to the Manager. The Manager will set fields on the Controller // and Start it when the Manager is Started. -func Add(mgr manager.Manager, local, minikube bool) error { - return add(mgr, newReconciler(mgr, local, minikube)) +func Add(mgr manager.Manager, local, minikube bool, events event.Recorder) error { + return add(mgr, newReconciler(mgr, local, minikube, events)) } // newReconciler returns a new reconcile.Reconciler -func newReconciler(mgr manager.Manager, local, minikube bool) reconcile.Reconciler { +func newReconciler(mgr manager.Manager, local, minikube bool, events event.Recorder) reconcile.Reconciler { return &ReconcileJenkins{ client: mgr.GetClient(), scheme: mgr.GetScheme(), local: local, minikube: minikube, + events: events, } } @@ -85,6 +94,7 @@ type ReconcileJenkins struct { client client.Client scheme *runtime.Scheme local, minikube bool + events event.Recorder } // Reconcile it's a main reconciliation loop which maintain desired state based on Jenkins.Spec @@ -93,7 +103,7 @@ func (r *ReconcileJenkins) Reconcile(request reconcile.Request) (reconcile.Resul logger.V(log.VDebug).Info("Reconciling Jenkins") result, err := r.reconcile(request, logger) - if err != nil && errors.IsConflict(err) { + if err != nil && apierrors.IsConflict(err) { logger.V(log.VWarn).Info(err.Error()) return reconcile.Result{Requeue: true}, nil } else if err != nil { @@ -108,7 +118,7 @@ func (r *ReconcileJenkins) reconcile(request reconcile.Request, logger logr.Logg jenkins := &virtuslabv1alpha1.Jenkins{} err := r.client.Get(context.TODO(), request.NamespacedName, jenkins) if err != nil { - if errors.IsNotFound(err) { + if apierrors.IsNotFound(err) { // Request object not found, could have been deleted after reconcile request. // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers. // Return and don't requeue @@ -128,16 +138,19 @@ func (r *ReconcileJenkins) reconcile(request reconcile.Request, logger logr.Logg valid, err := baseConfiguration.Validate(jenkins) if err != nil { - return reconcile.Result{}, err + r.events.Emitf(jenkins, event.TypeWarning, ReasonBaseConfigurationFailure, "Base configuration failed: %s", err) + return reconcile.Result{}, errors.Wrap(err, "Base configuration failed") } if !valid { - logger.V(log.VWarn).Info("Validation of user configuration failed, please correct Jenkins CR") + r.events.Emit(jenkins, event.TypeWarning, ReasonCRValidationFailure, "Base CR validation failed") + logger.V(log.VWarn).Info("Validation of base configuration failed, please correct Jenkins CR") return reconcile.Result{}, nil // don't requeue } result, jenkinsClient, err := baseConfiguration.Reconcile() if err != nil { - return reconcile.Result{}, err + r.events.Emitf(jenkins, event.TypeWarning, ReasonBaseConfigurationFailure, "Base configuration failed: %s", err) + return reconcile.Result{}, errors.Wrap(err, "Base configuration failed") } if result.Requeue { return result, nil @@ -151,9 +164,9 @@ func (r *ReconcileJenkins) reconcile(request reconcile.Request, logger logr.Logg if err != nil { return reconcile.Result{}, err } + r.events.Emit(jenkins, event.TypeNormal, ReasonBaseConfigurationSuccess, "Base configuration completed") logger.Info("Base configuration completed time has been updated") } - // Reconcile user configuration userConfiguration := user.New(r.client, jenkinsClient, logger, jenkins) @@ -163,12 +176,13 @@ func (r *ReconcileJenkins) reconcile(request reconcile.Request, logger logr.Logg } if !valid { logger.V(log.VWarn).Info("Validation of user configuration failed, please correct Jenkins CR") + r.events.Emit(jenkins, event.TypeWarning, ReasonCRValidationFailure, "User CR validation failed") return reconcile.Result{}, nil // don't requeue } result, err = userConfiguration.Reconcile() if err != nil { - return reconcile.Result{}, err + return reconcile.Result{}, errors.Wrap(err, "Base configuration failed") } if result.Requeue { return result, nil @@ -188,7 +202,7 @@ func (r *ReconcileJenkins) reconcile(request reconcile.Request, logger logr.Logg return reconcile.Result{}, nil } -func (r *ReconcileJenkins) buildLogger(jenkinsName string) logr.Logger { +func (*ReconcileJenkins) buildLogger(jenkinsName string) logr.Logger { return log.Log.WithValues("cr", jenkinsName) } diff --git a/pkg/event/event.go b/pkg/event/event.go new file mode 100644 index 00000000..763cc3aa --- /dev/null +++ b/pkg/event/event.go @@ -0,0 +1,71 @@ +package event + +import ( + "fmt" + + "github.com/VirtusLab/jenkins-operator/pkg/controller/jenkins/constants" + + "github.com/golang/glog" + "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/kubernetes/scheme" + typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/record" +) + +const ( + // Information only and will not cause any problems + TypeNormal = Type("Normal") + // These events are to warn that something might go wrong + TypeWarning = Type("Warning") +) + +type Type string +type Reason string + +type Recorder interface { + Emit(object runtime.Object, eventType Type, reason Reason, message string) + Emitf(object runtime.Object, eventType Type, reason Reason, format string, args ...interface{}) +} + +type recorder struct { + recorder record.EventRecorder +} + +func New(config *rest.Config) (Recorder, error) { + eventRecorder, err := initializeEventRecorder(config) + if err != nil { + return nil, err + } + + return &recorder{ + recorder: eventRecorder, + }, nil +} + +func initializeEventRecorder(config *rest.Config) (record.EventRecorder, error) { + client, err := kubernetes.NewForConfig(config) + if err != nil { + return nil, err + } + eventBroadcaster := record.NewBroadcaster() + eventBroadcaster.StartLogging(glog.Infof) + eventBroadcaster.StartRecordingToSink( + &typedcorev1.EventSinkImpl{ + Interface: client.CoreV1().Events("")}) + eventRecorder := eventBroadcaster.NewRecorder( + scheme.Scheme, + v1.EventSource{ + Component: constants.OperatorName}) + return eventRecorder, nil +} + +func (r recorder) Emit(object runtime.Object, eventType Type, reason Reason, message string) { + r.recorder.Event(object, string(eventType), string(reason), message) +} + +func (r recorder) Emitf(object runtime.Object, eventType Type, reason Reason, format string, args ...interface{}) { + r.recorder.Event(object, string(eventType), string(reason), fmt.Sprintf(format, args...)) +}