From ae1cc4941ef7a96f704ff16ae694cda455eb9640 Mon Sep 17 00:00:00 2001 From: Hy3n4 Date: Tue, 28 Nov 2023 12:43:55 +0100 Subject: [PATCH] feat(mimir): add MimirRule type - some other changes in rule creation flow - fix missing tags in osko_slo_target record rule - other changes and fixes (hard to track the mess) Signed-off-by: Hy3n4 --- api/openslo/v1/slo_types.go | 2 +- api/osko/v1alpha1/mimirrule_types.go | 31 ++- api/osko/v1alpha1/zz_generated.deepcopy.go | 7 + config/crd/bases/openslo.com_slos.yaml | 2 +- config/crd/bases/osko.dev_mimirrules.yaml | 45 +++- config/samples/osko_v1alpha1_mimirrule.yaml | 12 + .../prometheusrule_controller.go | 187 +++----------- internal/controller/openslo/slo_controller.go | 145 ++++++----- .../controller/osko/mimirrule_controller.go | 243 +++++++++++------- internal/helpers/mimirtool_helper.go | 104 +++++++- internal/utils/common_utils.go | 10 +- 11 files changed, 453 insertions(+), 335 deletions(-) create mode 100644 config/samples/osko_v1alpha1_mimirrule.yaml diff --git a/api/openslo/v1/slo_types.go b/api/openslo/v1/slo_types.go index 8d49540..43c3fe5 100644 --- a/api/openslo/v1/slo_types.go +++ b/api/openslo/v1/slo_types.go @@ -77,7 +77,7 @@ type SLOStatus struct { //+kubebuilder:object:root=true //+kubebuilder:subresource:status -//+kubebuilder:printcolumn:name="Status",type=string,JSONPath=.status.ready,description="The reason for the current status of the SLO resource" +//+kubebuilder:printcolumn:name="Ready",type=string,JSONPath=.status.ready,description="The reason for the current status of the SLO resource" //+kubebuilder:printcolumn:name="Window",type=string,JSONPath=.spec.timeWindow[0].duration,description="The time window for the SLO resource" //+kubebuilder:printcolumn:name="Age",type=date,JSONPath=.metadata.creationTimestamp,description="The time when the SLO resource was created" diff --git a/api/osko/v1alpha1/mimirrule_types.go b/api/osko/v1alpha1/mimirrule_types.go index ea2c286..c58dda3 100644 --- a/api/osko/v1alpha1/mimirrule_types.go +++ b/api/osko/v1alpha1/mimirrule_types.go @@ -1,6 +1,7 @@ package v1alpha1 import ( + "github.com/prometheus/common/model" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -15,28 +16,34 @@ type MimirRuleSpec struct { // MimirRuleStatus defines the observed state of MimirRule type MimirRuleStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file - Conditions []metav1.Condition `json:"conditions,omitempty"` + Conditions []metav1.Condition `json:"conditions,omitempty"` + LastEvaluationTime metav1.Time `json:"lastEvaluationTime,omitempty"` + Ready string `json:"ready,omitempty"` } type RuleGroup struct { - Name string `json:"name"` - SourceTenants []string `json:"source_tenants,omitempty"` - Rules []Rule `json:"rules"` + Name string `json:"name"` + SourceTenants []string `json:"source_tenants,omitempty"` + Rules []Rule `json:"rules"` + Interval model.Duration `json:"interval,omitempty"` + EvaluationDelay *model.Duration `json:"evaluation_delay,omitempty"` + Limit int `json:"limit,omitempty"` + AlignEvaluationTimeOnInterval bool `json:"align_evaluation_time_on_interval,omitempty"` } type Rule struct { - Record string `json:"record,omitempty"` - Alert string `json:"alert,omitempty"` - Expr string `json:"expr"` - For string `json:"for,omitempty"` - Labels map[string]string `json:"labels,omitempty"` - Annotations map[string]string `json:"annotations,omitempty"` + Record string `json:"record,omitempty"` + Alert string `json:"alert,omitempty"` + Expr string `json:"expr"` + For model.Duration `json:"for,omitempty"` + KeepFiringFor model.Duration `json:"keep_firing_for,omitempty"` + Labels map[string]string `json:"labels,omitempty"` + Annotations map[string]string `json:"annotations,omitempty"` } //+kubebuilder:object:root=true //+kubebuilder:subresource:status +//+kubebuilder:printcolumn:name="Ready",type=string,JSONPath=.status.ready,description="The reason for the current status of the MimirRule resource" // MimirRule is the Schema for the mimirrules API type MimirRule struct { diff --git a/api/osko/v1alpha1/zz_generated.deepcopy.go b/api/osko/v1alpha1/zz_generated.deepcopy.go index 4aeaabc..48ea7dc 100644 --- a/api/osko/v1alpha1/zz_generated.deepcopy.go +++ b/api/osko/v1alpha1/zz_generated.deepcopy.go @@ -6,6 +6,7 @@ package v1alpha1 import ( + "github.com/prometheus/common/model" "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -155,6 +156,7 @@ func (in *MimirRuleStatus) DeepCopyInto(out *MimirRuleStatus) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + in.LastEvaluationTime.DeepCopyInto(&out.LastEvaluationTime) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MimirRuleStatus. @@ -231,6 +233,11 @@ func (in *RuleGroup) DeepCopyInto(out *RuleGroup) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.EvaluationDelay != nil { + in, out := &in.EvaluationDelay, &out.EvaluationDelay + *out = new(model.Duration) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RuleGroup. diff --git a/config/crd/bases/openslo.com_slos.yaml b/config/crd/bases/openslo.com_slos.yaml index 69b31eb..26ac106 100644 --- a/config/crd/bases/openslo.com_slos.yaml +++ b/config/crd/bases/openslo.com_slos.yaml @@ -17,7 +17,7 @@ spec: - additionalPrinterColumns: - description: The reason for the current status of the SLO resource jsonPath: .status.ready - name: Status + name: Ready type: string - description: The time window for the SLO resource jsonPath: .spec.timeWindow[0].duration diff --git a/config/crd/bases/osko.dev_mimirrules.yaml b/config/crd/bases/osko.dev_mimirrules.yaml index a7ce5aa..cc9ccee 100644 --- a/config/crd/bases/osko.dev_mimirrules.yaml +++ b/config/crd/bases/osko.dev_mimirrules.yaml @@ -14,7 +14,12 @@ spec: singular: mimirrule scope: Namespaced versions: - - name: v1alpha1 + - additionalPrinterColumns: + - description: The reason for the current status of the MimirRule resource + jsonPath: .status.ready + name: Ready + type: string + name: v1alpha1 schema: openAPIV3Schema: description: MimirRule is the Schema for the mimirrules API @@ -39,6 +44,22 @@ spec: to remove/update items: properties: + align_evaluation_time_on_interval: + type: boolean + evaluation_delay: + description: Duration wraps time.Duration. It is used to parse + the custom duration format from YAML. This type should not + propagate beyond the scope of input/output processing. + format: int64 + type: integer + interval: + description: Duration wraps time.Duration. It is used to parse + the custom duration format from YAML. This type should not + propagate beyond the scope of input/output processing. + format: int64 + type: integer + limit: + type: integer name: type: string rules: @@ -53,7 +74,19 @@ spec: expr: type: string for: - type: string + description: Duration wraps time.Duration. It is used + to parse the custom duration format from YAML. This + type should not propagate beyond the scope of input/output + processing. + format: int64 + type: integer + keep_firing_for: + description: Duration wraps time.Duration. It is used + to parse the custom duration format from YAML. This + type should not propagate beyond the scope of input/output + processing. + format: int64 + type: integer labels: additionalProperties: type: string @@ -80,9 +113,6 @@ spec: description: MimirRuleStatus defines the observed state of MimirRule properties: conditions: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state - of cluster Important: Run "make" to regenerate code after modifying - this file' items: description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct @@ -150,6 +180,11 @@ spec: - type type: object type: array + lastEvaluationTime: + format: date-time + type: string + ready: + type: string type: object type: object served: true diff --git a/config/samples/osko_v1alpha1_mimirrule.yaml b/config/samples/osko_v1alpha1_mimirrule.yaml new file mode 100644 index 0000000..79ab404 --- /dev/null +++ b/config/samples/osko_v1alpha1_mimirrule.yaml @@ -0,0 +1,12 @@ +apiVersion: osko.openslo/v1alpha1 +kind: MimirRule +metadata: + labels: + app.kubernetes.io/name: mimirrule + app.kubernetes.io/instance: mimirrule-sample + app.kubernetes.io/part-of: osko + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/created-by: osko + name: mimirrule-sample +spec: + # TODO(user): Add fields here diff --git a/internal/controller/monitoring.coreos.com/prometheusrule_controller.go b/internal/controller/monitoring.coreos.com/prometheusrule_controller.go index 9cf41cd..8c9b900 100644 --- a/internal/controller/monitoring.coreos.com/prometheusrule_controller.go +++ b/internal/controller/monitoring.coreos.com/prometheusrule_controller.go @@ -2,12 +2,8 @@ package monitoringcoreoscom import ( "context" - "github.com/go-logr/logr" - mimirclient "github.com/grafana/mimir/pkg/mimirtool/client" - "github.com/grafana/mimir/pkg/mimirtool/rules/rwrulefmt" openslov1 "github.com/oskoperator/osko/api/openslo/v1" "github.com/oskoperator/osko/internal/helpers" - "github.com/oskoperator/osko/internal/mimirtool" "github.com/oskoperator/osko/internal/utils" monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -19,17 +15,13 @@ import ( "reflect" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/handler" ctrllog "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/reconcile" ) const ( - mimirRuleNamespace = "osko" - objectiveRef = ".metaData.ownerReferences.name" - datasourceRef = ".metaData.annotations.datasource" - mimirRuleFinalizer = "finalizer.mimir.osko.dev" + objectiveRef = ".metaData.ownerReferences.name" ) // PrometheusRuleReconciler reconciles a PrometheusRule object @@ -60,9 +52,6 @@ func (r *PrometheusRuleReconciler) Reconcile(ctx context.Context, req ctrl.Reque sli := &openslov1.SLI{} prometheusRule := &monitoringv1.PrometheusRule{} newPrometheusRule := &monitoringv1.PrometheusRule{} - mimirRuleGroup := &rwrulefmt.RuleGroup{} - newMimirRuleGroup := &rwrulefmt.RuleGroup{} - mimirClient := &mimirclient.MimirClient{} err := r.Get(ctx, req.NamespacedName, prometheusRule) if err != nil { @@ -97,44 +86,6 @@ func (r *PrometheusRuleReconciler) Reconcile(ctx context.Context, req ctrl.Reque return ctrl.Result{}, err } - mClient := mimirtool.MimirClientConfig{ - Address: ds.Spec.ConnectionDetails.Address, - TenantId: ds.Spec.ConnectionDetails.TargetTenant, - } - - mimirClient, err = mClient.NewMimirClient() - if err != nil { - log.Error(err, "Failed to create Mimir client") - return ctrl.Result{}, err - } - - mimirRuleGroup = r.getMimirRuleGroup(log, mimirClient, prometheusRule) - - isPrometheusRuleMarkedToBeDeleted := prometheusRule.GetDeletionTimestamp() != nil - if isPrometheusRuleMarkedToBeDeleted { - log.Info("--- PrometheusRule is marked to be deleted ---") - if utils.ContainString(prometheusRule.GetFinalizers(), mimirRuleFinalizer) { - err := r.deleteMimirRuleGroup(log, mimirClient, mimirRuleGroup) - if err != nil { - return ctrl.Result{}, err - } - log.Info("PrometheusRule finalizers completed") - controllerutil.RemoveFinalizer(prometheusRule, mimirRuleFinalizer) - err = r.Update(ctx, prometheusRule) - if err != nil { - return ctrl.Result{}, err - } - log.Info("PrometheusRule can be deleted now") - return ctrl.Result{}, nil - } - } - - if !utils.ContainString(prometheusRule.GetFinalizers(), mimirRuleFinalizer) { - if err := r.addFinalizer(log, prometheusRule); err != nil { - return ctrl.Result{}, err - } - } - if apierrors.IsNotFound(err) { log.Info("PrometheusRule not found. Let's make one.") prometheusRule, err = helpers.CreatePrometheusRule(slo, sli) @@ -145,7 +96,6 @@ func (r *PrometheusRuleReconciler) Reconcile(ctx context.Context, req ctrl.Reque r.Client, "Ready", metav1.ConditionFalse, - "FailedToCreatePrometheusRule", "Failed to create Prometheus Rule", ) if err != nil { @@ -167,118 +117,52 @@ func (r *PrometheusRuleReconciler) Reconcile(ctx context.Context, req ctrl.Reque return ctrl.Result{}, err } } else { - // This is the main logic for the PrometheusRule update - // Here we should take the existing PrometheusRule and update it with the new one - log.Info("PrometheusRule already exists, we should update it") - newPrometheusRule, err = helpers.CreatePrometheusRule(slo, sli) - if err != nil { - log.Error(err, "Failed to create new PrometheusRule") - return ctrl.Result{}, err - } - newMimirRuleGroup, err = mimirtool.NewMimirRuleGroup(prometheusRule, ds) - if err != nil { - log.Error(err, "Failed to create new Mimir rule group") - return ctrl.Result{}, err - } - - compareResult := reflect.DeepEqual(prometheusRule, newPrometheusRule) - if compareResult { - log.Info("PrometheusRule is already up to date") - return ctrl.Result{}, nil - } - - // has to be the same as for previous object, otherwise it will not be updated and throw an error - newPrometheusRule.ResourceVersion = prometheusRule.ResourceVersion - - log.Info("Updating PrometheusRule", "PrometheusRule Name", newPrometheusRule.Name, "PrometheusRule Namespace", newPrometheusRule.Namespace) - if err := r.Update(ctx, newPrometheusRule); err != nil { - log.Error(err, "Failed to update PrometheusRule") - return ctrl.Result{}, err - } + log.Info("PrometheusRule created successfully") + r.Recorder.Event(slo, "Normal", "PrometheusRuleCreated", "PrometheusRule created successfully") + slo.Status.Ready = "True" if err := r.Status().Update(ctx, slo); err != nil { - log.Error(err, "Failed to update SLO status") - slo.Status.Ready = "Failed" - if err := r.Status().Update(ctx, slo); err != nil { - log.Error(err, "Failed to update SLO ready status") - return ctrl.Result{}, err - } - return ctrl.Result{}, err - } - err := r.updateMimirRuleGroup(log, mimirClient, mimirRuleGroup, newMimirRuleGroup) - if err != nil { - log.Error(err, "Failed to update Mimir rule group") - return ctrl.Result{}, err + log.Error(err, "Failed to update SLO ready status") + return ctrl.Result{}, nil } } } - err = r.createMimirRuleGroup(log, mimirClient, prometheusRule, ds) + // Update PrometheusRule + // This is the main logic for the PrometheusRule update + // Here we should take the existing PrometheusRule and update it with the new one + log.Info("PrometheusRule already exists, we should update it") + newPrometheusRule, err = helpers.CreatePrometheusRule(slo, sli) if err != nil { - log.Error(err, "Failed to create Mimir rule") + log.Error(err, "Failed to create new PrometheusRule") return ctrl.Result{}, err } - log.Info("PrometheusRule reconciled") - return ctrl.Result{}, nil -} - -func (r *PrometheusRuleReconciler) getMimirRuleGroup(log logr.Logger, mimirClient *mimirclient.MimirClient, rule *monitoringv1.PrometheusRule) *rwrulefmt.RuleGroup { - mimirRuleGroup, err := mimirClient.GetRuleGroup(context.Background(), mimirRuleNamespace, rule.Name) - if err != nil { - log.Error(err, "Failed to get rule group") - return nil - } - - return mimirRuleGroup -} - -func (r *PrometheusRuleReconciler) createMimirRuleGroup(log logr.Logger, mimirClient *mimirclient.MimirClient, rule *monitoringv1.PrometheusRule, ds *openslov1.Datasource) error { - mimirRuleGroup, err := mimirtool.NewMimirRuleGroup(rule, ds) - if err != nil { - log.Error(err, "Failed to create Mimir rule group") - return err - } - - if err := mimirClient.CreateRuleGroup(context.Background(), mimirRuleNamespace, *mimirRuleGroup); err != nil { - log.Error(err, "Failed to create rule group") - return err + compareResult := reflect.DeepEqual(prometheusRule, newPrometheusRule) + if compareResult { + log.Info("PrometheusRule is already up to date") + return ctrl.Result{}, nil } - return nil -} + // has to be the same as for previous object, otherwise it will not be updated and throw an error + newPrometheusRule.ResourceVersion = prometheusRule.ResourceVersion -func (r *PrometheusRuleReconciler) deleteMimirRuleGroup(log logr.Logger, mimirClient *mimirclient.MimirClient, ruleGroup *rwrulefmt.RuleGroup) error { - if err := mimirClient.DeleteRuleGroup(context.Background(), mimirRuleNamespace, ruleGroup.Name); err != nil { - log.Error(err, "Failed to delete rule group") - return err - } - - return nil -} - -func (r *PrometheusRuleReconciler) updateMimirRuleGroup(log logr.Logger, mimirClient *mimirclient.MimirClient, existingGroup *rwrulefmt.RuleGroup, desiredGroup *rwrulefmt.RuleGroup) error { - log.Info("Updating Mimir rule group") - if reflect.DeepEqual(existingGroup, desiredGroup) { - log.Info("Mimir rule group is already up to date") - return nil + log.Info("Updating PrometheusRule", "PrometheusRule Name", newPrometheusRule.Name, "PrometheusRule Namespace", newPrometheusRule.Namespace) + if err := r.Update(ctx, newPrometheusRule); err != nil { + log.Error(err, "Failed to update PrometheusRule") + return ctrl.Result{}, err } - err := r.deleteMimirRuleGroup(log, mimirClient, existingGroup) - if err != nil { - return err + if err := r.Status().Update(ctx, slo); err != nil { + log.Error(err, "Failed to update SLO status") + slo.Status.Ready = "Failed" + if err := r.Status().Update(ctx, slo); err != nil { + log.Error(err, "Failed to update SLO ready status") + return ctrl.Result{}, err + } + return ctrl.Result{}, err } - return nil -} - -func (r *PrometheusRuleReconciler) addFinalizer(log logr.Logger, rule *monitoringv1.PrometheusRule) error { - log.Info("Adding Finalizer for the PrometheusRule") - controllerutil.AddFinalizer(rule, mimirRuleFinalizer) - err := r.Update(context.Background(), rule) - if err != nil { - log.Error(err, "Failed to update PrometheusRule with finalizer") - return err - } - return nil + log.Info("PrometheusRule reconciled") + return ctrl.Result{}, nil } func (r *PrometheusRuleReconciler) createIndices(mgr ctrl.Manager) error { @@ -330,8 +214,9 @@ func (r *PrometheusRuleReconciler) SetupWithManager(mgr ctrl.Manager) error { Watches( &openslov1.SLO{}, handler.EnqueueRequestsFromMapFunc(r.findObjectsForSlo()), - ).Watches( - &openslov1.Datasource{}, - handler.EnqueueRequestsFromMapFunc(r.findObjectsForSlo())). + ). + Watches( + &openslov1.Datasource{}, + handler.EnqueueRequestsFromMapFunc(r.findObjectsForSlo())). Complete(r) } diff --git a/internal/controller/openslo/slo_controller.go b/internal/controller/openslo/slo_controller.go index eef8816..873112f 100644 --- a/internal/controller/openslo/slo_controller.go +++ b/internal/controller/openslo/slo_controller.go @@ -2,8 +2,9 @@ package controller import ( "context" - "fmt" + "github.com/go-logr/logr" openslov1 "github.com/oskoperator/osko/api/openslo/v1" + oskov1alpha1 "github.com/oskoperator/osko/api/osko/v1alpha1" "github.com/oskoperator/osko/internal/helpers" "github.com/oskoperator/osko/internal/utils" monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" @@ -13,17 +14,18 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/tools/record" - "reflect" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/handler" - "sigs.k8s.io/controller-runtime/pkg/log" + ctrllog "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/reconcile" ) const ( - indicatorRef = ".spec.indicatorRef" - errGetSLO = "could not get SLO Object" + indicatorRef = ".spec.indicatorRef" + errGetSLO = "could not get SLO Object" + mimirRuleFinalizer = "finalizer.mimir.osko.dev" ) // SLOReconciler reconciles a SLO object @@ -49,13 +51,11 @@ type SLOReconciler struct { // For more details, check Reconcile and its Result here: // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.15.0/pkg/reconcile func (r *SLOReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - log := log.FromContext(ctx) + log := ctrllog.FromContext(ctx) + log.Info("Reconciling SLO") sli := &openslov1.SLI{} slo := &openslov1.SLO{} - prometheusRule := &monitoringv1.PrometheusRule{} - newPrometheusRule := &monitoringv1.PrometheusRule{} - err := r.Get(ctx, req.NamespacedName, slo) if err != nil { if apierrors.IsNotFound(err) { @@ -79,7 +79,6 @@ func (r *SLOReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.R r.Client, "Ready", metav1.ConditionFalse, - "SLIObjectNotFound", "SLI Object not found", ) if err != nil { @@ -96,18 +95,8 @@ func (r *SLOReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.R if slo.Spec.Indicator.Spec.RatioMetric != (openslov1.RatioMetricSpec{}) { sli.Spec.RatioMetric = slo.Spec.Indicator.Spec.RatioMetric } - log.Info("SLI created", "SLI Name", sli.Name, "SLI Namespace", sli.Namespace) - r.Recorder.Event(slo, "Normal", "SLICreated", fmt.Sprintf("SLI %s created", sli.Name)) } else { - err = utils.UpdateStatus( - ctx, - slo, - r.Client, - "Ready", - metav1.ConditionFalse, - "SLIObjectNotFound", - "SLI Object not found", - ) + err = utils.UpdateStatus(ctx, slo, r.Client, "Ready", metav1.ConditionFalse, "SLI Object not found") if err != nil { log.Error(err, "Failed to update SLO status") r.Recorder.Event(slo, "Error", "SLIObjectNotFound", "SLI Object not found") @@ -125,17 +114,9 @@ func (r *SLOReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.R if apierrors.IsNotFound(err) { log.Info("PrometheusRule not found. Let's make one.") - prometheusRule, err = helpers.CreatePrometheusRule(slo, sli) + promRule, err = helpers.CreatePrometheusRule(slo, sli) if err != nil { - err = utils.UpdateStatus( - ctx, - slo, - r.Client, - "Ready", - metav1.ConditionFalse, - "FailedToCreatePrometheusRule", - "Failed to create Prometheus Rule", - ) + err = utils.UpdateStatus(ctx, slo, r.Client, "Ready", metav1.ConditionFalse, "Failed to create Prometheus Rule") if err != nil { log.Error(err, "Failed to update SLO status") return ctrl.Result{}, err @@ -143,67 +124,84 @@ func (r *SLOReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.R log.Error(err, "Failed to create new PrometheusRule") return ctrl.Result{}, err } - if err := r.Create(ctx, prometheusRule); err != nil { + if err := r.Create(ctx, promRule); err != nil { r.Recorder.Event(slo, "Error", "FailedToCreatePrometheusRule", "Failed to create Prometheus Rule") - if err := r.Status().Update(ctx, prometheusRule); err != nil { + if err := r.Status().Update(ctx, promRule); err != nil { log.Error(err, "Failed to update SLO status") - slo.Status.Ready = "Failed" - if err := r.Status().Update(ctx, slo); err != nil { + if err = utils.UpdateStatus(ctx, slo, r.Client, "Ready", metav1.ConditionFalse, "Failed to create Prometheus Rule"); err != nil { log.Error(err, "Failed to update SLO ready status") return ctrl.Result{}, err } return ctrl.Result{}, err } } else { - // This is the main logic for the PrometheusRule update - // Here we should take the existing PrometheusRule and update it with the new one - log.Info("PrometheusRule already exists, we should update it") - newPrometheusRule, err = helpers.CreatePrometheusRule(slo, sli) - if err != nil { - log.Error(err, "Failed to create new PrometheusRule") + log.Info("PrometheusRule created successfully") + r.Recorder.Event(slo, "Normal", "PrometheusRuleCreated", "PrometheusRule created successfully") + slo.Status.Ready = "True" + if err := r.Status().Update(ctx, slo); err != nil { + log.Error(err, "Failed to update SLO ready status") return ctrl.Result{}, err } - compareResult := reflect.DeepEqual(prometheusRule, newPrometheusRule) - if compareResult { - log.Info("PrometheusRule is already up to date") - return ctrl.Result{}, nil - } + return ctrl.Result{}, nil + } + } - // has to be the same as for previous object, otherwise it will not be updated and throw an error - newPrometheusRule.ResourceVersion = prometheusRule.ResourceVersion + mimirRule := &oskov1alpha1.MimirRule{} + err = r.Get(ctx, types.NamespacedName{ + Name: slo.Name, + Namespace: slo.Namespace, + }, mimirRule) - log.Info("Updating PrometheusRule", "PrometheusRule Name", newPrometheusRule.Name, "PrometheusRule Namespace", newPrometheusRule.Namespace) - if err := r.Update(ctx, newPrometheusRule); err != nil { - log.Error(err, "Failed to update PrometheusRule") + if apierrors.IsNotFound(err) { + log.Info("MimirRule not found. Let's make one.") + mimirRule, err = helpers.NewMimirRule(slo, promRule) + if err != nil { + err = utils.UpdateStatus( + ctx, + slo, + r.Client, + "Ready", + metav1.ConditionFalse, + "Failed to create Mimir Rule Object", + ) + if err != nil { + log.Error(err, "Failed to update SLO status") return ctrl.Result{}, err } - if err := r.Status().Update(ctx, slo); err != nil { + log.Error(err, "Failed to create new PrometheusRule") + return ctrl.Result{}, err + } + if err = r.Create(ctx, mimirRule); err != nil { + r.Recorder.Event(slo, "Error", "FailedToCreateMimirRule", "Failed to create Mimir Rule") + if err = r.Status().Update(ctx, slo); err != nil { log.Error(err, "Failed to update SLO status") - slo.Status.Ready = "Failed" - if err := r.Status().Update(ctx, slo); err != nil { + if err = utils.UpdateStatus(ctx, slo, r.Client, "Ready", metav1.ConditionFalse, "Failed to create Mimir Rule"); err != nil { log.Error(err, "Failed to update SLO ready status") return ctrl.Result{}, err } return ctrl.Result{}, err } + } else { + log.Info("MimirRule created successfully") + r.Recorder.Event(slo, "Normal", "MimirRuleCreated", "MimirRule created successfully") + slo.Status.Ready = "True" + if err := r.Status().Update(ctx, slo); err != nil { + log.Error(err, "Failed to update SLO ready status") + return ctrl.Result{}, err + } + if !utils.ContainString(mimirRule.GetFinalizers(), mimirRuleFinalizer) { + if err := r.addFinalizer(log, mimirRule); err != nil { + return ctrl.Result{}, err + } + } + return ctrl.Result{}, nil } } - err = utils.UpdateStatus( - ctx, - slo, - r.Client, - "Ready", - metav1.ConditionTrue, - "PrometheusRuleCreated", - "PrometheusRule created", - ) - if err != nil { + + if err = utils.UpdateStatus(ctx, slo, r.Client, "Ready", metav1.ConditionTrue, "PrometheusRule created"); err != nil { return ctrl.Result{}, err } - r.Recorder.Event(slo, "Normal", "PrometheusRuleCreated", "PrometheusRule created successfully") - log.Info("Reconciling SLO") - return ctrl.Result{}, nil } @@ -246,6 +244,18 @@ func (r *SLOReconciler) findObjectsForSli() func(ctx context.Context, a client.O } } +func (r *SLOReconciler) addFinalizer(log logr.Logger, rule *oskov1alpha1.MimirRule) error { + log.Info("Adding Finalizer for the MimirRule") + controllerutil.AddFinalizer(rule, mimirRuleFinalizer) + + err := r.Update(context.Background(), rule) + if err != nil { + log.Error(err, "Failed to update MimirRule with finalizer") + return err + } + return nil +} + // SetupWithManager sets up the controller with the Manager. func (r *SLOReconciler) SetupWithManager(mgr ctrl.Manager) error { if err := r.createIndices(mgr); err != nil { @@ -254,6 +264,7 @@ func (r *SLOReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&openslov1.SLO{}). Owns(&monitoringv1.PrometheusRule{}). + Owns(&oskov1alpha1.MimirRule{}). Watches( &openslov1.SLI{}, handler.EnqueueRequestsFromMapFunc(r.findObjectsForSli()), diff --git a/internal/controller/osko/mimirrule_controller.go b/internal/controller/osko/mimirrule_controller.go index dce851b..21d3c7c 100644 --- a/internal/controller/osko/mimirrule_controller.go +++ b/internal/controller/osko/mimirrule_controller.go @@ -2,15 +2,19 @@ package osko import ( "context" + "github.com/go-logr/logr" mimirclient "github.com/grafana/mimir/pkg/mimirtool/client" "github.com/grafana/mimir/pkg/mimirtool/rules/rwrulefmt" openslov1 "github.com/oskoperator/osko/api/openslo/v1" "github.com/oskoperator/osko/internal/helpers" "github.com/oskoperator/osko/internal/mimirtool" + "github.com/oskoperator/osko/internal/utils" monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/tools/record" "reflect" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ctrllog "sigs.k8s.io/controller-runtime/pkg/log" oskov1alpha1 "github.com/oskoperator/osko/api/osko/v1alpha1" @@ -24,11 +28,18 @@ type MimirRuleReconciler struct { client.Client Scheme *runtime.Scheme Recorder record.EventRecorder + mimirclient.MimirClient } -//+kubebuilder:rbac:groups=osko.openslo,resources=mimirrules,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=osko.openslo,resources=mimirrules/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=osko.openslo,resources=mimirrules/finalizers,verbs=update +const ( + mimirRuleFinalizer = "finalizer.mimir.osko.dev" + mimirRuleNamespace = "osko" +) + +// +kubebuilder:rbac:groups=osko.openslo,resources=mimirrules,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=osko.openslo,resources=mimirrules/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=osko.openslo,resources=mimirrules/finalizers,verbs=update +// +kubebuilder:rbac:groups=core,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. @@ -43,28 +54,14 @@ func (r *MimirRuleReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( log := ctrllog.FromContext(ctx) slo := &openslov1.SLO{} - sli := &openslov1.SLI{} prometheusRule := &monitoringv1.PrometheusRule{} - newPrometheusRule := &monitoringv1.PrometheusRule{} - mimirRuleGroup := &rwrulefmt.RuleGroup{} - newMimirRuleGroup := &rwrulefmt.RuleGroup{} - mimirClient := &mimirclient.MimirClient{} mimirRule := &oskov1alpha1.MimirRule{} + newMimirRule := &oskov1alpha1.MimirRule{} - err := r.Get(ctx, req.NamespacedName, mimirRule) - if err != nil { - if apierrors.IsNotFound(err) { - log.Info("MimirRule resource not found. Ignoring since object mus be deleted") - return ctrl.Result{}, nil - } - log.Error(err, "Failed to get MimirRule") - return ctrl.Result{}, err - } - - err = r.Get(ctx, req.NamespacedName, prometheusRule) + err := r.Get(ctx, req.NamespacedName, prometheusRule) if err != nil { if apierrors.IsNotFound(err) { - log.Info("PrometheusRule resource not found. Ignoring since object mus be deleted") + log.Info("PrometheusRule resource not found. Ignoring since object must be deleted") return ctrl.Result{}, nil } log.Error(err, "Failed to get PrometheusRule") @@ -80,90 +77,90 @@ func (r *MimirRuleReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( return ctrl.Result{}, err } - mClient := mimirtool.MimirClientConfig{ - Address: ds.Spec.ConnectionDetails.Address, - TenantId: ds.Spec.ConnectionDetails.TargetTenant, - } - - mimirClient, err = mClient.NewMimirClient() + err = r.Get(ctx, req.NamespacedName, mimirRule) if err != nil { - log.Error(err, "Failed to create Mimir client") + if apierrors.IsNotFound(err) { + log.Info("MimirRule resource not found. Ignoring since object must be deleted") + return ctrl.Result{}, nil + } + log.Error(err, "Failed to get MimirRule") return ctrl.Result{}, err } - mimirRuleGroup = helpers.GetMimirRuleGroup(log, mimirClient, prometheusRule) - if apierrors.IsNotFound(err) { - log.Info("PrometheusRule not found. Let's make one.") - prometheusRule, err = helpers.CreatePrometheusRule(slo, sli) - if err != nil { - if err != nil { - log.Error(err, "Failed to update SLO status") + log.Info("MimirRule not found. Let's make one.") + mimirRule, err = helpers.NewMimirRule(slo, prometheusRule) + + if err = r.Create(ctx, mimirRule); err != nil { + r.Recorder.Event(mimirRule, "Error", "FailedToCreateMimirRule", "Failed to create Mimir Rule") + if err = r.Status().Update(ctx, mimirRule); err != nil { + log.Error(err, "Failed to update MimirRule status") return ctrl.Result{}, err - } - log.Error(err, "Failed to create new PrometheusRule") - return ctrl.Result{}, err - } - if err := r.Create(ctx, prometheusRule); err != nil { - r.Recorder.Event(slo, "Error", "FailedToCreatePrometheusRule", "Failed to create Prometheus Rule") - if err := r.Status().Update(ctx, prometheusRule); err != nil { - log.Error(err, "Failed to update SLO status") - slo.Status.Ready = "Failed" - if err := r.Status().Update(ctx, slo); err != nil { - log.Error(err, "Failed to update SLO ready status") + } else { + log.Info("MimirRule created successfully") + r.Recorder.Event(mimirRule, "Normal", "MimirRuleCreated", "MimirRule created successfully") + mimirRule.Status.Ready = "True" + if err := r.Status().Update(ctx, mimirRule); err != nil { + log.Error(err, "Failed to update MimirRule ready status") return ctrl.Result{}, err } - return ctrl.Result{}, err - } - } else { - // This is the main logic for the PrometheusRule update - // Here we should take the existing PrometheusRule and update it with the new one - log.Info("PrometheusRule already exists, we should update it") - newPrometheusRule, err = helpers.CreatePrometheusRule(slo, sli) - if err != nil { - log.Error(err, "Failed to create new PrometheusRule") - return ctrl.Result{}, err - } - newMimirRuleGroup, err = mimirtool.NewMimirRuleGroup(prometheusRule, ds) - if err != nil { - log.Error(err, "Failed to create new Mimir rule group") - return ctrl.Result{}, err - } - - compareResult := reflect.DeepEqual(prometheusRule, newPrometheusRule) - if compareResult { - log.Info("PrometheusRule is already up to date") return ctrl.Result{}, nil } + } + } - // has to be the same as for previous object, otherwise it will not be updated and throw an error - newPrometheusRule.ResourceVersion = prometheusRule.ResourceVersion - - log.Info("Updating PrometheusRule", "PrometheusRule Name", newPrometheusRule.Name, "PrometheusRule Namespace", newPrometheusRule.Namespace) - if err := r.Update(ctx, newPrometheusRule); err != nil { - log.Error(err, "Failed to update PrometheusRule") - return ctrl.Result{}, err + for _, ref := range mimirRule.ObjectMeta.OwnerReferences { + if ref.Kind == "SLO" { + sloNamespacedName := types.NamespacedName{ + Name: ref.Name, + Namespace: req.Namespace, } - if err := r.Status().Update(ctx, slo); err != nil { - log.Error(err, "Failed to update SLO status") - slo.Status.Ready = "Failed" - if err := r.Status().Update(ctx, slo); err != nil { - log.Error(err, "Failed to update SLO ready status") - return ctrl.Result{}, err - } - return ctrl.Result{}, err - } - err := helpers.UpdateMimirRuleGroup(log, mimirClient, mimirRuleGroup, newMimirRuleGroup) - if err != nil { - log.Error(err, "Failed to update Mimir rule group") + + if err := r.Get(ctx, sloNamespacedName, slo); err != nil { + log.Error(err, "Failed to get SLO") return ctrl.Result{}, err } } } - err = helpers.CreateMimirRuleGroup(log, mimirClient, prometheusRule, ds) + if !utils.ContainString(mimirRule.GetFinalizers(), mimirRuleFinalizer) { + if err := r.addFinalizer(log, mimirRule); err != nil { + return ctrl.Result{}, err + } + } + + log.Info("MmimirRule already exists, we should update it.") + newMimirRule, err = helpers.NewMimirRule(slo, prometheusRule) if err != nil { - log.Error(err, "Failed to create Mimir rule") + log.Error(err, "Failed to create new MimirRule") + return ctrl.Result{}, err + } + + compareResult := reflect.DeepEqual(mimirRule.Spec, newMimirRule.Spec) + if compareResult { + log.Info("MimirRule is up to date") + return ctrl.Result{}, nil + } + + newMimirRule.ResourceVersion = mimirRule.ResourceVersion + + if err := r.newMimirClient(ds); err != nil { + log.Error(err, "Failed to initialize Mimir client") + return ctrl.Result{}, err + } + + if err := r.Update(ctx, newMimirRule); err != nil { + log.Error(err, "Failed to update MimirRule") + mimirRule.Status.Ready = "False" + if err := r.Status().Update(ctx, mimirRule); err != nil { + log.Error(err, "Failed to update SLO status") + return ctrl.Result{}, err + } + return ctrl.Result{}, err + } + + if err = helpers.CreateMimirRuleGroupAPI(log, &r.MimirClient, &mimirRule.Spec.Groups[0], ds); err != nil { + log.Error(err, "Failed to create MimirRuleGroup") return ctrl.Result{}, err } @@ -171,6 +168,80 @@ func (r *MimirRuleReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( return ctrl.Result{}, nil } +func (r *MimirRuleReconciler) newMimirClient(ds *openslov1.Datasource) error { + mClient := mimirtool.MimirClientConfig{ + Address: ds.Spec.ConnectionDetails.Address, + TenantId: ds.Spec.ConnectionDetails.TargetTenant, + } + + mimirClient, err := mClient.NewMimirClient() + if err != nil { + return err + } + + r.MimirClient = *mimirClient + return nil +} + +func (r *MimirRuleReconciler) getMimirRuleGroup(log logr.Logger, mimirClient *mimirclient.MimirClient, rule *monitoringv1.PrometheusRule) *rwrulefmt.RuleGroup { + mimirRuleGroup, err := mimirClient.GetRuleGroup(context.Background(), mimirRuleNamespace, rule.Name) + if err != nil { + log.Error(err, "Failed to get rule group") + return nil + } + + return mimirRuleGroup +} + +func (r *MimirRuleReconciler) createMimirRuleGroup(log logr.Logger, mimirClient *mimirclient.MimirClient, rule *monitoringv1.PrometheusRule, ds *openslov1.Datasource) error { + mimirRuleGroup, err := mimirtool.NewMimirRuleGroup(rule, ds) + if err != nil { + log.Error(err, "Failed to create Mimir rule group") + return err + } + + if err := mimirClient.CreateRuleGroup(context.Background(), mimirRuleNamespace, *mimirRuleGroup); err != nil { + log.Error(err, "Failed to create rule group") + return err + } + + return nil +} + +func (r *MimirRuleReconciler) deleteMimirRuleGroup(log logr.Logger, mimirClient *mimirclient.MimirClient, ruleGroup *rwrulefmt.RuleGroup) error { + if err := mimirClient.DeleteRuleGroup(context.Background(), mimirRuleNamespace, ruleGroup.Name); err != nil { + log.Error(err, "Failed to delete rule group") + return err + } + + return nil +} + +func (r *MimirRuleReconciler) updateMimirRuleGroup(log logr.Logger, mimirClient *mimirclient.MimirClient, existingGroup *rwrulefmt.RuleGroup, desiredGroup *rwrulefmt.RuleGroup) error { + log.Info("Updating Mimir rule group") + if reflect.DeepEqual(existingGroup, desiredGroup) { + log.Info("Mimir rule group is already up to date") + return nil + } + err := r.deleteMimirRuleGroup(log, mimirClient, existingGroup) + if err != nil { + return err + } + return nil +} + +func (r *MimirRuleReconciler) addFinalizer(log logr.Logger, rule *oskov1alpha1.MimirRule) error { + log.Info("Adding Finalizer for the MimirRule") + controllerutil.AddFinalizer(rule, mimirRuleFinalizer) + + err := r.Update(context.Background(), rule) + if err != nil { + log.Error(err, "Failed to update MimirRule with finalizer") + return err + } + return nil +} + // SetupWithManager sets up the controller with the Manager. func (r *MimirRuleReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). diff --git a/internal/helpers/mimirtool_helper.go b/internal/helpers/mimirtool_helper.go index 0d80942..2ab4e12 100644 --- a/internal/helpers/mimirtool_helper.go +++ b/internal/helpers/mimirtool_helper.go @@ -6,8 +6,11 @@ import ( mimirclient "github.com/grafana/mimir/pkg/mimirtool/client" "github.com/grafana/mimir/pkg/mimirtool/rules/rwrulefmt" openslov1 "github.com/oskoperator/osko/api/openslo/v1" - "github.com/oskoperator/osko/internal/mimirtool" + oskov1alpha1 "github.com/oskoperator/osko/api/osko/v1alpha1" monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + "github.com/prometheus/prometheus/model/rulefmt" + "gopkg.in/yaml.v3" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "reflect" ) @@ -15,6 +18,80 @@ const ( mimirRuleNamespace = "osko" ) +func NewMimirRule(slo *openslov1.SLO, rule *monitoringv1.PrometheusRule) (mimirRule *oskov1alpha1.MimirRule, err error) { + ownerRef := []metav1.OwnerReference{ + *metav1.NewControllerRef( + slo, + openslov1.GroupVersion.WithKind("SLO"), + ), + } + + objectMeta := metav1.ObjectMeta{ + Name: rule.Name, + Namespace: rule.Namespace, + Labels: rule.Labels, + Annotations: rule.Annotations, + OwnerReferences: ownerRef, + } + + mimirRule = &oskov1alpha1.MimirRule{ + ObjectMeta: objectMeta, + Spec: oskov1alpha1.MimirRuleSpec{ + Groups: []oskov1alpha1.RuleGroup{ + { + Name: rule.Name, + SourceTenants: nil, + Rules: []oskov1alpha1.Rule{ + { + Record: rule.Spec.Groups[0].Rules[0].Record, + Expr: rule.Spec.Groups[0].Rules[0].Expr.String(), + Labels: rule.Spec.Groups[0].Rules[0].Labels, + Annotations: rule.Spec.Groups[0].Rules[0].Annotations, + }, + }, + }, + }, + }, + } + return mimirRule, nil +} + +func NewMimirRuleGroup(rule *monitoringv1.PrometheusRule) (*rwrulefmt.RuleGroup, error) { + var mimirRuleNodes []rulefmt.RuleNode + + for _, group := range rule.Spec.Groups { + for _, r := range group.Rules { + mimirRuleNode := rulefmt.RuleNode{ + Record: yaml.Node{ + Kind: 8, + Value: r.Record, + }, + Alert: yaml.Node{}, + Expr: yaml.Node{ + Kind: 8, + Value: r.Expr.StrVal, + }, + Labels: rule.Labels, + } + mimirRuleNodes = append(mimirRuleNodes, mimirRuleNode) + } + } + + //dsConfig := utils.DataSourceConfig{DataSource: ds} + //sourceTenants := dsConfig.ParseTenantAnnotation() + + mimirRuleGroup := &rwrulefmt.RuleGroup{ + RuleGroup: rulefmt.RuleGroup{ + Name: rule.Name, + //SourceTenants: sourceTenants, + Rules: mimirRuleNodes, + }, + RWConfigs: []rwrulefmt.RemoteWriteConfig{}, + } + + return mimirRuleGroup, nil +} + func GetMimirRuleGroup(log logr.Logger, mimirClient *mimirclient.MimirClient, rule *monitoringv1.PrometheusRule) *rwrulefmt.RuleGroup { mimirRuleGroup, err := mimirClient.GetRuleGroup(context.Background(), mimirRuleNamespace, rule.Name) if err != nil { @@ -25,14 +102,27 @@ func GetMimirRuleGroup(log logr.Logger, mimirClient *mimirclient.MimirClient, ru return mimirRuleGroup } -func CreateMimirRuleGroup(log logr.Logger, mimirClient *mimirclient.MimirClient, rule *monitoringv1.PrometheusRule, ds *openslov1.Datasource) error { - mimirRuleGroup, err := mimirtool.NewMimirRuleGroup(rule, ds) - if err != nil { - log.Error(err, "Failed to create Mimir rule group") - return err +func CreateMimirRuleGroupAPI(log logr.Logger, mimirClient *mimirclient.MimirClient, rule *oskov1alpha1.RuleGroup, ds *openslov1.Datasource) error { + mimirRule := &rwrulefmt.RuleGroup{ + RuleGroup: rulefmt.RuleGroup{ + Name: rule.Name, + Rules: []rulefmt.RuleNode{ + { + Record: yaml.Node{ + Kind: 8, + Value: rule.Rules[0].Record, + }, + Alert: yaml.Node{}, + Expr: yaml.Node{ + Kind: 8, + Value: rule.Rules[0].Expr, + }, + }, + }, + }, } - if err := mimirClient.CreateRuleGroup(context.Background(), mimirRuleNamespace, *mimirRuleGroup); err != nil { + if err := mimirClient.CreateRuleGroup(context.Background(), mimirRuleNamespace, *mimirRule); err != nil { log.Error(err, "Failed to create rule group") return err } diff --git a/internal/utils/common_utils.go b/internal/utils/common_utils.go index e83c567..545a56e 100644 --- a/internal/utils/common_utils.go +++ b/internal/utils/common_utils.go @@ -73,7 +73,7 @@ func updateCondition(conditions []metav1.Condition, newCondition metav1.Conditio } // Filter the existing condition (if it exists) - updatedConditions := []metav1.Condition{} + var updatedConditions []metav1.Condition for _, condition := range conditions { if condition.Type != newCondition.Type { updatedConditions = append(updatedConditions, condition) @@ -82,23 +82,22 @@ func updateCondition(conditions []metav1.Condition, newCondition metav1.Conditio // Append the new condition newCondition.LastTransitionTime = metav1.NewTime(time.Now()) - updatedConditions = append(updatedConditions, newCondition) return updatedConditions } -func UpdateStatus(ctx context.Context, slo *openslov1.SLO, r client.Client, conditionType string, status metav1.ConditionStatus, reason string, message string) error { +func UpdateStatus(ctx context.Context, slo *openslov1.SLO, r client.Client, conditionType string, status metav1.ConditionStatus, message string) error { // Update the conditions based on provided arguments condition := metav1.Condition{ Type: conditionType, Status: status, - Reason: reason, + Reason: string(status), Message: message, LastTransitionTime: metav1.NewTime(time.Now()), } slo.Status.Conditions = updateCondition(slo.Status.Conditions, condition) - slo.Status.Ready = reason + slo.Status.Ready = string(status) return r.Status().Update(ctx, slo) } @@ -187,6 +186,7 @@ func (c RuleConfig) NewSupportiveRule(baseRule monitoringv1.Rule) (rule monitori func (c RuleConfig) NewTargetRule() (rule monitoringv1.Rule) { rule.Record = fmt.Sprintf("osko_%s", c.Record) rule.Expr = intstr.Parse(fmt.Sprintf("vector(%s)", c.Slo.Spec.Objectives[0].Target)) + rule.Labels = c.MetricLabelCompiler.NewMetricLabelGenerator() return rule }