mirror of
https://github.com/k8sgpt-ai/k8sgpt.git
synced 2025-09-23 12:17:41 +00:00
feat: find parent objects
Signed-off-by: Thomas Schuetz <thomas.schuetz@t-sc.eu>
This commit is contained in:
@@ -79,8 +79,7 @@ var problemsCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
fmt.Println(string(j))
|
fmt.Println(string(j))
|
||||||
default:
|
default:
|
||||||
fmt.Printf("%s %s: %s \n%s\n", color.CyanString("%d", n), color.YellowString(analysis.Name), color.RedString(analysis.Error), color.GreenString(analysis.Details))
|
fmt.Printf("%s %s(%s): %s \n%s\n", color.CyanString("%d", n), color.YellowString(analysis.Name), color.CyanString(analysis.ParentObject), color.RedString(analysis.Error), color.GreenString(analysis.Details))
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,8 +1,20 @@
|
|||||||
package analyzer
|
package analyzer
|
||||||
|
|
||||||
type Analysis struct {
|
import (
|
||||||
Kind string
|
appsv1 "k8s.io/api/apps/v1"
|
||||||
Name string
|
v1 "k8s.io/api/core/v1"
|
||||||
Error string
|
)
|
||||||
Details string
|
|
||||||
|
type PreAnalysis struct {
|
||||||
|
Pod v1.Pod
|
||||||
|
FailureDetails []string
|
||||||
|
ReplicaSet appsv1.ReplicaSet
|
||||||
|
}
|
||||||
|
|
||||||
|
type Analysis struct {
|
||||||
|
Kind string `json:"kind"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Error string `json:"error"`
|
||||||
|
Details string `json:"details"`
|
||||||
|
ParentObject string `json:"parentObject"`
|
||||||
}
|
}
|
||||||
|
@@ -22,30 +22,30 @@ func AnalyzePod(ctx context.Context, client *kubernetes.Client, aiClient ai.IAI,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
var preAnalysis = map[string]PreAnalysis{}
|
||||||
var brokenPods = map[string][]string{}
|
|
||||||
|
|
||||||
for _, pod := range list.Items {
|
for _, pod := range list.Items {
|
||||||
|
var failures []string
|
||||||
// Check for pending pods
|
// Check for pending pods
|
||||||
if pod.Status.Phase == "Pending" {
|
if pod.Status.Phase == "Pending" {
|
||||||
|
|
||||||
// Check through container status to check for crashes
|
// Check through container status to check for crashes
|
||||||
for _, containerStatus := range pod.Status.Conditions {
|
for _, containerStatus := range pod.Status.Conditions {
|
||||||
if containerStatus.Type == "PodScheduled" && containerStatus.Reason == "Unschedulable" {
|
if containerStatus.Type == "PodScheduled" && containerStatus.Reason == "Unschedulable" {
|
||||||
brokenPods[fmt.Sprintf("%s/%s", pod.Namespace, pod.Name)] = []string{containerStatus.Message}
|
if containerStatus.Message != "" {
|
||||||
|
failures = []string{containerStatus.Message}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check through container status to check for crashes
|
// Check through container status to check for crashes
|
||||||
var failureDetails = []string{}
|
|
||||||
for _, containerStatus := range pod.Status.ContainerStatuses {
|
for _, containerStatus := range pod.Status.ContainerStatuses {
|
||||||
if containerStatus.State.Waiting != nil {
|
if containerStatus.State.Waiting != nil {
|
||||||
if containerStatus.State.Waiting.Reason == "CrashLoopBackOff" || containerStatus.State.Waiting.Reason == "ImagePullBackOff" {
|
if containerStatus.State.Waiting.Reason == "CrashLoopBackOff" || containerStatus.State.Waiting.Reason == "ImagePullBackOff" {
|
||||||
|
if containerStatus.State.Waiting.Message != "" {
|
||||||
failureDetails = append(failureDetails, containerStatus.State.Waiting.Message)
|
failures = append(failures, containerStatus.State.Waiting.Message)
|
||||||
brokenPods[fmt.Sprintf("%s/%s", pod.Namespace, pod.Name)] = failureDetails
|
}
|
||||||
}
|
}
|
||||||
// This represents a container that is still being created or blocked due to conditions such as OOMKilled
|
// This represents a container that is still being created or blocked due to conditions such as OOMKilled
|
||||||
if containerStatus.State.Waiting.Reason == "ContainerCreating" && pod.Status.Phase == "Pending" {
|
if containerStatus.State.Waiting.Reason == "ContainerCreating" && pod.Status.Phase == "Pending" {
|
||||||
@@ -55,24 +55,30 @@ func AnalyzePod(ctx context.Context, client *kubernetes.Client, aiClient ai.IAI,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if evt.Reason == "FailedCreatePodSandBox" {
|
if evt.Reason == "FailedCreatePodSandBox" && evt.Message != "" {
|
||||||
failureDetails = append(failureDetails, evt.Message)
|
failures = append(failures, evt.Message)
|
||||||
brokenPods[fmt.Sprintf("%s/%s", pod.Namespace, pod.Name)] = failureDetails
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(failures) > 0 {
|
||||||
|
preAnalysis[fmt.Sprintf("%s/%s", pod.Namespace, pod.Name)] = PreAnalysis{
|
||||||
|
Pod: pod,
|
||||||
|
FailureDetails: failures,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
for key, value := range preAnalysis {
|
||||||
}
|
inputValue := strings.Join(value.FailureDetails, " ")
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
for key, value := range brokenPods {
|
|
||||||
inputValue := strings.Join(value, " ")
|
|
||||||
var currentAnalysis = Analysis{
|
var currentAnalysis = Analysis{
|
||||||
Kind: "Pod",
|
Kind: "Pod",
|
||||||
Name: key,
|
Name: key,
|
||||||
Error: value[0],
|
Error: value.FailureDetails[0],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parent, _ := getParent(client, value.Pod.ObjectMeta)
|
||||||
|
|
||||||
if explain {
|
if explain {
|
||||||
s := spinner.New(spinner.CharSets[35], 100*time.Millisecond) // Build our new spinner
|
s := spinner.New(spinner.CharSets[35], 100*time.Millisecond) // Build our new spinner
|
||||||
s.Start()
|
s.Start()
|
||||||
@@ -112,10 +118,59 @@ func AnalyzePod(ctx context.Context, client *kubernetes.Client, aiClient ai.IAI,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
currentAnalysis.Details = response
|
currentAnalysis.Details = response
|
||||||
|
|
||||||
}
|
}
|
||||||
|
currentAnalysis.ParentObject = parent
|
||||||
*analysisResults = append(*analysisResults, currentAnalysis)
|
*analysisResults = append(*analysisResults, currentAnalysis)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getParent(client *kubernetes.Client, meta metav1.ObjectMeta) (string, bool) {
|
||||||
|
if meta.OwnerReferences != nil {
|
||||||
|
for _, owner := range meta.OwnerReferences {
|
||||||
|
switch owner.Kind {
|
||||||
|
case "ReplicaSet":
|
||||||
|
rs, err := client.GetClient().AppsV1().ReplicaSets(meta.Namespace).Get(context.Background(), owner.Name, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
if rs.OwnerReferences != nil {
|
||||||
|
return getParent(client, rs.ObjectMeta)
|
||||||
|
}
|
||||||
|
return "ReplicaSet/" + rs.Name, false
|
||||||
|
|
||||||
|
case "Deployment":
|
||||||
|
dep, err := client.GetClient().AppsV1().Deployments(meta.Namespace).Get(context.Background(), owner.Name, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
if dep.OwnerReferences != nil {
|
||||||
|
return getParent(client, dep.ObjectMeta)
|
||||||
|
}
|
||||||
|
return "Deployment/" + dep.Name, false
|
||||||
|
|
||||||
|
case "StatefulSet":
|
||||||
|
sts, err := client.GetClient().AppsV1().StatefulSets(meta.Namespace).Get(context.Background(), owner.Name, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
if sts.OwnerReferences != nil {
|
||||||
|
return getParent(client, sts.ObjectMeta)
|
||||||
|
}
|
||||||
|
return "StatefulSet/" + sts.Name, false
|
||||||
|
|
||||||
|
case "DaemonSet":
|
||||||
|
ds, err := client.GetClient().AppsV1().DaemonSets(meta.Namespace).Get(context.Background(), owner.Name, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
if ds.OwnerReferences != nil {
|
||||||
|
return getParent(client, ds.ObjectMeta)
|
||||||
|
}
|
||||||
|
return "DaemonSet/" + ds.Name, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return meta.Name, false
|
||||||
|
}
|
||||||
|
@@ -23,9 +23,10 @@ func AnalyzeReplicaSet(ctx context.Context, client *kubernetes.Client, aiClient
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var brokenRS = map[string][]string{}
|
var preAnalysis = map[string]PreAnalysis{}
|
||||||
|
|
||||||
for _, rs := range list.Items {
|
for _, rs := range list.Items {
|
||||||
|
var failures []string
|
||||||
|
|
||||||
// Check for empty rs
|
// Check for empty rs
|
||||||
if rs.Status.Replicas == 0 {
|
if rs.Status.Replicas == 0 {
|
||||||
@@ -33,24 +34,32 @@ func AnalyzeReplicaSet(ctx context.Context, client *kubernetes.Client, aiClient
|
|||||||
// Check through container status to check for crashes
|
// Check through container status to check for crashes
|
||||||
for _, rsStatus := range rs.Status.Conditions {
|
for _, rsStatus := range rs.Status.Conditions {
|
||||||
if rsStatus.Type == "ReplicaFailure" && rsStatus.Reason == "FailedCreate" {
|
if rsStatus.Type == "ReplicaFailure" && rsStatus.Reason == "FailedCreate" {
|
||||||
brokenRS[fmt.Sprintf("%s/%s", rs.Namespace, rs.Name)] = []string{rsStatus.Message}
|
failures = []string{rsStatus.Message}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(failures) > 0 {
|
||||||
|
preAnalysis[fmt.Sprintf("%s/%s", rs.Namespace, rs.Name)] = PreAnalysis{
|
||||||
|
ReplicaSet: rs,
|
||||||
|
FailureDetails: failures,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for key, value := range brokenRS {
|
for key, value := range preAnalysis {
|
||||||
var currentAnalysis = Analysis{
|
var currentAnalysis = Analysis{
|
||||||
Kind: "ReplicaSet",
|
Kind: "ReplicaSet",
|
||||||
Name: key,
|
Name: key,
|
||||||
Error: value[0],
|
Error: value.FailureDetails[0],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parent, _ := getParent(client, value.ReplicaSet.ObjectMeta)
|
||||||
|
|
||||||
if explain {
|
if explain {
|
||||||
s := spinner.New(spinner.CharSets[35], 100*time.Millisecond) // Build our new spinner
|
s := spinner.New(spinner.CharSets[35], 100*time.Millisecond) // Build our new spinner
|
||||||
s.Start()
|
s.Start()
|
||||||
|
|
||||||
inputValue := strings.Join(value, " ")
|
inputValue := strings.Join(value.FailureDetails, " ")
|
||||||
|
|
||||||
// Check for cached data
|
// Check for cached data
|
||||||
sEnc := base64.StdEncoding.EncodeToString([]byte(inputValue))
|
sEnc := base64.StdEncoding.EncodeToString([]byte(inputValue))
|
||||||
@@ -88,6 +97,7 @@ func AnalyzeReplicaSet(ctx context.Context, client *kubernetes.Client, aiClient
|
|||||||
}
|
}
|
||||||
currentAnalysis.Details = response
|
currentAnalysis.Details = response
|
||||||
}
|
}
|
||||||
|
currentAnalysis.ParentObject = parent
|
||||||
*analysisResults = append(*analysisResults, currentAnalysis)
|
*analysisResults = append(*analysisResults, currentAnalysis)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user