Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 57 additions & 1 deletion api/v1/monitor_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,62 @@ type MonitorSpec struct {
// Alertmanager is the configuration for the Alertmanager.
// +optional
Alertmanager *Alertmanager `json:"alertmanager,omitempty"`

// Alerts enables or disables Calico Enterprise's built-in alerts.
// +optional
Alerts Alerts `json:"alerts,omitempty"`
}

// Alerts enables or disables Calico Enterprise's built-in alerts and whether their events appear on the
// Manager Alerts page.
type Alerts struct {
// DeniedPackets fires when calico-node is denying packets.
// +optional
DeniedPackets Alert `json:"deniedPackets,omitempty"`

// TigeraStatus fires when a component stays degraded or progressing.
// +optional
TigeraStatus Alert `json:"tigeraStatus,omitempty"`

// TLSCertExpiry fires when a TLS certificate is close to expiry.
// +optional
TLSCertExpiry Alert `json:"tlsCertExpiry,omitempty"`

// LicenseExpiry fires when the license is close to expiry or invalid.
// +optional
LicenseExpiry Alert `json:"licenseExpiry,omitempty"`

// IPPoolExhaustion fires when an IP pool is nearly (>=90%) or fully (>=100%) exhausted.
// +optional
IPPoolExhaustion Alert `json:"ipPoolExhaustion,omitempty"`
}

type Alert struct {
// Status enables or disables the alert. Defaults to Enabled when unset.
// +optional
Status AlertStatusType `json:"status,omitempty"`
}

// Enabled reports whether the alert is active. An unset status defaults to Enabled.
func (a Alert) Enabled() bool {
return a.Status != AlertStatusDisabled
}

// +kubebuilder:validation:Enum=Enabled;Disabled
type AlertStatusType string

const (
// AlertStatusEnabled creates an alert for Alertmanager and displays its events in the Manager UI.
AlertStatusEnabled AlertStatusType = "Enabled"
// AlertStatusDisabled disables this alert.
AlertStatusDisabled AlertStatusType = "Disabled"
)

// UIAlertsEnabled reports whether any alert is configured to surface its events on the Manager Alerts page.
func (s *MonitorSpec) UIAlertsEnabled() bool {
return s.Alerts.DeniedPackets.Enabled() || s.Alerts.TigeraStatus.Enabled() ||
s.Alerts.TLSCertExpiry.Enabled() || s.Alerts.LicenseExpiry.Enabled() ||
s.Alerts.IPPoolExhaustion.Enabled()
}

