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 773f3b8806..b167cbdfd7 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) } @@ -6518,6 +6523,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/common/common.go b/pkg/controller/logstorage/common/common.go index da77fdca32..3ab7508153 100644 --- a/pkg/controller/logstorage/common/common.go +++ b/pkg/controller/logstorage/common/common.go @@ -110,6 +110,18 @@ func CreateKubeControllersSecrets(ctx context.Context, esAdminUserSecret *corev1 return kubeControllersGatewaySecret, kubeControllersVerificationSecret, kubeControllersSecureUserSecret, nil } +// KibanaEnabled returns true if Kibana should be enabled based on the LogStorage spec and multi-tenancy mode. +func KibanaEnabled(ls *operatorv1.LogStorage, multiTenant bool) bool { + if multiTenant { + return false + } + if ls.Spec.Kibana != nil && ls.Spec.Kibana.Spec != nil && + ls.Spec.Kibana.Spec.Replicas != nil && *ls.Spec.Kibana.Spec.Replicas == 0 { + return false + } + return true +} + func CalculateFlowShards(nodesSpecifications *operatorv1.Nodes, defaultShards int) int { if nodesSpecifications == nil || nodesSpecifications.ResourceRequirements == nil || nodesSpecifications.ResourceRequirements.Requests == nil { return defaultShards diff --git a/pkg/controller/logstorage/dashboards/dashboards_controller.go b/pkg/controller/logstorage/dashboards/dashboards_controller.go index 84448e4401..e732539ba2 100644 --- a/pkg/controller/logstorage/dashboards/dashboards_controller.go +++ b/pkg/controller/logstorage/dashboards/dashboards_controller.go @@ -37,6 +37,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" operatorv1 "github.com/tigera/operator/api/v1" + lscommon "github.com/tigera/operator/pkg/controller/logstorage/common" "github.com/tigera/operator/pkg/controller/logstorage/initializer" "github.com/tigera/operator/pkg/controller/options" "github.com/tigera/operator/pkg/controller/status" @@ -349,6 +350,7 @@ func (d DashboardsSubController) Reconcile(ctx context.Context, request reconcil KibanaPort: kibanaPort, ExternalKibanaClientSecret: externalKibanaSecret, Credentials: []*corev1.Secret{&credentials}, + KibanaEnabled: lscommon.KibanaEnabled(logStorage, d.multiTenant), } dashboardsComponent := dashboards.Dashboards(cfg) diff --git a/pkg/controller/logstorage/elastic/elastic_controller.go b/pkg/controller/logstorage/elastic/elastic_controller.go index 11f6ec4b6a..c980ecc632 100644 --- a/pkg/controller/logstorage/elastic/elastic_controller.go +++ b/pkg/controller/logstorage/elastic/elastic_controller.go @@ -363,7 +363,7 @@ func (r *ElasticSubController) Reconcile(ctx context.Context, request reconcile. return reconcile.Result{}, err } - kibanaEnabled := !r.multiTenant + kibanaEnabled := logstoragecommon.KibanaEnabled(ls, r.multiTenant) // 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 2c6181f80e..d133b384c5 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 e32ef6caaf..e51aff248f 100644 --- a/pkg/controller/logstorage/initializer/initializing_controller_test.go +++ b/pkg/controller/logstorage/initializer/initializing_controller_test.go @@ -508,6 +508,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) @@ -526,6 +527,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/manager/manager_controller.go b/pkg/controller/manager/manager_controller.go index c866b569a1..47062da3e8 100644 --- a/pkg/controller/manager/manager_controller.go +++ b/pkg/controller/manager/manager_controller.go @@ -35,6 +35,7 @@ import ( "github.com/tigera/operator/pkg/common" "github.com/tigera/operator/pkg/controller/certificatemanager" "github.com/tigera/operator/pkg/controller/compliance" + lscommon "github.com/tigera/operator/pkg/controller/logstorage/common" "github.com/tigera/operator/pkg/controller/options" "github.com/tigera/operator/pkg/controller/status" "github.com/tigera/operator/pkg/controller/utils" @@ -145,6 +146,9 @@ func Add(mgr manager.Manager, opts options.ControllerOptions) error { if err = c.WatchObject(&operatorv1.ImageSet{}, eventHandler); err != nil { return fmt.Errorf("manager-controller failed to watch ImageSet: %w", err) } + if err = c.WatchObject(&operatorv1.LogStorage{}, eventHandler); err != nil { + return fmt.Errorf("manager-controller failed to watch LogStorage resource: %w", err) + } if opts.MultiTenant { if err = c.WatchObject(&operatorv1.Tenant{}, &handler.EnqueueRequestForObject{}); err != nil { return fmt.Errorf("manager-controller failed to watch Tenant resource: %w", err) @@ -652,6 +656,20 @@ func (r *ReconcileManager) Reconcile(ctx context.Context, request reconcile.Requ } } + // Determine if Kibana is enabled based on multi-tenancy and LogStorage replicas. + kibanaEnabled := !r.opts.MultiTenant + if kibanaEnabled { + ls := &operatorv1.LogStorage{} + if err := r.client.Get(ctx, utils.DefaultTSEEInstanceKey, ls); err != nil { + if !errors.IsNotFound(err) { + r.status.SetDegraded(operatorv1.ResourceReadError, "Failed to query LogStorage resource", err, logc) + return reconcile.Result{}, err + } + } else { + kibanaEnabled = lscommon.KibanaEnabled(ls, r.opts.MultiTenant) + } + } + managerCfg := &render.ManagerConfiguration{ VoltronRouteConfig: routeConfig, KeyValidatorConfig: keyValidatorConfig, @@ -678,6 +696,7 @@ func (r *ReconcileManager) Reconcile(ctx context.Context, request reconcile.Requ BindingNamespaces: namespaces, OSSTenantNamespaces: ossTenantNamespaces, Manager: instance, + KibanaEnabled: kibanaEnabled, } // Render the desired objects from the CRD and create or update them. diff --git a/pkg/controller/monitor/monitor_controller.go b/pkg/controller/monitor/monitor_controller.go index 8f3038464b..dd17ffb47b 100644 --- a/pkg/controller/monitor/monitor_controller.go +++ b/pkg/controller/monitor/monitor_controller.go @@ -112,7 +112,6 @@ func newReconciler(mgr manager.Manager, opts options.ControllerOptions, promethe } r.status.AddStatefulSets([]types.NamespacedName{ - {Namespace: common.TigeraPrometheusNamespace, Name: fmt.Sprintf("alertmanager-%s", monitor.CalicoNodeAlertmanager)}, {Namespace: common.TigeraPrometheusNamespace, Name: fmt.Sprintf("prometheus-%s", monitor.CalicoNodePrometheus)}, }) @@ -242,6 +241,15 @@ func (r *ReconcileMonitor) Reconcile(ctx context.Context, request reconcile.Requ r.status.SetDegraded(operatorv1.ResourceUpdateError, "Failed to write defaults", err, reqLogger) return reconcile.Result{}, err } + + // Track alertmanager statefulset health only when it has replicas. + alertmanagerSS := types.NamespacedName{Namespace: common.TigeraPrometheusNamespace, Name: fmt.Sprintf("alertmanager-%s", monitor.CalicoNodeAlertmanager)} + if instance.Spec.AlertManager != nil && instance.Spec.AlertManager.AlertManagerSpec != nil && + instance.Spec.AlertManager.AlertManagerSpec.Replicas != nil && *instance.Spec.AlertManager.AlertManagerSpec.Replicas > 0 { + r.status.AddStatefulSets([]types.NamespacedName{alertmanagerSS}) + } else { + r.status.RemoveStatefulSets(alertmanagerSS) + } if instance.Spec.ExternalPrometheus != nil { if err = r.client.Get(ctx, client.ObjectKey{Name: instance.Spec.ExternalPrometheus.Namespace}, &corev1.Namespace{}); err != nil { r.status.SetDegraded(operatorv1.ResourceReadError, fmt.Sprintf("Failed to get external prometheus namespace %s", @@ -494,6 +502,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..cd772b4a34 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()) @@ -253,6 +260,8 @@ var _ = Describe("Monitor controller tests", func() { mockStatus.On("OnCRFound").Return() mockStatus.On("RemoveCertificateSigningRequests", mock.Anything) mockStatus.On("SetMetaData", mock.Anything).Return() + mockStatus.On("AddStatefulSets", mock.Anything).Return() + mockStatus.On("RemoveStatefulSets", mock.Anything).Return().Maybe() r.status = mockStatus test.ExpectWaitForTierWatch(ctx, &r, mockStatus) 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/dashboards/dashboards.go b/pkg/render/logstorage/dashboards/dashboards.go index ab3ff5aabf..85d94b3cb5 100644 --- a/pkg/render/logstorage/dashboards/dashboards.go +++ b/pkg/render/logstorage/dashboards/dashboards.go @@ -92,6 +92,9 @@ type Config struct { // Credentials are used to provide annotations for elastic search users Credentials []*corev1.Secret + + // KibanaEnabled indicates whether Kibana is being rendered. + KibanaEnabled bool } func (d *dashboards) ResolveImages(is *operatorv1.ImageSet) error { @@ -124,7 +127,7 @@ func (d *dashboards) Objects() (objsToCreate, objsToDelete []client.Object) { objsToDelete = append(objsToDelete, networkpolicy.DeprecatedAllowTigeraNetworkPolicyObject("dashboards-installer", d.cfg.Namespace), ) - if d.cfg.IsManaged { + if d.cfg.IsManaged || !d.cfg.KibanaEnabled { objsToDelete = append(objsToDelete, d.resources()...) } else { objsToCreate = append(objsToCreate, d.resources()...) 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/manager.go b/pkg/render/manager.go index 8bc1869466..82f64f0047 100644 --- a/pkg/render/manager.go +++ b/pkg/render/manager.go @@ -87,9 +87,6 @@ const ( ManagerClusterSettingsLayerTigera = "cluster-settings.layer.tigera-infrastructure" ManagerClusterSettingsViewDefault = "cluster-settings.view.default" - ElasticsearchManagerUserSecret = "calico-ee-manager-elasticsearch-access" - TlsSecretHashAnnotation = "hash.operator.tigera.io/tls-secret" - KibanaTLSHashAnnotation = "hash.operator.tigera.io/kibana-secrets" ElasticsearchUserHashAnnotation = "hash.operator.tigera.io/elasticsearch-user" ManagerMultiTenantManagedClustersAccessClusterRoleBindingName = "calico-manager-managed-cluster-access" LegacyManagerMultiTenantManagedClustersAccessClusterRoleBindingName = "tigera-manager-managed-cluster-access" @@ -198,7 +195,8 @@ type ManagerConfiguration struct { Tenant *operatorv1.Tenant ExternalElastic bool - Manager *operatorv1.Manager + Manager *operatorv1.Manager + KibanaEnabled bool } type managerComponent struct { @@ -466,7 +464,7 @@ func (c *managerComponent) managerEnvVars() []corev1.EnvVar { {Name: "CNX_CLUSTER_NAME", Value: "cluster"}, {Name: "CNX_POLICY_RECOMMENDATION_SUPPORT", Value: "true"}, {Name: "ENABLE_MULTI_CLUSTER_MANAGEMENT", Value: strconv.FormatBool(c.cfg.ManagementCluster != nil)}, - {Name: "ENABLE_KIBANA", Value: strconv.FormatBool(!c.cfg.Tenant.MultiTenant())}, + {Name: "ENABLE_KIBANA", Value: strconv.FormatBool(c.cfg.KibanaEnabled)}, } envs = append(envs, c.managerOAuth2EnvVars()...) 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, }, }