diff --git a/api/v1/kibana_types.go b/api/v1/kibana_types.go index 5ad62b9d9f..509205717b 100644 --- a/api/v1/kibana_types.go +++ b/api/v1/kibana_types.go @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Tigera, Inc. All rights reserved. +// Copyright (c) 2024-2026 Tigera, Inc. All rights reserved. /* Licensed under the Apache License, Version 2.0 (the "License"); @@ -28,6 +28,11 @@ type Kibana struct { } type KibanaSpec struct { + // Replicas defines the number of Kibana replicas. When set to 0, Kibana is not rendered. + // Default: 1 + // +optional + Replicas *int32 `json:"replicas,omitempty"` + // Template describes the Kibana pod that will be created. // +optional Template *KibanaPodTemplateSpec `json:"template,omitempty"` diff --git a/api/v1/monitor_types.go b/api/v1/monitor_types.go index f7e71af90b..401ff51427 100644 --- a/api/v1/monitor_types.go +++ b/api/v1/monitor_types.go @@ -175,6 +175,11 @@ type AlertManager struct { AlertManagerSpec *AlertManagerSpec `json:"spec,omitempty"` } type AlertManagerSpec struct { + // Replicas defines the number of Alertmanager replicas. When set to 0, Alertmanager is not rendered. + // Default: 0 + // +optional + Replicas *int32 `json:"replicas,omitempty"` + // Define resources requests and limits for single Pods. Resources corev1.ResourceRequirements `json:"resources,omitempty"` } diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go index 7aab497499..5a92ab5299 100644 --- a/api/v1/zz_generated.deepcopy.go +++ b/api/v1/zz_generated.deepcopy.go @@ -473,6 +473,11 @@ func (in *AlertManager) DeepCopy() *AlertManager { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AlertManagerSpec) DeepCopyInto(out *AlertManagerSpec) { *out = *in + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = new(int32) + **out = **in + } in.Resources.DeepCopyInto(&out.Resources) } @@ -6513,6 +6518,11 @@ func (in *KibanaPodTemplateSpec) DeepCopy() *KibanaPodTemplateSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *KibanaSpec) DeepCopyInto(out *KibanaSpec) { *out = *in + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = new(int32) + **out = **in + } if in.Template != nil { in, out := &in.Template, &out.Template *out = new(KibanaPodTemplateSpec) diff --git a/pkg/controller/logstorage/elastic/elastic_controller.go b/pkg/controller/logstorage/elastic/elastic_controller.go index 11f6ec4b6a..ed55a8cb8b 100644 --- a/pkg/controller/logstorage/elastic/elastic_controller.go +++ b/pkg/controller/logstorage/elastic/elastic_controller.go @@ -364,6 +364,10 @@ func (r *ElasticSubController) Reconcile(ctx context.Context, request reconcile. } kibanaEnabled := !r.multiTenant + if kibanaEnabled && ls.Spec.Kibana != nil && ls.Spec.Kibana.Spec != nil && + ls.Spec.Kibana.Spec.Replicas != nil && *ls.Spec.Kibana.Spec.Replicas == 0 { + kibanaEnabled = false + } // Wait for dependencies to exist. if elasticKeyPair == nil { diff --git a/pkg/controller/logstorage/initializer/initializing_controller.go b/pkg/controller/logstorage/initializer/initializing_controller.go index a3f9f04c6a..19f5f879f2 100644 --- a/pkg/controller/logstorage/initializer/initializing_controller.go +++ b/pkg/controller/logstorage/initializer/initializing_controller.go @@ -151,6 +151,17 @@ func FillDefaults(opr *operatorv1.LogStorage) { opr.Spec.Nodes = &operatorv1.Nodes{Count: 1} } + if opr.Spec.Kibana == nil { + opr.Spec.Kibana = &operatorv1.Kibana{} + } + if opr.Spec.Kibana.Spec == nil { + opr.Spec.Kibana.Spec = &operatorv1.KibanaSpec{} + } + if opr.Spec.Kibana.Spec.Replicas == nil { + var replicas int32 = 1 + opr.Spec.Kibana.Spec.Replicas = &replicas + } + if opr.Spec.ComponentResources == nil { limits := corev1.ResourceList{} requests := corev1.ResourceList{} diff --git a/pkg/controller/logstorage/initializer/initializing_controller_test.go b/pkg/controller/logstorage/initializer/initializing_controller_test.go index 93e6b31f21..9a0f125a9c 100644 --- a/pkg/controller/logstorage/initializer/initializing_controller_test.go +++ b/pkg/controller/logstorage/initializer/initializing_controller_test.go @@ -435,6 +435,7 @@ var _ = Describe("LogStorage Initializing controller", func() { var dlr int32 = 8 var bgp int32 = 8 var replicas int32 = render.DefaultElasticsearchReplicas + var kibanaReplicas int32 = 1 limits := corev1.ResourceList{} requests := corev1.ResourceList{} limits[corev1.ResourceMemory] = resource.MustParse(defaultEckOperatorMemorySetting) @@ -453,6 +454,11 @@ var _ = Describe("LogStorage Initializing controller", func() { Indices: &operatorv1.Indices{ Replicas: &replicas, }, + Kibana: &operatorv1.Kibana{ + Spec: &operatorv1.KibanaSpec{ + Replicas: &kibanaReplicas, + }, + }, StorageClassName: DefaultElasticsearchStorageClass, ComponentResources: []operatorv1.LogStorageComponentResource{ { diff --git a/pkg/controller/monitor/monitor_controller.go b/pkg/controller/monitor/monitor_controller.go index 8f3038464b..de7efa5c25 100644 --- a/pkg/controller/monitor/monitor_controller.go +++ b/pkg/controller/monitor/monitor_controller.go @@ -433,6 +433,7 @@ func (r *ReconcileMonitor) Reconcile(ctx context.Context, request reconcile.Requ KeyPairOptions: []rcertificatemanagement.KeyPairOption{ rcertificatemanagement.NewKeyPairOption(serverTLSSecret, true, true), rcertificatemanagement.NewKeyPairOption(clientTLSSecret, true, true), + rcertificatemanagement.NewKeyPairOption(operatorTLSSecret, true, true), }, TrustedBundle: trustedBundle, }), @@ -494,6 +495,17 @@ func (r *ReconcileMonitor) Reconcile(ctx context.Context, request reconcile.Requ } func fillDefaults(instance *operatorv1.Monitor) { + if instance.Spec.AlertManager == nil { + instance.Spec.AlertManager = &operatorv1.AlertManager{} + } + if instance.Spec.AlertManager.AlertManagerSpec == nil { + instance.Spec.AlertManager.AlertManagerSpec = &operatorv1.AlertManagerSpec{} + } + if instance.Spec.AlertManager.AlertManagerSpec.Replicas == nil { + var replicas int32 = 0 + instance.Spec.AlertManager.AlertManagerSpec.Replicas = &replicas + } + if instance.Spec.ExternalPrometheus != nil && instance.Spec.ExternalPrometheus.ServiceMonitor != nil { if len(instance.Spec.ExternalPrometheus.ServiceMonitor.Labels) == 0 { diff --git a/pkg/controller/monitor/monitor_controller_test.go b/pkg/controller/monitor/monitor_controller_test.go index 1265840e67..af24a71d62 100644 --- a/pkg/controller/monitor/monitor_controller_test.go +++ b/pkg/controller/monitor/monitor_controller_test.go @@ -123,6 +123,13 @@ var _ = Describe("Monitor controller tests", func() { monitorCR = &operatorv1.Monitor{ TypeMeta: metav1.TypeMeta{Kind: "Monitor", APIVersion: "operator.tigera.io/v1"}, ObjectMeta: metav1.ObjectMeta{Name: "tigera-secure"}, + Spec: operatorv1.MonitorSpec{ + AlertManager: &operatorv1.AlertManager{ + AlertManagerSpec: &operatorv1.AlertManagerSpec{ + Replicas: ptr.To(int32(3)), + }, + }, + }, } Expect(cli.Create(ctx, monitorCR)).NotTo(HaveOccurred()) Expect(cli.Create(ctx, render.CreateCertificateConfigMap("test", render.TyphaCAConfigMapName, common.OperatorNamespace()))).NotTo(HaveOccurred()) diff --git a/pkg/imports/crds/operator/operator.tigera.io_logstorages.yaml b/pkg/imports/crds/operator/operator.tigera.io_logstorages.yaml index b2c3491e57..d89534914b 100644 --- a/pkg/imports/crds/operator/operator.tigera.io_logstorages.yaml +++ b/pkg/imports/crds/operator/operator.tigera.io_logstorages.yaml @@ -706,6 +706,12 @@ spec: spec: description: Spec is the specification of the Kibana. properties: + replicas: + description: |- + Replicas defines the number of Kibana replicas. When set to 0, Kibana is not rendered. + Default: 1 + format: int32 + type: integer template: description: Template describes the Kibana pod that will be diff --git a/pkg/imports/crds/operator/operator.tigera.io_monitors.yaml b/pkg/imports/crds/operator/operator.tigera.io_monitors.yaml index 2be3c03d1f..f9ccfb22c9 100644 --- a/pkg/imports/crds/operator/operator.tigera.io_monitors.yaml +++ b/pkg/imports/crds/operator/operator.tigera.io_monitors.yaml @@ -46,6 +46,12 @@ spec: spec: description: Spec is the specification of the Alertmanager. properties: + replicas: + description: |- + Replicas defines the number of Alertmanager replicas. When set to 0, Alertmanager is not rendered. + Default: 0 + format: int32 + type: integer resources: description: Define resources requests and limits for single diff --git a/pkg/render/logstorage/kibana/kibana.go b/pkg/render/logstorage/kibana/kibana.go index baeffddcb8..6ba5b07c6c 100644 --- a/pkg/render/logstorage/kibana/kibana.go +++ b/pkg/render/logstorage/kibana/kibana.go @@ -304,8 +304,9 @@ func (k *kibana) kibanaCR() *kbv1.Kibana { } count := int32(1) - if k.cfg.Installation.ControlPlaneReplicas != nil { - count = *k.cfg.Installation.ControlPlaneReplicas + if k.cfg.LogStorage != nil && k.cfg.LogStorage.Spec.Kibana != nil && + k.cfg.LogStorage.Spec.Kibana.Spec != nil && k.cfg.LogStorage.Spec.Kibana.Spec.Replicas != nil { + count = *k.cfg.LogStorage.Spec.Kibana.Spec.Replicas } tolerations := k.cfg.Installation.ControlPlaneTolerations @@ -380,7 +381,7 @@ func (k *kibana) kibanaCR() *kbv1.Kibana { }, } - if k.cfg.Installation.ControlPlaneReplicas != nil && *k.cfg.Installation.ControlPlaneReplicas > 1 { + if count > 1 { kibana.Spec.PodTemplate.Spec.Affinity = podaffinity.NewPodAntiAffinity(CRName, []string{Namespace}) } diff --git a/pkg/render/logstorage/kibana/kibana_test.go b/pkg/render/logstorage/kibana/kibana_test.go index d53c27c183..487cf41cfb 100644 --- a/pkg/render/logstorage/kibana/kibana_test.go +++ b/pkg/render/logstorage/kibana/kibana_test.go @@ -420,6 +420,11 @@ var _ = Describe("Kibana rendering tests", func() { It("should render PodAffinity when ControlPlaneReplicas is greater than 1", func() { var replicas int32 = 2 cfg.Installation.ControlPlaneReplicas = &replicas + cfg.LogStorage.Spec.Kibana = &operatorv1.Kibana{ + Spec: &operatorv1.KibanaSpec{ + Replicas: &replicas, + }, + } component := kibana.Kibana(cfg) resources, _ := component.Objects() diff --git a/pkg/render/monitor/monitor.go b/pkg/render/monitor/monitor.go index 0f524e2f80..b530b9b9a1 100644 --- a/pkg/render/monitor/monitor.go +++ b/pkg/render/monitor/monitor.go @@ -110,25 +110,35 @@ func Monitor(cfg *Config) render.Component { } func MonitorPolicy(cfg *Config) render.Component { - return render.NewPassthrough( - []client.Object{ + toCreate := []client.Object{ + calicoSystemPrometheusPolicy(cfg), + calicoSystemPrometheusAPIPolicy(cfg), + calicoSystemPrometheusOperatorPolicy(cfg), + networkpolicy.CalicoSystemDefaultDeny(common.TigeraPrometheusNamespace), + } + toDelete := []client.Object{ + // allow-tigera Tier was renamed to calico-system + networkpolicy.DeprecatedAllowTigeraNetworkPolicyObject("calico-node-alertmanager", common.TigeraPrometheusNamespace), + networkpolicy.DeprecatedAllowTigeraNetworkPolicyObject("calico-node-alertmanager-mesh", common.TigeraPrometheusNamespace), + networkpolicy.DeprecatedAllowTigeraNetworkPolicyObject("prometheus", common.TigeraPrometheusNamespace), + networkpolicy.DeprecatedAllowTigeraNetworkPolicyObject("tigera-prometheus-api", common.TigeraPrometheusNamespace), + networkpolicy.DeprecatedAllowTigeraNetworkPolicyObject("prometheus-operator", common.TigeraPrometheusNamespace), + networkpolicy.DeprecatedAllowTigeraNetworkPolicyObject("default-deny", common.TigeraPrometheusNamespace), + } + + if alertmanagerReplicasFromConfig(cfg) > 0 { + toCreate = append(toCreate, calicoSystemAlertManagerPolicy(cfg), calicoSystemAlertManagerMeshPolicy(cfg), - calicoSystemPrometheusPolicy(cfg), - calicoSystemPrometheusAPIPolicy(cfg), - calicoSystemPrometheusOperatorPolicy(cfg), - networkpolicy.CalicoSystemDefaultDeny(common.TigeraPrometheusNamespace), - }, - []client.Object{ - // allow-tigera Tier was renamed to calico-system - networkpolicy.DeprecatedAllowTigeraNetworkPolicyObject("calico-node-alertmanager", common.TigeraPrometheusNamespace), - networkpolicy.DeprecatedAllowTigeraNetworkPolicyObject("calico-node-alertmanager-mesh", common.TigeraPrometheusNamespace), - networkpolicy.DeprecatedAllowTigeraNetworkPolicyObject("prometheus", common.TigeraPrometheusNamespace), - networkpolicy.DeprecatedAllowTigeraNetworkPolicyObject("tigera-prometheus-api", common.TigeraPrometheusNamespace), - networkpolicy.DeprecatedAllowTigeraNetworkPolicyObject("prometheus-operator", common.TigeraPrometheusNamespace), - networkpolicy.DeprecatedAllowTigeraNetworkPolicyObject("default-deny", common.TigeraPrometheusNamespace), - }, - ) + ) + } else { + toDelete = append(toDelete, + calicoSystemAlertManagerPolicy(cfg), + calicoSystemAlertManagerMeshPolicy(cfg), + ) + } + + return render.NewPassthrough(toCreate, toDelete) } // Config contains all the config information needed to render the Monitor component. @@ -215,7 +225,6 @@ func (mc *monitorComponent) Objects() ([]client.Object, []client.Object) { } toCreate = append(toCreate, secret.ToRuntimeObjects(secret.CopyToNamespace(common.TigeraPrometheusNamespace, mc.cfg.PullSecrets...)...)...) - toCreate = append(toCreate, secret.ToRuntimeObjects(secret.CopyToNamespace(common.TigeraPrometheusNamespace, mc.cfg.AlertmanagerConfigSecret)...)...) toCreate = append(toCreate, mc.prometheusOperatorServiceAccount(), @@ -225,8 +234,6 @@ func (mc *monitorComponent) Objects() ([]client.Object, []client.Object) { mc.prometheusClusterRole(), mc.prometheusClusterRoleBinding(), mc.prometheus(), - mc.alertmanagerService(), - mc.alertmanager(), mc.prometheusServiceService(), mc.prometheusServiceClusterRole(), mc.prometheusServiceClusterRoleBinding(), @@ -235,6 +242,23 @@ func (mc *monitorComponent) Objects() ([]client.Object, []client.Object) { var toDelete []client.Object + if mc.alertmanagerReplicas() > 0 { + toCreate = append(toCreate, secret.ToRuntimeObjects(secret.CopyToNamespace(common.TigeraPrometheusNamespace, mc.cfg.AlertmanagerConfigSecret)...)...) + toCreate = append(toCreate, + mc.alertmanagerService(), + mc.alertmanager(), + ) + } else { + toDelete = append(toDelete, + mc.alertmanager(), + mc.alertmanagerService(), + &corev1.Secret{ObjectMeta: metav1.ObjectMeta{ + Name: "alertmanager-" + CalicoNodeAlertmanager, + Namespace: common.TigeraPrometheusNamespace, + }}, + ) + } + serviceMonitors := []client.Object{ mc.serviceMonitorCalicoNode(), mc.serviceMonitorElasticsearch(), @@ -433,6 +457,19 @@ func (mc *monitorComponent) prometheusOperatorClusterRoleBinding() *rbacv1.Clust } } +func alertmanagerReplicasFromConfig(cfg *Config) int32 { + if cfg.Monitor.AlertManager != nil && + cfg.Monitor.AlertManager.AlertManagerSpec != nil && + cfg.Monitor.AlertManager.AlertManagerSpec.Replicas != nil { + return *cfg.Monitor.AlertManager.AlertManagerSpec.Replicas + } + return 0 +} + +func (mc *monitorComponent) alertmanagerReplicas() int32 { + return alertmanagerReplicasFromConfig(mc.cfg) +} + func (mc *monitorComponent) alertmanager() *monitoringv1.Alertmanager { resources := corev1.ResourceRequirements{} @@ -458,7 +495,7 @@ func (mc *monitorComponent) alertmanager() *monitoringv1.Alertmanager { ImagePullPolicy: render.ImagePullPolicy(), ImagePullSecrets: secret.GetReferenceList(mc.cfg.PullSecrets), NodeSelector: mc.cfg.Installation.ControlPlaneNodeSelector, - Replicas: mc.cfg.Installation.ControlPlaneReplicas, + Replicas: mc.cfg.Monitor.AlertManager.AlertManagerSpec.Replicas, SecurityContext: securitycontext.NewNonRootPodContext(), ServiceAccountName: PrometheusServiceAccountName, Tolerations: tolerations, @@ -555,8 +592,6 @@ func (mc *monitorComponent) prometheus() *monitoringv1.Prometheus { tolerations = append(tolerations, rmeta.TolerateGKEARM64NoSchedule) } - promNamespace := common.TigeraPrometheusNamespace - prometheus := &monitoringv1.Prometheus{ TypeMeta: metav1.TypeMeta{Kind: monitoringv1.PrometheusesKind, APIVersion: MonitoringAPIVersion}, ObjectMeta: metav1.ObjectMeta{ @@ -625,21 +660,6 @@ func (mc *monitorComponent) prometheus() *monitoringv1.Prometheus { VolumeMounts: volumeMounts, Volumes: volumes, }, - Alerting: &monitoringv1.AlertingSpec{ - Alertmanagers: []monitoringv1.AlertmanagerEndpoints{ - { - Name: CalicoNodeAlertmanager, - Namespace: &promNamespace, - Port: intstr.FromString("web"), - RelabelConfigs: []monitoringv1.RelabelConfig{ - { - TargetLabel: "__scheme__", - Replacement: ptr.To("http"), - }, - }, - }, - }, - }, Retention: "24h", RuleSelector: &metav1.LabelSelector{MatchLabels: map[string]string{ "prometheus": CalicoNodePrometheus, @@ -648,6 +668,25 @@ func (mc *monitorComponent) prometheus() *monitoringv1.Prometheus { }, } + if mc.alertmanagerReplicas() > 0 { + promNamespace := common.TigeraPrometheusNamespace + prometheus.Spec.Alerting = &monitoringv1.AlertingSpec{ + Alertmanagers: []monitoringv1.AlertmanagerEndpoints{ + { + Name: CalicoNodeAlertmanager, + Namespace: &promNamespace, + Port: intstr.FromString("web"), + RelabelConfigs: []monitoringv1.RelabelConfig{ + { + TargetLabel: "__scheme__", + Replacement: ptr.To("http"), + }, + }, + }, + }, + } + } + if overrides := mc.cfg.Monitor.Prometheus; overrides != nil { rcomponents.ApplyPrometheusOverrides(prometheus, overrides) } diff --git a/pkg/render/monitor/monitor_test.go b/pkg/render/monitor/monitor_test.go index 69f76bf78f..878af76ed2 100644 --- a/pkg/render/monitor/monitor_test.go +++ b/pkg/render/monitor/monitor_test.go @@ -93,6 +93,13 @@ var _ = Describe("monitor rendering tests", func() { Installation: &operatorv1.InstallationSpec{ ControlPlaneReplicas: ptr.To(int32(3)), }, + Monitor: operatorv1.MonitorSpec{ + AlertManager: &operatorv1.AlertManager{ + AlertManagerSpec: &operatorv1.AlertManagerSpec{ + Replicas: ptr.To(int32(3)), + }, + }, + }, PullSecrets: []*corev1.Secret{ {ObjectMeta: metav1.ObjectMeta{Name: "tigera-pull-secret"}}, }, @@ -162,6 +169,7 @@ var _ = Describe("monitor rendering tests", func() { cfg.Monitor.AlertManager = &operatorv1.AlertManager{ AlertManagerSpec: &operatorv1.AlertManagerSpec{ + Replicas: ptr.To(int32(3)), Resources: alertManagerResources, }, }