~ knotless Search/
↑↓ move↵ open⇧↵ all results
0
0
main
116 lines · 2.8 KB Raw History
package scale

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"time"

	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
	KindDeployment  = "Deployment"
	KindStatefulSet = "StatefulSet"
	KindDaemonSet   = "DaemonSet"
	KindJob         = "Job"
	KindReplicaSet  = "ReplicaSet"
)

type WorkloadKey struct {
	Kind      string `json:"kind"`
	Namespace string `json:"namespace"`
	Name      string `json:"name"`
}

func (k WorkloadKey) String() string {
	return k.Kind + "/" + k.Namespace + "/" + k.Name
}

type BaselineEntry struct {
	WorkloadKey
	SpecHash string `json:"specHash"`
}

type Baseline struct {
	Manager    string          `json:"manager"`
	MeasuredAt time.Time       `json:"measuredAt"`
	Workloads  []BaselineEntry `json:"workloads"`
}

func SpecHash(spec any) (string, error) {
	raw, err := json.Marshal(spec)
	if err != nil {
		return "", fmt.Errorf("scale: encoding a workload spec to hash it: %w", err)
	}
	sum := sha256.Sum256(raw)
	return hex.EncodeToString(sum[:]), nil
}

func (b Baseline) Holds(k WorkloadKey, spec any) bool {
	for _, e := range b.Workloads {
		if e.WorkloadKey != k {
			continue
		}
		h, err := SpecHash(spec)
		return err == nil && h == e.SpecHash
	}
	return false
}

func Narrow(o Observation, replicaSets []appsv1.ReplicaSet, b Baseline) Observation {
	present := map[WorkloadKey]bool{}
	out := Observation{Placement: o.Placement, UnreconciledCustomResources: o.UnreconciledCustomResources}
	for _, d := range o.Deployments {
		k := WorkloadKey{Kind: KindDeployment, Namespace: d.Namespace, Name: d.Name}
		present[k] = true
		if !b.Holds(k, d.Spec) {
			out.Deployments = append(out.Deployments, d)
		}
	}
	for _, s := range o.StatefulSets {
		k := WorkloadKey{Kind: KindStatefulSet, Namespace: s.Namespace, Name: s.Name}
		present[k] = true
		if !b.Holds(k, s.Spec) {
			out.StatefulSets = append(out.StatefulSets, s)
		}
	}
	for _, d := range o.DaemonSets {
		k := WorkloadKey{Kind: KindDaemonSet, Namespace: d.Namespace, Name: d.Name}
		present[k] = true
		if !b.Holds(k, d.Spec) {
			out.DaemonSets = append(out.DaemonSets, d)
		}
	}
	for _, j := range o.Jobs {
		k := WorkloadKey{Kind: KindJob, Namespace: j.Namespace, Name: j.Name}
		present[k] = true
		if !b.Holds(k, j.Spec) {
			out.Jobs = append(out.Jobs, j)
		}
	}
	deploymentOf := deploymentsOfReplicaSets(replicaSets)
	for _, p := range o.Pods {
		if k, ok := workloadOf(&p, deploymentOf); ok && present[k] {
			continue
		}
		out.Pods = append(out.Pods, p)
	}
	return out
}

func workloadOf(p *corev1.Pod, deploymentOf map[WorkloadKey]WorkloadKey) (WorkloadKey, bool) {
	c := metav1.GetControllerOf(p)
	if c == nil {
		return WorkloadKey{}, false
	}
	k := WorkloadKey{Kind: c.Kind, Namespace: p.Namespace, Name: c.Name}
	if c.Kind == KindReplicaSet {
		d, ok := deploymentOf[k]
		return d, ok
	}
	return k, true
}