From 1c7a20bb4ae020d3404b90791dc802ee53b7b051 Mon Sep 17 00:00:00 2001 From: Thomas Polkowski Date: Thu, 23 Nov 2023 18:18:28 -0500 Subject: [PATCH] BOP-85/87 Add events & status to Addons & Manifests --- api/v1alpha1/addon_types.go | 30 ++++++++ api/v1alpha1/manifest_types.go | 13 ++++ api/v1alpha1/zz_generated.deepcopy.go | 6 +- .../bases/boundless.mirantis.com_addons.yaml | 26 ++++++- .../boundless.mirantis.com_manifests.yaml | 26 ++++++- config/rbac/role.yaml | 7 ++ controllers/addon_controller.go | 56 +++++++++++++- controllers/manifest_controller.go | 75 ++++++++++++++++--- main.go | 10 ++- pkg/event/event.go | 15 ++++ 10 files changed, 244 insertions(+), 20 deletions(-) create mode 100644 pkg/event/event.go diff --git a/api/v1alpha1/addon_types.go b/api/v1alpha1/addon_types.go index 1bea06ec..f409329d 100644 --- a/api/v1alpha1/addon_types.go +++ b/api/v1alpha1/addon_types.go @@ -33,14 +33,44 @@ type ManifestInfo struct { URL string `json:"url"` } +// StatusConditionType is a type of condition that may apply to a particular component. +type StatusConditionType string + +const ( + // ComponentAvailable indicates that the component is healthy. + ComponentAvailable StatusConditionType = "Available" + + // ComponentProgressing means that the component is in the process of being installed or upgraded. + ComponentProgressing StatusConditionType = "Progressing" + + // ComponentDegraded means the component is not operating as desired and user action is required. + ComponentDegraded StatusConditionType = "Degraded" + + // ComponentReady indicates that the component is healthy and ready.it is identical to Available and used in Status conditions for CRs. + ComponentReady StatusConditionType = "Ready" +) + // AddonStatus defines the observed state of Addon type AddonStatus struct { // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster // Important: Run "make" to regenerate code after modifying this file + + // The type of condition. May be Available, Progressing, or Degraded. + Type StatusConditionType `json:"type"` + + // The timestamp representing the start time for the current status. + LastTransitionTime metav1.Time `json:"lastTransitionTime"` + + // A brief reason explaining the condition. + Reason string `json:"reason,omitempty"` + + // Optionally, a detailed message providing additional context. + Message string `json:"message,omitempty"` } //+kubebuilder:object:root=true //+kubebuilder:subresource:status +//+kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.type",description="Whether the component is running and stable." // Addon is the Schema for the addons API type Addon struct { diff --git a/api/v1alpha1/manifest_types.go b/api/v1alpha1/manifest_types.go index 94500b54..3a911490 100644 --- a/api/v1alpha1/manifest_types.go +++ b/api/v1alpha1/manifest_types.go @@ -21,6 +21,18 @@ type ManifestSpec struct { type ManifestStatus struct { // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster // Important: Run "make" to regenerate code after modifying this file + + // The type of condition. May be Available, Progressing, or Degraded. + Type StatusConditionType `json:"type"` + + // The timestamp representing the start time for the current status. + LastTransitionTime metav1.Time `json:"lastTransitionTime"` + + // A brief reason explaining the condition. + Reason string `json:"reason,omitempty"` + + // Optionally, a detailed message providing additional context. + Message string `json:"message,omitempty"` } // ManifestObject consists of the fields required to update/delete an object @@ -32,6 +44,7 @@ type ManifestObject struct { //+kubebuilder:object:root=true //+kubebuilder:subresource:status +//+kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.type",description="Whether the component is running and stable." // Manifest is the Schema for the manifests API type Manifest struct { diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 1839d7ff..1b98491d 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -16,7 +16,7 @@ func (in *Addon) DeepCopyInto(out *Addon) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status + in.Status.DeepCopyInto(&out.Status) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Addon. @@ -89,6 +89,7 @@ func (in *AddonSpec) DeepCopy() *AddonSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AddonStatus) DeepCopyInto(out *AddonStatus) { *out = *in + in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AddonStatus. @@ -355,7 +356,7 @@ func (in *Manifest) DeepCopyInto(out *Manifest) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status + in.Status.DeepCopyInto(&out.Status) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Manifest. @@ -461,6 +462,7 @@ func (in *ManifestSpec) DeepCopy() *ManifestSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ManifestStatus) DeepCopyInto(out *ManifestStatus) { *out = *in + in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ManifestStatus. diff --git a/config/crd/bases/boundless.mirantis.com_addons.yaml b/config/crd/bases/boundless.mirantis.com_addons.yaml index 68b357d1..6f085f21 100644 --- a/config/crd/bases/boundless.mirantis.com_addons.yaml +++ b/config/crd/bases/boundless.mirantis.com_addons.yaml @@ -15,7 +15,12 @@ spec: singular: addon scope: Namespaced versions: - - name: v1alpha1 + - additionalPrinterColumns: + - description: Whether the component is running and stable. + jsonPath: .status.type + name: Status + type: string + name: v1alpha1 schema: openAPIV3Schema: description: Addon is the Schema for the addons API @@ -79,6 +84,25 @@ spec: type: object status: description: AddonStatus defines the observed state of Addon + properties: + lastTransitionTime: + description: The timestamp representing the start time for the current + status. + format: date-time + type: string + message: + description: Optionally, a detailed message providing additional context. + type: string + reason: + description: A brief reason explaining the condition. + type: string + type: + description: The type of condition. May be Available, Progressing, + or Degraded. + type: string + required: + - lastTransitionTime + - type type: object type: object served: true diff --git a/config/crd/bases/boundless.mirantis.com_manifests.yaml b/config/crd/bases/boundless.mirantis.com_manifests.yaml index bcde6684..1883446b 100644 --- a/config/crd/bases/boundless.mirantis.com_manifests.yaml +++ b/config/crd/bases/boundless.mirantis.com_manifests.yaml @@ -15,7 +15,12 @@ spec: singular: manifest scope: Namespaced versions: - - name: v1alpha1 + - additionalPrinterColumns: + - description: Whether the component is running and stable. + jsonPath: .status.type + name: Status + type: string + name: v1alpha1 schema: openAPIV3Schema: description: Manifest is the Schema for the manifests API @@ -62,6 +67,25 @@ spec: type: object status: description: ManifestStatus defines the observed state of Manifest + properties: + lastTransitionTime: + description: The timestamp representing the start time for the current + status. + format: date-time + type: string + message: + description: Optionally, a detailed message providing additional context. + type: string + reason: + description: A brief reason explaining the condition. + type: string + type: + description: The type of condition. May be Available, Progressing, + or Degraded. + type: string + required: + - lastTransitionTime + - type type: object type: object served: true diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 19d42820..5ecbb652 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -5,6 +5,13 @@ metadata: creationTimestamp: null name: manager-role rules: +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch - apiGroups: - boundless.mirantis.com resources: diff --git a/controllers/addon_controller.go b/controllers/addon_controller.go index 4ff7c49a..63cacc4f 100644 --- a/controllers/addon_controller.go +++ b/controllers/addon_controller.go @@ -3,9 +3,13 @@ package controllers import ( "context" "fmt" - + "github.com/go-logr/logr" + "github.com/mirantis/boundless-operator/pkg/event" "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "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" @@ -27,12 +31,14 @@ const ( // AddonReconciler reconciles a Addon object type AddonReconciler struct { client.Client - Scheme *runtime.Scheme + Scheme *runtime.Scheme + Recorder record.EventRecorder } //+kubebuilder:rbac:groups=boundless.mirantis.com,resources=addons,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=boundless.mirantis.com,resources=addons/status,verbs=get;update;patch //+kubebuilder:rbac:groups=boundless.mirantis.com,resources=addons/finalizers,verbs=update +//+kubebuilder:rbac:groups="",resources=events,verbs=create;patch // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. @@ -63,6 +69,11 @@ func (r *AddonReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl return ctrl.Result{}, err } + // only update the status to progressing if its not already set to not trigger infinite reconciliations + if instance.Status.Type == "" { + r.updateStatus(ctx, logger, req.NamespacedName, boundlessv1alpha1.ComponentProgressing, "Creating Addon") + } + switch instance.Spec.Kind { case kindChart: chart := helm.Chart{ @@ -94,6 +105,7 @@ func (r *AddonReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl if err := hc.DeleteHelmChart(chart, instance.Spec.Namespace); err != nil { // if fail to delete the helm chart here, return with error // so that it can be retried + r.Recorder.AnnotatedEventf(instance, map[string]string{event.AddonAnnotationKey: instance.Name}, event.TypeWarning, event.ReasonFailedDelete, "Failed to Delete Chart Addon %s/%s: %s", instance.Spec.Namespace, instance.Name, err) return ctrl.Result{}, err } @@ -108,11 +120,15 @@ func (r *AddonReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl return ctrl.Result{}, nil } - logger.Info("Creating Addon HelmChart resource", "Name", chart.Name, "Version", chart.Version) if err := hc.CreateHelmChart(chart, instance.Spec.Namespace); err != nil { logger.Error(err, "failed to install addon", "Name", chart.Name, "Version", chart.Version) + r.Recorder.AnnotatedEventf(instance, map[string]string{event.AddonAnnotationKey: instance.Name}, event.TypeWarning, event.ReasonFailedCreate, "Failed to Create Chart Addon %s/%s : %s", instance.Spec.Namespace, instance.Name, err) + r.updateStatus(ctx, logger, req.NamespacedName, boundlessv1alpha1.ComponentDegraded, "Failed to Create HelmChart") return ctrl.Result{Requeue: true}, err } + r.Recorder.AnnotatedEventf(instance, map[string]string{event.AddonAnnotationKey: instance.Name}, event.TypeNormal, event.ReasonSuccessfulCreate, "Created Chart Addon %s/%s", instance.Spec.Namespace, instance.Name) + r.updateStatus(ctx, logger, req.NamespacedName, boundlessv1alpha1.ComponentAvailable, "Chart Addon Created") + case kindManifest: mc := manifest.NewManifestController(r.Client, logger) @@ -133,6 +149,8 @@ func (r *AddonReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl if err := mc.DeleteManifest(BoundlessNamespace, instance.Spec.Name, instance.Spec.Manifest.URL); err != nil { // if fail to delete the manifest here, return with error // so that it can be retried + r.Recorder.AnnotatedEventf(instance, map[string]string{event.AddonAnnotationKey: instance.Name}, event.TypeWarning, event.ReasonFailedDelete, "Failed to Delete Manifest Addon %s/%s : %s", instance.Spec.Namespace, instance.Name, err) + r.updateStatus(ctx, logger, req.NamespacedName, boundlessv1alpha1.ComponentDegraded, "Failed to Cleanup Manifest") return ctrl.Result{}, err } @@ -150,9 +168,14 @@ func (r *AddonReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl err = mc.CreateManifest(BoundlessNamespace, instance.Spec.Name, instance.Spec.Manifest.URL) if err != nil { logger.Error(err, "failed to install addon via manifest", "URL", instance.Spec.Manifest.URL) + r.Recorder.AnnotatedEventf(instance, map[string]string{event.AddonAnnotationKey: instance.Name}, event.TypeWarning, event.ReasonFailedCreate, "Failed to Create Manifest Addon %s/%s : %s", instance.Spec.Namespace, instance.Name, err) + r.updateStatus(ctx, logger, req.NamespacedName, boundlessv1alpha1.ComponentDegraded, "Failed to Create Manifest") return ctrl.Result{Requeue: true}, err } + r.Recorder.AnnotatedEventf(instance, map[string]string{event.AddonAnnotationKey: instance.Name}, event.TypeNormal, event.ReasonSuccessfulCreate, "Created Manifest Addon %s/%s", instance.Spec.Namespace, instance.Name) + r.updateStatus(ctx, logger, req.NamespacedName, boundlessv1alpha1.ComponentAvailable, "Manifest Addon Created") + default: logger.Info("Unknown AddOn kind", "Kind", instance.Spec.Kind) return ctrl.Result{Requeue: false}, fmt.Errorf("Unknown AddOn Kind: %w", err) @@ -168,3 +191,30 @@ func (r *AddonReconciler) SetupWithManager(mgr ctrl.Manager) error { For(&boundlessv1alpha1.Addon{}). Complete(r) } + +func (r *AddonReconciler) updateStatus(ctx context.Context, logger logr.Logger, namespacedName types.NamespacedName, conditionTypeToApply boundlessv1alpha1.StatusConditionType, reasonToApply string, messageToApply ...string) error { + addon := &boundlessv1alpha1.Addon{} + err := r.Get(ctx, namespacedName, addon) + if err != nil { + logger.Error(err, "Failed to get addon to update status") + return err + } + + if addon.Status.Type == conditionTypeToApply && addon.Status.Reason == reasonToApply { + // avoid infinite reconciliation loops + logger.Info("No updates to status needed") + return nil + } + + logger.Info("Update status for addon", "Name", addon.Name) + + patch := client.MergeFrom(addon.DeepCopy()) + addon.Status.Type = conditionTypeToApply + addon.Status.Reason = reasonToApply + if len(messageToApply) > 0 { + addon.Status.Message = messageToApply[0] + } + addon.Status.LastTransitionTime = metav1.Now() + + return r.Status().Patch(ctx, addon, patch) +} diff --git a/controllers/manifest_controller.go b/controllers/manifest_controller.go index 8d241cea..f96cf256 100644 --- a/controllers/manifest_controller.go +++ b/controllers/manifest_controller.go @@ -2,7 +2,10 @@ package controllers import ( "context" + "fmt" + "github.com/mirantis/boundless-operator/pkg/event" "io" + "k8s.io/client-go/tools/record" "net/http" "strings" "time" @@ -32,7 +35,8 @@ import ( // ManifestReconciler reconciles a Manifest object type ManifestReconciler struct { client.Client - Scheme *runtime.Scheme + Scheme *runtime.Scheme + Recorder record.EventRecorder } // The checkSum map stores the checksum for each manifest. @@ -43,6 +47,7 @@ var checkSum = make(map[string]string) //+kubebuilder:rbac:groups=boundless.mirantis.com,resources=manifests,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=boundless.mirantis.com,resources=manifests/status,verbs=get;update;patch //+kubebuilder:rbac:groups=boundless.mirantis.com,resources=manifests/finalizers,verbs=update +//+kubebuilder:rbac:groups="",resources=events,verbs=create;patch // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. @@ -77,6 +82,14 @@ func (r *ManifestReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c } } + // only update the status to progressing if its not already set to not trigger infinite reconciliations + if existing.Status.Type == "" { + err := r.updateStatus(ctx, logger, key, boundlessv1alpha1.ComponentProgressing, "Creating Manifest") + if err != nil { + return ctrl.Result{Requeue: true}, err + } + } + addonFinalizerName := "manifest/finalizer" if existing.ObjectMeta.DeletionTimestamp.IsZero() { @@ -87,7 +100,7 @@ func (r *ManifestReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c controllerutil.AddFinalizer(existing, addonFinalizerName) if err := r.Update(ctx, existing); err != nil { logger.Info("failed to update manifest object with finalizer", "Name", req.Name, "Finalizer", addonFinalizerName) - return ctrl.Result{}, err + return ctrl.Result{Requeue: true}, err } } } else { @@ -97,7 +110,9 @@ func (r *ManifestReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c if err := r.DeleteManifestObjects(req, ctx); err != nil { logger.Error(err, "failed to remove finalizer") - return ctrl.Result{}, err + r.Recorder.AnnotatedEventf(existing, map[string]string{event.AddonAnnotationKey: existing.Name}, event.TypeWarning, event.ReasonFailedCreate, "Failed to Delete Manifest %s/%s", existing.Namespace, existing.Name) + r.updateStatus(ctx, logger, key, boundlessv1alpha1.ComponentDegraded, "Failed to remove finalizer", fmt.Sprintf("Failed to remove finalizer : %s", err)) + return ctrl.Result{Requeue: true}, err } // delete checksum entry @@ -107,7 +122,7 @@ func (r *ManifestReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c controllerutil.RemoveFinalizer(existing, addonFinalizerName) if err := r.Update(ctx, existing); err != nil { logger.Error(err, "failed to remove finalizer") - return ctrl.Result{}, err + return ctrl.Result{Requeue: true}, err } } @@ -123,7 +138,7 @@ func (r *ManifestReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c checkSum[req.Name] = existing.Spec.Checksum } else { // Present. Compare it with the new request. - if sum == existing.Spec.Checksum { + if sum == existing.Spec.Checksum && existing.Status.Type == boundlessv1alpha1.ComponentAvailable { // Do nothing logger.Info("checksum is same, no update needed", "Cache", sum, "Object", existing.Spec.Checksum) return ctrl.Result{}, nil @@ -140,7 +155,9 @@ func (r *ManifestReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c resp, err := client.Get(existing.Spec.Url) if err != nil { logger.Error(err, "failed to fetch manifest file content for url: %s", existing.Spec.Url) - return ctrl.Result{}, err + r.Recorder.AnnotatedEventf(existing, map[string]string{event.AddonAnnotationKey: existing.Name}, event.TypeWarning, event.ReasonFailedCreate, "Failed to Create Manifest %s/%s : %s", existing.Namespace, existing.Name, err.Error()) + r.updateStatus(ctx, logger, key, boundlessv1alpha1.ComponentDegraded, "Failed to Get Spec URL", fmt.Sprintf("Failed to get Spec URL : %s", err)) + return ctrl.Result{Requeue: true}, err } defer resp.Body.Close() @@ -150,17 +167,30 @@ func (r *ManifestReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c bodyBytes, err = io.ReadAll(resp.Body) if err != nil { logger.Error(err, "failed to read http response body") - return ctrl.Result{}, err + r.Recorder.AnnotatedEventf(existing, map[string]string{event.AddonAnnotationKey: existing.Name}, event.TypeWarning, event.ReasonFailedCreate, "Failed to Create Manifest %s/%s : %s", existing.Namespace, existing.Name, err.Error()) + r.updateStatus(ctx, logger, key, boundlessv1alpha1.ComponentDegraded, "Failed to Read HTTP Response Body", fmt.Sprintf("Failed to Read HTTP Response Body : %s", err)) + return ctrl.Result{Requeue: true}, err } } else { logger.Error(err, "failure in http get request", "ResponseCode", resp.StatusCode) - return ctrl.Result{}, err + r.Recorder.AnnotatedEventf(existing, map[string]string{event.AddonAnnotationKey: existing.Name}, event.TypeWarning, event.ReasonFailedCreate, "Failed to Create Manifest %s/%s : failure in http get request response code : %d", existing.Namespace, existing.Name, resp.StatusCode) + r.updateStatus(ctx, logger, key, boundlessv1alpha1.ComponentDegraded, "Failure in http get request", fmt.Sprintf("Failure in http get request : %d", resp.StatusCode)) + return ctrl.Result{Requeue: true}, err } err = r.CreateManifestObjects(req, bodyBytes, logger, ctx) if err != nil { logger.Error(err, "failed to create manifest objects", "ResponseCode", resp.StatusCode) - return ctrl.Result{}, err + r.Recorder.AnnotatedEventf(existing, map[string]string{event.AddonAnnotationKey: existing.Name}, event.TypeWarning, event.ReasonFailedCreate, "Failed to Create Manifest %s/%s : %s", existing.Namespace, existing.Name, err) + r.updateStatus(ctx, logger, key, boundlessv1alpha1.ComponentDegraded, "Failed to create manifest objects", fmt.Sprintf("Failed to create manifest objects : %d, %s", resp.StatusCode, err)) + return ctrl.Result{Requeue: true}, err + } + + r.Recorder.AnnotatedEventf(existing, map[string]string{event.AddonAnnotationKey: existing.Name}, event.TypeNormal, event.ReasonSuccessfulCreate, "Created Manifest %s/%s", existing.Namespace, existing.Name) + err = r.updateStatus(ctx, logger, key, boundlessv1alpha1.ComponentAvailable, "Manifest Created") + if err != nil { + logger.Error(err, "Failed to update status after manifest creation") + return ctrl.Result{Requeue: true}, err } return ctrl.Result{}, nil @@ -1159,3 +1189,30 @@ func (r *ManifestReconciler) deleteValidatingWebhookObject(val boundlessv1alpha1 return nil } + +func (r *ManifestReconciler) updateStatus(ctx context.Context, logger logr.Logger, namespacedName types.NamespacedName, typeToApply boundlessv1alpha1.StatusConditionType, reasonToApply string, messageToApply ...string) error { + manifest := &boundlessv1alpha1.Manifest{} + err := r.Get(ctx, namespacedName, manifest) + if err != nil { + logger.Error(err, "Failed to get manifest to update status") + return err + } + + if manifest.Status.Type == typeToApply && manifest.Status.Reason == reasonToApply { + // avoid infinite reconciliation loops + logger.Info("No updates to status needed") + return nil + } + + logger.Info("Update status for manifest", "Name", manifest.Name) + + patch := client.MergeFrom(manifest.DeepCopy()) + manifest.Status.Type = typeToApply + manifest.Status.Reason = reasonToApply + if len(messageToApply) > 0 { + manifest.Status.Message = messageToApply[0] + } + manifest.Status.LastTransitionTime = metav1.Now() + + return r.Status().Patch(ctx, manifest, patch) +} diff --git a/main.go b/main.go index 02c88bc4..434345aa 100644 --- a/main.go +++ b/main.go @@ -79,8 +79,9 @@ func main() { } if err = (&controllers.AddonReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Recorder: mgr.GetEventRecorderFor("addon controller"), }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Addon") os.Exit(1) @@ -100,8 +101,9 @@ func main() { os.Exit(1) } if err = (&controllers.ManifestReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Recorder: mgr.GetEventRecorderFor("manifest controller"), }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Manifest") os.Exit(1) diff --git a/pkg/event/event.go b/pkg/event/event.go new file mode 100644 index 00000000..0b561a4c --- /dev/null +++ b/pkg/event/event.go @@ -0,0 +1,15 @@ +package event + +const AddonAnnotationKey = "Addon" + +const ReasonSuccessfulCreate = "SuccessfulCreate" +const ReasonSuccessfulDelete = "SuccessfulDelete" +const ReasonFailedCreate = "FailedCreate" +const ReasonFailedDelete = "FailedDelete" + +const TypeWarning = "Warning" +const TypeNormal = "Normal" + +func createEvent() { + +}