diff --git a/Makefile b/Makefile index e0e1ee3..428cf90 100644 --- a/Makefile +++ b/Makefile @@ -74,6 +74,10 @@ build: manifests generate fmt vet ## Build manager binary. run: manifests generate fmt vet ## Run a controller from your host. go run ./cmd/main.go +.PHONY: run-pretty-debug +run-pretty-debug: manifests generate fmt vet ## Run a controller from your host with pretty debug output. + go run ./cmd/main.go --zap-log-level=debug 2>&1 | zap-pretty + # If you wish built the manager image targeting other platforms you can use the --platform flag. # (i.e. docker build --platform linux/arm64 ). However, you must enable docker buildKit for it. # More info: https://docs.docker.com/develop/develop-images/build_enhancements/ diff --git a/api/osko/v1alpha1/mimirrule_types.go b/api/osko/v1alpha1/mimirrule_types.go index c58dda3..97a45b2 100644 --- a/api/osko/v1alpha1/mimirrule_types.go +++ b/api/osko/v1alpha1/mimirrule_types.go @@ -44,6 +44,7 @@ type Rule struct { //+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" +//+kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // MimirRule is the Schema for the mimirrules API type MimirRule struct { diff --git a/config/crd/bases/osko.dev_mimirrules.yaml b/config/crd/bases/osko.dev_mimirrules.yaml index cc9ccee..c3a5807 100644 --- a/config/crd/bases/osko.dev_mimirrules.yaml +++ b/config/crd/bases/osko.dev_mimirrules.yaml @@ -19,6 +19,9 @@ spec: jsonPath: .status.ready name: Ready type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date name: v1alpha1 schema: openAPIV3Schema: diff --git a/internal/controller/openslo/slo_controller.go b/internal/controller/openslo/slo_controller.go index 873112f..3583b01 100644 --- a/internal/controller/openslo/slo_controller.go +++ b/internal/controller/openslo/slo_controller.go @@ -59,7 +59,7 @@ func (r *SLOReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.R err := r.Get(ctx, req.NamespacedName, slo) if err != nil { if apierrors.IsNotFound(err) { - log.Info("SLO resource not found. Object must have been deleted.") + log.V(1).Info("SLO resource not found. Object must have been deleted.") return ctrl.Result{}, nil } log.Error(err, errGetSLO) @@ -73,14 +73,7 @@ func (r *SLOReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.R apierrors.IsNotFound(err) { log.Error(err, errGetSLI) - err = utils.UpdateStatus( - ctx, - slo, - r.Client, - "Ready", - metav1.ConditionFalse, - "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") return ctrl.Result{}, err @@ -89,7 +82,7 @@ func (r *SLOReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.R } } } else if slo.Spec.Indicator != nil { - log.Info("SLO has an inline SLI") + log.V(1).Info("SLO has an inline SLI") sli.Name = slo.Spec.Indicator.Metadata.Name sli.Spec.Description = slo.Spec.Indicator.Spec.Description if slo.Spec.Indicator.Spec.RatioMetric != (openslov1.RatioMetricSpec{}) { @@ -156,15 +149,7 @@ func (r *SLOReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.R 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 { + if err = utils.UpdateStatus(ctx, slo, r.Client, "Ready", metav1.ConditionFalse, "Failed to create Mimir Rule Object"); err != nil { log.Error(err, "Failed to update SLO status") return ctrl.Result{}, err } diff --git a/internal/controller/osko/mimirrule_controller.go b/internal/controller/osko/mimirrule_controller.go index 21d3c7c..3dc50be 100644 --- a/internal/controller/osko/mimirrule_controller.go +++ b/internal/controller/osko/mimirrule_controller.go @@ -7,9 +7,10 @@ import ( "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" + "github.com/prometheus/prometheus/model/rulefmt" + "gopkg.in/yaml.v3" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/tools/record" @@ -26,9 +27,9 @@ import ( // MimirRuleReconciler reconciles a MimirRule object type MimirRuleReconciler struct { client.Client - Scheme *runtime.Scheme - Recorder record.EventRecorder - mimirclient.MimirClient + Scheme *runtime.Scheme + Recorder record.EventRecorder + MimirClient *mimirclient.MimirClient } const ( @@ -53,6 +54,7 @@ const ( func (r *MimirRuleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log := ctrllog.FromContext(ctx) + ds := &openslov1.Datasource{} slo := &openslov1.SLO{} prometheusRule := &monitoringv1.PrometheusRule{} mimirRule := &oskov1alpha1.MimirRule{} @@ -68,7 +70,6 @@ func (r *MimirRuleReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( return ctrl.Result{}, err } - ds := &openslov1.Datasource{} if err := r.Get(ctx, client.ObjectKey{ Namespace: prometheusRule.Namespace, Name: "logging-ds", @@ -77,6 +78,24 @@ func (r *MimirRuleReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( return ctrl.Result{}, err } + log.Info("Datasource found", "Datasource", ds) + + if err := r.newMimirClient(ds); err != nil { + log.Error(err, "Failed to create MimirClient") + return ctrl.Result{}, err + } + + rgs, err := helpers.NewMimirRuleGroup(prometheusRule) + if err != nil { + log.Error(err, "Failed to convert MimirRuleGroup") + return ctrl.Result{}, err + } + + if err := r.createMimirRuleGroupAPI(log, rgs); err != nil { + log.Error(err, "Failed to create MimirRuleGroup") + return ctrl.Result{}, err + } + err = r.Get(ctx, req.NamespacedName, mimirRule) if err != nil { if apierrors.IsNotFound(err) { @@ -144,11 +163,6 @@ func (r *MimirRuleReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( 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" @@ -159,48 +173,55 @@ func (r *MimirRuleReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( 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 - } - log.Info("MimirRule reconciled") return ctrl.Result{}, nil } func (r *MimirRuleReconciler) newMimirClient(ds *openslov1.Datasource) error { - mClient := mimirtool.MimirClientConfig{ + mClientConfig := helpers.MimirClientConfig{ Address: ds.Spec.ConnectionDetails.Address, TenantId: ds.Spec.ConnectionDetails.TargetTenant, } - mimirClient, err := mClient.NewMimirClient() + mimirClient, err := mClientConfig.NewMimirClient() if err != nil { return err } - r.MimirClient = *mimirClient + 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 +func (r *MimirRuleReconciler) createMimirRuleGroupAPI(log logr.Logger, rule *oskov1alpha1.RuleGroup) error { + var mimirRuleNodes []rulefmt.RuleNode + for _, r := range rule.Rules { + mimirRuleNode := rulefmt.RuleNode{ + Record: yaml.Node{ + Kind: 8, + Value: r.Record, + }, + Alert: yaml.Node{}, + Expr: yaml.Node{ + Kind: 8, + Value: r.Expr, + }, + Labels: r.Labels, + } + mimirRuleNodes = append(mimirRuleNodes, mimirRuleNode) } - return mimirRuleGroup -} + log.Info("Source tenants", "SourceTenants", rule.SourceTenants) -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 + mimirRule := rwrulefmt.RuleGroup{ + RuleGroup: rulefmt.RuleGroup{ + Name: rule.Name, + Rules: mimirRuleNodes, + SourceTenants: rule.SourceTenants, + }, } - if err := mimirClient.CreateRuleGroup(context.Background(), mimirRuleNamespace, *mimirRuleGroup); err != nil { + if err := r.MimirClient.CreateRuleGroup(context.Background(), mimirRuleNamespace, mimirRule); err != nil { log.Error(err, "Failed to create rule group") return err } @@ -208,6 +229,31 @@ func (r *MimirRuleReconciler) createMimirRuleGroup(log logr.Logger, mimirClient return nil } +func (r *MimirRuleReconciler) getMimirRuleGroupAPI(log logr.Logger, rule *monitoringv1.PrometheusRule) *rwrulefmt.RuleGroup { + mimirRuleGroup, err := r.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 := helpers.NewMimirRuleGroup(rule) +// 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") diff --git a/internal/helpers/mimirtool_helper.go b/internal/helpers/mimirtool_helper.go index 2ab4e12..d833868 100644 --- a/internal/helpers/mimirtool_helper.go +++ b/internal/helpers/mimirtool_helper.go @@ -8,8 +8,6 @@ import ( openslov1 "github.com/oskoperator/osko/api/openslo/v1" 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" ) @@ -18,6 +16,20 @@ const ( mimirRuleNamespace = "osko" ) +type MimirClientConfig struct { + Address string + TenantId string +} + +func (m *MimirClientConfig) NewMimirClient() (*mimirclient.MimirClient, error) { + return mimirclient.New( + mimirclient.Config{ + Address: m.Address, + ID: m.TenantId, + }, + ) +} + func NewMimirRule(slo *openslov1.SLO, rule *monitoringv1.PrometheusRule) (mimirRule *oskov1alpha1.MimirRule, err error) { ownerRef := []metav1.OwnerReference{ *metav1.NewControllerRef( @@ -34,21 +46,30 @@ func NewMimirRule(slo *openslov1.SLO, rule *monitoringv1.PrometheusRule) (mimirR OwnerReferences: ownerRef, } + var mimirRules []oskov1alpha1.Rule + + for _, group := range rule.Spec.Groups { + for _, r := range group.Rules { + mimirRuleNode := oskov1alpha1.Rule{ + Record: r.Record, + Expr: r.Expr.String(), + Labels: r.Labels, + } + mimirRules = append(mimirRules, mimirRuleNode) + } + } + 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, - }, + Name: rule.Name, + SourceTenants: []string{ + "infra", + "logging", }, + Rules: mimirRules, }, }, }, @@ -56,37 +77,31 @@ func NewMimirRule(slo *openslov1.SLO, rule *monitoringv1.PrometheusRule) (mimirR return mimirRule, nil } -func NewMimirRuleGroup(rule *monitoringv1.PrometheusRule) (*rwrulefmt.RuleGroup, error) { - var mimirRuleNodes []rulefmt.RuleNode +func NewMimirRuleGroup(rule *monitoringv1.PrometheusRule) (*oskov1alpha1.RuleGroup, error) { + var mimirRules []oskov1alpha1.Rule 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, + mimirRuleNode := oskov1alpha1.Rule{ + Record: r.Record, + Expr: r.Expr.String(), + Labels: r.Labels, } - mimirRuleNodes = append(mimirRuleNodes, mimirRuleNode) + mimirRules = append(mimirRules, mimirRuleNode) } } //dsConfig := utils.DataSourceConfig{DataSource: ds} - //sourceTenants := dsConfig.ParseTenantAnnotation() + sourceTenants := []string{ + "infra", + "logging", + "billing", + } - mimirRuleGroup := &rwrulefmt.RuleGroup{ - RuleGroup: rulefmt.RuleGroup{ - Name: rule.Name, - //SourceTenants: sourceTenants, - Rules: mimirRuleNodes, - }, - RWConfigs: []rwrulefmt.RemoteWriteConfig{}, + mimirRuleGroup := &oskov1alpha1.RuleGroup{ + Name: rule.Name, + Rules: mimirRules, + SourceTenants: sourceTenants, } return mimirRuleGroup, nil @@ -102,34 +117,6 @@ func GetMimirRuleGroup(log logr.Logger, mimirClient *mimirclient.MimirClient, ru return mimirRuleGroup } -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, *mimirRule); err != nil { - log.Error(err, "Failed to create rule group") - return err - } - - return nil -} - func 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) { diff --git a/internal/helpers/prometheus_helper.go b/internal/helpers/prometheus_helper.go index 66f4969..adfb864 100644 --- a/internal/helpers/prometheus_helper.go +++ b/internal/helpers/prometheus_helper.go @@ -9,13 +9,17 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" ) +const ( + RecordPrefix = "osko" +) + func CreatePrometheusRule(slo *openslov1.SLO, sli *openslov1.SLI) (*monitoringv1.PrometheusRule, error) { var monitoringRules []monitoringv1.Rule var targetVector monitoringv1.Rule defaultRateWindow := "1m" //burnRateTimeWindows := []string{"1h", "6h", "3d"} sloTimeWindowDuration := string(slo.Spec.TimeWindow[0].Duration) - m := utils.MetricLabelParams{Slo: slo, Sli: sli} + m := utils.MetricLabel{Slo: slo, Sli: sli} targetVector.Record = "osko_slo_target" targetVector.Expr = intstr.Parse(fmt.Sprintf("vector(%s)", slo.Spec.Objectives[0].Value)) @@ -24,7 +28,7 @@ func CreatePrometheusRule(slo *openslov1.SLO, sli *openslov1.SLI) (*monitoringv1 // for now, total and good are required. bad is optional and is calculated as (total - good) if not provided // TODO: validate that the SLO budgeting method is Occurrences and that the SLIs are all ratio metrics in other case throw an error - targetVectorConfig := utils.RuleConfig{ + targetVectorConfig := utils.Rule{ Record: "slo_target", Expr: "", TimeWindow: sloTimeWindowDuration, @@ -33,7 +37,7 @@ func CreatePrometheusRule(slo *openslov1.SLO, sli *openslov1.SLI) (*monitoringv1 MetricLabelCompiler: &m, } - totalRule28Config := utils.RuleConfig{ + totalRule28Config := utils.Rule{ RuleType: "total", Record: "sli_ratio_total", Expr: "sum(increase(%s[%s]))", @@ -43,7 +47,7 @@ func CreatePrometheusRule(slo *openslov1.SLO, sli *openslov1.SLI) (*monitoringv1 MetricLabelCompiler: &m, } - goodRule28Config := utils.RuleConfig{ + goodRule28Config := utils.Rule{ RuleType: "good", Record: "sli_ratio_total", Expr: "sum(increase(%s[%s]))", @@ -53,7 +57,7 @@ func CreatePrometheusRule(slo *openslov1.SLO, sli *openslov1.SLI) (*monitoringv1 MetricLabelCompiler: &m, } - badRule28Config := utils.RuleConfig{ + badRule28Config := utils.Rule{ RuleType: "bad", Record: "sli_ratio_total", Expr: "sum(increase(%s[%s]))", @@ -63,7 +67,7 @@ func CreatePrometheusRule(slo *openslov1.SLO, sli *openslov1.SLI) (*monitoringv1 MetricLabelCompiler: &m, } - totalRuleConfig := utils.RuleConfig{ + totalRuleConfig := utils.Rule{ RuleType: "total", Record: "sli_ratio_total", Expr: "sum(increase(%s[%s]))", @@ -74,7 +78,7 @@ func CreatePrometheusRule(slo *openslov1.SLO, sli *openslov1.SLI) (*monitoringv1 MetricLabelCompiler: &m, } - goodRuleConfig := utils.RuleConfig{ + goodRuleConfig := utils.Rule{ RuleType: "good", Record: "sli_ratio_good", Expr: "sum(increase(%s[%s]))", @@ -85,7 +89,7 @@ func CreatePrometheusRule(slo *openslov1.SLO, sli *openslov1.SLI) (*monitoringv1 MetricLabelCompiler: &m, } - badRuleConfig := utils.RuleConfig{ + badRuleConfig := utils.Rule{ RuleType: "bad", Record: "sli_ratio_bad", Expr: "sum(increase(%s[%s]))", @@ -96,16 +100,17 @@ func CreatePrometheusRule(slo *openslov1.SLO, sli *openslov1.SLI) (*monitoringv1 MetricLabelCompiler: &m, } - errorBudgetRuleConfig := utils.BudgetRuleConfig{ + errorBudgetRuleConfig := utils.BudgetRule{ Record: "error_budget_available", Slo: slo, Sli: sli, TargetRuleConfig: &targetVectorConfig, TotalRuleConfig: &totalRuleConfig, BadRuleConfig: &badRuleConfig, + GoodRuleConfig: &goodRuleConfig, } - configs := []utils.RuleConfig{ + configs := []utils.Rule{ totalRuleConfig, goodRuleConfig, badRuleConfig, diff --git a/internal/mimirtool/mimirtool.go b/internal/mimirtool/mimirtool.go deleted file mode 100644 index 20b508f..0000000 --- a/internal/mimirtool/mimirtool.go +++ /dev/null @@ -1,61 +0,0 @@ -package mimirtool - -import ( - mimirtool "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/utils" - monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" - "github.com/prometheus/prometheus/model/rulefmt" - "gopkg.in/yaml.v3" -) - -type MimirClientConfig struct { - Address string - TenantId string -} - -func (m *MimirClientConfig) NewMimirClient() (*mimirtool.MimirClient, error) { - return mimirtool.New( - mimirtool.Config{ - Address: m.Address, - ID: m.TenantId, - }, - ) -} - -func NewMimirRuleGroup(rule *monitoringv1.PrometheusRule, ds *openslov1.Datasource) (*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 -} diff --git a/internal/utils/common_utils.go b/internal/utils/common_utils.go index 545a56e..90e6235 100644 --- a/internal/utils/common_utils.go +++ b/internal/utils/common_utils.go @@ -9,17 +9,18 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" "reflect" "sigs.k8s.io/controller-runtime/pkg/client" + ctrllog "sigs.k8s.io/controller-runtime/pkg/log" "time" ) -type MetricLabelParams struct { +type MetricLabel struct { Slo *openslov1.SLO Sli *openslov1.SLI TimeWindow string Labels map[string]string } -type RuleConfig struct { +type Rule struct { Sli *openslov1.SLI Slo *openslov1.SLO BaseRule *monitoringv1.Rule @@ -28,17 +29,18 @@ type RuleConfig struct { Expr string RateWindow string TimeWindow string - SupportiveRule *RuleConfig - MetricLabelCompiler *MetricLabelParams + SupportiveRule *Rule + MetricLabelCompiler *MetricLabel } -type BudgetRuleConfig struct { +type BudgetRule struct { Record string Sli *openslov1.SLI Slo *openslov1.SLO - TotalRuleConfig *RuleConfig - BadRuleConfig *RuleConfig - TargetRuleConfig *RuleConfig + TotalRuleConfig *Rule + BadRuleConfig *Rule + GoodRuleConfig *Rule + TargetRuleConfig *Rule } type DataSourceConfig struct { @@ -46,10 +48,11 @@ type DataSourceConfig struct { } const ( - TypeTotal = "total" - TypeBad = "bad" - TypeGood = "good" - ExprFmt = "sum(increase(%s[%s]))" + RecordPrefix = "osko" + TypeTotal = "total" + TypeBad = "bad" + TypeGood = "good" + ExprFmt = "sum(increase(%s[%s]))" ) // UpdateCondition checks if the condition of the given type is already in the slice @@ -101,7 +104,7 @@ func UpdateStatus(ctx context.Context, slo *openslov1.SLO, r client.Client, cond return r.Status().Update(ctx, slo) } -func (m MetricLabelParams) NewMetricLabelCompiler(rule *monitoringv1.Rule, window string) string { +func (m MetricLabel) NewMetricLabelCompiler(rule *monitoringv1.Rule, window string) string { labelString := "" emptyRule := monitoringv1.Rule{} if !reflect.DeepEqual(rule, emptyRule) { @@ -122,7 +125,7 @@ func (m MetricLabelParams) NewMetricLabelCompiler(rule *monitoringv1.Rule, windo return labelString } -func (m MetricLabelParams) NewMetricLabelGenerator() map[string]string { +func (m MetricLabel) NewMetricLabelGenerator() map[string]string { window := string(m.Slo.Spec.TimeWindow[0].Duration) if m.TimeWindow != "" { window = m.TimeWindow @@ -135,7 +138,7 @@ func (m MetricLabelParams) NewMetricLabelGenerator() map[string]string { } } -func (c RuleConfig) getFieldsByType() (string, error) { +func (c Rule) getFieldsByType() (string, error) { switch c.RuleType { case TypeTotal: return c.Sli.Spec.RatioMetric.Total.MetricSource.Spec, nil @@ -148,8 +151,8 @@ func (c RuleConfig) getFieldsByType() (string, error) { } } -func (c RuleConfig) NewRatioRule(window string) (*monitoringv1.Rule, *monitoringv1.Rule) { - +func (c Rule) NewRatioRule(window string) (*monitoringv1.Rule, *monitoringv1.Rule) { + // field, err := c.getFieldsByType() if err != nil || field == "" { return nil, nil @@ -158,7 +161,7 @@ func (c RuleConfig) NewRatioRule(window string) (*monitoringv1.Rule, *monitoring expr := fmt.Sprintf(ExprFmt, field, window) rule := monitoringv1.Rule{ - Record: fmt.Sprintf("osko_%s", c.Record), + Record: fmt.Sprintf("%s_%s", RecordPrefix, c.Record), Expr: intstr.Parse(expr), } @@ -171,8 +174,8 @@ func (c RuleConfig) NewRatioRule(window string) (*monitoringv1.Rule, *monitoring return &rule, &supportiveRule } -func (c RuleConfig) NewSupportiveRule(baseRule monitoringv1.Rule) (rule monitoringv1.Rule) { - rule.Record = fmt.Sprintf("osko_%s", c.Record) +func (c Rule) NewSupportiveRule(baseRule monitoringv1.Rule) (rule monitoringv1.Rule) { + rule.Record = fmt.Sprintf("%s_%s", RecordPrefix, c.Record) labels := c.SupportiveRule.MetricLabelCompiler.NewMetricLabelCompiler(&baseRule, baseRule.Labels["window"]) expr := fmt.Sprintf("sum(increase(%s{%s}[%s])) by (service, sli_name, slo_name)", baseRule.Record, labels, c.SupportiveRule.TimeWindow) rule.Expr = intstr.Parse(expr) @@ -183,22 +186,34 @@ func (c RuleConfig) NewSupportiveRule(baseRule monitoringv1.Rule) (rule monitori return rule } -func (c RuleConfig) NewTargetRule() (rule monitoringv1.Rule) { - rule.Record = fmt.Sprintf("osko_%s", c.Record) +func (c Rule) NewTargetRule() (rule monitoringv1.Rule) { + rule.Record = fmt.Sprintf("%s_%s", RecordPrefix, c.Record) rule.Expr = intstr.Parse(fmt.Sprintf("vector(%s)", c.Slo.Spec.Objectives[0].Target)) rule.Labels = c.MetricLabelCompiler.NewMetricLabelGenerator() return rule } -func (b BudgetRuleConfig) NewBudgetRule() (rule monitoringv1.Rule) { - rule.Record = fmt.Sprintf("osko_%s", b.Record) - expr := fmt.Sprintf("(1 - %s{%s}) * (%s{%s} - %s{%s})", +func (b BudgetRule) NewBudgetRule() (rule monitoringv1.Rule) { + log := ctrllog.FromContext(context.Background()) + gbRule := &Rule{} + if b.BadRuleConfig.Sli.Spec.RatioMetric.Bad.MetricSource.Spec == "" || b.BadRuleConfig.Slo.Spec.Indicator.Spec.RatioMetric.Bad.MetricSource.Spec == "" { + log.Info("Bad rule not provided, calculating bad as (total - good)") + gbRule = b.GoodRuleConfig + } else { + log.Info("Bad rule provided") + gbRule = b.BadRuleConfig + } + rule.Record = fmt.Sprintf("%s_%s", RecordPrefix, b.Record) + expr := fmt.Sprintf("(1 - %s_%s{%s}) * (%s_%s{%s} - %s_%s{%s})", + RecordPrefix, b.TargetRuleConfig.Record, b.TargetRuleConfig.MetricLabelCompiler.NewMetricLabelCompiler(nil, ""), + RecordPrefix, b.TotalRuleConfig.Record, b.TotalRuleConfig.MetricLabelCompiler.NewMetricLabelCompiler(nil, ""), - b.BadRuleConfig.Record, - b.BadRuleConfig.MetricLabelCompiler.NewMetricLabelCompiler(nil, ""), + RecordPrefix, + gbRule.Record, + gbRule.MetricLabelCompiler.NewMetricLabelCompiler(nil, ""), ) rule.Expr = intstr.Parse(expr) return rule