Adding ResourceRequirementSpec to v1beta1, v1beta2, and v1beta3 APIs. The old resource

quantities 'CPU' and 'Memory' will be preserved until support for v1beta1 and v1beta2 APIs are
dropped.
Improved resource validation in the process.
This commit is contained in:
Vishnu Kannan
2015-01-25 04:19:36 +00:00
parent aa3b45d17b
commit 5e36f63f8b
23 changed files with 782 additions and 175 deletions

View File

@@ -103,8 +103,8 @@ func PodLimitFunc(limitRange *api.LimitRange, kind string, obj runtime.Object) e
for i := range pod.Spec.Containers {
container := pod.Spec.Containers[i]
containerCPU := container.CPU.MilliValue()
containerMem := container.Memory.Value()
containerCPU := container.Resources.Limits.Cpu().MilliValue()
containerMem := container.Resources.Limits.Memory().Value()
if i == 0 {
minContainerCPU = containerCPU
@@ -113,8 +113,8 @@ func PodLimitFunc(limitRange *api.LimitRange, kind string, obj runtime.Object) e
maxContainerMem = containerMem
}
podCPU = podCPU + container.CPU.MilliValue()
podMem = podMem + container.Memory.Value()
podCPU = podCPU + container.Resources.Limits.Cpu().MilliValue()
podMem = podMem + container.Resources.Limits.Memory().Value()
minContainerCPU = Min(containerCPU, minContainerCPU)
minContainerMem = Min(containerMem, minContainerMem)

View File

@@ -23,6 +23,19 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource"
)
func getResourceRequirements(cpu, memory string) api.ResourceRequirementSpec {
res := api.ResourceRequirementSpec{}
res.Limits = api.ResourceList{}
if cpu != "" {
res.Limits[api.ResourceCPU] = resource.MustParse(cpu)
}
if memory != "" {
res.Limits[api.ResourceMemory] = resource.MustParse(memory)
}
return res
}
func TestPodLimitFunc(t *testing.T) {
limitRange := &api.LimitRange{
ObjectMeta: api.ObjectMeta{
@@ -32,25 +45,13 @@ func TestPodLimitFunc(t *testing.T) {
Limits: []api.LimitRangeItem{
{
Type: api.LimitTypePod,
Max: api.ResourceList{
api.ResourceCPU: resource.MustParse("200m"),
api.ResourceMemory: resource.MustParse("4Gi"),
},
Min: api.ResourceList{
api.ResourceCPU: resource.MustParse("50m"),
api.ResourceMemory: resource.MustParse("2Mi"),
},
Max: getResourceRequirements("200m", "4Gi").Limits,
Min: getResourceRequirements("50m", "2Mi").Limits,
},
{
Type: api.LimitTypeContainer,
Max: api.ResourceList{
api.ResourceCPU: resource.MustParse("100m"),
api.ResourceMemory: resource.MustParse("2Gi"),
},
Min: api.ResourceList{
api.ResourceCPU: resource.MustParse("25m"),
api.ResourceMemory: resource.MustParse("1Mi"),
},
Max: getResourceRequirements("100m", "2Gi").Limits,
Min: getResourceRequirements("25m", "1Mi").Limits,
},
},
},
@@ -62,14 +63,12 @@ func TestPodLimitFunc(t *testing.T) {
Spec: api.PodSpec{
Containers: []api.Container{
{
Image: "foo:V1",
CPU: resource.MustParse("100m"),
Memory: resource.MustParse("2Gi"),
Image: "foo:V1",
Resources: getResourceRequirements("100m", "2Gi"),
},
{
Image: "boo:V1",
CPU: resource.MustParse("100m"),
Memory: resource.MustParse("2Gi"),
Image: "boo:V1",
Resources: getResourceRequirements("100m", "2Gi"),
},
},
},
@@ -79,9 +78,8 @@ func TestPodLimitFunc(t *testing.T) {
Spec: api.PodSpec{
Containers: []api.Container{
{
Image: "boo:V1",
CPU: resource.MustParse("100m"),
Memory: resource.MustParse("2Gi"),
Image: "boo:V1",
Resources: getResourceRequirements("100m", "2Gi"),
},
},
},
@@ -94,9 +92,8 @@ func TestPodLimitFunc(t *testing.T) {
Spec: api.PodSpec{
Containers: []api.Container{
{
Image: "boo:V1",
CPU: resource.MustParse("25m"),
Memory: resource.MustParse("2Gi"),
Image: "boo:V1",
Resources: getResourceRequirements("25m", "2Gi"),
},
},
},
@@ -106,9 +103,8 @@ func TestPodLimitFunc(t *testing.T) {
Spec: api.PodSpec{
Containers: []api.Container{
{
Image: "boo:V1",
CPU: resource.MustParse("110m"),
Memory: resource.MustParse("1Gi"),
Image: "boo:V1",
Resources: getResourceRequirements("110m", "1Gi"),
},
},
},
@@ -118,9 +114,8 @@ func TestPodLimitFunc(t *testing.T) {
Spec: api.PodSpec{
Containers: []api.Container{
{
Image: "boo:V1",
CPU: resource.MustParse("30m"),
Memory: resource.MustParse("0"),
Image: "boo:V1",
Resources: getResourceRequirements("30m", "0"),
},
},
},
@@ -130,9 +125,8 @@ func TestPodLimitFunc(t *testing.T) {
Spec: api.PodSpec{
Containers: []api.Container{
{
Image: "boo:V1",
CPU: resource.MustParse("30m"),
Memory: resource.MustParse("3Gi"),
Image: "boo:V1",
Resources: getResourceRequirements("30m", "3Gi"),
},
},
},
@@ -142,9 +136,8 @@ func TestPodLimitFunc(t *testing.T) {
Spec: api.PodSpec{
Containers: []api.Container{
{
Image: "boo:V1",
CPU: resource.MustParse("40m"),
Memory: resource.MustParse("2Gi"),
Image: "boo:V1",
Resources: getResourceRequirements("40m", "2Gi"),
},
},
},
@@ -154,24 +147,20 @@ func TestPodLimitFunc(t *testing.T) {
Spec: api.PodSpec{
Containers: []api.Container{
{
Image: "boo:V1",
CPU: resource.MustParse("60m"),
Memory: resource.MustParse("1Mi"),
Image: "boo:V1",
Resources: getResourceRequirements("60m", "1Mi"),
},
{
Image: "boo:V2",
CPU: resource.MustParse("60m"),
Memory: resource.MustParse("1Mi"),
Image: "boo:V2",
Resources: getResourceRequirements("60m", "1Mi"),
},
{
Image: "boo:V3",
CPU: resource.MustParse("60m"),
Memory: resource.MustParse("1Mi"),
Image: "boo:V3",
Resources: getResourceRequirements("60m", "1Mi"),
},
{
Image: "boo:V4",
CPU: resource.MustParse("60m"),
Memory: resource.MustParse("1Mi"),
Image: "boo:V4",
Resources: getResourceRequirements("60m", "1Mi"),
},
},
},
@@ -181,19 +170,16 @@ func TestPodLimitFunc(t *testing.T) {
Spec: api.PodSpec{
Containers: []api.Container{
{
Image: "boo:V1",
CPU: resource.MustParse("60m"),
Memory: resource.MustParse("2Gi"),
Image: "boo:V1",
Resources: getResourceRequirements("60m", "2Gi"),
},
{
Image: "boo:V2",
CPU: resource.MustParse("60m"),
Memory: resource.MustParse("2Gi"),
Image: "boo:V2",
Resources: getResourceRequirements("60m", "2Gi"),
},
{
Image: "boo:V3",
CPU: resource.MustParse("60m"),
Memory: resource.MustParse("2Gi"),
Image: "boo:V3",
Resources: getResourceRequirements("60m", "2Gi"),
},
},
},
@@ -203,19 +189,16 @@ func TestPodLimitFunc(t *testing.T) {
Spec: api.PodSpec{
Containers: []api.Container{
{
Image: "boo:V1",
CPU: resource.MustParse("60m"),
Memory: resource.MustParse("0"),
Image: "boo:V1",
Resources: getResourceRequirements("60m", "0"),
},
{
Image: "boo:V2",
CPU: resource.MustParse("60m"),
Memory: resource.MustParse("0"),
Image: "boo:V2",
Resources: getResourceRequirements("60m", "0"),
},
{
Image: "boo:V3",
CPU: resource.MustParse("60m"),
Memory: resource.MustParse("0"),
Image: "boo:V3",
Resources: getResourceRequirements("60m", "0"),
},
},
},

View File

@@ -55,11 +55,12 @@ func (resourceDefaults) Admit(a admission.Attributes) (err error) {
obj := a.GetObject()
pod := obj.(*api.Pod)
for index := range pod.Spec.Containers {
if pod.Spec.Containers[index].Memory.Value() == 0 {
pod.Spec.Containers[index].Memory = resource.MustParse(defaultMemory)
pod.Spec.Containers[index].Resources.Limits = api.ResourceList{}
if pod.Spec.Containers[index].Resources.Limits.Memory().Value() == 0 {
pod.Spec.Containers[index].Resources.Limits[api.ResourceMemory] = resource.MustParse(defaultMemory)
}
if pod.Spec.Containers[index].CPU.Value() == 0 {
pod.Spec.Containers[index].CPU = resource.MustParse(defaultCPU)
if pod.Spec.Containers[index].Resources.Limits.Cpu().Value() == 0 {
pod.Spec.Containers[index].Resources.Limits[api.ResourceCPU] = resource.MustParse(defaultCPU)
}
}
return nil

View File

@@ -41,11 +41,13 @@ func TestAdmission(t *testing.T) {
}
for i := range pod.Spec.Containers {
if pod.Spec.Containers[i].Memory.String() != "512Mi" {
t.Errorf("Unexpected memory value %s", pod.Spec.Containers[i].Memory.String())
memory := pod.Spec.Containers[i].Resources.Limits.Memory().String()
cpu := pod.Spec.Containers[i].Resources.Limits.Cpu().String()
if memory != "512Mi" {
t.Errorf("Unexpected memory value %s", memory)
}
if pod.Spec.Containers[i].CPU.String() != "1" {
t.Errorf("Unexpected cpu value %s", pod.Spec.Containers[i].CPU.String())
if cpu != "1" {
t.Errorf("Unexpected cpu value %s", cpu)
}
}
}

View File

@@ -25,6 +25,19 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
)
func getResourceRequirements(cpu, memory string) api.ResourceRequirementSpec {
res := api.ResourceRequirementSpec{}
res.Limits = api.ResourceList{}
if cpu != "" {
res.Limits[api.ResourceCPU] = resource.MustParse(cpu)
}
if memory != "" {
res.Limits[api.ResourceMemory] = resource.MustParse(memory)
}
return res
}
func TestAdmissionIgnoresDelete(t *testing.T) {
namespace := "default"
handler := NewResourceQuota(&client.Fake{})
@@ -43,7 +56,7 @@ func TestIncrementUsagePods(t *testing.T) {
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace},
Spec: api.PodSpec{
Volumes: []api.Volume{{Name: "vol"}},
Containers: []api.Container{{Name: "ctr", Image: "image", Memory: resource.MustParse("1Gi"), CPU: resource.MustParse("100m")}},
Containers: []api.Container{{Name: "ctr", Image: "image", Resources: getResourceRequirements("100m", "1Gi")}},
},
},
},
@@ -78,7 +91,7 @@ func TestIncrementUsageMemory(t *testing.T) {
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace},
Spec: api.PodSpec{
Volumes: []api.Volume{{Name: "vol"}},
Containers: []api.Container{{Name: "ctr", Image: "image", Memory: resource.MustParse("1Gi"), CPU: resource.MustParse("100m")}},
Containers: []api.Container{{Name: "ctr", Image: "image", Resources: getResourceRequirements("100m", "1Gi")}},
},
},
},
@@ -96,7 +109,7 @@ func TestIncrementUsageMemory(t *testing.T) {
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace},
Spec: api.PodSpec{
Volumes: []api.Volume{{Name: "vol"}},
Containers: []api.Container{{Name: "ctr", Image: "image", Memory: resource.MustParse("1Gi"), CPU: resource.MustParse("100m")}},
Containers: []api.Container{{Name: "ctr", Image: "image", Resources: getResourceRequirements("100m", "1Gi")}},
}}
dirty, err := IncrementUsage(admission.NewAttributesRecord(newPod, namespace, "pods", "CREATE"), status, client)
if err != nil {
@@ -121,7 +134,7 @@ func TestExceedUsageMemory(t *testing.T) {
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace},
Spec: api.PodSpec{
Volumes: []api.Volume{{Name: "vol"}},
Containers: []api.Container{{Name: "ctr", Image: "image", Memory: resource.MustParse("1Gi"), CPU: resource.MustParse("100m")}},
Containers: []api.Container{{Name: "ctr", Image: "image", Resources: getResourceRequirements("100m", "1Gi")}},
},
},
},
@@ -139,7 +152,7 @@ func TestExceedUsageMemory(t *testing.T) {
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace},
Spec: api.PodSpec{
Volumes: []api.Volume{{Name: "vol"}},
Containers: []api.Container{{Name: "ctr", Image: "image", Memory: resource.MustParse("3Gi"), CPU: resource.MustParse("100m")}},
Containers: []api.Container{{Name: "ctr", Image: "image", Resources: getResourceRequirements("100m", "3Gi")}},
}}
_, err := IncrementUsage(admission.NewAttributesRecord(newPod, namespace, "pods", "CREATE"), status, client)
if err == nil {
@@ -156,7 +169,7 @@ func TestIncrementUsageCPU(t *testing.T) {
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace},
Spec: api.PodSpec{
Volumes: []api.Volume{{Name: "vol"}},
Containers: []api.Container{{Name: "ctr", Image: "image", Memory: resource.MustParse("1Gi"), CPU: resource.MustParse("100m")}},
Containers: []api.Container{{Name: "ctr", Image: "image", Resources: getResourceRequirements("100m", "1Gi")}},
},
},
},
@@ -174,7 +187,7 @@ func TestIncrementUsageCPU(t *testing.T) {
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace},
Spec: api.PodSpec{
Volumes: []api.Volume{{Name: "vol"}},
Containers: []api.Container{{Name: "ctr", Image: "image", Memory: resource.MustParse("1Gi"), CPU: resource.MustParse("100m")}},
Containers: []api.Container{{Name: "ctr", Image: "image", Resources: getResourceRequirements("100m", "1Gi")}},
}}
dirty, err := IncrementUsage(admission.NewAttributesRecord(newPod, namespace, "pods", "CREATE"), status, client)
if err != nil {
@@ -199,7 +212,7 @@ func TestExceedUsageCPU(t *testing.T) {
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace},
Spec: api.PodSpec{
Volumes: []api.Volume{{Name: "vol"}},
Containers: []api.Container{{Name: "ctr", Image: "image", Memory: resource.MustParse("1Gi"), CPU: resource.MustParse("100m")}},
Containers: []api.Container{{Name: "ctr", Image: "image", Resources: getResourceRequirements("100m", "1Gi")}},
},
},
},
@@ -217,7 +230,7 @@ func TestExceedUsageCPU(t *testing.T) {
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace},
Spec: api.PodSpec{
Volumes: []api.Volume{{Name: "vol"}},
Containers: []api.Container{{Name: "ctr", Image: "image", Memory: resource.MustParse("1Gi"), CPU: resource.MustParse("500m")}},
Containers: []api.Container{{Name: "ctr", Image: "image", Resources: getResourceRequirements("500m", "1Gi")}},
}}
_, err := IncrementUsage(admission.NewAttributesRecord(newPod, namespace, "pods", "CREATE"), status, client)
if err == nil {
@@ -234,7 +247,7 @@ func TestExceedUsagePods(t *testing.T) {
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace},
Spec: api.PodSpec{
Volumes: []api.Volume{{Name: "vol"}},
Containers: []api.Container{{Name: "ctr", Image: "image", Memory: resource.MustParse("1Gi"), CPU: resource.MustParse("100m")}},
Containers: []api.Container{{Name: "ctr", Image: "image", Resources: getResourceRequirements("100m", "1Gi")}},
},
},
},