Merge pull request #644 from replicatedhq/danj-custom-absent-messages

Allow custom messages to be passed if a deployment/statefulset is absent
This commit is contained in:
xavpaice
2022-08-19 09:06:08 +12:00
committed by GitHub
5 changed files with 177 additions and 28 deletions
+66 -7
View File
@@ -3,21 +3,31 @@ package analyzer
import (
"strconv"
"strings"
"fmt"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
)
func commonStatus(outcomes []*troubleshootv1beta2.Outcome, title, iconKey string, iconURI string, readyReplicas int) (*AnalyzeResult, error) {
func commonStatus(outcomes []*troubleshootv1beta2.Outcome, name string, iconKey string, iconURI string, readyReplicas int, exists bool, resourceType string) (*AnalyzeResult, error) {
result := &AnalyzeResult{
Title: title,
Title: fmt.Sprintf("%s Status", name),
IconKey: iconKey,
IconURI: iconURI,
}
// ordering from the spec is important, the first one that matches returns
for _, outcome := range outcomes {
if outcome.Fail != nil {
// if we're not checking that something is absent but it is, we should throw a default but meaningful error.
if exists == false && outcome.Fail.When != "absent" {
result.IsFail = true
result.Message = fmt.Sprintf("The %s %q was not found", resourceType, name)
result.URI = outcome.Fail.URI
return result, nil
}
if outcome.Fail.When == "" {
result.IsFail = true
result.Message = outcome.Fail.Message
@@ -26,7 +36,18 @@ func commonStatus(outcomes []*troubleshootv1beta2.Outcome, title, iconKey string
return result, nil
}
match, err := compareActualToWhen(outcome.Fail.When, readyReplicas)
if outcome.Fail.When == "absent" {
if exists == false {
result.IsFail = true
result.Message = outcome.Fail.Message
result.URI = outcome.Fail.URI
return result, nil
} else {
continue
}
}
match, err := compareActualToWhen(outcome.Fail.When, readyReplicas, exists)
if err != nil {
return nil, errors.Wrap(err, "failed to parse fail range")
}
@@ -39,6 +60,14 @@ func commonStatus(outcomes []*troubleshootv1beta2.Outcome, title, iconKey string
return result, nil
}
} else if outcome.Warn != nil {
if exists == false && outcome.Warn.When != "absent" {
result.IsFail = true
result.Message = fmt.Sprintf("The %s %q was not found", resourceType, name)
result.URI = outcome.Fail.URI
return result, nil
}
if outcome.Warn.When == "" {
result.IsWarn = true
result.Message = outcome.Warn.Message
@@ -47,7 +76,18 @@ func commonStatus(outcomes []*troubleshootv1beta2.Outcome, title, iconKey string
return result, nil
}
match, err := compareActualToWhen(outcome.Warn.When, readyReplicas)
if outcome.Warn.When == "absent" {
if exists == false {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.URI = outcome.Warn.URI
return result, nil
} else {
continue
}
}
match, err := compareActualToWhen(outcome.Warn.When, readyReplicas, exists)
if err != nil {
return nil, errors.Wrap(err, "failed to parse warn range")
}
@@ -60,6 +100,14 @@ func commonStatus(outcomes []*troubleshootv1beta2.Outcome, title, iconKey string
return result, nil
}
} else if outcome.Pass != nil {
if exists == false && outcome.Pass.When != "absent" {
result.IsFail = true
result.Message = fmt.Sprintf("The %s %q was not found", resourceType, name)
result.URI = outcome.Fail.URI
return result, nil
}
if outcome.Pass.When == "" {
result.IsPass = true
result.Message = outcome.Pass.Message
@@ -68,7 +116,18 @@ func commonStatus(outcomes []*troubleshootv1beta2.Outcome, title, iconKey string
return result, nil
}
match, err := compareActualToWhen(outcome.Pass.When, readyReplicas)
if outcome.Pass.When == "absent" {
if exists == false {
result.IsPass = true
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
return result, nil
} else {
continue
}
}
match, err := compareActualToWhen(outcome.Pass.When, readyReplicas, exists)
if err != nil {
return nil, errors.Wrap(err, "failed to parse pass range")
}
@@ -86,7 +145,7 @@ func commonStatus(outcomes []*troubleshootv1beta2.Outcome, title, iconKey string
return result, nil
}
func compareActualToWhen(when string, actual int) (bool, error) {
func compareActualToWhen(when string, actual int, exists bool) (bool, error) {
parts := strings.Split(strings.TrimSpace(when), " ")
// we can make this a lot more flexible
+11 -12
View File
@@ -26,6 +26,9 @@ func analyzeOneDeploymentStatus(analyzer *troubleshootv1beta2.DeploymentStatus,
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")
@@ -39,19 +42,15 @@ func analyzeOneDeploymentStatus(analyzer *troubleshootv1beta2.DeploymentStatus,
}
if status == nil {
// there's not an error, but maybe the requested deployment is not even deployed
result = &AnalyzeResult{
Title: fmt.Sprintf("%s Deployment Status", analyzer.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 %q was not found", analyzer.Name),
}
exists = false
readyReplicas = 0
} else {
result, err = commonStatus(analyzer.Outcomes, fmt.Sprintf("%s Status", analyzer.Name), "kubernetes_deployment_status", "https://troubleshoot.sh/images/analyzer-icons/deployment-status.svg?w=17&h=17", int(status.ReadyReplicas))
if err != nil {
return nil, errors.Wrap(err, "failed to process status")
}
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")
}
}
+55
View File
@@ -15,10 +15,53 @@ func Test_deploymentStatus(t *testing.T) {
expectResult []*AnalyzeResult
files map[string][]byte
}{
{
name: "1/1, fail when absent",
analyzer: troubleshootv1beta2.DeploymentStatus{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "absent",
Message: "fail",
},
},
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "= 1",
Message: "pass",
},
},
},
Namespace: "default",
Name: "nonexistant-deployment",
},
expectResult: []*AnalyzeResult{
{
IsPass: false,
IsWarn: false,
IsFail: true,
Title: "nonexistant-deployment Status",
Message: "fail",
IconKey: "kubernetes_deployment_status",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/deployment-status.svg?w=17&h=17",
},
},
files: map[string][]byte{
"cluster-resources/deployments/default.json": []byte(defaultDeployments),
"cluster-resources/deployments/monitoring.json": []byte(monitoringDeployments),
"cluster-resources/deployments/kube-system.json": []byte(kubeSystemDeployments),
},
},
{
name: "1/1, pass when = 1",
analyzer: troubleshootv1beta2.DeploymentStatus{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "absent",
Message: "fail",
},
},
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "= 1",
@@ -55,6 +98,12 @@ func Test_deploymentStatus(t *testing.T) {
name: "1/1, pass when = 2",
analyzer: troubleshootv1beta2.DeploymentStatus{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "absent",
Message: "fail",
},
},
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "= 2",
@@ -91,6 +140,12 @@ func Test_deploymentStatus(t *testing.T) {
name: "1/1, pass when >= 2, warn when = 1, fail when 0",
analyzer: troubleshootv1beta2.DeploymentStatus{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "absent",
Message: "fail",
},
},
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: ">= 2",
+10 -9
View File
@@ -26,6 +26,9 @@ func analyzeOneStatefulsetStatus(analyzer *troubleshootv1beta2.StatefulsetStatus
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")
@@ -40,15 +43,13 @@ func analyzeOneStatefulsetStatus(analyzer *troubleshootv1beta2.StatefulsetStatus
}
if statefulset == nil {
result = &AnalyzeResult{
Title: fmt.Sprintf("%s Statefulset Status", analyzer.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 %q was not found", analyzer.Name),
}
} else if len(analyzer.Outcomes) > 0 {
result, err = commonStatus(analyzer.Outcomes, fmt.Sprintf("%s Status", analyzer.Name), "kubernetes_statefulset_status", "https://troubleshoot.sh/images/analyzer-icons/statefulset-status.svg?w=23&h=14", int(statefulset.Status.ReadyReplicas))
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")
}
+35
View File
@@ -15,6 +15,41 @@ func Test_analyzeStatefulsetStatus(t *testing.T) {
expectResult []*AnalyzeResult
files map[string][]byte
}{
{
name: "fail when absent",
analyzer: troubleshootv1beta2.StatefulsetStatus{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "absent",
Message: "fail",
},
},
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "= 1",
Message: "pass",
},
},
},
Namespace: "default",
Name: "nonexistant",
},
expectResult: []*AnalyzeResult{
{
IsPass: false,
IsWarn: false,
IsFail: true,
Title: "nonexistant Status",
Message: "fail",
IconKey: "kubernetes_statefulset_status",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/statefulset-status.svg?w=23&h=14",
},
},
files: map[string][]byte{
"cluster-resources/statefulsets/default.json": []byte(defaultStatefulSets),
},
},
{
name: "analyze all statefulsets",
analyzer: troubleshootv1beta2.StatefulsetStatus{},