Skip to content

Commit

Permalink
feat: add pda analyzer
Browse files Browse the repository at this point in the history
Adds a PodDisruptionBudget analyzer, that checks if PDBs have matching
pods with their defined selector.

Signed-off-by: Dominik Augustin <dom.augustin@gmx.at>
  • Loading branch information
yeahservice committed Apr 2, 2023
1 parent 071ee56 commit 532a5ce
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 30 deletions.
2 changes: 2 additions & 0 deletions pkg/analyzer/analysis.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
autoscalingv1 "k8s.io/api/autoscaling/v1"
v1 "k8s.io/api/core/v1"
networkingv1 "k8s.io/api/networking/v1"
policyv1 "k8s.io/api/policy/v1"
)

type AnalysisConfiguration struct {
Expand All @@ -21,6 +22,7 @@ type PreAnalysis struct {
Endpoint v1.Endpoints
Ingress networkingv1.Ingress
HorizontalPodAutoscalers autoscalingv1.HorizontalPodAutoscaler
PodDiscruptionBudget policyv1.PodDisruptionBudget
}

type Analysis struct {
Expand Down
1 change: 1 addition & 0 deletions pkg/analyzer/analyzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ var coreAnalyzerMap = map[string]func(ctx context.Context, config *AnalysisConfi
var additionalAnalyzerMap = map[string]func(ctx context.Context, config *AnalysisConfiguration,
client *kubernetes.Client, aiClient ai.IAI, analysisResults *[]Analysis) error{
"HorizontalPodAutoScaler": AnalyzeHpa,
"PodDisruptionBudget": AnalyzePdb,
}

func RunAnalysis(ctx context.Context, filters []string, config *AnalysisConfiguration,
Expand Down
31 changes: 3 additions & 28 deletions pkg/analyzer/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,17 @@ package analyzer

import (
"context"

"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func FetchLatestPodEvent(ctx context.Context, kubernetesClient *kubernetes.Client, pod *v1.Pod) (*v1.Event, error) {

// get the list of events
events, err := kubernetesClient.GetClient().CoreV1().Events(pod.Namespace).List(ctx,
metav1.ListOptions{
FieldSelector: "involvedObject.name=" + pod.Name,
})

if err != nil {
return nil, err
}
// find most recent event
var latestEvent *v1.Event
for _, event := range events.Items {
if latestEvent == nil {
latestEvent = &event
}
if event.LastTimestamp.After(latestEvent.LastTimestamp.Time) {
latestEvent = &event
}
}
return latestEvent, nil
}

func FetchLatestPvcEvent(ctx context.Context, kubernetesClient *kubernetes.Client, pvc *v1.PersistentVolumeClaim) (*v1.Event, error) {
func FetchLatestEvent(ctx context.Context, kubernetesClient *kubernetes.Client, namespace string, name string) (*v1.Event, error) {

// get the list of events
events, err := kubernetesClient.GetClient().CoreV1().Events(pvc.Namespace).List(ctx,
events, err := kubernetesClient.GetClient().CoreV1().Events(namespace).List(ctx,
metav1.ListOptions{
FieldSelector: "involvedObject.name=" + pvc.Name,
FieldSelector: "involvedObject.name=" + name,
})

if err != nil {
Expand Down
64 changes: 64 additions & 0 deletions pkg/analyzer/pdbAnalyzer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package analyzer

import (
"context"
"fmt"
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func AnalyzePdb(ctx context.Context, config *AnalysisConfiguration, client *kubernetes.Client, aiClient ai.IAI,
analysisResults *[]Analysis) error {

list, err := client.GetClient().PolicyV1().PodDisruptionBudgets(config.Namespace).List(ctx, metav1.ListOptions{})
if err != nil {
return err
}

var preAnalysis = map[string]PreAnalysis{}

for _, pdb := range list.Items {
var failures []string

evt, err := FetchLatestEvent(ctx, client, pdb.Namespace, pdb.Name)
if err != nil || evt == nil {
continue
}

if evt.Reason == "NoPods" && evt.Message != "" {
if pdb.Spec.Selector != nil {
for k, v := range pdb.Spec.Selector.MatchLabels {
failures = append(failures, fmt.Sprintf("%s, expected label %s=%s", evt.Message, k, v))
}
for _, v := range pdb.Spec.Selector.MatchExpressions {
failures = append(failures, fmt.Sprintf("%s, expected expression %s", evt.Message, v))
}
} else {
failures = append(failures, fmt.Sprintf("%s, selector is nil", evt.Message))
}
}

if len(failures) > 0 {
preAnalysis[fmt.Sprintf("%s/%s", pdb.Namespace, pdb.Name)] = PreAnalysis{
PodDiscruptionBudget: pdb,
FailureDetails: failures,
}
}
}

for key, value := range preAnalysis {
var currentAnalysis = Analysis{
Kind: "PodDisruptionBudget",
Name: key,
Error: value.FailureDetails,
}

parent, _ := util.GetParent(client, value.PodDiscruptionBudget.ObjectMeta)
currentAnalysis.ParentObject = parent
*analysisResults = append(*analysisResults, currentAnalysis)
}

return nil
}
2 changes: 1 addition & 1 deletion pkg/analyzer/podAnalyzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func AnalyzePod(ctx context.Context, config *AnalysisConfiguration,
if containerStatus.State.Waiting.Reason == "ContainerCreating" && pod.Status.Phase == "Pending" {

// parse the event log and append details
evt, err := FetchLatestPodEvent(ctx, client, &pod)
evt, err := FetchLatestEvent(ctx, client, pod.Namespace, pod.Name)
if err != nil || evt == nil {
continue
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/analyzer/pvcAnalyzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func AnalyzePersistentVolumeClaim(ctx context.Context, config *AnalysisConfigura
if pvc.Status.Phase == "Pending" {

// parse the event log and append details
evt, err := FetchLatestPvcEvent(ctx, client, &pvc)
evt, err := FetchLatestEvent(ctx, client, pvc.Namespace, pvc.Name)
if err != nil || evt == nil {
continue
}
Expand Down

0 comments on commit 532a5ce

Please sign in to comment.