package analyzer import ( "fmt" "strconv" "strings" "github.com/pkg/errors" troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" ) func commonStatus(outcomes []*troubleshootv1beta2.Outcome, name string, iconKey string, iconURI string, readyReplicas int, exists bool, resourceType string) (*AnalyzeResult, error) { result := &AnalyzeResult{ 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 result.URI = outcome.Fail.URI return result, nil } 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) if err != nil { return nil, errors.Wrap(err, "failed to parse fail range") } if match { result.IsFail = true result.Message = outcome.Fail.Message result.URI = outcome.Fail.URI 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.Warn.URI return result, nil } if outcome.Warn.When == "" { result.IsWarn = true result.Message = outcome.Warn.Message result.URI = outcome.Warn.URI return result, nil } 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) if err != nil { return nil, errors.Wrap(err, "failed to parse warn range") } if match { result.IsWarn = true result.Message = outcome.Warn.Message result.URI = outcome.Warn.URI 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.Pass.URI return result, nil } if outcome.Pass.When == "" { result.IsPass = true result.Message = outcome.Pass.Message result.URI = outcome.Pass.URI return result, nil } 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) if err != nil { return nil, errors.Wrap(err, "failed to parse pass range") } if match { result.IsPass = true result.Message = outcome.Pass.Message result.URI = outcome.Pass.URI return result, nil } } } return result, nil } func compareActualToWhen(when string, actual int) (bool, error) { parts := strings.Split(strings.TrimSpace(when), " ") // we can make this a lot more flexible if len(parts) != 2 { return false, errors.New("unable to parse when range") } value, err := strconv.Atoi(parts[1]) if err != nil { return false, errors.Wrap(err, "failed to parse when value") } switch parts[0] { case "=": fallthrough case "==": fallthrough case "===": return actual == value, nil case "<": return actual < value, nil case ">": return actual > value, nil case "<=": return actual <= value, nil case ">=": return actual >= value, nil } return false, errors.Errorf("unknown comparator: %q", parts[0]) }