Skip to content

Commit

Permalink
[CHORE] adding operator analyzer
Browse files Browse the repository at this point in the history
Signed-off-by: Nicolas Takashi <nicolas.tcs@hotmail.com>
  • Loading branch information
nicolastakashi committed Jul 26, 2024
1 parent 4627fd5 commit d210d0b
Show file tree
Hide file tree
Showing 5 changed files with 472 additions and 6 deletions.
3 changes: 3 additions & 0 deletions cmd/analyze.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type AnalyzeKind string

const (
ServiceMonitor AnalyzeKind = "servicemonitor"
Operator AnalyzeKind = "operator"
)

type AnalyzeFlags struct {
Expand Down Expand Up @@ -68,6 +69,8 @@ func run(cmd *cobra.Command, _ []string) error {
switch AnalyzeKind(strings.ToLower(analyzerFlags.Kind)) {
case ServiceMonitor:
return analyzers.RunServiceMonitorAnalyzer(cmd.Context(), clientSets, analyzerFlags.Name, analyzerFlags.Namespace)
case Operator:
return analyzers.RunOperatorAnalyzer(cmd.Context(), clientSets, analyzerFlags.Name, analyzerFlags.Namespace)
default:
return fmt.Errorf("kind %s not supported", analyzerFlags.Kind)
}
Expand Down
116 changes: 116 additions & 0 deletions internal/analyzers/operator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// 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"

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

func RunOperatorAnalyzer(ctx context.Context, clientSets *k8sutil.ClientSets, name, namespace string) error {
op, err := clientSets.KClient.AppsV1().Deployments(namespace).Get(ctx, name, metav1.GetOptions{})
if err != nil {
return fmt.Errorf("failed to get Prometheus Operator deployment: %w", err)
}

cRb, err := clientSets.KClient.RbacV1().ClusterRoleBindings().List(ctx, metav1.ListOptions{
LabelSelector: "app.kubernetes.io/name=prometheus-operator",
})

if err != nil {
return fmt.Errorf("failed to list RoleBindings: %w", err)
}

// Check if the ServiceAccount is bound to any ClusterRoleBindings
if !isServiceAccountBoundToRoleBindingList(cRb, op.Spec.Template.Spec.ServiceAccountName) {
return fmt.Errorf("ServiceAccount %s is not bound to any RoleBindings", op.Spec.Template.Spec.ServiceAccountName)
}

for _, crb := range cRb.Items {
cr, err := clientSets.KClient.RbacV1().ClusterRoles().Get(ctx, crb.RoleRef.Name, metav1.GetOptions{})
if err != nil {
return fmt.Errorf("failed to get ClusterRole %s", crb.RoleRef.Name)
}

err = analyzeClusterRoleAndCRDRules(ctx, clientSets, crb, cr)
if err != nil {
return err
}
}

return nil
}

func analyzeClusterRoleAndCRDRules(ctx context.Context, clientSets *k8sutil.ClientSets, crb v1.ClusterRoleBinding, cr *v1.ClusterRole) error {
foundAPIGroup := false
for _, rule := range cr.Rules {
for _, apiGroup := range rule.APIGroups {
if apiGroup == "monitoring.coreos.com" {
foundAPIGroup = true
err := analyzeCRDRules(ctx, clientSets, crb, rule)
if err != nil {
return err
}
break
}
}
}

if !foundAPIGroup {
return fmt.Errorf("ClusterRole %s does not have monitoring.coreos.com APIGroup in its rules", crb.RoleRef.Name)
}

return nil
}

func analyzeCRDRules(ctx context.Context, clientSets *k8sutil.ClientSets, crb v1.ClusterRoleBinding, rule v1.PolicyRule) error {
for _, crd := range crds.List {
crd, err := clientSets.APIExtensionsClient.ApiextensionsV1().CustomResourceDefinitions().Get(ctx, crd, metav1.GetOptions{})
if err != nil {
return fmt.Errorf("failed to get CRD %s", crd)
}

found := false
for _, r := range rule.Resources {
if r == crd.Spec.Names.Plural || r == crd.Spec.Names.Singular || r == crd.Spec.Names.Plural+"/finalizers" || r == crd.Spec.Names.Singular+"/finalizers" {
found = true
break
}
}

if !found {
return fmt.Errorf("ClusterRole %s does not have %s in its rules", crb.RoleRef.Name, crd.Spec.Names.Plural)
}
}
return nil
}

func isServiceAccountBoundToRoleBindingList(clusterRoleBindings *v1.ClusterRoleBindingList, serviceAccountName string) bool {
for _, roleBinding := range clusterRoleBindings.Items {
if roleBinding.Subjects != nil {
for _, subject := range roleBinding.Subjects {
if subject.Kind == "ServiceAccount" && subject.Name == serviceAccountName {
return true
}
}
}
}
return false
}
Loading

0 comments on commit d210d0b

Please sign in to comment.