mirror of https://github.com/k3s-io/k3s
Update limit ranging to handle init containers
parent
1b6591312d
commit
f2008152f4
|
@ -208,13 +208,8 @@ func defaultContainerResourceRequirements(limitRange *api.LimitRange) api.Resour
|
||||||
return requirements
|
return requirements
|
||||||
}
|
}
|
||||||
|
|
||||||
// mergePodResourceRequirements merges enumerated requirements with default requirements
|
// mergeContainerResources handles defaulting all of the resources on a container.
|
||||||
// it annotates the pod with information about what requirements were modified
|
func mergeContainerResources(container *api.Container, defaultRequirements *api.ResourceRequirements, annotationPrefix string, annotations []string) []string {
|
||||||
func mergePodResourceRequirements(pod *api.Pod, defaultRequirements *api.ResourceRequirements) {
|
|
||||||
annotations := []string{}
|
|
||||||
|
|
||||||
for i := range pod.Spec.Containers {
|
|
||||||
container := &pod.Spec.Containers[i]
|
|
||||||
setRequests := []string{}
|
setRequests := []string{}
|
||||||
setLimits := []string{}
|
setLimits := []string{}
|
||||||
if container.Resources.Limits == nil {
|
if container.Resources.Limits == nil {
|
||||||
|
@ -239,14 +234,28 @@ func mergePodResourceRequirements(pod *api.Pod, defaultRequirements *api.Resourc
|
||||||
}
|
}
|
||||||
if len(setRequests) > 0 {
|
if len(setRequests) > 0 {
|
||||||
sort.Strings(setRequests)
|
sort.Strings(setRequests)
|
||||||
a := strings.Join(setRequests, ", ") + " request for container " + container.Name
|
a := strings.Join(setRequests, ", ") + fmt.Sprintf(" request for %s %s", annotationPrefix, container.Name)
|
||||||
annotations = append(annotations, a)
|
annotations = append(annotations, a)
|
||||||
}
|
}
|
||||||
if len(setLimits) > 0 {
|
if len(setLimits) > 0 {
|
||||||
sort.Strings(setLimits)
|
sort.Strings(setLimits)
|
||||||
a := strings.Join(setLimits, ", ") + " limit for container " + container.Name
|
a := strings.Join(setLimits, ", ") + fmt.Sprintf(" limit for %s %s", annotationPrefix, container.Name)
|
||||||
annotations = append(annotations, a)
|
annotations = append(annotations, a)
|
||||||
}
|
}
|
||||||
|
return annotations
|
||||||
|
}
|
||||||
|
|
||||||
|
// mergePodResourceRequirements merges enumerated requirements with default requirements
|
||||||
|
// it annotates the pod with information about what requirements were modified
|
||||||
|
func mergePodResourceRequirements(pod *api.Pod, defaultRequirements *api.ResourceRequirements) {
|
||||||
|
annotations := []string{}
|
||||||
|
|
||||||
|
for i := range pod.Spec.Containers {
|
||||||
|
annotations = mergeContainerResources(&pod.Spec.Containers[i], defaultRequirements, "container", annotations)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range pod.Spec.InitContainers {
|
||||||
|
annotations = mergeContainerResources(&pod.Spec.InitContainers[i], defaultRequirements, "init container", annotations)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(annotations) > 0 {
|
if len(annotations) > 0 {
|
||||||
|
@ -441,7 +450,7 @@ func PodLimitFunc(limitRange *api.LimitRange, pod *api.Pod) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// enforce pod limits
|
// enforce pod limits on init containers
|
||||||
if limitType == api.LimitTypePod {
|
if limitType == api.LimitTypePod {
|
||||||
containerRequests, containerLimits := []api.ResourceList{}, []api.ResourceList{}
|
containerRequests, containerLimits := []api.ResourceList{}, []api.ResourceList{}
|
||||||
for j := range pod.Spec.Containers {
|
for j := range pod.Spec.Containers {
|
||||||
|
@ -451,6 +460,28 @@ func PodLimitFunc(limitRange *api.LimitRange, pod *api.Pod) error {
|
||||||
}
|
}
|
||||||
podRequests := sum(containerRequests)
|
podRequests := sum(containerRequests)
|
||||||
podLimits := sum(containerLimits)
|
podLimits := sum(containerLimits)
|
||||||
|
for j := range pod.Spec.InitContainers {
|
||||||
|
container := &pod.Spec.InitContainers[j]
|
||||||
|
// take max(sum_containers, any_init_container)
|
||||||
|
for k, v := range container.Resources.Requests {
|
||||||
|
if v2, ok := podRequests[k]; ok {
|
||||||
|
if v.Cmp(v2) > 0 {
|
||||||
|
podRequests[k] = v
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
podRequests[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for k, v := range container.Resources.Limits {
|
||||||
|
if v2, ok := podLimits[k]; ok {
|
||||||
|
if v.Cmp(v2) > 0 {
|
||||||
|
podLimits[k] = v
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
podLimits[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
for k, v := range limit.Min {
|
for k, v := range limit.Min {
|
||||||
if err := minConstraint(limitType, k, v, podRequests, podLimits); err != nil {
|
if err := minConstraint(limitType, k, v, podRequests, podLimits); err != nil {
|
||||||
errs = append(errs, err)
|
errs = append(errs, err)
|
||||||
|
|
|
@ -134,6 +134,17 @@ func validPod(name string, numContainers int, resources api.ResourceRequirements
|
||||||
return pod
|
return pod
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validPodInit(pod api.Pod, resources ...api.ResourceRequirements) api.Pod {
|
||||||
|
for i := 0; i < len(resources); i++ {
|
||||||
|
pod.Spec.InitContainers = append(pod.Spec.InitContainers, api.Container{
|
||||||
|
Image: "foo:V" + strconv.Itoa(i),
|
||||||
|
Resources: resources[i],
|
||||||
|
Name: "foo-" + strconv.Itoa(i),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return pod
|
||||||
|
}
|
||||||
|
|
||||||
func TestDefaultContainerResourceRequirements(t *testing.T) {
|
func TestDefaultContainerResourceRequirements(t *testing.T) {
|
||||||
limitRange := validLimitRange()
|
limitRange := validLimitRange()
|
||||||
expected := api.ResourceRequirements{
|
expected := api.ResourceRequirements{
|
||||||
|
@ -183,7 +194,7 @@ func TestMergePodResourceRequirements(t *testing.T) {
|
||||||
|
|
||||||
// pod with some resources enumerated should only merge empty
|
// pod with some resources enumerated should only merge empty
|
||||||
input := getResourceRequirements(getResourceList("", "512Mi"), getResourceList("", ""))
|
input := getResourceRequirements(getResourceList("", "512Mi"), getResourceList("", ""))
|
||||||
pod = validPod("limit-memory", 1, input)
|
pod = validPodInit(validPod("limit-memory", 1, input), input)
|
||||||
expected = api.ResourceRequirements{
|
expected = api.ResourceRequirements{
|
||||||
Requests: api.ResourceList{
|
Requests: api.ResourceList{
|
||||||
api.ResourceCPU: defaultRequirements.Requests[api.ResourceCPU],
|
api.ResourceCPU: defaultRequirements.Requests[api.ResourceCPU],
|
||||||
|
@ -198,11 +209,18 @@ func TestMergePodResourceRequirements(t *testing.T) {
|
||||||
t.Errorf("pod %v, expected != actual; %v != %v", pod.Name, expected, actual)
|
t.Errorf("pod %v, expected != actual; %v != %v", pod.Name, expected, actual)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for i := range pod.Spec.InitContainers {
|
||||||
|
actual := pod.Spec.InitContainers[i].Resources
|
||||||
|
if !api.Semantic.DeepEqual(expected, actual) {
|
||||||
|
t.Errorf("pod %v, expected != actual; %v != %v", pod.Name, expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
verifyAnnotation(t, &pod, "LimitRanger plugin set: cpu request for container foo-0; cpu, memory limit for container foo-0")
|
verifyAnnotation(t, &pod, "LimitRanger plugin set: cpu request for container foo-0; cpu, memory limit for container foo-0")
|
||||||
|
|
||||||
// pod with all resources enumerated should not merge anything
|
// pod with all resources enumerated should not merge anything
|
||||||
input = getResourceRequirements(getResourceList("100m", "512Mi"), getResourceList("200m", "1G"))
|
input = getResourceRequirements(getResourceList("100m", "512Mi"), getResourceList("200m", "1G"))
|
||||||
pod = validPod("limit-memory", 1, input)
|
initInputs := []api.ResourceRequirements{getResourceRequirements(getResourceList("200m", "1G"), getResourceList("400m", "2G"))}
|
||||||
|
pod = validPodInit(validPod("limit-memory", 1, input), initInputs...)
|
||||||
expected = input
|
expected = input
|
||||||
mergePodResourceRequirements(&pod, &defaultRequirements)
|
mergePodResourceRequirements(&pod, &defaultRequirements)
|
||||||
for i := range pod.Spec.Containers {
|
for i := range pod.Spec.Containers {
|
||||||
|
@ -211,6 +229,12 @@ func TestMergePodResourceRequirements(t *testing.T) {
|
||||||
t.Errorf("pod %v, expected != actual; %v != %v", pod.Name, expected, actual)
|
t.Errorf("pod %v, expected != actual; %v != %v", pod.Name, expected, actual)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for i := range pod.Spec.InitContainers {
|
||||||
|
actual := pod.Spec.InitContainers[i].Resources
|
||||||
|
if !api.Semantic.DeepEqual(initInputs[i], actual) {
|
||||||
|
t.Errorf("pod %v, expected != actual; %v != %v", pod.Name, initInputs[i], actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
expectNoAnnotation(t, &pod)
|
expectNoAnnotation(t, &pod)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,6 +297,20 @@ func TestPodLimitFunc(t *testing.T) {
|
||||||
pod: validPod("pod-min-memory-request-limit", 2, getResourceRequirements(getResourceList("", "60Mi"), getResourceList("", "100Mi"))),
|
pod: validPod("pod-min-memory-request-limit", 2, getResourceRequirements(getResourceList("", "60Mi"), getResourceList("", "100Mi"))),
|
||||||
limitRange: createLimitRange(api.LimitTypePod, getResourceList("", "100Mi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
|
limitRange: createLimitRange(api.LimitTypePod, getResourceList("", "100Mi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
pod: validPodInit(
|
||||||
|
validPod("pod-init-min-memory-request", 2, getResourceRequirements(getResourceList("", "60Mi"), getResourceList("", ""))),
|
||||||
|
getResourceRequirements(getResourceList("", "100Mi"), getResourceList("", "")),
|
||||||
|
),
|
||||||
|
limitRange: createLimitRange(api.LimitTypePod, getResourceList("", "100Mi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pod: validPodInit(
|
||||||
|
validPod("pod-init-min-memory-request-limit", 2, getResourceRequirements(getResourceList("", "60Mi"), getResourceList("", "100Mi"))),
|
||||||
|
getResourceRequirements(getResourceList("", "80Mi"), getResourceList("", "100Mi")),
|
||||||
|
),
|
||||||
|
limitRange: createLimitRange(api.LimitTypePod, getResourceList("", "100Mi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
pod: validPod("pod-max-cpu-request-limit", 2, getResourceRequirements(getResourceList("500m", ""), getResourceList("1", ""))),
|
pod: validPod("pod-max-cpu-request-limit", 2, getResourceRequirements(getResourceList("500m", ""), getResourceList("1", ""))),
|
||||||
limitRange: createLimitRange(api.LimitTypePod, api.ResourceList{}, getResourceList("2", ""), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
|
limitRange: createLimitRange(api.LimitTypePod, api.ResourceList{}, getResourceList("2", ""), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
|
||||||
|
@ -281,6 +319,22 @@ func TestPodLimitFunc(t *testing.T) {
|
||||||
pod: validPod("pod-max-cpu-limit", 2, getResourceRequirements(getResourceList("", ""), getResourceList("1", ""))),
|
pod: validPod("pod-max-cpu-limit", 2, getResourceRequirements(getResourceList("", ""), getResourceList("1", ""))),
|
||||||
limitRange: createLimitRange(api.LimitTypePod, api.ResourceList{}, getResourceList("2", ""), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
|
limitRange: createLimitRange(api.LimitTypePod, api.ResourceList{}, getResourceList("2", ""), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
pod: validPodInit(
|
||||||
|
validPod("pod-init-max-cpu-request-limit", 2, getResourceRequirements(getResourceList("500m", ""), getResourceList("1", ""))),
|
||||||
|
getResourceRequirements(getResourceList("1", ""), getResourceList("2", "")),
|
||||||
|
getResourceRequirements(getResourceList("1", ""), getResourceList("1", "")),
|
||||||
|
),
|
||||||
|
limitRange: createLimitRange(api.LimitTypePod, api.ResourceList{}, getResourceList("2", ""), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pod: validPodInit(
|
||||||
|
validPod("pod-init-max-cpu-limit", 2, getResourceRequirements(getResourceList("", ""), getResourceList("1", ""))),
|
||||||
|
getResourceRequirements(getResourceList("", ""), getResourceList("2", "")),
|
||||||
|
getResourceRequirements(getResourceList("", ""), getResourceList("2", "")),
|
||||||
|
),
|
||||||
|
limitRange: createLimitRange(api.LimitTypePod, api.ResourceList{}, getResourceList("2", ""), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
pod: validPod("pod-max-mem-request-limit", 2, getResourceRequirements(getResourceList("", "250Mi"), getResourceList("", "500Mi"))),
|
pod: validPod("pod-max-mem-request-limit", 2, getResourceRequirements(getResourceList("", "250Mi"), getResourceList("", "500Mi"))),
|
||||||
limitRange: createLimitRange(api.LimitTypePod, api.ResourceList{}, getResourceList("", "1Gi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
|
limitRange: createLimitRange(api.LimitTypePod, api.ResourceList{}, getResourceList("", "1Gi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
|
||||||
|
@ -387,6 +441,13 @@ func TestPodLimitFunc(t *testing.T) {
|
||||||
pod: validPod("pod-max-mem-limit", 3, getResourceRequirements(getResourceList("", ""), getResourceList("", "500Mi"))),
|
pod: validPod("pod-max-mem-limit", 3, getResourceRequirements(getResourceList("", ""), getResourceList("", "500Mi"))),
|
||||||
limitRange: createLimitRange(api.LimitTypePod, api.ResourceList{}, getResourceList("", "1Gi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
|
limitRange: createLimitRange(api.LimitTypePod, api.ResourceList{}, getResourceList("", "1Gi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
pod: validPodInit(
|
||||||
|
validPod("pod-init-max-mem-limit", 1, getResourceRequirements(getResourceList("", ""), getResourceList("", "500Mi"))),
|
||||||
|
getResourceRequirements(getResourceList("", ""), getResourceList("", "1.5Gi")),
|
||||||
|
),
|
||||||
|
limitRange: createLimitRange(api.LimitTypePod, api.ResourceList{}, getResourceList("", "1Gi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
pod: validPod("pod-max-mem-ratio", 3, getResourceRequirements(getResourceList("", "250Mi"), getResourceList("", "500Mi"))),
|
pod: validPod("pod-max-mem-ratio", 3, getResourceRequirements(getResourceList("", "250Mi"), getResourceList("", "500Mi"))),
|
||||||
limitRange: createLimitRange(api.LimitTypePod, api.ResourceList{}, getResourceList("", "2Gi"), api.ResourceList{}, api.ResourceList{}, getResourceList("", "1.5")),
|
limitRange: createLimitRange(api.LimitTypePod, api.ResourceList{}, getResourceList("", "2Gi"), api.ResourceList{}, api.ResourceList{}, getResourceList("", "1.5")),
|
||||||
|
@ -403,7 +464,7 @@ func TestPodLimitFunc(t *testing.T) {
|
||||||
|
|
||||||
func TestPodLimitFuncApplyDefault(t *testing.T) {
|
func TestPodLimitFuncApplyDefault(t *testing.T) {
|
||||||
limitRange := validLimitRange()
|
limitRange := validLimitRange()
|
||||||
testPod := validPod("foo", 1, getResourceRequirements(api.ResourceList{}, api.ResourceList{}))
|
testPod := validPodInit(validPod("foo", 1, getResourceRequirements(api.ResourceList{}, api.ResourceList{})), getResourceRequirements(api.ResourceList{}, api.ResourceList{}))
|
||||||
err := PodLimitFunc(&limitRange, &testPod)
|
err := PodLimitFunc(&limitRange, &testPod)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Unexpected error for valid pod: %v, %v", testPod.Name, err)
|
t.Errorf("Unexpected error for valid pod: %v, %v", testPod.Name, err)
|
||||||
|
@ -429,6 +490,27 @@ func TestPodLimitFuncApplyDefault(t *testing.T) {
|
||||||
t.Errorf("Unexpected cpu value %s", requestCpu)
|
t.Errorf("Unexpected cpu value %s", requestCpu)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for i := range testPod.Spec.InitContainers {
|
||||||
|
container := testPod.Spec.InitContainers[i]
|
||||||
|
limitMemory := container.Resources.Limits.Memory().String()
|
||||||
|
limitCpu := container.Resources.Limits.Cpu().String()
|
||||||
|
requestMemory := container.Resources.Requests.Memory().String()
|
||||||
|
requestCpu := container.Resources.Requests.Cpu().String()
|
||||||
|
|
||||||
|
if limitMemory != "10Mi" {
|
||||||
|
t.Errorf("Unexpected memory value %s", limitMemory)
|
||||||
|
}
|
||||||
|
if limitCpu != "75m" {
|
||||||
|
t.Errorf("Unexpected cpu value %s", limitCpu)
|
||||||
|
}
|
||||||
|
if requestMemory != "5Mi" {
|
||||||
|
t.Errorf("Unexpected memory value %s", requestMemory)
|
||||||
|
}
|
||||||
|
if requestCpu != "50m" {
|
||||||
|
t.Errorf("Unexpected cpu value %s", requestCpu)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLimitRangerIgnoresSubresource(t *testing.T) {
|
func TestLimitRangerIgnoresSubresource(t *testing.T) {
|
||||||
|
|
Loading…
Reference in New Issue