internal/issues/autoscalers.go
Rama main · réplica de lectura del repositorio original.
package issues
import (
"strconv"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"github.com/sophotechlabs/spinoza/internal/unstr"
)
const detectorAutoscaler = "hpa"
const autoscalerKind = "HorizontalPodAutoscaler"
func autoscalerFindings(snap *snapshot) []finding {
out := []finding{}
for _, item := range snap.of(autoscalingGroup, autoscalerKind) {
found, ok := autoscalerSymptom(item.obj)
if !ok {
continue
}
out = append(out, finding{
detector: detectorAutoscaler,
severity: found.severity,
title: found.title,
detail: found.detail,
action: found.action,
kind: item.desc.Kind,
subject: item,
since: conditionSince(item.obj, "ScalingActive"),
})
}
return out
}
func autoscalerSymptom(obj *unstructured.Unstructured) (symptom, bool) {
if blind, ok := metricsUnreadable(obj); ok {
return blind, true
}
if frozen, ok := scalingDisabled(obj); ok {
return frozen, true
}
if pinned, ok := replicasPinned(obj); ok {
return pinned, true
}
return atMaximum(obj)
}
func metricsUnreadable(obj *unstructured.Unstructured) (symptom, bool) {
entry, ok := conditionOf(obj, "ScalingActive")
if !ok || unstr.At(entry, "status") != conditionFalse {
return symptom{}, false
}
reason := unstr.At(entry, "reason")
if reason == "ScalingDisabled" {
return symptom{}, false
}
return symptom{
severity: severityDegraded,
title: reasonOr(entry, "NoMetrics"),
detail: messageOr(entry, "the autoscaler cannot read the metric it scales on"),
action: "check metrics-server or the custom metrics adapter",
}, true
}
func scalingDisabled(obj *unstructured.Unstructured) (symptom, bool) {
entry, ok := conditionOf(obj, "AbleToScale")
if !ok || unstr.At(entry, "status") != conditionFalse {
return symptom{}, false
}
return symptom{
severity: severityDegraded,
title: reasonOr(entry, "CannotScale"),
detail: messageOr(entry, "the autoscaler is not able to change the replica count"),
action: "check that the scale target exists and is scalable",
}, true
}
func replicasPinned(obj *unstructured.Unstructured) (symptom, bool) {
low := unstr.Int(obj, "spec", "minReplicas")
high := unstr.Int(obj, "spec", "maxReplicas")
if low == 0 || low != high {
return symptom{}, false
}
return symptom{
severity: severityWarning,
title: "Pinned",
detail: "minReplicas and maxReplicas are both " + strconv.FormatInt(low, 10) + ", so it cannot scale",
action: "widen the range, or drop the autoscaler",
}, true
}
func atMaximum(obj *unstructured.Unstructured) (symptom, bool) {
high := unstr.Int(obj, "spec", "maxReplicas")
current := unstr.Int(obj, "status", "currentReplicas")
if high == 0 || current < high {
return symptom{}, false
}
if !conditionIsTrue(obj, "ScalingLimited") {
return symptom{}, false
}
return symptom{
severity: severityWarning,
title: "AtMaximum",
detail: "running at maxReplicas " + strconv.FormatInt(high, 10) + " and still asking for more",
action: "raise maxReplicas, or find out why each replica is loaded",
}, true
}