Skip to content

Commit

Permalink
Merge pull request #45 from heliapb/feat/am_analyser
Browse files Browse the repository at this point in the history
[feat] - Add Alertmanager Analyzer
  • Loading branch information
nicolastakashi authored Dec 9, 2024
2 parents c38350b + fb3dda5 commit c51fef3
Show file tree
Hide file tree
Showing 5 changed files with 467 additions and 33 deletions.
3 changes: 3 additions & 0 deletions cmd/analyze.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const (
ServiceMonitor AnalyzeKind = "servicemonitor"
Operator AnalyzeKind = "operator"
Prometheus AnalyzeKind = "prometheus"
Alertmanager AnalyzeKind = "alertmanager"
)

type AnalyzeFlags struct {
Expand Down Expand Up @@ -81,6 +82,8 @@ func run(cmd *cobra.Command, _ []string) error {
return analyzers.RunOperatorAnalyzer(cmd.Context(), clientSets, analyzerFlags.Name, analyzerFlags.Namespace)
case Prometheus:
return analyzers.RunPrometheusAnalyzer(cmd.Context(), clientSets, analyzerFlags.Name, analyzerFlags.Namespace)
case Alertmanager:
return analyzers.RunAlertmanagerAnalyzer(cmd.Context(), clientSets, analyzerFlags.Name, analyzerFlags.Namespace)
default:
return fmt.Errorf("kind %s not supported", analyzerFlags.Kind)
}
Expand Down
124 changes: 124 additions & 0 deletions internal/analyzers/alertmanager.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// Copyright 2024 The prometheus-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package analyzers

import (
"context"
"fmt"
"log/slog"

"github.com/prometheus-operator/poctl/internal/k8sutil"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
)

func RunAlertmanagerAnalyzer(ctx context.Context, clientSets *k8sutil.ClientSets, name, namespace string) error {
alertmanager, err := clientSets.MClient.MonitoringV1().Alertmanagers(namespace).Get(ctx, name, metav1.GetOptions{})
if err != nil {
if errors.IsNotFound(err) {
return fmt.Errorf("alertmanager %s not found in namespace %s", name, namespace)
}
return fmt.Errorf("error while getting Alertmanager: %v", err)
}

_, err = clientSets.KClient.CoreV1().ServiceAccounts(namespace).Get(ctx, alertmanager.Spec.ServiceAccountName, metav1.GetOptions{})
if err != nil {
if errors.IsNotFound(err) {
return fmt.Errorf("alertmanager serviceaccount not found in namespace %s", namespace)
}
return fmt.Errorf("error while getting ServiceAcounts: %w", err)
}

if alertmanager.Spec.AlertmanagerConfigSelector == nil && alertmanager.Spec.AlertmanagerConfiguration == nil {
if alertmanager.Spec.ConfigSecret != "" {
// use provided config secret
if err := checkAlertmanagerSecret(ctx, clientSets, alertmanager.Spec.ConfigSecret, namespace, "alertmanager.yaml"); err != nil {
return fmt.Errorf("error checking Alertmanager secret: %w", err)
}
}
if alertmanager.Spec.ConfigSecret == "" {
// use the default generated secret from pkg/alertmanager/statefulset.go
amConfigSecretName := fmt.Sprintf("alertmanager-%s-generated", alertmanager.Name)
if err := checkAlertmanagerSecret(ctx, clientSets, amConfigSecretName, namespace, "alertmanager.yaml.gz"); err != nil {
return fmt.Errorf("error checking Alertmanager secret: %w", err)
}
}
}
// If 'AlertmanagerConfigNamespaceSelector' is nil, only check own namespace.
if alertmanager.Spec.AlertmanagerConfigNamespaceSelector != nil {
if err := k8sutil.CheckResourceNamespaceSelectors(ctx, *clientSets, alertmanager.Spec.AlertmanagerConfigNamespaceSelector); err != nil {
return fmt.Errorf("alertmanagerConfigNamespaceSelector is not properly defined: %s", err)
}
}

if alertmanager.Spec.AlertmanagerConfigSelector != nil {
if err := checkAlertmanagerConfigs(ctx, clientSets, alertmanager.Spec.AlertmanagerConfigSelector, namespace); err != nil {
return fmt.Errorf("alertmanagerConfigSelectors is not properly defined: %s", err)
}
}

if alertmanager.Spec.AlertmanagerConfiguration != nil {
_, err := clientSets.MClient.MonitoringV1alpha1().AlertmanagerConfigs(namespace).Get(ctx, alertmanager.Spec.AlertmanagerConfiguration.Name, metav1.GetOptions{})
if err != nil {
if errors.IsNotFound(err) {
return fmt.Errorf("alertmanagerConfigs not found in namespace %s", namespace)
}
return fmt.Errorf("error while getting AlertmanagerConfig: %w", err)
}
}

slog.Info("Alertmanager is compliant, no issues found", "name", name, "namespace", namespace)
return nil
}

func checkAlertmanagerSecret(ctx context.Context, clientSets *k8sutil.ClientSets, secretName, namespace string, secretData string) error {
alertmanagerSecret, err := clientSets.KClient.CoreV1().Secrets(namespace).Get(ctx, secretName, metav1.GetOptions{})
if err != nil {
if errors.IsNotFound(err) {
return fmt.Errorf("failed to get alertmanager secret %s not found in namespace %s", secretName, namespace)
}
return fmt.Errorf("error while getting alertmanager secret%s %v", secretName, err)
}
if len(alertmanagerSecret.Data) == 0 {
return fmt.Errorf("alertmanager Secret %s is empty", secretName)
}
_, found := alertmanagerSecret.Data[secretData]
if !found {
return fmt.Errorf("the %s key not found in Secret %s", secretData, secretName)
}
return nil
}

func checkAlertmanagerConfigs(ctx context.Context, clientSets *k8sutil.ClientSets, labelSelector *metav1.LabelSelector, namespace string) error {
if len(labelSelector.MatchLabels) == 0 && len(labelSelector.MatchExpressions) == 0 {
return nil
}

labelMap, err := metav1.LabelSelectorAsMap(labelSelector)
if err != nil {
return fmt.Errorf("invalid label selector format in %v", err)
}

alertmamagerConfigs, err := clientSets.MClient.MonitoringV1alpha1().AlertmanagerConfigs(namespace).List(ctx, metav1.ListOptions{LabelSelector: labels.SelectorFromSet(labelMap).String()})
if err != nil {
return fmt.Errorf("failed to list AlertmanagerConfigs in %s: %v", namespace, err)
}
if len(alertmamagerConfigs.Items) == 0 {
return fmt.Errorf("no AlertmanagerConfigs match the provided selector in %s", namespace)
}

return nil
}
Loading

0 comments on commit c51fef3

Please sign in to comment.