Skip to content

Commit

Permalink
feat: add hpa analyzer and init additionalAnalyzers
Browse files Browse the repository at this point in the history
Signed-off-by: Matthis Holleville <matthish29@gmail.com>
  • Loading branch information
matthisholleville committed Mar 31, 2023
1 parent 0e3ef8d commit 3603872
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 17 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,19 @@ you will be able to write your own analyzers.

### Built in analyzers

#### Enabled by default

- [x] podAnalyzer
- [x] pvcAnalyzer
- [x] rsAnalyzer
- [x] serviceAnalyzer
- [x] eventAnalyzer
- [x] ingressAnalyzer

#### Optional

- [x] hpaAnalyzer

## Usage

```
Expand Down
6 changes: 4 additions & 2 deletions cmd/filters/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ var addCmd = &cobra.Command{
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
inputFilters := strings.Split(args[0], ",")
availableFilters := analyzer.ListFilters()
coreFilters, additionalFilters := analyzer.ListFilters()

availableFilters := append(coreFilters, additionalFilters...)

// Verify filter exist
invalidFilters := []string{}
Expand Down Expand Up @@ -47,7 +49,7 @@ var addCmd = &cobra.Command{
// Get defined active_filters
activeFilters := viper.GetStringSlice("active_filters")
if len(activeFilters) == 0 {
activeFilters = availableFilters
activeFilters = coreFilters
}

mergedFilters := append(activeFilters, inputFilters...)
Expand Down
5 changes: 3 additions & 2 deletions cmd/filters/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ var listCmd = &cobra.Command{
Long: `The list command displays a list of available filters that can be used to analyze Kubernetes resources.`,
Run: func(cmd *cobra.Command, args []string) {
activeFilters := viper.GetStringSlice("active_filters")
availableFilters := analyzer.ListFilters()
coreFilters, additionalFilters := analyzer.ListFilters()

availableFilters := append(coreFilters, additionalFilters...)
if len(activeFilters) == 0 {
activeFilters = availableFilters
activeFilters = coreFilters
}

inactiveFilters := util.SliceDiff(availableFilters, activeFilters)
Expand Down
4 changes: 3 additions & 1 deletion cmd/filters/remove.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ var removeCmd = &cobra.Command{

// Get defined active_filters
activeFilters := viper.GetStringSlice("active_filters")
coreFilters, _ := analyzer.ListFilters()

if len(activeFilters) == 0 {
activeFilters = analyzer.ListFilters()
activeFilters = coreFilters
}

// Check if input input filters is not empty
Expand Down
14 changes: 8 additions & 6 deletions pkg/analyzer/analysis.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package analyzer

import (
appsv1 "k8s.io/api/apps/v1"
autoscalingv1 "k8s.io/api/autoscaling/v1"
v1 "k8s.io/api/core/v1"
networkingv1 "k8s.io/api/networking/v1"
)
Expand All @@ -13,12 +14,13 @@ type AnalysisConfiguration struct {
}

type PreAnalysis struct {
Pod v1.Pod
FailureDetails []string
ReplicaSet appsv1.ReplicaSet
PersistentVolumeClaim v1.PersistentVolumeClaim
Endpoint v1.Endpoints
Ingress networkingv1.Ingress
Pod v1.Pod
FailureDetails []string
ReplicaSet appsv1.ReplicaSet
PersistentVolumeClaim v1.PersistentVolumeClaim
Endpoint v1.Endpoints
Ingress networkingv1.Ingress
HorizontalPodAutoscalers autoscalingv1.HorizontalPodAutoscaler
}

type Analysis struct {
Expand Down
43 changes: 37 additions & 6 deletions pkg/analyzer/analyzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"github.com/spf13/viper"
)

var analyzerMap = map[string]func(ctx context.Context, config *AnalysisConfiguration,
var coreAnalyzerMap = map[string]func(ctx context.Context, config *AnalysisConfiguration,
client *kubernetes.Client, aiClient ai.IAI, analysisResults *[]Analysis) error{
"Pod": AnalyzePod,
"ReplicaSet": AnalyzeReplicaSet,
Expand All @@ -20,12 +20,19 @@ var analyzerMap = map[string]func(ctx context.Context, config *AnalysisConfigura
"Ingress": AnalyzeIngress,
}

var additionalAnalyzerMap = map[string]func(ctx context.Context, config *AnalysisConfiguration,
client *kubernetes.Client, aiClient ai.IAI, analysisResults *[]Analysis) error{
"HorizontalPodAutoScaler": AnalyzeHpa,
}

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

activeFilters := viper.GetStringSlice("active_filters")

analyzerMap := getAnalyzerMap()

// if there are no filters selected and no active_filters then run all of them
if len(filters) == 0 && len(activeFilters) == 0 {
for _, analyzer := range analyzerMap {
Expand Down Expand Up @@ -97,10 +104,34 @@ func ParseViaAI(ctx context.Context, config *AnalysisConfiguration,
return response, nil
}

func ListFilters() []string {
keys := make([]string, 0, len(analyzerMap))
for k := range analyzerMap {
keys = append(keys, k)
func ListFilters() ([]string, []string) {
coreKeys := make([]string, 0, len(coreAnalyzerMap))
for k := range coreAnalyzerMap {
coreKeys = append(coreKeys, k)
}

additionalKeys := make([]string, 0, len(additionalAnalyzerMap))
for k := range additionalAnalyzerMap {
additionalKeys = append(additionalKeys, k)
}
return coreKeys, additionalKeys
}

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

mergedMap := make(map[string]func(ctx context.Context, config *AnalysisConfiguration,
client *kubernetes.Client, aiClient ai.IAI, analysisResults *[]Analysis) error)

// add core analyzer
for key, value := range coreAnalyzerMap {
mergedMap[key] = value
}
return keys

// add additional analyzer
for key, value := range additionalAnalyzerMap {
mergedMap[key] = value
}

return mergedMap
}
79 changes: 79 additions & 0 deletions pkg/analyzer/hpaAnalyzer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
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 AnalyzeHpa(ctx context.Context, config *AnalysisConfiguration, client *kubernetes.Client, aiClient ai.IAI,
analysisResults *[]Analysis) error {

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

var preAnalysis = map[string]PreAnalysis{}

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

// check ScaleTargetRef exist
scaleTargetRef := hpa.Spec.ScaleTargetRef
scaleTargetRefNotFound := false

switch scaleTargetRef.Kind {
case "Deployment":
_, err := client.GetClient().AppsV1().Deployments(config.Namespace).Get(ctx, scaleTargetRef.Name, metav1.GetOptions{})
if err != nil {
scaleTargetRefNotFound = true
}
case "ReplicationController":
_, err := client.GetClient().CoreV1().ReplicationControllers(config.Namespace).Get(ctx, scaleTargetRef.Name, metav1.GetOptions{})
if err != nil {
scaleTargetRefNotFound = true
}
case "ReplicaSet":
_, err := client.GetClient().AppsV1().ReplicaSets(config.Namespace).Get(ctx, scaleTargetRef.Name, metav1.GetOptions{})
if err != nil {
scaleTargetRefNotFound = true
}
case "StatefulSet":
_, err := client.GetClient().AppsV1().Deployments(config.Namespace).Get(ctx, scaleTargetRef.Name, metav1.GetOptions{})
if err != nil {
scaleTargetRefNotFound = true
}
}

if scaleTargetRefNotFound {
failures = append(failures, fmt.Sprintf("HorizontalPodAutoscaler uses %s/%s as ScaleTargetRef which does not exist.", scaleTargetRef.Kind, scaleTargetRef.Name))
}

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

}

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

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

return nil
}

0 comments on commit 3603872

Please sign in to comment.