mirror of
https://github.com/replicatedhq/troubleshoot.git
synced 2026-02-14 18:29:53 +00:00
ClusterPodStatuses analyzer (#456)
* ClusterPodStatuses analyzer Co-authored-by: divolgin <dmitriy@replicated.com>
This commit is contained in:
committed by
GitHub
parent
9cbdb70d16
commit
3d1d53ee9d
@@ -85,6 +85,52 @@ spec:
|
||||
- namespace
|
||||
- outcomes
|
||||
type: object
|
||||
clusterPodStatuses:
|
||||
properties:
|
||||
checkName:
|
||||
type: string
|
||||
exclude:
|
||||
type: BoolString
|
||||
namespaces:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
outcomes:
|
||||
items:
|
||||
properties:
|
||||
fail:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
pass:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
warn:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- namespaces
|
||||
- outcomes
|
||||
type: object
|
||||
clusterVersion:
|
||||
properties:
|
||||
checkName:
|
||||
|
||||
@@ -85,6 +85,52 @@ spec:
|
||||
- namespace
|
||||
- outcomes
|
||||
type: object
|
||||
clusterPodStatuses:
|
||||
properties:
|
||||
checkName:
|
||||
type: string
|
||||
exclude:
|
||||
type: BoolString
|
||||
namespaces:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
outcomes:
|
||||
items:
|
||||
properties:
|
||||
fail:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
pass:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
warn:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- namespaces
|
||||
- outcomes
|
||||
type: object
|
||||
clusterVersion:
|
||||
properties:
|
||||
checkName:
|
||||
|
||||
@@ -116,6 +116,52 @@ spec:
|
||||
- namespace
|
||||
- outcomes
|
||||
type: object
|
||||
clusterPodStatuses:
|
||||
properties:
|
||||
checkName:
|
||||
type: string
|
||||
exclude:
|
||||
type: BoolString
|
||||
namespaces:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
outcomes:
|
||||
items:
|
||||
properties:
|
||||
fail:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
pass:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
warn:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- namespaces
|
||||
- outcomes
|
||||
type: object
|
||||
clusterVersion:
|
||||
properties:
|
||||
checkName:
|
||||
|
||||
@@ -205,6 +205,20 @@ func Analyze(analyzer *troubleshootv1beta2.Analyze, getFile getCollectedFileCont
|
||||
}
|
||||
return []*AnalyzeResult{result}, nil
|
||||
}
|
||||
if analyzer.ClusterPodStatuses != nil {
|
||||
isExcluded, err := isExcluded(analyzer.ClusterPodStatuses.Exclude)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if isExcluded {
|
||||
return nil, nil
|
||||
}
|
||||
results, err := clusterPodStatuses(analyzer.ClusterPodStatuses, findFiles)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
if analyzer.ContainerRuntime != nil {
|
||||
isExcluded, err := isExcluded(analyzer.ContainerRuntime.Exclude)
|
||||
if err != nil {
|
||||
|
||||
130
pkg/analyze/cluster_pod_statuses.go
Normal file
130
pkg/analyze/cluster_pod_statuses.go
Normal file
@@ -0,0 +1,130 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func clusterPodStatuses(analyzer *troubleshootv1beta2.ClusterPodStatuses, getChildCollectedFileContents func(string) (map[string][]byte, error)) ([]*AnalyzeResult, error) {
|
||||
collected, err := getChildCollectedFileContents(filepath.Join("cluster-resources", "pods", "*.json"))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to read collected pods")
|
||||
}
|
||||
|
||||
var pods []corev1.Pod
|
||||
for fileName, fileContent := range collected {
|
||||
podsNs := strings.TrimSuffix(fileName, ".json")
|
||||
include := len(analyzer.Namespaces) == 0
|
||||
for _, ns := range analyzer.Namespaces {
|
||||
if ns == podsNs {
|
||||
include = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if include {
|
||||
var nsPods []corev1.Pod
|
||||
if err := json.Unmarshal(fileContent, &nsPods); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to unmarshal pods list for namespace %s", podsNs)
|
||||
}
|
||||
pods = append(pods, nsPods...)
|
||||
}
|
||||
}
|
||||
|
||||
allResults := []*AnalyzeResult{}
|
||||
|
||||
for _, pod := range pods {
|
||||
podResults := []*AnalyzeResult{}
|
||||
|
||||
for _, outcome := range analyzer.Outcomes {
|
||||
r := AnalyzeResult{}
|
||||
when := ""
|
||||
|
||||
if outcome.Fail != nil {
|
||||
r.IsFail = true
|
||||
r.Message = outcome.Fail.Message
|
||||
r.URI = outcome.Fail.URI
|
||||
when = outcome.Fail.When
|
||||
} else if outcome.Warn != nil {
|
||||
r.IsWarn = true
|
||||
r.Message = outcome.Warn.Message
|
||||
r.URI = outcome.Warn.URI
|
||||
when = outcome.Warn.When
|
||||
} else if outcome.Pass != nil {
|
||||
r.IsPass = true
|
||||
r.Message = outcome.Pass.Message
|
||||
r.URI = outcome.Pass.URI
|
||||
when = outcome.Pass.When
|
||||
} else {
|
||||
fmt.Println("error: found an empty outcome in a clusterPodStatuses analyzer") // don't stop
|
||||
continue
|
||||
}
|
||||
|
||||
parts := strings.Split(strings.TrimSpace(when), " ")
|
||||
if len(parts) < 2 {
|
||||
fmt.Printf("invalid 'when' format: %s\n", when) // don't stop
|
||||
continue
|
||||
}
|
||||
|
||||
match := false
|
||||
switch parts[0] {
|
||||
case "=", "==", "===":
|
||||
match = parts[1] == string(pod.Status.Phase)
|
||||
case "!=", "!==":
|
||||
match = parts[1] != string(pod.Status.Phase)
|
||||
}
|
||||
|
||||
if !match {
|
||||
continue
|
||||
}
|
||||
|
||||
r.Title = analyzer.CheckName
|
||||
if r.Title == "" {
|
||||
r.Title = "Pod {{ .Name }} status"
|
||||
}
|
||||
|
||||
if r.Message == "" {
|
||||
r.Message = "Pod {{ .Name }} status is {{ .Status.Phase }}"
|
||||
}
|
||||
|
||||
tmpl := template.New("pod")
|
||||
|
||||
// template the title
|
||||
titleTmpl, err := tmpl.Parse(r.Title)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create new title template")
|
||||
}
|
||||
var t bytes.Buffer
|
||||
err = titleTmpl.Execute(&t, pod)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to execute template")
|
||||
}
|
||||
r.Title = t.String()
|
||||
|
||||
// template the message
|
||||
msgTmpl, err := tmpl.Parse(r.Message)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create new title template")
|
||||
}
|
||||
var m bytes.Buffer
|
||||
err = msgTmpl.Execute(&m, pod)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to execute template")
|
||||
}
|
||||
r.Message = m.String()
|
||||
|
||||
podResults = append(podResults, &r)
|
||||
}
|
||||
|
||||
allResults = append(allResults, podResults...)
|
||||
}
|
||||
|
||||
return allResults, nil
|
||||
}
|
||||
@@ -63,6 +63,12 @@ type StatefulsetStatus struct {
|
||||
Name string `json:"name" yaml:"name"`
|
||||
}
|
||||
|
||||
type ClusterPodStatuses struct {
|
||||
AnalyzeMeta `json:",inline" yaml:",inline"`
|
||||
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
|
||||
Namespaces []string `json:"namespaces" yaml:"namespaces"`
|
||||
}
|
||||
|
||||
type ContainerRuntime struct {
|
||||
AnalyzeMeta `json:",inline" yaml:",inline"`
|
||||
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
|
||||
@@ -162,6 +168,7 @@ type Analyze struct {
|
||||
ImagePullSecret *ImagePullSecret `json:"imagePullSecret,omitempty" yaml:"imagePullSecret,omitempty"`
|
||||
DeploymentStatus *DeploymentStatus `json:"deploymentStatus,omitempty" yaml:"deploymentStatus,omitempty"`
|
||||
StatefulsetStatus *StatefulsetStatus `json:"statefulsetStatus,omitempty" yaml:"statefulsetStatus,omitempty"`
|
||||
ClusterPodStatuses *ClusterPodStatuses `json:"clusterPodStatuses,omitempty" yaml:"clusterPodStatuses,omitempty"`
|
||||
ContainerRuntime *ContainerRuntime `json:"containerRuntime,omitempty" yaml:"containerRuntime,omitempty"`
|
||||
Distribution *Distribution `json:"distribution,omitempty" yaml:"distribution,omitempty"`
|
||||
NodeResources *NodeResources `json:"nodeResources,omitempty" yaml:"nodeResources,omitempty"`
|
||||
|
||||
@@ -97,6 +97,11 @@ func (in *Analyze) DeepCopyInto(out *Analyze) {
|
||||
*out = new(StatefulsetStatus)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ClusterPodStatuses != nil {
|
||||
in, out := &in.ClusterPodStatuses, &out.ClusterPodStatuses
|
||||
*out = new(ClusterPodStatuses)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ContainerRuntime != nil {
|
||||
in, out := &in.ContainerRuntime, &out.ContainerRuntime
|
||||
*out = new(ContainerRuntime)
|
||||
@@ -511,6 +516,38 @@ func (in *ClusterInfo) DeepCopy() *ClusterInfo {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ClusterPodStatuses) DeepCopyInto(out *ClusterPodStatuses) {
|
||||
*out = *in
|
||||
out.AnalyzeMeta = in.AnalyzeMeta
|
||||
if in.Outcomes != nil {
|
||||
in, out := &in.Outcomes, &out.Outcomes
|
||||
*out = make([]*Outcome, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(Outcome)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
if in.Namespaces != nil {
|
||||
in, out := &in.Namespaces, &out.Namespaces
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterPodStatuses.
|
||||
func (in *ClusterPodStatuses) DeepCopy() *ClusterPodStatuses {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ClusterPodStatuses)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ClusterResources) DeepCopyInto(out *ClusterResources) {
|
||||
*out = *in
|
||||
|
||||
@@ -93,6 +93,77 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"clusterPodStatuses": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"namespaces",
|
||||
"outcomes"
|
||||
],
|
||||
"properties": {
|
||||
"checkName": {
|
||||
"type": "string"
|
||||
},
|
||||
"exclude": {
|
||||
"oneOf": [{"type": "string"},{"type": "boolean"}]
|
||||
},
|
||||
"namespaces": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"outcomes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"fail": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"uri": {
|
||||
"type": "string"
|
||||
},
|
||||
"when": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"pass": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"uri": {
|
||||
"type": "string"
|
||||
},
|
||||
"when": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"warn": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"uri": {
|
||||
"type": "string"
|
||||
},
|
||||
"when": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"clusterVersion": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
||||
@@ -93,6 +93,77 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"clusterPodStatuses": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"namespaces",
|
||||
"outcomes"
|
||||
],
|
||||
"properties": {
|
||||
"checkName": {
|
||||
"type": "string"
|
||||
},
|
||||
"exclude": {
|
||||
"oneOf": [{"type": "string"},{"type": "boolean"}]
|
||||
},
|
||||
"namespaces": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"outcomes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"fail": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"uri": {
|
||||
"type": "string"
|
||||
},
|
||||
"when": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"pass": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"uri": {
|
||||
"type": "string"
|
||||
},
|
||||
"when": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"warn": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"uri": {
|
||||
"type": "string"
|
||||
},
|
||||
"when": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"clusterVersion": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
||||
@@ -139,6 +139,77 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"clusterPodStatuses": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"namespaces",
|
||||
"outcomes"
|
||||
],
|
||||
"properties": {
|
||||
"checkName": {
|
||||
"type": "string"
|
||||
},
|
||||
"exclude": {
|
||||
"oneOf": [{"type": "string"},{"type": "boolean"}]
|
||||
},
|
||||
"namespaces": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"outcomes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"fail": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"uri": {
|
||||
"type": "string"
|
||||
},
|
||||
"when": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"pass": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"uri": {
|
||||
"type": "string"
|
||||
},
|
||||
"when": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"warn": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"uri": {
|
||||
"type": "string"
|
||||
},
|
||||
"when": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"clusterVersion": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
||||
Reference in New Issue
Block a user