in internal/elasticsearch/client.go [226:299]
func (client *Client) redHealthCause(ctx context.Context) (string, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "/_internal/_health", nil)
if err != nil {
return "", fmt.Errorf("error creating internal health request: %w", err)
}
resp, err := client.Transport.Perform(req)
if err != nil {
return "", fmt.Errorf("error performing internal health request: %w", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("error reading internal health response: %w", err)
}
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("failed to get cause of red health; API status code = %d; response body = %s", resp.StatusCode, string(body))
}
var internalHealth struct {
Status string `json:"status"`
Indicators map[string]struct {
Status string `json:"status"`
Impacts []struct {
Severity int `json:"severity"`
} `json:"impacts"`
Diagnosis []struct {
Cause string `json:"cause"`
} `json:"diagnosis"`
} `json:"indicators"`
}
err = json.Unmarshal(body, &internalHealth)
if err != nil {
return "", fmt.Errorf("error decoding internal health response: %w", err)
}
if internalHealth.Status != "red" {
return "", errors.New("cluster state is not red?")
}
// Only diagnostics with the highest severity impacts are returned.
var highestSeverity int
var causes []string
for _, indicator := range internalHealth.Indicators {
if indicator.Status != "red" {
continue
}
var severity int
for _, impact := range indicator.Impacts {
if impact.Severity > severity {
severity = impact.Severity
}
}
switch {
case severity < highestSeverity:
continue
case severity > highestSeverity:
highestSeverity = severity
causes = nil
case severity == highestSeverity:
// Continue appending for current severity.
}
for _, diagnosis := range indicator.Diagnosis {
causes = append(causes, diagnosis.Cause)
}
}
if len(causes) == 0 {
return "", errors.New("no causes found")
}
return strings.Join(causes, ", "), nil
}