Ctrl K
ComunidadGuías…

internal/waste/pods.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
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
}