From 56e9c738ace7cdc527b2f4d23dcf6e076c8e495d Mon Sep 17 00:00:00 2001 From: Nikolay Sivko Date: Wed, 18 Feb 2026 20:00:18 -0300 Subject: [PATCH] add alertingRules and disableBuiltinAlerts fields to the CRD --- api/v1/config.go | 80 +++++++++ api/v1/coroot_types.go | 2 + api/v1/zz_generated.deepcopy.go | 202 ++++++++++++++++++++++ config/crd/coroot.com_coroots.yaml | 140 +++++++++++++++ config/crd/coroot.com_coroots_legacy.yaml | 124 +++++++++++++ controller/coroot.go | 20 ++- 6 files changed, 559 insertions(+), 9 deletions(-) diff --git a/api/v1/config.go b/api/v1/config.go index 60926b5..88eb686 100644 --- a/api/v1/config.go +++ b/api/v1/config.go @@ -105,6 +105,8 @@ type ProjectSpec struct { ApplicationCategories []ApplicationCategorySpec `json:"applicationCategories,omitempty"` // Custom applications. CustomApplications []CustomApplicationSpec `json:"customApplications,omitempty"` + // Alerting rules. Rules defined here are shown with a lock icon in the UI and cannot be edited or deleted through the UI. + AlertingRules []AlertingRuleSpec `json:"alertingRules,omitempty"` // Inspection overrides. InspectionOverrides *InspectionOverrides `json:"inspectionOverrides,omitempty"` } @@ -266,6 +268,84 @@ type ApplicationCategoryNotificationSettingsWebhookSpec struct { Enabled bool `json:"enabled,omitempty"` } +type AlertingRuleSpec struct { + // Rule ID (required). For built-in rules, use the existing rule ID (e.g., storage-space, memory-pressure). + // For custom rules, choose any unique ID. The ID is shown in the rule detail dialog. + // +kubebuilder:validation:Required + Id string `json:"id"` + // Rule name. + Name *string `json:"name,omitempty"` + // Alert source configuration. + Source *AlertSourceSpec `json:"source,omitempty"` + // Application selector. + Selector *AppSelectorSpec `json:"selector,omitempty"` + // Severity level (warning or critical). + // +kubebuilder:validation:Enum=warning;critical + Severity *string `json:"severity,omitempty"` + // How long the condition must be true before firing (e.g., 5m, 1h). + // +kubebuilder:validation:Pattern="^[0-9]+[smhdwy]$" + For *string `json:"for,omitempty"` + // How long to keep firing after condition clears (e.g., 5m). + // +kubebuilder:validation:Pattern="^[0-9]+[smhdwy]$" + KeepFiringFor *string `json:"keepFiringFor,omitempty"` + // Notification templates. + Templates *AlertTemplatesSpec `json:"templates,omitempty"` + // Override the notification category for this rule. + NotificationCategory *string `json:"notificationCategory,omitempty"` + // Whether the rule is enabled. + Enabled *bool `json:"enabled,omitempty"` +} + +type AlertSourceSpec struct { + // Source type: check, log_patterns, or promql. + // +kubebuilder:validation:Enum=check;log_patterns;promql + Type string `json:"type"` + // Check source configuration (required if type is check). + Check *CheckSourceSpec `json:"check,omitempty"` + // Log pattern source configuration (required if type is log_patterns). + LogPattern *LogPatternSourceSpec `json:"logPattern,omitempty"` + // PromQL source configuration (required if type is promql). + PromQL *PromQLSourceSpec `json:"promql,omitempty"` +} + +type CheckSourceSpec struct { + // The inspection check ID (e.g., cpu-utilization, storage-space). + CheckId string `json:"checkId"` +} + +type LogPatternSourceSpec struct { + // Log severities to match (e.g., ["error", "fatal"]). + Severities []string `json:"severities"` + // Minimum number of occurrences before alerting. + MinCount *int `json:"minCount,omitempty"` + // Maximum number of alerts per application for this rule. + MaxAlertsPerApp *int `json:"maxAlertsPerApp,omitempty"` + // Use AI to evaluate log patterns and reduce noise. + EvaluateWithAi *bool `json:"evaluateWithAi,omitempty"` +} + +type PromQLSourceSpec struct { + // PromQL expression that triggers the alert when it returns results. + Expression string `json:"expression"` +} + +type AppSelectorSpec struct { + // Selector type: all, category, or applications. + // +kubebuilder:validation:Enum=all;category;applications + Type string `json:"type"` + // Application categories to match (when type is category). + Categories []string `json:"categories,omitempty"` + // Application ID patterns to match (when type is applications). + ApplicationIdPatterns []string `json:"applicationIdPatterns,omitempty"` +} + +type AlertTemplatesSpec struct { + // Summary template. + Summary string `json:"summary,omitempty"` + // Description template. + Description string `json:"description,omitempty"` +} + type CustomApplicationSpec struct { // Custom application name (required). Name string `json:"name"` diff --git a/api/v1/coroot_types.go b/api/v1/coroot_types.go index 9028f8f..5df5ddf 100644 --- a/api/v1/coroot_types.go +++ b/api/v1/coroot_types.go @@ -319,6 +319,8 @@ type CorootSpec struct { // Coroot Cloud integration. CorootCloud *CorootCloudSpec `json:"corootCloud,omitempty"` + // Disable all built-in alerting rules on startup. + DisableBuiltinAlerts bool `json:"disableBuiltinAlerts,omitempty"` // Projects configuration (Coroot will create or update the specified projects). Projects []ProjectSpec `json:"projects,omitempty"` // Single Sign-On configuration (Coroot Enterprise Edition only). diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go index 5c1e167..522681b 100644 --- a/api/v1/zz_generated.deepcopy.go +++ b/api/v1/zz_generated.deepcopy.go @@ -55,6 +55,111 @@ func (in *AgentsOnlySpec) DeepCopy() *AgentsOnlySpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AlertSourceSpec) DeepCopyInto(out *AlertSourceSpec) { + *out = *in + if in.Check != nil { + in, out := &in.Check, &out.Check + *out = new(CheckSourceSpec) + **out = **in + } + if in.LogPattern != nil { + in, out := &in.LogPattern, &out.LogPattern + *out = new(LogPatternSourceSpec) + (*in).DeepCopyInto(*out) + } + if in.PromQL != nil { + in, out := &in.PromQL, &out.PromQL + *out = new(PromQLSourceSpec) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AlertSourceSpec. +func (in *AlertSourceSpec) DeepCopy() *AlertSourceSpec { + if in == nil { + return nil + } + out := new(AlertSourceSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AlertTemplatesSpec) DeepCopyInto(out *AlertTemplatesSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AlertTemplatesSpec. +func (in *AlertTemplatesSpec) DeepCopy() *AlertTemplatesSpec { + if in == nil { + return nil + } + out := new(AlertTemplatesSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AlertingRuleSpec) DeepCopyInto(out *AlertingRuleSpec) { + *out = *in + if in.Name != nil { + in, out := &in.Name, &out.Name + *out = new(string) + **out = **in + } + if in.Source != nil { + in, out := &in.Source, &out.Source + *out = new(AlertSourceSpec) + (*in).DeepCopyInto(*out) + } + if in.Selector != nil { + in, out := &in.Selector, &out.Selector + *out = new(AppSelectorSpec) + (*in).DeepCopyInto(*out) + } + if in.Severity != nil { + in, out := &in.Severity, &out.Severity + *out = new(string) + **out = **in + } + if in.For != nil { + in, out := &in.For, &out.For + *out = new(string) + **out = **in + } + if in.KeepFiringFor != nil { + in, out := &in.KeepFiringFor, &out.KeepFiringFor + *out = new(string) + **out = **in + } + if in.Templates != nil { + in, out := &in.Templates, &out.Templates + *out = new(AlertTemplatesSpec) + **out = **in + } + if in.NotificationCategory != nil { + in, out := &in.NotificationCategory, &out.NotificationCategory + *out = new(string) + **out = **in + } + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AlertingRuleSpec. +func (in *AlertingRuleSpec) DeepCopy() *AlertingRuleSpec { + if in == nil { + return nil + } + out := new(AlertingRuleSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AnthropicSpec) DeepCopyInto(out *AnthropicSpec) { *out = *in @@ -95,6 +200,31 @@ func (in *ApiKeySpec) DeepCopy() *ApiKeySpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AppSelectorSpec) DeepCopyInto(out *AppSelectorSpec) { + *out = *in + if in.Categories != nil { + in, out := &in.Categories, &out.Categories + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ApplicationIdPatterns != nil { + in, out := &in.ApplicationIdPatterns, &out.ApplicationIdPatterns + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AppSelectorSpec. +func (in *AppSelectorSpec) DeepCopy() *AppSelectorSpec { + if in == nil { + return nil + } + out := new(AppSelectorSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ApplicationCategoryNotificationSettingsDeploymentsSpec) DeepCopyInto(out *ApplicationCategoryNotificationSettingsDeploymentsSpec) { *out = *in @@ -298,6 +428,21 @@ func (in *BasicAuthSpec) DeepCopy() *BasicAuthSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CheckSourceSpec) DeepCopyInto(out *CheckSourceSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CheckSourceSpec. +func (in *CheckSourceSpec) DeepCopy() *CheckSourceSpec { + if in == nil { + return nil + } + out := new(CheckSourceSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClickhouseKeeperSpec) DeepCopyInto(out *ClickhouseKeeperSpec) { *out = *in @@ -973,6 +1118,41 @@ func (in *LogCollectorSpec) DeepCopy() *LogCollectorSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LogPatternSourceSpec) DeepCopyInto(out *LogPatternSourceSpec) { + *out = *in + if in.Severities != nil { + in, out := &in.Severities, &out.Severities + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.MinCount != nil { + in, out := &in.MinCount, &out.MinCount + *out = new(int) + **out = **in + } + if in.MaxAlertsPerApp != nil { + in, out := &in.MaxAlertsPerApp, &out.MaxAlertsPerApp + *out = new(int) + **out = **in + } + if in.EvaluateWithAi != nil { + in, out := &in.EvaluateWithAi, &out.EvaluateWithAi + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LogPatternSourceSpec. +func (in *LogPatternSourceSpec) DeepCopy() *LogPatternSourceSpec { + if in == nil { + return nil + } + out := new(LogPatternSourceSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *NodeAgentSpec) DeepCopyInto(out *NodeAgentSpec) { *out = *in @@ -1283,6 +1463,13 @@ func (in *ProjectSpec) DeepCopyInto(out *ProjectSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.AlertingRules != nil { + in, out := &in.AlertingRules, &out.AlertingRules + *out = make([]AlertingRuleSpec, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } if in.InspectionOverrides != nil { in, out := &in.InspectionOverrides, &out.InspectionOverrides *out = new(InspectionOverrides) @@ -1300,6 +1487,21 @@ func (in *ProjectSpec) DeepCopy() *ProjectSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PromQLSourceSpec) DeepCopyInto(out *PromQLSourceSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PromQLSourceSpec. +func (in *PromQLSourceSpec) DeepCopy() *PromQLSourceSpec { + if in == nil { + return nil + } + out := new(PromQLSourceSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PrometheusSpec) DeepCopyInto(out *PrometheusSpec) { *out = *in diff --git a/config/crd/coroot.com_coroots.yaml b/config/crd/coroot.com_coroots.yaml index f5332b6..849d539 100644 --- a/config/crd/coroot.com_coroots.yaml +++ b/config/crd/coroot.com_coroots.yaml @@ -4706,6 +4706,9 @@ spec: type: boolean type: object type: object + disableBuiltinAlerts: + description: Disable all built-in alerting rules on startup. + type: boolean enterpriseEdition: description: Configurations for Coroot Enterprise Edition. properties: @@ -6416,6 +6419,143 @@ spec: the specified projects). items: properties: + alertingRules: + description: Alerting rules. Rules defined here are shown with + a lock icon in the UI and cannot be edited or deleted through + the UI. + items: + properties: + enabled: + description: Whether the rule is enabled. + type: boolean + for: + description: How long the condition must be true before + firing (e.g., 5m, 1h). + pattern: ^[0-9]+[smhdwy]$ + type: string + id: + description: |- + Rule ID (required). For built-in rules, use the existing rule ID (e.g., storage-space, memory-pressure). + For custom rules, choose any unique ID. The ID is shown in the rule detail dialog. + type: string + keepFiringFor: + description: How long to keep firing after condition clears + (e.g., 5m). + pattern: ^[0-9]+[smhdwy]$ + type: string + name: + description: Rule name. + type: string + notificationCategory: + description: Override the notification category for this + rule. + type: string + selector: + description: Application selector. + properties: + applicationIdPatterns: + description: Application ID patterns to match (when + type is applications). + items: + type: string + type: array + categories: + description: Application categories to match (when + type is category). + items: + type: string + type: array + type: + description: 'Selector type: all, category, or applications.' + enum: + - all + - category + - applications + type: string + required: + - type + type: object + severity: + description: Severity level (warning or critical). + enum: + - warning + - critical + type: string + source: + description: Alert source configuration. + properties: + check: + description: Check source configuration (required + if type is check). + properties: + checkId: + description: The inspection check ID (e.g., cpu-utilization, + storage-space). + type: string + required: + - checkId + type: object + logPattern: + description: Log pattern source configuration (required + if type is log_patterns). + properties: + evaluateWithAi: + description: Use AI to evaluate log patterns and + reduce noise. + type: boolean + maxAlertsPerApp: + description: Maximum number of alerts per application + for this rule. + type: integer + minCount: + description: Minimum number of occurrences before + alerting. + type: integer + severities: + description: Log severities to match (e.g., ["error", + "fatal"]). + items: + type: string + type: array + required: + - severities + type: object + promql: + description: PromQL source configuration (required + if type is promql). + properties: + expression: + description: PromQL expression that triggers the + alert when it returns results. + type: string + required: + - expression + type: object + type: + description: 'Source type: check, log_patterns, or + promql.' + enum: + - check + - log_patterns + - promql + type: string + required: + - type + type: object + templates: + description: Notification templates. + properties: + description: + description: Description template. + type: string + summary: + description: Summary template. + type: string + type: object + required: + - id + type: object + type: array apiKeys: description: Project API keys, used by agents to send telemetry data (required unless memberProjects or remoteCoroot is set). diff --git a/config/crd/coroot.com_coroots_legacy.yaml b/config/crd/coroot.com_coroots_legacy.yaml index e14a43f..d076cbf 100644 --- a/config/crd/coroot.com_coroots_legacy.yaml +++ b/config/crd/coroot.com_coroots_legacy.yaml @@ -4244,6 +4244,10 @@ spec: type: object type: object nullable: true + disableBuiltinAlerts: + description: Disable all built-in alerting rules on startup. + type: boolean + nullable: true enterpriseEdition: description: Configurations for Coroot Enterprise Edition. properties: @@ -5794,6 +5798,126 @@ spec: description: Projects configuration (Coroot will create or update the specified projects). items: properties: + alertingRules: + description: Alerting rules. Rules defined here are shown with a lock icon in the UI and cannot be edited or deleted through the UI. + items: + properties: + enabled: + description: Whether the rule is enabled. + type: boolean + for: + description: How long the condition must be true before firing (e.g., 5m, 1h). + pattern: ^[0-9]+[smhdwy]$ + type: string + id: + description: |- + Rule ID (required). For built-in rules, use the existing rule ID (e.g., storage-space, memory-pressure). + For custom rules, choose any unique ID. The ID is shown in the rule detail dialog. + type: string + keepFiringFor: + description: How long to keep firing after condition clears (e.g., 5m). + pattern: ^[0-9]+[smhdwy]$ + type: string + name: + description: Rule name. + type: string + notificationCategory: + description: Override the notification category for this rule. + type: string + selector: + description: Application selector. + properties: + applicationIdPatterns: + description: Application ID patterns to match (when type is applications). + items: + type: string + type: array + categories: + description: Application categories to match (when type is category). + items: + type: string + type: array + type: + description: 'Selector type: all, category, or applications.' + enum: + - all + - category + - applications + type: string + required: + - type + type: object + severity: + description: Severity level (warning or critical). + enum: + - warning + - critical + type: string + source: + description: Alert source configuration. + properties: + check: + description: Check source configuration (required if type is check). + properties: + checkId: + description: The inspection check ID (e.g., cpu-utilization, storage-space). + type: string + required: + - checkId + type: object + logPattern: + description: Log pattern source configuration (required if type is log_patterns). + properties: + evaluateWithAi: + description: Use AI to evaluate log patterns and reduce noise. + type: boolean + maxAlertsPerApp: + description: Maximum number of alerts per application for this rule. + type: integer + minCount: + description: Minimum number of occurrences before alerting. + type: integer + severities: + description: Log severities to match (e.g., ["error", "fatal"]). + items: + type: string + type: array + required: + - severities + type: object + promql: + description: PromQL source configuration (required if type is promql). + properties: + expression: + description: PromQL expression that triggers the alert when it returns results. + type: string + required: + - expression + type: object + type: + description: 'Source type: check, log_patterns, or promql.' + enum: + - check + - log_patterns + - promql + type: string + required: + - type + type: object + templates: + description: Notification templates. + properties: + description: + description: Description template. + type: string + summary: + description: Summary template. + type: string + type: object + required: + - id + type: object + type: array apiKeys: description: Project API keys, used by agents to send telemetry data (required unless memberProjects or remoteCoroot is set). items: diff --git a/controller/coroot.go b/controller/coroot.go index f7c3330..a68a05e 100644 --- a/controller/coroot.go +++ b/controller/coroot.go @@ -734,18 +734,20 @@ func (r *CorootReconciler) corootConfigMap(ctx context.Context, cr *corootv1.Cor } type Config struct { - Projects []corootv1.ProjectSpec `json:"projects,omitempty"` - SSO *corootv1.SSOSpec `json:"sso,omitempty"` - AI *corootv1.AISpec `json:"ai,omitempty"` - CorootCloud *corootv1.CorootCloudSpec `json:"corootCloud,omitempty"` - TLS *TLS `json:"tls,omitempty"` + DisableBuiltinAlerts bool `json:"disableBuiltinAlerts,omitempty"` + Projects []corootv1.ProjectSpec `json:"projects,omitempty"` + SSO *corootv1.SSOSpec `json:"sso,omitempty"` + AI *corootv1.AISpec `json:"ai,omitempty"` + CorootCloud *corootv1.CorootCloudSpec `json:"corootCloud,omitempty"` + TLS *TLS `json:"tls,omitempty"` } var cfg = Config{ - Projects: cr.Spec.Projects, - SSO: cr.Spec.SSO, - AI: cr.Spec.AI, - CorootCloud: cr.Spec.CorootCloud, + DisableBuiltinAlerts: cr.Spec.DisableBuiltinAlerts, + Projects: cr.Spec.Projects, + SSO: cr.Spec.SSO, + AI: cr.Spec.AI, + CorootCloud: cr.Spec.CorootCloud, } if cr.Spec.TLS != nil { cfg.TLS = &TLS{