mirror of
https://github.com/projectcapsule/capsule.git
synced 2026-08-20 21:16:49 +00:00
* fix(controller): decode old object for delete requests Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * chore: modernize golang Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * chore: modernize golang Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * chore: modernize golang Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * fix: preserve ca-bundles injected from external providers Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * fix: improve rules api Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * fix: improve rules api Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * fix: improve rules api Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * fix: improve rules api Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * fix: improve rules api Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * fix: improve rules api Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * fix: improve rules api Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * fix: improve rules api Signed-off-by: Oliver Baehler <oliver@sudo-i.net> --------- Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> Signed-off-by: Oliver Baehler <oliver@sudo-i.net>
836 lines
20 KiB
Go
836 lines
20 KiB
Go
package workloads
|
|
|
|
import (
|
|
"testing"
|
|
|
|
corev1 "k8s.io/api/core/v1"
|
|
"k8s.io/apimachinery/pkg/api/resource"
|
|
)
|
|
|
|
func TestGetPodQoSClass(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
pod *corev1.Pod
|
|
want corev1.PodQOSClass
|
|
}{
|
|
{
|
|
name: "nil pod returns BestEffort",
|
|
pod: nil,
|
|
want: corev1.PodQOSBestEffort,
|
|
},
|
|
{
|
|
name: "status QoS class takes precedence over computed value",
|
|
pod: &corev1.Pod{
|
|
Status: corev1.PodStatus{
|
|
QOSClass: corev1.PodQOSGuaranteed,
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
bestEffortContainer("app"),
|
|
},
|
|
},
|
|
},
|
|
want: corev1.PodQOSGuaranteed,
|
|
},
|
|
{
|
|
name: "empty status computes QoS class",
|
|
pod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
bestEffortContainer("app"),
|
|
},
|
|
},
|
|
},
|
|
want: corev1.PodQOSBestEffort,
|
|
},
|
|
{
|
|
name: "pod-level Guaranteed takes precedence over BestEffort containers",
|
|
pod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
Resources: guaranteedPodResourcesPtr("100m", "128Mi"),
|
|
Containers: []corev1.Container{
|
|
bestEffortContainer("app"),
|
|
},
|
|
},
|
|
},
|
|
want: corev1.PodQOSGuaranteed,
|
|
},
|
|
{
|
|
name: "pod-level Burstable takes precedence over Guaranteed containers",
|
|
pod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
Resources: requestOnlyPodResourcesPtr("100m", "128Mi"),
|
|
Containers: []corev1.Container{
|
|
guaranteedContainer("app", "100m", "128Mi"),
|
|
},
|
|
},
|
|
},
|
|
want: corev1.PodQOSBurstable,
|
|
},
|
|
{
|
|
name: "BestEffort without pod-level or container-level resources",
|
|
pod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
bestEffortContainer("app"),
|
|
},
|
|
},
|
|
},
|
|
want: corev1.PodQOSBestEffort,
|
|
},
|
|
{
|
|
name: "Burstable with container requests only",
|
|
pod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
requestOnlyContainer("app", "100m", "128Mi"),
|
|
},
|
|
},
|
|
},
|
|
want: corev1.PodQOSBurstable,
|
|
},
|
|
{
|
|
name: "Burstable with container limits only",
|
|
pod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
limitOnlyContainer("app", "100m", "128Mi"),
|
|
},
|
|
},
|
|
},
|
|
want: corev1.PodQOSBurstable,
|
|
},
|
|
{
|
|
name: "Guaranteed with equal CPU and memory requests and limits",
|
|
pod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
guaranteedContainer("app", "100m", "128Mi"),
|
|
},
|
|
},
|
|
},
|
|
want: corev1.PodQOSGuaranteed,
|
|
},
|
|
{
|
|
name: "Burstable when CPU request and limit differ",
|
|
pod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
containerWithResources(
|
|
"app",
|
|
corev1.ResourceList{
|
|
corev1.ResourceCPU: resource.MustParse("100m"),
|
|
corev1.ResourceMemory: resource.MustParse("128Mi"),
|
|
},
|
|
corev1.ResourceList{
|
|
corev1.ResourceCPU: resource.MustParse("200m"),
|
|
corev1.ResourceMemory: resource.MustParse("128Mi"),
|
|
},
|
|
),
|
|
},
|
|
},
|
|
},
|
|
want: corev1.PodQOSBurstable,
|
|
},
|
|
{
|
|
name: "Burstable when memory request and limit differ",
|
|
pod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
containerWithResources(
|
|
"app",
|
|
corev1.ResourceList{
|
|
corev1.ResourceCPU: resource.MustParse("100m"),
|
|
corev1.ResourceMemory: resource.MustParse("128Mi"),
|
|
},
|
|
corev1.ResourceList{
|
|
corev1.ResourceCPU: resource.MustParse("100m"),
|
|
corev1.ResourceMemory: resource.MustParse("256Mi"),
|
|
},
|
|
),
|
|
},
|
|
},
|
|
},
|
|
want: corev1.PodQOSBurstable,
|
|
},
|
|
{
|
|
name: "Burstable when one container is BestEffort",
|
|
pod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
guaranteedContainer("app", "100m", "128Mi"),
|
|
bestEffortContainer("sidecar"),
|
|
},
|
|
},
|
|
},
|
|
want: corev1.PodQOSBurstable,
|
|
},
|
|
{
|
|
name: "Guaranteed with multiple guaranteed containers",
|
|
pod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
guaranteedContainer("app", "100m", "128Mi"),
|
|
guaranteedContainer("sidecar", "50m", "64Mi"),
|
|
},
|
|
},
|
|
},
|
|
want: corev1.PodQOSGuaranteed,
|
|
},
|
|
{
|
|
name: "Burstable when init container has requests only",
|
|
pod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
InitContainers: []corev1.Container{
|
|
requestOnlyContainer("init", "100m", "128Mi"),
|
|
},
|
|
Containers: []corev1.Container{
|
|
guaranteedContainer("app", "100m", "128Mi"),
|
|
},
|
|
},
|
|
},
|
|
want: corev1.PodQOSBurstable,
|
|
},
|
|
{
|
|
name: "Guaranteed when init and regular containers are guaranteed",
|
|
pod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
InitContainers: []corev1.Container{
|
|
guaranteedContainer("init", "100m", "128Mi"),
|
|
},
|
|
Containers: []corev1.Container{
|
|
guaranteedContainer("app", "100m", "128Mi"),
|
|
},
|
|
},
|
|
},
|
|
want: corev1.PodQOSGuaranteed,
|
|
},
|
|
{
|
|
name: "Burstable when ephemeral container has requests only",
|
|
pod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
guaranteedContainer("app", "100m", "128Mi"),
|
|
},
|
|
EphemeralContainers: []corev1.EphemeralContainer{
|
|
{
|
|
EphemeralContainerCommon: corev1.EphemeralContainerCommon{
|
|
Name: "debug",
|
|
Resources: requestOnlyRequirements("100m", "128Mi"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
want: corev1.PodQOSBurstable,
|
|
},
|
|
{
|
|
name: "Guaranteed when ephemeral container is guaranteed",
|
|
pod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
guaranteedContainer("app", "100m", "128Mi"),
|
|
},
|
|
EphemeralContainers: []corev1.EphemeralContainer{
|
|
{
|
|
EphemeralContainerCommon: corev1.EphemeralContainerCommon{
|
|
Name: "debug",
|
|
Resources: guaranteedRequirements("100m", "128Mi"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
want: corev1.PodQOSGuaranteed,
|
|
},
|
|
{
|
|
name: "zero CPU and memory requests and limits are ignored",
|
|
pod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
containerWithResources(
|
|
"app",
|
|
corev1.ResourceList{
|
|
corev1.ResourceCPU: resource.MustParse("0"),
|
|
corev1.ResourceMemory: resource.MustParse("0"),
|
|
},
|
|
corev1.ResourceList{
|
|
corev1.ResourceCPU: resource.MustParse("0"),
|
|
corev1.ResourceMemory: resource.MustParse("0"),
|
|
},
|
|
),
|
|
},
|
|
},
|
|
},
|
|
want: corev1.PodQOSBestEffort,
|
|
},
|
|
{
|
|
name: "unsupported resources do not influence QoS",
|
|
pod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
containerWithResources(
|
|
"app",
|
|
corev1.ResourceList{
|
|
corev1.ResourceName("example.com/gpu"): resource.MustParse("1"),
|
|
},
|
|
corev1.ResourceList{
|
|
corev1.ResourceName("example.com/gpu"): resource.MustParse("1"),
|
|
},
|
|
),
|
|
},
|
|
},
|
|
},
|
|
want: corev1.PodQOSBestEffort,
|
|
},
|
|
{
|
|
name: "hugepages do not influence QoS",
|
|
pod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
containerWithResources(
|
|
"app",
|
|
corev1.ResourceList{
|
|
corev1.ResourceName("hugepages-2Mi"): resource.MustParse("2Mi"),
|
|
},
|
|
corev1.ResourceList{
|
|
corev1.ResourceName("hugepages-2Mi"): resource.MustParse("2Mi"),
|
|
},
|
|
),
|
|
},
|
|
},
|
|
},
|
|
want: corev1.PodQOSBestEffort,
|
|
},
|
|
{
|
|
name: "pod-level unsupported resources do not influence QoS",
|
|
pod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
Resources: &corev1.ResourceRequirements{
|
|
Requests: corev1.ResourceList{
|
|
corev1.ResourceName("example.com/gpu"): resource.MustParse("1"),
|
|
},
|
|
Limits: corev1.ResourceList{
|
|
corev1.ResourceName("example.com/gpu"): resource.MustParse("1"),
|
|
},
|
|
},
|
|
Containers: []corev1.Container{
|
|
bestEffortContainer("app"),
|
|
},
|
|
},
|
|
},
|
|
want: corev1.PodQOSBestEffort,
|
|
},
|
|
{
|
|
name: "pod-level zero resources do not mask container-level Guaranteed",
|
|
pod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
Resources: &corev1.ResourceRequirements{
|
|
Requests: corev1.ResourceList{
|
|
corev1.ResourceCPU: resource.MustParse("0"),
|
|
corev1.ResourceMemory: resource.MustParse("0"),
|
|
},
|
|
Limits: corev1.ResourceList{
|
|
corev1.ResourceCPU: resource.MustParse("0"),
|
|
corev1.ResourceMemory: resource.MustParse("0"),
|
|
},
|
|
},
|
|
Containers: []corev1.Container{
|
|
guaranteedContainer("app", "100m", "128Mi"),
|
|
},
|
|
},
|
|
},
|
|
want: corev1.PodQOSGuaranteed,
|
|
},
|
|
{
|
|
name: "pod-level CPU only request is Burstable",
|
|
pod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
Resources: &corev1.ResourceRequirements{
|
|
Requests: corev1.ResourceList{
|
|
corev1.ResourceCPU: resource.MustParse("100m"),
|
|
},
|
|
},
|
|
Containers: []corev1.Container{
|
|
bestEffortContainer("app"),
|
|
},
|
|
},
|
|
},
|
|
want: corev1.PodQOSBurstable,
|
|
},
|
|
{
|
|
name: "pod-level memory only limit is Burstable",
|
|
pod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
Resources: &corev1.ResourceRequirements{
|
|
Limits: corev1.ResourceList{
|
|
corev1.ResourceMemory: resource.MustParse("128Mi"),
|
|
},
|
|
},
|
|
Containers: []corev1.Container{
|
|
bestEffortContainer("app"),
|
|
},
|
|
},
|
|
},
|
|
want: corev1.PodQOSBurstable,
|
|
},
|
|
{
|
|
name: "pod-level CPU and memory unequal request and limit is Burstable",
|
|
pod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
Resources: &corev1.ResourceRequirements{
|
|
Requests: corev1.ResourceList{
|
|
corev1.ResourceCPU: resource.MustParse("100m"),
|
|
corev1.ResourceMemory: resource.MustParse("128Mi"),
|
|
},
|
|
Limits: corev1.ResourceList{
|
|
corev1.ResourceCPU: resource.MustParse("200m"),
|
|
corev1.ResourceMemory: resource.MustParse("128Mi"),
|
|
},
|
|
},
|
|
Containers: []corev1.Container{
|
|
bestEffortContainer("app"),
|
|
},
|
|
},
|
|
},
|
|
want: corev1.PodQOSBurstable,
|
|
},
|
|
{
|
|
name: "no containers and no resources is BestEffort",
|
|
pod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{},
|
|
},
|
|
want: corev1.PodQOSBestEffort,
|
|
},
|
|
{
|
|
name: "aggregate container requests and limits equal returns Guaranteed",
|
|
pod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
guaranteedContainer("app", "100m", "128Mi"),
|
|
guaranteedContainer("sidecar", "200m", "256Mi"),
|
|
},
|
|
},
|
|
},
|
|
want: corev1.PodQOSGuaranteed,
|
|
},
|
|
{
|
|
name: "mismatched container requests/limits are Burstable even if totals match",
|
|
pod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
containerWithResources(
|
|
"a",
|
|
corev1.ResourceList{
|
|
corev1.ResourceCPU: resource.MustParse("100m"),
|
|
corev1.ResourceMemory: resource.MustParse("128Mi"),
|
|
},
|
|
corev1.ResourceList{
|
|
corev1.ResourceCPU: resource.MustParse("200m"),
|
|
corev1.ResourceMemory: resource.MustParse("128Mi"),
|
|
},
|
|
),
|
|
containerWithResources(
|
|
"b",
|
|
corev1.ResourceList{
|
|
corev1.ResourceCPU: resource.MustParse("200m"),
|
|
corev1.ResourceMemory: resource.MustParse("128Mi"),
|
|
},
|
|
corev1.ResourceList{
|
|
corev1.ResourceCPU: resource.MustParse("100m"),
|
|
corev1.ResourceMemory: resource.MustParse("128Mi"),
|
|
},
|
|
),
|
|
},
|
|
},
|
|
},
|
|
want: corev1.PodQOSBurstable,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
got := GetPodQoSClass(tt.pod)
|
|
if got != tt.want {
|
|
t.Fatalf("expected QoS class %q, got %q", tt.want, got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestComputePodLevelQoSClassNilPodResources(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
got, ok := computePodLevelQoSClass(&corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
Resources: nil,
|
|
},
|
|
})
|
|
|
|
if got != corev1.PodQOSBestEffort {
|
|
t.Fatalf("expected QoS class %q, got %q", corev1.PodQOSBestEffort, got)
|
|
}
|
|
|
|
if ok {
|
|
t.Fatalf("expected ok=false, got true")
|
|
}
|
|
}
|
|
|
|
func TestComputePodLevelQoSClass(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
pod *corev1.Pod
|
|
want corev1.PodQOSClass
|
|
ok bool
|
|
}{
|
|
{
|
|
name: "nil pod returns false",
|
|
pod: nil,
|
|
want: corev1.PodQOSBestEffort,
|
|
ok: false,
|
|
},
|
|
{
|
|
name: "no pod-level resources returns false",
|
|
pod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{},
|
|
},
|
|
want: corev1.PodQOSBestEffort,
|
|
ok: false,
|
|
},
|
|
{
|
|
name: "pod-level Guaranteed",
|
|
pod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
Resources: guaranteedPodResourcesPtr("100m", "128Mi"),
|
|
},
|
|
},
|
|
want: corev1.PodQOSGuaranteed,
|
|
ok: true,
|
|
},
|
|
{
|
|
name: "pod-level request only is Burstable",
|
|
pod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
Resources: requestOnlyPodResourcesPtr("100m", "128Mi"),
|
|
},
|
|
},
|
|
want: corev1.PodQOSBurstable,
|
|
ok: true,
|
|
},
|
|
{
|
|
name: "pod-level limit only is Burstable",
|
|
pod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
Resources: limitOnlyPodResourcesPtr("100m", "128Mi"),
|
|
},
|
|
},
|
|
want: corev1.PodQOSBurstable,
|
|
ok: true,
|
|
},
|
|
{
|
|
name: "pod-level CPU only equal request and limit is Burstable",
|
|
pod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
Resources: &corev1.ResourceRequirements{
|
|
Requests: corev1.ResourceList{
|
|
corev1.ResourceCPU: resource.MustParse("100m"),
|
|
},
|
|
Limits: corev1.ResourceList{
|
|
corev1.ResourceCPU: resource.MustParse("100m"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
want: corev1.PodQOSBurstable,
|
|
ok: true,
|
|
},
|
|
{
|
|
name: "pod-level memory only equal request and limit is Burstable",
|
|
pod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
Resources: &corev1.ResourceRequirements{
|
|
Requests: corev1.ResourceList{
|
|
corev1.ResourceMemory: resource.MustParse("128Mi"),
|
|
},
|
|
Limits: corev1.ResourceList{
|
|
corev1.ResourceMemory: resource.MustParse("128Mi"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
want: corev1.PodQOSBurstable,
|
|
ok: true,
|
|
},
|
|
{
|
|
name: "pod-level zero resources return false",
|
|
pod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
Resources: &corev1.ResourceRequirements{
|
|
Requests: corev1.ResourceList{
|
|
corev1.ResourceCPU: resource.MustParse("0"),
|
|
corev1.ResourceMemory: resource.MustParse("0"),
|
|
},
|
|
Limits: corev1.ResourceList{
|
|
corev1.ResourceCPU: resource.MustParse("0"),
|
|
corev1.ResourceMemory: resource.MustParse("0"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
want: corev1.PodQOSBestEffort,
|
|
ok: false,
|
|
},
|
|
{
|
|
name: "pod-level unsupported resources return false",
|
|
pod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
Resources: &corev1.ResourceRequirements{
|
|
Requests: corev1.ResourceList{
|
|
corev1.ResourceName("example.com/gpu"): resource.MustParse("1"),
|
|
},
|
|
Limits: corev1.ResourceList{
|
|
corev1.ResourceName("example.com/gpu"): resource.MustParse("1"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
want: corev1.PodQOSBestEffort,
|
|
ok: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
got, ok := computePodLevelQoSClass(tt.pod)
|
|
if got != tt.want {
|
|
t.Fatalf("expected QoS class %q, got %q", tt.want, got)
|
|
}
|
|
|
|
if ok != tt.ok {
|
|
t.Fatalf("expected ok=%t, got %t", tt.ok, ok)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestQoSHelpers(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("hasSupportedQoSResource", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
resources corev1.ResourceList
|
|
want bool
|
|
}{
|
|
{
|
|
name: "nil resources",
|
|
resources: nil,
|
|
want: false,
|
|
},
|
|
{
|
|
name: "empty resources",
|
|
resources: corev1.ResourceList{},
|
|
want: false,
|
|
},
|
|
{
|
|
name: "CPU positive",
|
|
resources: corev1.ResourceList{
|
|
corev1.ResourceCPU: resource.MustParse("1"),
|
|
},
|
|
want: true,
|
|
},
|
|
{
|
|
name: "memory positive",
|
|
resources: corev1.ResourceList{
|
|
corev1.ResourceMemory: resource.MustParse("1Mi"),
|
|
},
|
|
want: true,
|
|
},
|
|
{
|
|
name: "CPU zero",
|
|
resources: corev1.ResourceList{
|
|
corev1.ResourceCPU: resource.MustParse("0"),
|
|
},
|
|
want: false,
|
|
},
|
|
{
|
|
name: "unsupported positive",
|
|
resources: corev1.ResourceList{
|
|
corev1.ResourceName("example.com/gpu"): resource.MustParse("1"),
|
|
},
|
|
want: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
got := hasSupportedQoSResource(tt.resources)
|
|
if got != tt.want {
|
|
t.Fatalf("expected %t, got %t", tt.want, got)
|
|
}
|
|
})
|
|
}
|
|
})
|
|
|
|
t.Run("positiveResource", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
resources := corev1.ResourceList{
|
|
corev1.ResourceCPU: resource.MustParse("100m"),
|
|
corev1.ResourceMemory: resource.MustParse("0"),
|
|
}
|
|
|
|
if got, ok := positiveResource(resources, corev1.ResourceCPU); !ok || got.Cmp(resource.MustParse("100m")) != 0 {
|
|
t.Fatalf("expected positive CPU resource, got %q ok=%t", got.String(), ok)
|
|
}
|
|
|
|
if got, ok := positiveResource(resources, corev1.ResourceMemory); ok {
|
|
t.Fatalf("expected zero memory to be ignored, got %q ok=%t", got.String(), ok)
|
|
}
|
|
|
|
if got, ok := positiveResource(resources, corev1.ResourceStorage); ok {
|
|
t.Fatalf("expected missing storage to be ignored, got %q ok=%t", got.String(), ok)
|
|
}
|
|
})
|
|
|
|
t.Run("isSupportedQoSComputeResource", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
in corev1.ResourceName
|
|
want bool
|
|
}{
|
|
{name: "CPU", in: corev1.ResourceCPU, want: true},
|
|
{name: "memory", in: corev1.ResourceMemory, want: true},
|
|
{name: "storage", in: corev1.ResourceStorage, want: false},
|
|
{name: "ephemeral storage", in: corev1.ResourceEphemeralStorage, want: false},
|
|
{name: "extended resource", in: corev1.ResourceName("example.com/gpu"), want: false},
|
|
{name: "hugepage", in: corev1.ResourceName("hugepages-2Mi"), want: false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
got := isSupportedQoSComputeResource(tt.in)
|
|
if got != tt.want {
|
|
t.Fatalf("expected %t, got %t", tt.want, got)
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
func bestEffortContainer(name string) corev1.Container {
|
|
return corev1.Container{Name: name}
|
|
}
|
|
|
|
func requestOnlyContainer(name, cpu, memory string) corev1.Container {
|
|
return corev1.Container{
|
|
Name: name,
|
|
Resources: requestOnlyRequirements(cpu, memory),
|
|
}
|
|
}
|
|
|
|
func limitOnlyContainer(name, cpu, memory string) corev1.Container {
|
|
return corev1.Container{
|
|
Name: name,
|
|
Resources: limitOnlyRequirements(cpu, memory),
|
|
}
|
|
}
|
|
|
|
func guaranteedContainer(name, cpu, memory string) corev1.Container {
|
|
return corev1.Container{
|
|
Name: name,
|
|
Resources: guaranteedRequirements(cpu, memory),
|
|
}
|
|
}
|
|
|
|
func containerWithResources(
|
|
name string,
|
|
requests corev1.ResourceList,
|
|
limits corev1.ResourceList,
|
|
) corev1.Container {
|
|
return corev1.Container{
|
|
Name: name,
|
|
Resources: corev1.ResourceRequirements{
|
|
Requests: requests,
|
|
Limits: limits,
|
|
},
|
|
}
|
|
}
|
|
|
|
func requestOnlyRequirements(cpu, memory string) corev1.ResourceRequirements {
|
|
return corev1.ResourceRequirements{
|
|
Requests: corev1.ResourceList{
|
|
corev1.ResourceCPU: resource.MustParse(cpu),
|
|
corev1.ResourceMemory: resource.MustParse(memory),
|
|
},
|
|
}
|
|
}
|
|
|
|
func limitOnlyRequirements(cpu, memory string) corev1.ResourceRequirements {
|
|
return corev1.ResourceRequirements{
|
|
Limits: corev1.ResourceList{
|
|
corev1.ResourceCPU: resource.MustParse(cpu),
|
|
corev1.ResourceMemory: resource.MustParse(memory),
|
|
},
|
|
}
|
|
}
|
|
|
|
func guaranteedRequirements(cpu, memory string) corev1.ResourceRequirements {
|
|
return corev1.ResourceRequirements{
|
|
Requests: corev1.ResourceList{
|
|
corev1.ResourceCPU: resource.MustParse(cpu),
|
|
corev1.ResourceMemory: resource.MustParse(memory),
|
|
},
|
|
Limits: corev1.ResourceList{
|
|
corev1.ResourceCPU: resource.MustParse(cpu),
|
|
corev1.ResourceMemory: resource.MustParse(memory),
|
|
},
|
|
}
|
|
}
|
|
|
|
func requestOnlyPodResources(cpu, memory string) corev1.ResourceRequirements {
|
|
return requestOnlyRequirements(cpu, memory)
|
|
}
|
|
|
|
func limitOnlyPodResources(cpu, memory string) corev1.ResourceRequirements {
|
|
return limitOnlyRequirements(cpu, memory)
|
|
}
|
|
|
|
func guaranteedPodResources(cpu, memory string) corev1.ResourceRequirements {
|
|
return guaranteedRequirements(cpu, memory)
|
|
}
|
|
|
|
func requestOnlyPodResourcesPtr(cpu, memory string) *corev1.ResourceRequirements {
|
|
resources := requestOnlyRequirements(cpu, memory)
|
|
|
|
return &resources
|
|
}
|
|
|
|
func limitOnlyPodResourcesPtr(cpu, memory string) *corev1.ResourceRequirements {
|
|
resources := limitOnlyRequirements(cpu, memory)
|
|
|
|
return &resources
|
|
}
|
|
|
|
func guaranteedPodResourcesPtr(cpu, memory string) *corev1.ResourceRequirements {
|
|
resources := guaranteedRequirements(cpu, memory)
|
|
|
|
return &resources
|
|
}
|