From 8c3bea7e794bd490c8539b7d41e4346e75ac1ee7 Mon Sep 17 00:00:00 2001 From: derekwaynecarr Date: Tue, 15 Sep 2015 17:13:05 -0400 Subject: [PATCH] LimitRanger plugin annotates the pods it modifies --- .../admission/initialresources/admission.go | 2 ++ plugin/pkg/admission/limitranger/admission.go | 31 ++++++++++++++++++ .../admission/limitranger/admission_test.go | 32 +++++++++++++++++++ 3 files changed, 65 insertions(+) diff --git a/plugin/pkg/admission/initialresources/admission.go b/plugin/pkg/admission/initialresources/admission.go index e594633f7e3..b151ed8bbdb 100644 --- a/plugin/pkg/admission/initialresources/admission.go +++ b/plugin/pkg/admission/initialresources/admission.go @@ -19,6 +19,7 @@ package initialresources import ( "flag" "io" + "sort" "strings" "time" @@ -123,6 +124,7 @@ func (ir initialResources) estimateAndFillResourcesIfNotSet(pod *api.Pod) { req[api.ResourceMemory] = *mem } if len(setRes) > 0 { + sort.Strings(setRes) a := strings.Join(setRes, ", ") + " request for container " + c.Name annotations = append(annotations, a) } diff --git a/plugin/pkg/admission/limitranger/admission.go b/plugin/pkg/admission/limitranger/admission.go index 012d08da8ad..5b28d0b7775 100644 --- a/plugin/pkg/admission/limitranger/admission.go +++ b/plugin/pkg/admission/limitranger/admission.go @@ -19,6 +19,8 @@ package limitranger import ( "fmt" "io" + "sort" + "strings" "k8s.io/kubernetes/pkg/admission" "k8s.io/kubernetes/pkg/api" @@ -33,6 +35,10 @@ import ( "k8s.io/kubernetes/pkg/watch" ) +const ( + limitRangerAnnotation = "kubernetes.io/limit-ranger" +) + func init() { admission.RegisterPlugin("LimitRanger", func(client client.Interface, config io.Reader) (admission.Interface, error) { return NewLimitRanger(client, Limit), nil @@ -162,9 +168,14 @@ func defaultContainerResourceRequirements(limitRange *api.LimitRange) api.Resour } // 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 { container := &pod.Spec.Containers[i] + setRequests := []string{} + setLimits := []string{} if container.Resources.Limits == nil { container.Resources.Limits = api.ResourceList{} } @@ -175,14 +186,34 @@ func mergePodResourceRequirements(pod *api.Pod, defaultRequirements *api.Resourc _, found := container.Resources.Limits[k] if !found { container.Resources.Limits[k] = *v.Copy() + setLimits = append(setLimits, string(k)) } } for k, v := range defaultRequirements.Requests { _, found := container.Resources.Requests[k] if !found { container.Resources.Requests[k] = *v.Copy() + setRequests = append(setRequests, string(k)) } } + if len(setRequests) > 0 { + sort.Strings(setRequests) + a := strings.Join(setRequests, ", ") + " request for container " + container.Name + annotations = append(annotations, a) + } + if len(setLimits) > 0 { + sort.Strings(setLimits) + a := strings.Join(setLimits, ", ") + " limit for container " + container.Name + annotations = append(annotations, a) + } + } + + if len(annotations) > 0 { + if pod.ObjectMeta.Annotations == nil { + pod.ObjectMeta.Annotations = make(map[string]string) + } + val := "LimitRanger plugin set: " + strings.Join(annotations, "; ") + pod.ObjectMeta.Annotations[limitRangerAnnotation] = val } } diff --git a/plugin/pkg/admission/limitranger/admission_test.go b/plugin/pkg/admission/limitranger/admission_test.go index 42aefb1818f..3b6de315c54 100644 --- a/plugin/pkg/admission/limitranger/admission_test.go +++ b/plugin/pkg/admission/limitranger/admission_test.go @@ -125,6 +125,7 @@ func validPod(name string, numContainers int, resources api.ResourceRequirements pod.Spec.Containers = append(pod.Spec.Containers, api.Container{ Image: "foo:V" + strconv.Itoa(i), Resources: resources, + Name: "foo-" + strconv.Itoa(i), }) } return pod @@ -145,6 +146,22 @@ func TestDefaultContainerResourceRequirements(t *testing.T) { } } +func verifyAnnotation(t *testing.T, pod *api.Pod, expected string) { + a, ok := pod.ObjectMeta.Annotations[limitRangerAnnotation] + if !ok { + t.Errorf("No annotation but expected %v", expected) + } + if a != expected { + t.Errorf("Wrong annotation set by Limit Ranger: got %v, expected %v", a, expected) + } +} + +func expectNoAnnotation(t *testing.T, pod *api.Pod) { + if a, ok := pod.ObjectMeta.Annotations[limitRangerAnnotation]; ok { + t.Errorf("Expected no annotation but got %v", a) + } +} + func TestMergePodResourceRequirements(t *testing.T) { limitRange := validLimitRange() @@ -159,6 +176,7 @@ func TestMergePodResourceRequirements(t *testing.T) { t.Errorf("pod %v, expected != actual; %v != %v", pod.Name, expected, actual) } } + verifyAnnotation(t, &pod, "LimitRanger plugin set: cpu, memory request for container foo-0; cpu, memory limit for container foo-0") // pod with some resources enumerated should only merge empty input := getResourceRequirements(getResourceList("", "512Mi"), getResourceList("", "")) @@ -177,6 +195,20 @@ func TestMergePodResourceRequirements(t *testing.T) { 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") + + // pod with all resources enumerated should not merge anything + input = getResourceRequirements(getResourceList("100m", "512Mi"), getResourceList("200m", "1G")) + pod = validPod("limit-memory", 1, input) + expected = input + mergePodResourceRequirements(&pod, &defaultRequirements) + for i := range pod.Spec.Containers { + actual := pod.Spec.Containers[i].Resources + if !api.Semantic.DeepEqual(expected, actual) { + t.Errorf("pod %v, expected != actual; %v != %v", pod.Name, expected, actual) + } + } + expectNoAnnotation(t, &pod) } func TestPodLimitFunc(t *testing.T) {