type ExternalPrometheus struct {
Expand Down Expand Up @@ -186,7 +242,7 @@ type Alertmanager struct {
}
type AlertmanagerSpec struct {
// Replicas defines the number of Alertmanager replicas. When set to 0, Alertmanager is not rendered.
// Default: 0
// Default: 1
// +optional
Replicas *int32 `json:"replicas,omitempty"`

Expand Down
36 changes: 36 additions & 0 deletions api/v1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions pkg/apis/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
configv1 "github.com/openshift/api/config/v1"
ocsv1 "github.com/openshift/api/security/v1"
monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
monitoringv1alpha1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1alpha1"
v3 "github.com/tigera/api/pkg/apis/projectcalico/v3"
operatorv1 "github.com/tigera/operator/api/v1"
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
Expand Down Expand Up @@ -72,6 +73,7 @@ func init() {
AddToSchemes = append(AddToSchemes, operatorv1.AddToScheme)
AddToSchemes = append(AddToSchemes, admissionregistrationv1.AddToScheme)
AddToSchemes = append(AddToSchemes, monitoringv1.AddToScheme)
AddToSchemes = append(AddToSchemes, monitoringv1alpha1.AddToScheme)
AddToSchemes = append(AddToSchemes, corev1.AddToScheme)
AddToSchemes = append(AddToSchemes, rbacv1.AddToScheme)
AddToSchemes = append(AddToSchemes, appsv1.AddToScheme)
Expand Down
12 changes: 0 additions & 12 deletions pkg/controller/monitor/alertmanager-config.yaml

This file was deleted.

123 changes: 39 additions & 84 deletions pkg/controller/monitor/monitor_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,11 @@ package monitor

import (
"context"
_ "embed"
"fmt"
"reflect"
"time"

corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
Expand All @@ -48,7 +45,6 @@ import (
rcertificatemanagement "github.com/tigera/operator/pkg/render/certificatemanagement"
rauth "github.com/tigera/operator/pkg/render/common/authentication"
"github.com/tigera/operator/pkg/render/common/networkpolicy"
rsecret "github.com/tigera/operator/pkg/render/common/secret"
"github.com/tigera/operator/pkg/render/kubecontrollers"
"github.com/tigera/operator/pkg/render/logstorage/esmetrics"
"github.com/tigera/operator/pkg/render/monitor"
Expand Down Expand Up @@ -398,12 +394,6 @@ func (r *ReconcileMonitor) Reconcile(ctx context.Context, request reconcile.Requ
// Create a component handler to manage the rendered component.
hdler := utils.NewComponentHandler(log, r.client, r.scheme, instance)

alertmanagerConfigSecret, createInOperatorNamespace, err := r.readAlertmanagerConfigSecret(ctx)
if err != nil {
r.status.SetDegraded(operatorv1.ResourceReadError, "Error retrieving Alertmanager configuration secret", err, reqLogger)
return reconcile.Result{}, err
}

kubeControllersMetricsPort, err := utils.GetKubeControllerMetricsPort(ctx, r.client)
if err != nil {
r.status.SetDegraded(operatorv1.ResourceReadError, "Unable to read KubeControllersConfiguration", err, reqLogger)
Expand Down Expand Up @@ -431,11 +421,48 @@ func (r *ReconcileMonitor) Reconcile(ctx context.Context, request reconcile.Requ
trustedBundle.AddCertificates(operatorTLSSecret)
}

managementClusterConnection, err := utils.GetManagementClusterConnection(ctx, r.client)
if err != nil {
r.status.SetDegraded(operatorv1.ResourceReadError, "Error reading ManagementClusterConnection", err, reqLogger)
return reconcile.Result{}, err
}
managedCluster := managementClusterConnection != nil

// Carry forward the token Kubernetes populated into the Alertmanager Linseed token secret so the
// component handler preserves it on reconcile instead of wiping it. Empty until Kubernetes first
// populates the created secret.
var alertmanagerLinseedTokenData map[string][]byte
if !managedCluster {
existingToken := &corev1.Secret{}
if err := r.client.Get(ctx, types.NamespacedName{Name: monitor.AlertmanagerLinseedTokenSecretName, Namespace: common.TigeraPrometheusNamespace}, existingToken); err == nil {
alertmanagerLinseedTokenData = existingToken.Data
} else if !errors.IsNotFound(err) {
r.status.SetDegraded(operatorv1.ResourceReadError, "Error reading Alertmanager Linseed token secret", err, reqLogger)
return reconcile.Result{}, err
}
}

// On a managed cluster, Alertmanager forwards alerts to the management cluster's Linseed through
// Guardian (see monitor.LinseedEventsURLManaged). Add the management Linseed public certificate
// to the Alertmanager's trusted bundle so the webhook can verify it.
if managedCluster {
linseedCertificate, err := certificateManager.GetCertificate(r.client, render.VoltronLinseedPublicCert, common.OperatorNamespace())
if err != nil {
r.status.SetDegraded(operatorv1.ResourceReadError, fmt.Sprintf("Error fetching Linseed certificate %s", render.VoltronLinseedPublicCert), err, reqLogger)
return reconcile.Result{}, err
} else if linseedCertificate == nil {
log.Info(fmt.Sprintf("Linseed certificate %s/%s not yet available; Alertmanager webhook trust will be incomplete until it is copied in", common.OperatorNamespace(), render.VoltronLinseedPublicCert))
} else {
trustedBundle.AddCertificates(linseedCertificate)
}
}

monitorCfg := &monitor.Config{
Monitor: instance.Spec,
ManagedCluster: managedCluster,
Installation: installationSpec,
PullSecrets: pullSecrets,
AlertmanagerConfigSecret: alertmanagerConfigSecret,
AlertmanagerLinseedTokenData: alertmanagerLinseedTokenData,
KeyValidatorConfig: keyValidatorConfig,
ServerTLSSecret: serverTLSSecret,
ClientTLSSecret: clientTLSSecret,
Expand Down Expand Up @@ -466,10 +493,6 @@ func (r *ReconcileMonitor) Reconcile(ctx context.Context, request reconcile.Requ
}),
}

if createInOperatorNamespace {
components = append(components, render.NewCreationPassthrough(alertmanagerConfigSecret))
}

// v3 NetworkPolicy will fail to reconcile if the Tier is not created, which can only occur once a License is created.
// In managed clusters, the monitor controller is a dependency for the License to be created. In case the License is
// unavailable and reconciliation of non-NetworkPolicy resources in the monitor controller would resolve it, we
Expand Down Expand Up @@ -538,7 +561,7 @@ func fillDefaults(instance *operatorv1.Monitor) {
instance.Spec.Alertmanager.AlertmanagerSpec = &operatorv1.AlertmanagerSpec{}
}
if instance.Spec.Alertmanager.AlertmanagerSpec.Replicas == nil {
var replicas int32 = 0
var replicas int32 = 1
instance.Spec.Alertmanager.AlertmanagerSpec.Replicas = &replicas
}

Expand Down Expand Up @@ -576,71 +599,3 @@ func fillDefaults(instance *operatorv1.Monitor) {
func PrometheusTLSServerDNSNames(clusterDomain string) []string {
return dns.GetServiceDNSNames(monitor.PrometheusServiceServiceName, common.TigeraPrometheusNamespace, clusterDomain)
}

//go:embed alertmanager-config.yaml
var alertmanagerConfig string

// readAlertmanagerConfigSecret attempts to retrieve Alertmanager configuration secret from either the Tigera Operator
// namespace or the Tigera Prometheus namespace. If it doesn't exist in either of the namespace, a new default configuration
// secret will be created.
func (r *ReconcileMonitor) readAlertmanagerConfigSecret(ctx context.Context) (*corev1.Secret, bool, error) {
// Previous to this change, a customer was expected to deploy the Alertmanager configuration secret
// in the tigera-prometheus namespace directly. Now that this secret is managed by the Operator,
// the customer must deploy this secret in the tigera-operator namespace. The Operator then copies
// the secret from the tigera-operator namespace to the tigera-prometheus namespace.
//
// For new installation:
// A new secret will be created in the tigera-operator namespace and then copied to the tigera-prometheus namespace.
// Monitor controller holds the ownership of this secret.
//
// To handle upgrades:
// The tigera-prometheus secret will be copied back to the tigera-operator namespace.
// If this secret is modified by the user, Monitor controller won't set the ownership. Otherwise, it is owned by the Monitor.
//
// Tigera Operator will then watch for secret changes in the tigera-operator namespace and overwrite
// any changes for this secret in the tigera-prometheus namespace. For future Alertmanager configuration changes,
// Monitor controller can verify the owner reference of the configuration secret and decide if we want to
// upgrade it automatically.

defaultConfigSecret := &corev1.Secret{
TypeMeta: metav1.TypeMeta{Kind: "Secret", APIVersion: "v1"},
ObjectMeta: metav1.ObjectMeta{
Name: monitor.AlertmanagerConfigSecret,
Namespace: common.OperatorNamespace(),
},
Data: map[string][]byte{
"alertmanager.yaml": []byte(alertmanagerConfig),
},
}

// Read Alertmanager configuration secret as-is if it is found in the tigera-operator namespace.
secret, err := utils.GetSecret(ctx, r.client, monitor.AlertmanagerConfigSecret, common.OperatorNamespace())
if err != nil {
return nil, false, err
} else if secret != nil {
return secret, false, nil
}

// When Alertmanager configuration isn't found in the tigera-operator namespace, copy it from the tigera-prometheus namespace (upgrade).
// If it is modified by the user, Monitor controller will not set the owner reference.
secret, err = utils.GetSecret(ctx, r.client, monitor.AlertmanagerConfigSecret, common.TigeraPrometheusNamespace)
if err != nil {
return nil, false, err
} else if secret != nil {
// Monitor controller will own the secret if it is the same.
if reflect.DeepEqual(defaultConfigSecret.Data, secret.Data) {
return rsecret.CopyToNamespace(common.OperatorNamespace(), secret)[0], true, nil
}

// If the secret isn't the same, leave it unmanaged.
s := rsecret.CopyToNamespace(common.OperatorNamespace(), secret)[0]
if err := r.client.Create(ctx, s); err != nil {
return nil, false, err
}
return s, false, nil
}

// Alertmanager configuration secret is not found in the tigera-operator or tigera-prometheus namespace (new install).
// Operator should create a new default secret and set the owner reference.
return defaultConfigSecret, true, nil
}
Loading
Loading