internal/waste/pods.go
Rama main · réplica de lectura del repositorio original.
package waste
import (
"context"
"strconv"
"strings"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"github.com/sophotechlabs/spinoza/internal/api"
"github.com/sophotechlabs/spinoza/internal/unstr"
)
const (
bytesPerMi = 1024 * 1024
ownerDepth = 4
podKind = "Pod"
)
var ownerKinds = []api.ObjectRef{
{Group: "apps", Version: "v1", Resource: "replicasets"},
{Group: "batch", Version: "v1", Resource: "jobs"},
}
func ownerIndex(
ctx context.Context,
lister Lister,
namespace string,
) (map[string]*unstructured.Unstructured, []string) {
held := map[string]*unstructured.Unstructured{}
notes := []string{}
for _, kind := range ownerKinds {
ref := kind
ref.Namespace = namespace
found, err := lister.ListKind(ctx, ref)
if err != nil {
notes = append(notes, ref.Resource+
" could not be read, so some workloads are named by the pod's own owner: "+err.Error())
continue
}
for _, obj := range found {
held[ownerKey(obj.GroupVersionKind().Group, obj.GetKind(), obj.GetNamespace(), obj.GetName())] = obj
}
}
return held, notes
}
func ownerKey(group, kind, namespace, name string) string {
return group + "/" + kind + "/" + namespace + "/" + name
}
func topOwner(obj *unstructured.Unstructured, held map[string]*unstructured.Unstructured) (string, string) {
kind := obj.GetKind()
if kind == "" {
kind = podKind
}
name := obj.GetName()
current := obj
for range ownerDepth {
ref, ok := controllerOf(current)
if !ok {
return kind, name
}
kind = ref.Kind
name = ref.Name
next, found := held[ownerKey(groupOf(ref.APIVersion), ref.Kind, current.GetNamespace(), ref.Name)]
if !found {
return kind, name
}
current = next
}
return kind, name
}
func controllerOf(obj *unstructured.Unstructured) (metav1.OwnerReference, bool) {
for _, ref := range obj.GetOwnerReferences() {
if ref.Controller == nil || !*ref.Controller {
continue
}
return ref, true
}
return metav1.OwnerReference{}, false
}
func groupOf(apiVersion string) string {
group, _, found := strings.Cut(apiVersion, "/")
if !found {
return ""
}
return group
}
func requestsOf(obj *unstructured.Unstructured) (int64, int64, bool) {
var cpuMilli, memMi int64
asks := false
for _, raw := range unstr.Slice(obj, "spec", "containers") {
container, ok := raw.(map[string]any)
if !ok {
continue
}
cpu, hasCPU := requestedIn(container, "cpu")
if hasCPU {
cpuMilli += cpu.MilliValue()
asks = true
}
mem, hasMemory := requestedIn(container, "memory")
if hasMemory {
memMi += mem.Value() / bytesPerMi
asks = true
}
}
return cpuMilli, memMi, asks
}
func requestedIn(container map[string]any, name string) (resource.Quantity, bool) {
held, ok := container["resources"].(map[string]any)
if !ok {
return resource.Quantity{}, false
}
asked, ok := held["requests"].(map[string]any)
if !ok {
return resource.Quantity{}, false
}
return quantityFrom(asked[name])
}
func quantityFrom(raw any) (resource.Quantity, bool) {
switch value := raw.(type) {
case string:
return parsed(value)
case int64:
return parsed(strconv.FormatInt(value, 10))
case float64:
return parsed(strconv.FormatFloat(value, 'f', -1, 64))
default:
return resource.Quantity{}, false
}
}
func parsed(raw string) (resource.Quantity, bool) {
value, err := resource.ParseQuantity(raw)
if err != nil {
return resource.Quantity{}, false
}
return value, true
}