diff --git a/pkg/api/v1/resource/helpers.go b/pkg/api/v1/resource/helpers.go index 792de06bc19..49a107c10d2 100644 --- a/pkg/api/v1/resource/helpers.go +++ b/pkg/api/v1/resource/helpers.go @@ -36,15 +36,17 @@ func PodRequestsAndLimits(pod *v1.Pod) (reqs, limits v1.ResourceList) { return PodRequestsAndLimitsReuse(pod, nil, nil) } -// PodRequestsAndLimitsReuse returns a dictionary of all defined resources summed up for all -// containers of the pod. If PodOverhead feature is enabled, pod overhead is added to the -// total container resource requests and to the total container limits which have a -// non-zero quantity. The caller may avoid allocations of resource lists by passing -// a requests and limits list to the function, which will be cleared before use. -func PodRequestsAndLimitsReuse(pod *v1.Pod, reuseReqs, reuseLimits v1.ResourceList) (reqs, limits v1.ResourceList) { - // attempt to reuse the maps if passed, or allocate otherwise - reqs, limits = reuseOrClearResourceList(reuseReqs), reuseOrClearResourceList(reuseLimits) +// PodRequestsAndLimitsWithoutOverhead will create a dictionary of all defined resources summed up for all +// containers of the pod. +func PodRequestsAndLimitsWithoutOverhead(pod *v1.Pod) (reqs, limits v1.ResourceList) { + reqs = make(v1.ResourceList, 4) + limits = make(v1.ResourceList, 4) + podRequestsAndLimitsWithoutOverhead(pod, reqs, limits) + return reqs, limits +} + +func podRequestsAndLimitsWithoutOverhead(pod *v1.Pod, reqs, limits v1.ResourceList) { for _, container := range pod.Spec.Containers { addResourceList(reqs, container.Resources.Requests) addResourceList(limits, container.Resources.Limits) @@ -54,6 +56,18 @@ func PodRequestsAndLimitsReuse(pod *v1.Pod, reuseReqs, reuseLimits v1.ResourceLi maxResourceList(reqs, container.Resources.Requests) maxResourceList(limits, container.Resources.Limits) } +} + +// PodRequestsAndLimitsReuse returns a dictionary of all defined resources summed up for all +// containers of the pod. If PodOverhead feature is enabled, pod overhead is added to the +// total container resource requests and to the total container limits which have a +// non-zero quantity. The caller may avoid allocations of resource lists by passing +// a requests and limits list to the function, which will be cleared before use. +func PodRequestsAndLimitsReuse(pod *v1.Pod, reuseReqs, reuseLimits v1.ResourceList) (reqs, limits v1.ResourceList) { + // attempt to reuse the maps if passed, or allocate otherwise + reqs, limits = reuseOrClearResourceList(reuseReqs), reuseOrClearResourceList(reuseLimits) + + podRequestsAndLimitsWithoutOverhead(pod, reqs, limits) // if PodOverhead feature is supported, add overhead for running a pod // to the sum of requests and to non-zero limits: diff --git a/pkg/api/v1/resource/helpers_test.go b/pkg/api/v1/resource/helpers_test.go index cc6a14b76de..9074030285b 100644 --- a/pkg/api/v1/resource/helpers_test.go +++ b/pkg/api/v1/resource/helpers_test.go @@ -21,7 +21,7 @@ import ( "github.com/stretchr/testify/assert" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/api/resource" ) @@ -337,6 +337,192 @@ func TestPodRequestsAndLimits(t *testing.T) { } } +func TestPodRequestsAndLimitsWithoutOverhead(t *testing.T) { + cases := []struct { + pod *v1.Pod + name string + expectedRequests v1.ResourceList + expectedLimits v1.ResourceList + }{ + { + name: "two container no overhead - should just be sum of containers", + pod: &v1.Pod{ + Spec: v1.PodSpec{ + Containers: []v1.Container{ + { + Name: "foobar", + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceName(v1.ResourceCPU): resource.MustParse("1"), + v1.ResourceName(v1.ResourceMemory): resource.MustParse("5"), + }, + Limits: v1.ResourceList{ + v1.ResourceName(v1.ResourceCPU): resource.MustParse("2"), + v1.ResourceName(v1.ResourceMemory): resource.MustParse("10"), + }, + }, + }, + { + Name: "foobar2", + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceName(v1.ResourceCPU): resource.MustParse("4"), + v1.ResourceName(v1.ResourceMemory): resource.MustParse("12"), + }, + Limits: v1.ResourceList{ + v1.ResourceName(v1.ResourceCPU): resource.MustParse("8"), + v1.ResourceName(v1.ResourceMemory): resource.MustParse("24"), + }, + }, + }, + }, + }, + }, + expectedRequests: v1.ResourceList{ + v1.ResourceName(v1.ResourceCPU): resource.MustParse("5"), + v1.ResourceName(v1.ResourceMemory): resource.MustParse("17"), + }, + expectedLimits: v1.ResourceList{ + v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"), + v1.ResourceName(v1.ResourceMemory): resource.MustParse("34"), + }, + }, + { + name: "two container with overhead - shouldn't consider overhead", + pod: &v1.Pod{ + Spec: v1.PodSpec{ + Overhead: v1.ResourceList{ + v1.ResourceName(v1.ResourceCPU): resource.MustParse("3"), + v1.ResourceName(v1.ResourceMemory): resource.MustParse("8"), + }, + Containers: []v1.Container{ + { + Name: "foobar", + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceName(v1.ResourceCPU): resource.MustParse("1"), + v1.ResourceName(v1.ResourceMemory): resource.MustParse("5"), + }, + Limits: v1.ResourceList{ + v1.ResourceName(v1.ResourceCPU): resource.MustParse("2"), + v1.ResourceName(v1.ResourceMemory): resource.MustParse("10"), + }, + }, + }, + { + Name: "foobar2", + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceName(v1.ResourceCPU): resource.MustParse("4"), + v1.ResourceName(v1.ResourceMemory): resource.MustParse("12"), + }, + Limits: v1.ResourceList{ + v1.ResourceName(v1.ResourceCPU): resource.MustParse("8"), + v1.ResourceName(v1.ResourceMemory): resource.MustParse("24"), + }, + }, + }, + }, + }, + }, + expectedRequests: v1.ResourceList{ + v1.ResourceName(v1.ResourceCPU): resource.MustParse("5"), + v1.ResourceName(v1.ResourceMemory): resource.MustParse("17"), + }, + expectedLimits: v1.ResourceList{ + v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"), + v1.ResourceName(v1.ResourceMemory): resource.MustParse("34"), + }, + }, + { + name: "two container with overhead, massive init - should just be the largest init", + pod: &v1.Pod{ + Spec: v1.PodSpec{ + Overhead: v1.ResourceList{ + v1.ResourceName(v1.ResourceCPU): resource.MustParse("3"), + v1.ResourceName(v1.ResourceMemory): resource.MustParse("8"), + }, + Containers: []v1.Container{ + { + Name: "foobar", + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceName(v1.ResourceCPU): resource.MustParse("1"), + v1.ResourceName(v1.ResourceMemory): resource.MustParse("5"), + }, + Limits: v1.ResourceList{ + v1.ResourceName(v1.ResourceCPU): resource.MustParse("2"), + v1.ResourceName(v1.ResourceMemory): resource.MustParse("10"), + }, + }, + }, + { + Name: "foobar2", + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceName(v1.ResourceCPU): resource.MustParse("4"), + v1.ResourceName(v1.ResourceMemory): resource.MustParse("12"), + }, + Limits: v1.ResourceList{ + v1.ResourceName(v1.ResourceCPU): resource.MustParse("8"), + v1.ResourceName(v1.ResourceMemory): resource.MustParse("24"), + }, + }, + }, + }, + InitContainers: []v1.Container{ + { + Name: "small-init", + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceName(v1.ResourceCPU): resource.MustParse("1"), + v1.ResourceName(v1.ResourceMemory): resource.MustParse("5"), + }, + Limits: v1.ResourceList{ + v1.ResourceName(v1.ResourceCPU): resource.MustParse("1"), + v1.ResourceName(v1.ResourceMemory): resource.MustParse("5"), + }, + }, + }, + { + Name: "big-init", + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceName(v1.ResourceCPU): resource.MustParse("40"), + v1.ResourceName(v1.ResourceMemory): resource.MustParse("120"), + }, + Limits: v1.ResourceList{ + v1.ResourceName(v1.ResourceCPU): resource.MustParse("80"), + v1.ResourceName(v1.ResourceMemory): resource.MustParse("240"), + }, + }, + }, + }, + }, + }, + expectedRequests: v1.ResourceList{ + v1.ResourceName(v1.ResourceCPU): resource.MustParse("40"), + v1.ResourceName(v1.ResourceMemory): resource.MustParse("120"), + }, + expectedLimits: v1.ResourceList{ + v1.ResourceName(v1.ResourceCPU): resource.MustParse("80"), + v1.ResourceName(v1.ResourceMemory): resource.MustParse("240"), + }, + }, + } + for idx, tc := range cases { + resRequests, resLimits := PodRequestsAndLimitsWithoutOverhead(tc.pod) + + if !equality.Semantic.DeepEqual(tc.expectedRequests, resRequests) { + t.Errorf("test case failure[%d]: %v, requests:\n expected:\t%v\ngot\t\t%v", idx, tc.name, tc.expectedRequests, resRequests) + } + + if !equality.Semantic.DeepEqual(tc.expectedLimits, resLimits) { + t.Errorf("test case failure[%d]: %v, limits:\n expected:\t%v\ngot\t\t%v", idx, tc.name, tc.expectedLimits, resLimits) + } + } +} + type podResources struct { cpuRequest, cpuLimit, memoryRequest, memoryLimit, cpuOverhead, memoryOverhead string }