mirror of
https://github.com/hanzoai/k8s.git
synced 2026-10-02 15:40:05 +00:00
The tree was staged but never committed — this is the repo's first commit, so it carries both the existing content and the zip v1.18.8 -> v1.20.0 migration. Migration: Prepare() is replaced by Build() error, and both test harnesses assert the verdict rather than discarding it. main.go:59's Use call needed no change — the three middleware constructors already return zip.Handler, which satisfies the closed Component interface. The committed projections (k8s.plugin.json, openapi.json, ops/zipdoc_gen.go) are regenerated: v1.20.0 splits an op's prose into summary (first line) + description and zipdoc now emits per-field docs, so all three artifacts move with the bump. Co-authored-by: Hanzo Dev <[email protected]>
67 lines
2.4 KiB
Go
67 lines
2.4 KiB
Go
package ops
|
|
|
|
import (
|
|
"context"
|
|
|
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
|
"k8s.io/apimachinery/pkg/api/meta"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/client-go/discovery"
|
|
|
|
"github.com/hanzoai/k8s/plane"
|
|
)
|
|
|
|
// podMetrics returns live CPU and memory use for pods, from metrics.k8s.io.
|
|
//
|
|
// `available` is the field that carries the honesty. metrics-server is absent from most
|
|
// of our clusters, and a cluster without it answers this call with an error — which,
|
|
// rendered as zeroes, tells an operator that a busy namespace is idle. So a missing
|
|
// metrics API is reported as available=false with no rows, and a caller must render
|
|
// "unmeasured" rather than "0m".
|
|
//
|
|
// Example: {"cluster":"hanzo-k8s","namespace":"acme-prod"}
|
|
// Response: {"pods":[{"namespace":"acme-prod","name":"api-7d9f-2xk","cpuMilli":250,"memoryBytes":536870912}],"available":true}
|
|
func (o Ops) podMetrics(ctx context.Context, in *plane.Selector) (*plane.PodMetrics, error) {
|
|
b, err := o.reach(ctx, in.Cluster)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
scope, err := b.Scope(in.Namespace)
|
|
if err != nil {
|
|
return nil, fail(err)
|
|
}
|
|
out := &plane.PodMetrics{Pods: []plane.PodMetric{}}
|
|
for _, ns := range scope {
|
|
list, err := b.Metrics.MetricsV1beta1().PodMetricses(ns).List(ctx, metav1.ListOptions{LabelSelector: in.LabelSelector})
|
|
if err != nil {
|
|
// One namespace failing on a cluster that HAS metrics is a real error and
|
|
// must surface; a cluster with no metrics API at all fails every namespace
|
|
// the same way, and the caller needs that distinction. The apiserver
|
|
// answers a missing aggregated API with NotFound or ServiceUnavailable,
|
|
// so those two are the "no metrics here" answer and nothing else is.
|
|
if noMetrics(err) {
|
|
return out, nil
|
|
}
|
|
return nil, fail(err)
|
|
}
|
|
for i := range list.Items {
|
|
m := list.Items[i]
|
|
row := plane.PodMetric{Namespace: m.Namespace, Name: m.Name}
|
|
for _, c := range m.Containers {
|
|
row.CPUMilli += c.Usage.Cpu().MilliValue()
|
|
row.MemoryBytes += c.Usage.Memory().Value()
|
|
}
|
|
out.Pods = append(out.Pods, row)
|
|
}
|
|
}
|
|
out.Available = true
|
|
return out, nil
|
|
}
|
|
|
|
// noMetrics reports whether an error means the cluster serves no metrics API, as
|
|
// distinct from a metrics API that refused this read.
|
|
func noMetrics(err error) bool {
|
|
return apierrors.IsNotFound(err) || apierrors.IsServiceUnavailable(err) ||
|
|
meta.IsNoMatchError(err) || discovery.IsGroupDiscoveryFailedError(err)
|
|
}
|