mirror of
https://github.com/k8sgpt-ai/k8sgpt.git
synced 2025-08-30 13:29:41 +00:00
Merge pull request #171 from matthisholleville/feature/hpa-analyzer
feat: add hpa analyzer and init additionalAnalyzers
This commit is contained in:
commit
a76d60a652
@ -60,6 +60,8 @@ you will be able to write your own analyzers.
|
|||||||
|
|
||||||
### Built in analyzers
|
### Built in analyzers
|
||||||
|
|
||||||
|
#### Enabled by default
|
||||||
|
|
||||||
- [x] podAnalyzer
|
- [x] podAnalyzer
|
||||||
- [x] pvcAnalyzer
|
- [x] pvcAnalyzer
|
||||||
- [x] rsAnalyzer
|
- [x] rsAnalyzer
|
||||||
@ -67,6 +69,10 @@ you will be able to write your own analyzers.
|
|||||||
- [x] eventAnalyzer
|
- [x] eventAnalyzer
|
||||||
- [x] ingressAnalyzer
|
- [x] ingressAnalyzer
|
||||||
|
|
||||||
|
#### Optional
|
||||||
|
|
||||||
|
- [x] hpaAnalyzer
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -18,7 +18,9 @@ var addCmd = &cobra.Command{
|
|||||||
Args: cobra.ExactArgs(1),
|
Args: cobra.ExactArgs(1),
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
inputFilters := strings.Split(args[0], ",")
|
inputFilters := strings.Split(args[0], ",")
|
||||||
availableFilters := analyzer.ListFilters()
|
coreFilters, additionalFilters := analyzer.ListFilters()
|
||||||
|
|
||||||
|
availableFilters := append(coreFilters, additionalFilters...)
|
||||||
|
|
||||||
// Verify filter exist
|
// Verify filter exist
|
||||||
invalidFilters := []string{}
|
invalidFilters := []string{}
|
||||||
@ -47,7 +49,7 @@ var addCmd = &cobra.Command{
|
|||||||
// Get defined active_filters
|
// Get defined active_filters
|
||||||
activeFilters := viper.GetStringSlice("active_filters")
|
activeFilters := viper.GetStringSlice("active_filters")
|
||||||
if len(activeFilters) == 0 {
|
if len(activeFilters) == 0 {
|
||||||
activeFilters = availableFilters
|
activeFilters = coreFilters
|
||||||
}
|
}
|
||||||
|
|
||||||
mergedFilters := append(activeFilters, inputFilters...)
|
mergedFilters := append(activeFilters, inputFilters...)
|
||||||
|
@ -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.`,
|
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) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
activeFilters := viper.GetStringSlice("active_filters")
|
activeFilters := viper.GetStringSlice("active_filters")
|
||||||
availableFilters := analyzer.ListFilters()
|
coreFilters, additionalFilters := analyzer.ListFilters()
|
||||||
|
|
||||||
|
availableFilters := append(coreFilters, additionalFilters...)
|
||||||
if len(activeFilters) == 0 {
|
if len(activeFilters) == 0 {
|
||||||
activeFilters = availableFilters
|
activeFilters = coreFilters
|
||||||
}
|
}
|
||||||
|
|
||||||
inactiveFilters := util.SliceDiff(availableFilters, activeFilters)
|
inactiveFilters := util.SliceDiff(availableFilters, activeFilters)
|
||||||
|
@ -21,8 +21,10 @@ var removeCmd = &cobra.Command{
|
|||||||
|
|
||||||
// Get defined active_filters
|
// Get defined active_filters
|
||||||
activeFilters := viper.GetStringSlice("active_filters")
|
activeFilters := viper.GetStringSlice("active_filters")
|
||||||
|
coreFilters, _ := analyzer.ListFilters()
|
||||||
|
|
||||||
if len(activeFilters) == 0 {
|
if len(activeFilters) == 0 {
|
||||||
activeFilters = analyzer.ListFilters()
|
activeFilters = coreFilters
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if input input filters is not empty
|
// Check if input input filters is not empty
|
||||||
|
@ -2,6 +2,7 @@ package analyzer
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
appsv1 "k8s.io/api/apps/v1"
|
appsv1 "k8s.io/api/apps/v1"
|
||||||
|
autoscalingv1 "k8s.io/api/autoscaling/v1"
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
networkingv1 "k8s.io/api/networking/v1"
|
networkingv1 "k8s.io/api/networking/v1"
|
||||||
)
|
)
|
||||||
@ -19,6 +20,7 @@ type PreAnalysis struct {
|
|||||||
PersistentVolumeClaim v1.PersistentVolumeClaim
|
PersistentVolumeClaim v1.PersistentVolumeClaim
|
||||||
Endpoint v1.Endpoints
|
Endpoint v1.Endpoints
|
||||||
Ingress networkingv1.Ingress
|
Ingress networkingv1.Ingress
|
||||||
|
HorizontalPodAutoscalers autoscalingv1.HorizontalPodAutoscaler
|
||||||
}
|
}
|
||||||
|
|
||||||
type Analysis struct {
|
type Analysis struct {
|
||||||
|
@ -11,7 +11,7 @@ import (
|
|||||||
"github.com/spf13/viper"
|
"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{
|
client *kubernetes.Client, aiClient ai.IAI, analysisResults *[]Analysis) error{
|
||||||
"Pod": AnalyzePod,
|
"Pod": AnalyzePod,
|
||||||
"ReplicaSet": AnalyzeReplicaSet,
|
"ReplicaSet": AnalyzeReplicaSet,
|
||||||
@ -20,12 +20,19 @@ var analyzerMap = map[string]func(ctx context.Context, config *AnalysisConfigura
|
|||||||
"Ingress": AnalyzeIngress,
|
"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,
|
func RunAnalysis(ctx context.Context, filters []string, config *AnalysisConfiguration,
|
||||||
client *kubernetes.Client,
|
client *kubernetes.Client,
|
||||||
aiClient ai.IAI, analysisResults *[]Analysis) error {
|
aiClient ai.IAI, analysisResults *[]Analysis) error {
|
||||||
|
|
||||||
activeFilters := viper.GetStringSlice("active_filters")
|
activeFilters := viper.GetStringSlice("active_filters")
|
||||||
|
|
||||||
|
analyzerMap := getAnalyzerMap()
|
||||||
|
|
||||||
// if there are no filters selected and no active_filters then run all of them
|
// if there are no filters selected and no active_filters then run all of them
|
||||||
if len(filters) == 0 && len(activeFilters) == 0 {
|
if len(filters) == 0 && len(activeFilters) == 0 {
|
||||||
for _, analyzer := range analyzerMap {
|
for _, analyzer := range analyzerMap {
|
||||||
@ -97,10 +104,34 @@ func ParseViaAI(ctx context.Context, config *AnalysisConfiguration,
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ListFilters() []string {
|
func ListFilters() ([]string, []string) {
|
||||||
keys := make([]string, 0, len(analyzerMap))
|
coreKeys := make([]string, 0, len(coreAnalyzerMap))
|
||||||
for k := range analyzerMap {
|
for k := range coreAnalyzerMap {
|
||||||
keys = append(keys, k)
|
coreKeys = append(coreKeys, k)
|
||||||
}
|
}
|
||||||
return keys
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// add additional analyzer
|
||||||
|
for key, value := range additionalAnalyzerMap {
|
||||||
|
mergedMap[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
return mergedMap
|
||||||
}
|
}
|
||||||
|
81
pkg/analyzer/hpaAnalyzer.go
Normal file
81
pkg/analyzer/hpaAnalyzer.go
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
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().StatefulSets(config.Namespace).Get(ctx, scaleTargetRef.Name, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
scaleTargetRefNotFound = true
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
failures = append(failures, fmt.Sprintf("HorizontalPodAutoscaler uses %s as ScaleTargetRef which does not possible option.", scaleTargetRef.Kind))
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user