Files
troubleshoot/pkg/analyze/deployment_status.go
2025-03-12 13:49:15 +00:00

152 lines
5.0 KiB
Go

package analyzer
import (
"encoding/json"
"fmt"
"path/filepath"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/constants"
appsv1 "k8s.io/api/apps/v1"
)
type AnalyzeDeploymentStatus struct {
analyzer *troubleshootv1beta2.DeploymentStatus
}
func (a *AnalyzeDeploymentStatus) Title() string {
if a.analyzer.CheckName != "" {
return a.analyzer.CheckName
}
if a.analyzer.Name != "" && a.analyzer.Namespace != "" {
return fmt.Sprintf("%s/%s Deployment Status", a.analyzer.Namespace, a.analyzer.Name)
}
if a.analyzer.Name != "" {
return fmt.Sprintf("%s Deployment Status", a.analyzer.Name)
}
if a.analyzer.Namespace != "" {
return fmt.Sprintf("%s Deployment Status", a.analyzer.Namespace)
}
return "Deployment Status"
}
func (a *AnalyzeDeploymentStatus) IsExcluded() (bool, error) {
return isExcluded(a.analyzer.Exclude)
}
func (a *AnalyzeDeploymentStatus) Analyze(getFile getCollectedFileContents, findFiles getChildCollectedFileContents) ([]*AnalyzeResult, error) {
results, err := analyzeDeploymentStatus(a.analyzer, findFiles)
if err != nil {
return nil, err
}
for i := range results {
results[i].Strict = a.analyzer.Strict.BoolOrDefaultFalse()
}
return results, nil
}
func analyzeDeploymentStatus(analyzer *troubleshootv1beta2.DeploymentStatus, getFileContents getChildCollectedFileContents) ([]*AnalyzeResult, error) {
if analyzer.Name == "" {
return analyzeAllDeploymentStatuses(analyzer, getFileContents)
} else {
return analyzeOneDeploymentStatus(analyzer, getFileContents)
}
}
func analyzeOneDeploymentStatus(analyzer *troubleshootv1beta2.DeploymentStatus, getFileContents getChildCollectedFileContents) ([]*AnalyzeResult, error) {
excludeFiles := []string{}
files, err := getFileContents(filepath.Join(constants.CLUSTER_RESOURCES_DIR, constants.CLUSTER_RESOURCES_DEPLOYMENTS, fmt.Sprintf("%s.json", analyzer.Namespace)), excludeFiles)
if err != nil {
return nil, errors.Wrap(err, "failed to read collected deployments from namespace")
}
var result *AnalyzeResult
for _, collected := range files { // only 1 file here
var exists bool = true
var readyReplicas int
var deployments appsv1.DeploymentList
if err := json.Unmarshal(collected, &deployments); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal deployment list")
}
var status *appsv1.DeploymentStatus
for _, deployment := range deployments.Items {
if deployment.Name == analyzer.Name {
status = deployment.Status.DeepCopy()
}
}
if status == nil {
exists = false
readyReplicas = 0
} else {
readyReplicas = int(status.ReadyReplicas)
}
result, err = commonStatus(analyzer.Outcomes, analyzer.Name, "kubernetes_deployment_status", "https://troubleshoot.sh/images/analyzer-icons/deployment-status.svg?w=17&h=17", readyReplicas, exists, "deployment")
if err != nil {
return nil, errors.Wrap(err, "failed to process status")
}
}
if result == nil {
return nil, nil
}
return []*AnalyzeResult{result}, nil
}
func analyzeAllDeploymentStatuses(analyzer *troubleshootv1beta2.DeploymentStatus, getFileContents getChildCollectedFileContents) ([]*AnalyzeResult, error) {
excludeFiles := []string{}
fileNames := make([]string, 0)
if analyzer.Namespace != "" {
fileNames = append(fileNames, filepath.Join(constants.CLUSTER_RESOURCES_DIR, constants.CLUSTER_RESOURCES_DEPLOYMENTS, fmt.Sprintf("%s.json", analyzer.Namespace)))
}
for _, ns := range analyzer.Namespaces {
fileNames = append(fileNames, filepath.Join(constants.CLUSTER_RESOURCES_DIR, constants.CLUSTER_RESOURCES_DEPLOYMENTS, fmt.Sprintf("%s.json", ns)))
}
// no namespace specified, so we need to analyze all deployments
if len(fileNames) == 0 {
fileNames = append(fileNames, filepath.Join(constants.CLUSTER_RESOURCES_DIR, constants.CLUSTER_RESOURCES_DEPLOYMENTS, "*.json"))
}
results := []*AnalyzeResult{}
for _, fileName := range fileNames {
files, err := getFileContents(fileName, excludeFiles)
if err != nil {
return nil, errors.Wrap(err, "failed to read collected deployments from file")
}
for _, collected := range files {
var deployments appsv1.DeploymentList
if err := json.Unmarshal(collected, &deployments); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal deployment list")
}
for _, deployment := range deployments.Items {
if deployment.Status.Replicas == deployment.Status.AvailableReplicas {
continue
}
result := &AnalyzeResult{
Title: fmt.Sprintf("%s/%s Deployment Status", deployment.Namespace, deployment.Name),
IconKey: "kubernetes_deployment_status",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/deployment-status.svg?w=17&h=17",
IsFail: true,
Message: fmt.Sprintf("The deployment %s/%s has %d/%d replicas", deployment.Namespace, deployment.Name, deployment.Status.ReadyReplicas, deployment.Status.Replicas),
}
results = append(results, result)
}
}
}
return results, nil
}