mirror of
https://github.com/replicatedhq/troubleshoot.git
synced 2026-04-15 07:16:34 +00:00
Have all in-cluster analysers implement the same interface. This will help with the implementation of code that requires making calls to all analysers Fixes #995
161 lines
5.2 KiB
Go
161 lines
5.2 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 AnalyzeStatefulsetStatus struct {
|
|
analyzer *troubleshootv1beta2.StatefulsetStatus
|
|
}
|
|
|
|
func (a *AnalyzeStatefulsetStatus) Title() string {
|
|
if a.analyzer.CheckName != "" {
|
|
return a.analyzer.CheckName
|
|
}
|
|
|
|
if a.analyzer.Name != "" && a.analyzer.Namespace != "" {
|
|
return fmt.Sprintf("%s/%s Statefulset Status", a.analyzer.Namespace, a.analyzer.Name)
|
|
}
|
|
|
|
if a.analyzer.Name != "" {
|
|
return fmt.Sprintf("%s Statefulset Status", a.analyzer.Name)
|
|
}
|
|
if a.analyzer.Namespace != "" {
|
|
return fmt.Sprintf("%s Statefulset Status", a.analyzer.Namespace)
|
|
}
|
|
|
|
return "Statefulset Status"
|
|
}
|
|
|
|
func (a *AnalyzeStatefulsetStatus) IsExcluded() (bool, error) {
|
|
return isExcluded(a.analyzer.Exclude)
|
|
}
|
|
|
|
func (a *AnalyzeStatefulsetStatus) Analyze(getFile getCollectedFileContents, findFiles getChildCollectedFileContents) ([]*AnalyzeResult, error) {
|
|
results, err := analyzeStatefulsetStatus(a.analyzer, findFiles)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for i := range results {
|
|
results[i].Strict = a.analyzer.Strict.BoolOrDefaultFalse()
|
|
}
|
|
return results, nil
|
|
}
|
|
|
|
func analyzeStatefulsetStatus(analyzer *troubleshootv1beta2.StatefulsetStatus, getFileContents getChildCollectedFileContents) ([]*AnalyzeResult, error) {
|
|
if analyzer.Name == "" {
|
|
return analyzeAllStatefulsetStatuses(analyzer, getFileContents)
|
|
} else {
|
|
return analyzeOneStatefulsetStatus(analyzer, getFileContents)
|
|
}
|
|
}
|
|
|
|
func analyzeOneStatefulsetStatus(analyzer *troubleshootv1beta2.StatefulsetStatus, getFileContents getChildCollectedFileContents) ([]*AnalyzeResult, error) {
|
|
excludeFiles := []string{}
|
|
files, err := getFileContents(filepath.Join(constants.CLUSTER_RESOURCES_DIR, constants.CLUSTER_RESOURCES_STATEFULSETS, fmt.Sprintf("%s.json", analyzer.Namespace)), excludeFiles)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed to read collected statefulsets from namespace")
|
|
}
|
|
|
|
var result *AnalyzeResult
|
|
for _, collected := range files { // only 1 file here
|
|
var exists bool = true
|
|
var readyReplicas int
|
|
|
|
var statefulsets appsv1.StatefulSetList
|
|
if err := json.Unmarshal(collected, &statefulsets); err != nil {
|
|
return nil, errors.Wrap(err, "failed to unmarshal statefulset list")
|
|
}
|
|
|
|
var statefulset *appsv1.StatefulSet
|
|
for _, s := range statefulsets.Items {
|
|
if s.Name == analyzer.Name {
|
|
statefulset = s.DeepCopy()
|
|
break
|
|
}
|
|
}
|
|
|
|
if statefulset == nil {
|
|
exists = false
|
|
readyReplicas = 0
|
|
} else {
|
|
readyReplicas = int(statefulset.Status.ReadyReplicas)
|
|
}
|
|
if len(analyzer.Outcomes) > 0 {
|
|
result, err = commonStatus(analyzer.Outcomes, analyzer.Name, "kubernetes_statefulset_status", "https://troubleshoot.sh/images/analyzer-icons/statefulset-status.svg?w=23&h=14", readyReplicas, exists, "statefulset")
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed to process status")
|
|
}
|
|
} else {
|
|
result = getDefaultStatefulSetResult(statefulset)
|
|
}
|
|
}
|
|
|
|
if result == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
return []*AnalyzeResult{result}, nil
|
|
}
|
|
|
|
func analyzeAllStatefulsetStatuses(analyzer *troubleshootv1beta2.StatefulsetStatus, getFileContents getChildCollectedFileContents) ([]*AnalyzeResult, error) {
|
|
fileNames := make([]string, 0)
|
|
if analyzer.Namespace != "" {
|
|
fileNames = append(fileNames, filepath.Join(constants.CLUSTER_RESOURCES_DIR, constants.CLUSTER_RESOURCES_STATEFULSETS, fmt.Sprintf("%s.json", analyzer.Namespace)))
|
|
}
|
|
for _, ns := range analyzer.Namespaces {
|
|
fileNames = append(fileNames, filepath.Join(constants.CLUSTER_RESOURCES_DIR, constants.CLUSTER_RESOURCES_STATEFULSETS, fmt.Sprintf("%s.json", ns)))
|
|
}
|
|
|
|
// no namespace specified, so we need to analyze all statefulsets
|
|
if len(fileNames) == 0 {
|
|
fileNames = append(fileNames, filepath.Join(constants.CLUSTER_RESOURCES_DIR, constants.CLUSTER_RESOURCES_STATEFULSETS, "*.json"))
|
|
}
|
|
|
|
excludeFiles := []string{}
|
|
results := []*AnalyzeResult{}
|
|
for _, fileName := range fileNames {
|
|
files, err := getFileContents(fileName, excludeFiles)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed to read collected statefulsets from namespace")
|
|
}
|
|
|
|
for _, collected := range files {
|
|
var statefulsets appsv1.StatefulSetList
|
|
if err := json.Unmarshal(collected, &statefulsets); err != nil {
|
|
return nil, errors.Wrap(err, "failed to unmarshal statefulset list")
|
|
}
|
|
|
|
for _, statefulset := range statefulsets.Items {
|
|
result := getDefaultStatefulSetResult(&statefulset)
|
|
if result != nil {
|
|
results = append(results, result)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return results, nil
|
|
}
|
|
|
|
func getDefaultStatefulSetResult(statefulset *appsv1.StatefulSet) *AnalyzeResult {
|
|
if statefulset.Status.Replicas == statefulset.Status.ReadyReplicas {
|
|
return nil
|
|
}
|
|
|
|
return &AnalyzeResult{
|
|
Title: fmt.Sprintf("%s/%s Statefulset Status", statefulset.Namespace, statefulset.Name),
|
|
IconKey: "kubernetes_statefulset_status",
|
|
IconURI: "https://troubleshoot.sh/images/analyzer-icons/statefulset-status.svg?w=23&h=14",
|
|
IsFail: true,
|
|
Message: fmt.Sprintf("The statefulset %s/%s has %d/%d replicas", statefulset.Namespace, statefulset.Name, statefulset.Status.ReadyReplicas, statefulset.Status.Replicas),
|
|
}
|
|
}
|