Skip to content

Commit

Permalink
[feat] - am analyzer
Browse files Browse the repository at this point in the history
Signed-off-by: Hélia Barroso <[email protected]>
  • Loading branch information
heliapb committed Nov 20, 2024
1 parent c38350b commit 75ee2d4
Show file tree
Hide file tree
Showing 4 changed files with 119 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
81 changes: 81 additions & 0 deletions internal/analyzers/alertmanager.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// 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"
)

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 {
return fmt.Errorf("failed to list 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)
}
} else 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)
}
} //else if alertmanager.Spec.AlertmanagerConfigNamespaceSelector == nil {

//}

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 {
return fmt.Errorf("failed to get 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
}
37 changes: 5 additions & 32 deletions internal/analyzers/prometheus.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,23 +67,23 @@ func RunPrometheusAnalyzer(ctx context.Context, clientSets *k8sutil.ClientSets,
}
}

if err := checkResourceNamespaceSelectors(ctx, clientSets, prometheus.Spec.PodMonitorNamespaceSelector); err != nil {
if err := k8sutil.CheckResourceNamespaceSelectors(ctx, *clientSets, prometheus.Spec.PodMonitorNamespaceSelector); err != nil {
return fmt.Errorf("podMonitorNamespaceSelector is not properly defined: %s", err)
}

if err := checkResourceNamespaceSelectors(ctx, clientSets, prometheus.Spec.ProbeNamespaceSelector); err != nil {
if err := k8sutil.CheckResourceNamespaceSelectors(ctx, *clientSets, prometheus.Spec.ProbeNamespaceSelector); err != nil {
return fmt.Errorf("probeNamespaceSelector is not properly defined: %s", err)
}

if err := checkResourceNamespaceSelectors(ctx, clientSets, prometheus.Spec.ServiceMonitorNamespaceSelector); err != nil {
if err := k8sutil.CheckResourceNamespaceSelectors(ctx, *clientSets, prometheus.Spec.ServiceMonitorNamespaceSelector); err != nil {
return fmt.Errorf("serviceMonitorNamespaceSelector is not properly defined: %s", err)
}

if err := checkResourceNamespaceSelectors(ctx, clientSets, prometheus.Spec.ScrapeConfigNamespaceSelector); err != nil {
if err := k8sutil.CheckResourceNamespaceSelectors(ctx, *clientSets, prometheus.Spec.ScrapeConfigNamespaceSelector); err != nil {
return fmt.Errorf("scrapeConfigNamespaceSelector is not properly defined: %s", err)
}

if err := checkResourceNamespaceSelectors(ctx, clientSets, prometheus.Spec.RuleNamespaceSelector); err != nil {
if err := k8sutil.CheckResourceNamespaceSelectors(ctx, *clientSets, prometheus.Spec.RuleNamespaceSelector); err != nil {
return fmt.Errorf("ruleNamespaceSelector is not properly defined: %s", err)
}

Expand Down Expand Up @@ -171,33 +171,6 @@ func checkClusterRoleRules(crb v1.ClusterRoleBinding, cr *v1.ClusterRole) error
return nil
}

func checkResourceNamespaceSelectors(ctx context.Context, clientSets *k8sutil.ClientSets, labelSelector *metav1.LabelSelector) error {
if labelSelector == nil {
return nil
}

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 %s: %v", labelSelector, err)
}

namespaces, err := clientSets.KClient.CoreV1().Namespaces().List(ctx, metav1.ListOptions{LabelSelector: labels.SelectorFromSet(labelMap).String()})

if err != nil {
return fmt.Errorf("failed to list Namespaces in %s: %v", labelSelector, err)
}

if len(namespaces.Items) == 0 {
return fmt.Errorf("no namespaces match the selector %s", labelSelector)
}

return nil
}

func checkResourceLabelSelectors(ctx context.Context, clientSets *k8sutil.ClientSets, labelSelector *metav1.LabelSelector, resourceName, namespace string) error {
if labelSelector == nil {
return fmt.Errorf("%s selector is not defined", resourceName)
Expand Down
31 changes: 30 additions & 1 deletion internal/k8sutil/k8sutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package k8sutil

import (
"bytes"
"context"
"fmt"
"io"
"log/slog"
Expand All @@ -26,18 +27,19 @@ import (
monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
monitoringv1alpha1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1alpha1"
monitoringclient "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned"
v1 "k8s.io/api/rbac/v1"
apiv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
apiExtensions "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
v1 "k8s.io/api/rbac/v1"
)

var ApplyOption = metav1.ApplyOptions{
Expand Down Expand Up @@ -158,3 +160,30 @@ func IsServiceAccountBoundToRoleBindingList(clusterRoleBindings *v1.ClusterRoleB
}
return false
}

func CheckResourceNamespaceSelectors(ctx context.Context, clientSets ClientSets, labelSelector *metav1.LabelSelector) error {
if labelSelector == nil {
return nil
}

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 %s: %v", labelSelector, err)
}

namespaces, err := clientSets.KClient.CoreV1().Namespaces().List(ctx, metav1.ListOptions{LabelSelector: labels.SelectorFromSet(labelMap).String()})

if err != nil {
return fmt.Errorf("failed to list Namespaces in %s: %v", labelSelector, err)
}

if len(namespaces.Items) == 0 {
return fmt.Errorf("no namespaces match the selector %s", labelSelector)
}

return nil
}

0 comments on commit 75ee2d4

Please sign in to comment.