From 7304454d6f66a2d52cb62b2b649b0a9a2044d656 Mon Sep 17 00:00:00 2001 From: Haoran Wang Date: Sun, 9 Oct 2016 17:31:53 +0800 Subject: [PATCH] Default resource limits for build pods --- pkg/build/admission/defaults/admission.go | 23 ++ .../admission/defaults/admission_test.go | 197 +++++++++++++++++- pkg/build/admission/defaults/api/types.go | 3 + .../admission/defaults/api/v1/swagger_doc.go | 1 + pkg/build/admission/defaults/api/v1/types.go | 3 + 5 files changed, 225 insertions(+), 2 deletions(-) diff --git a/pkg/build/admission/defaults/admission.go b/pkg/build/admission/defaults/admission.go index 26ee44db4517..81d85d2d71ba 100644 --- a/pkg/build/admission/defaults/admission.go +++ b/pkg/build/admission/defaults/admission.go @@ -130,6 +130,29 @@ func (a *buildDefaults) applyBuildDefaults(build *buildapi.Build) { build.Spec.Source.Git.NoProxy = &t } } + + //Apply default resources + defaultResources := a.defaultsConfig.Resources + if defaultResources != nil { + if len(build.Spec.Resources.Limits) == 0 { + build.Spec.Resources.Limits = kapi.ResourceList{} + } + for name, value := range defaultResources.Limits { + if _, ok := build.Spec.Resources.Limits[name]; !ok { + glog.V(5).Infof("Setting default resource limit %s for build %s/%s to %s", name, build.Namespace, build.Name, value) + build.Spec.Resources.Limits[name] = value + } + } + if len(build.Spec.Resources.Requests) == 0 { + build.Spec.Resources.Requests = kapi.ResourceList{} + } + for name, value := range defaultResources.Requests { + if _, ok := build.Spec.Resources.Requests[name]; !ok { + glog.V(5).Infof("Setting default resource request %s for build %s/%s to %s", name, build.Namespace, build.Name, value) + build.Spec.Resources.Requests[name] = value + } + } + } } func getBuildEnv(build *buildapi.Build) *[]kapi.EnvVar { diff --git a/pkg/build/admission/defaults/admission_test.go b/pkg/build/admission/defaults/admission_test.go index 73ed06919cd8..3d5b99a38d15 100644 --- a/pkg/build/admission/defaults/admission_test.go +++ b/pkg/build/admission/defaults/admission_test.go @@ -4,12 +4,12 @@ import ( "reflect" "testing" - kapi "k8s.io/kubernetes/pkg/api" - buildadmission "github.com/openshift/origin/pkg/build/admission" defaultsapi "github.com/openshift/origin/pkg/build/admission/defaults/api" u "github.com/openshift/origin/pkg/build/admission/testutil" buildapi "github.com/openshift/origin/pkg/build/api" + kapi "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/resource" _ "github.com/openshift/origin/pkg/api/install" ) @@ -266,3 +266,196 @@ func TestLabelDefaults(t *testing.T) { } } } + +func TestResourceDefaults(t *testing.T) { + tests := map[string]struct { + DefaultResource *kapi.ResourceRequirements + BuildResource kapi.ResourceRequirements + ExpectedResource kapi.ResourceRequirements + }{ + "BuildDefaults plugin and Build object both defined resource limits and requests": { + DefaultResource: &kapi.ResourceRequirements{ + Limits: kapi.ResourceList{ + kapi.ResourceName(kapi.ResourceCPU): resource.MustParse("10"), + kapi.ResourceName(kapi.ResourceMemory): resource.MustParse("1G"), + }, + Requests: kapi.ResourceList{ + kapi.ResourceName(kapi.ResourceCPU): resource.MustParse("20"), + kapi.ResourceName(kapi.ResourceMemory): resource.MustParse("2G"), + }, + }, + BuildResource: kapi.ResourceRequirements{ + Limits: kapi.ResourceList{ + kapi.ResourceName(kapi.ResourceCPU): resource.MustParse("30"), + kapi.ResourceName(kapi.ResourceMemory): resource.MustParse("3G"), + }, + Requests: kapi.ResourceList{ + kapi.ResourceName(kapi.ResourceCPU): resource.MustParse("40"), + kapi.ResourceName(kapi.ResourceMemory): resource.MustParse("4G"), + }, + }, + ExpectedResource: kapi.ResourceRequirements{ + Limits: kapi.ResourceList{ + kapi.ResourceName(kapi.ResourceCPU): resource.MustParse("30"), + kapi.ResourceName(kapi.ResourceMemory): resource.MustParse("3G"), + }, + Requests: kapi.ResourceList{ + kapi.ResourceName(kapi.ResourceCPU): resource.MustParse("40"), + kapi.ResourceName(kapi.ResourceMemory): resource.MustParse("4G"), + }, + }, + }, + "BuildDefaults plugin defined limits and requests, Build object defined resource requests": { + DefaultResource: &kapi.ResourceRequirements{ + Limits: kapi.ResourceList{ + kapi.ResourceName(kapi.ResourceCPU): resource.MustParse("10"), + kapi.ResourceName(kapi.ResourceMemory): resource.MustParse("1G"), + }, + Requests: kapi.ResourceList{ + kapi.ResourceName(kapi.ResourceCPU): resource.MustParse("20"), + kapi.ResourceName(kapi.ResourceMemory): resource.MustParse("2G"), + }, + }, + BuildResource: kapi.ResourceRequirements{ + Requests: kapi.ResourceList{ + kapi.ResourceName(kapi.ResourceCPU): resource.MustParse("30"), + kapi.ResourceName(kapi.ResourceMemory): resource.MustParse("3G"), + }, + }, + ExpectedResource: kapi.ResourceRequirements{ + Limits: kapi.ResourceList{ + kapi.ResourceName(kapi.ResourceCPU): resource.MustParse("10"), + kapi.ResourceName(kapi.ResourceMemory): resource.MustParse("1G"), + }, + Requests: kapi.ResourceList{ + kapi.ResourceName(kapi.ResourceCPU): resource.MustParse("30"), + kapi.ResourceName(kapi.ResourceMemory): resource.MustParse("3G"), + }, + }, + }, + "BuildDefaults plugin defined limits and requests, Build object defined resource limits": { + DefaultResource: &kapi.ResourceRequirements{ + Limits: kapi.ResourceList{ + kapi.ResourceName(kapi.ResourceCPU): resource.MustParse("10"), + kapi.ResourceName(kapi.ResourceMemory): resource.MustParse("1G"), + }, + Requests: kapi.ResourceList{ + kapi.ResourceName(kapi.ResourceCPU): resource.MustParse("20"), + kapi.ResourceName(kapi.ResourceMemory): resource.MustParse("2G"), + }, + }, + BuildResource: kapi.ResourceRequirements{ + Limits: kapi.ResourceList{ + kapi.ResourceName(kapi.ResourceCPU): resource.MustParse("30"), + kapi.ResourceName(kapi.ResourceMemory): resource.MustParse("3G"), + }, + }, + ExpectedResource: kapi.ResourceRequirements{ + Limits: kapi.ResourceList{ + kapi.ResourceName(kapi.ResourceCPU): resource.MustParse("30"), + kapi.ResourceName(kapi.ResourceMemory): resource.MustParse("3G"), + }, + Requests: kapi.ResourceList{ + kapi.ResourceName(kapi.ResourceMemory): resource.MustParse("2G"), + kapi.ResourceName(kapi.ResourceCPU): resource.MustParse("20"), + }, + }, + }, + "BuildDefaults plugin defined nothing, Build object defined resource limits": { + DefaultResource: &kapi.ResourceRequirements{}, + BuildResource: kapi.ResourceRequirements{ + Limits: kapi.ResourceList{ + kapi.ResourceName(kapi.ResourceCPU): resource.MustParse("10"), + kapi.ResourceName(kapi.ResourceMemory): resource.MustParse("1G"), + }, + Requests: kapi.ResourceList{ + kapi.ResourceName(kapi.ResourceCPU): resource.MustParse("20"), + kapi.ResourceName(kapi.ResourceMemory): resource.MustParse("2G"), + }, + }, + ExpectedResource: kapi.ResourceRequirements{ + Limits: kapi.ResourceList{ + kapi.ResourceName(kapi.ResourceCPU): resource.MustParse("10"), + kapi.ResourceName(kapi.ResourceMemory): resource.MustParse("1G"), + }, + Requests: kapi.ResourceList{ + kapi.ResourceName(kapi.ResourceCPU): resource.MustParse("20"), + kapi.ResourceName(kapi.ResourceMemory): resource.MustParse("2G"), + }, + }, + }, + "BuildDefaults plugin defined nil, Build object defined resource limits": { + DefaultResource: nil, + BuildResource: kapi.ResourceRequirements{ + Limits: kapi.ResourceList{ + kapi.ResourceName(kapi.ResourceCPU): resource.MustParse("10"), + kapi.ResourceName(kapi.ResourceMemory): resource.MustParse("1G"), + }, + Requests: kapi.ResourceList{ + kapi.ResourceName(kapi.ResourceCPU): resource.MustParse("20"), + kapi.ResourceName(kapi.ResourceMemory): resource.MustParse("2G"), + }, + }, + ExpectedResource: kapi.ResourceRequirements{ + Limits: kapi.ResourceList{ + kapi.ResourceName(kapi.ResourceCPU): resource.MustParse("10"), + kapi.ResourceName(kapi.ResourceMemory): resource.MustParse("1G"), + }, + Requests: kapi.ResourceList{ + kapi.ResourceName(kapi.ResourceCPU): resource.MustParse("20"), + kapi.ResourceName(kapi.ResourceMemory): resource.MustParse("2G"), + }, + }, + }, + "BuildDefaults plugin and Build object defined nothing": { + DefaultResource: &kapi.ResourceRequirements{}, + BuildResource: kapi.ResourceRequirements{}, + ExpectedResource: kapi.ResourceRequirements{}, + }, + "BuildDefaults plugin defined limits and requests, Build object defined nothing": { + DefaultResource: &kapi.ResourceRequirements{ + Limits: kapi.ResourceList{ + kapi.ResourceName(kapi.ResourceCPU): resource.MustParse("10"), + kapi.ResourceName(kapi.ResourceMemory): resource.MustParse("1G"), + }, + Requests: kapi.ResourceList{ + kapi.ResourceName(kapi.ResourceCPU): resource.MustParse("20"), + kapi.ResourceName(kapi.ResourceMemory): resource.MustParse("2G"), + }, + }, + BuildResource: kapi.ResourceRequirements{}, + ExpectedResource: kapi.ResourceRequirements{ + Limits: kapi.ResourceList{ + kapi.ResourceName(kapi.ResourceCPU): resource.MustParse("10"), + kapi.ResourceName(kapi.ResourceMemory): resource.MustParse("1G"), + }, + Requests: kapi.ResourceList{ + kapi.ResourceName(kapi.ResourceCPU): resource.MustParse("20"), + kapi.ResourceName(kapi.ResourceMemory): resource.MustParse("2G"), + }, + }, + }, + } + + for name, test := range tests { + defaultsConfig := &defaultsapi.BuildDefaultsConfig{ + Resources: test.DefaultResource, + } + admitter := NewBuildDefaults(defaultsConfig) + + build := u.Build().WithSourceStrategy().AsBuild() + build.Spec.Resources = test.BuildResource + pod := u.Pod().WithBuild(t, build, "v1") + err := admitter.Admit(pod.ToAttributes()) + if err != nil { + t.Fatalf("%v :unexpected error: %v", name, err) + } + build, _, err = buildadmission.GetBuild(pod.ToAttributes()) + if err != nil { + t.Fatalf("%v :unexpected error: %v", name, err) + } + if !kapi.Semantic.DeepEqual(test.ExpectedResource, build.Spec.Resources) { + t.Fatalf("%v:Expected expected=actual, %v != %v", name, test.ExpectedResource, build.Spec.Resources) + } + } +} diff --git a/pkg/build/admission/defaults/api/types.go b/pkg/build/admission/defaults/api/types.go index 80195a44a684..35e8dc773b51 100644 --- a/pkg/build/admission/defaults/api/types.go +++ b/pkg/build/admission/defaults/api/types.go @@ -32,6 +32,9 @@ type BuildDefaultsConfig struct { // User can override a default label by providing a label with the same name in their // Build/BuildConfig. ImageLabels []buildapi.ImageLabel + + // Resources computes resource requirements to execute the build. + Resources *kapi.ResourceRequirements } // SourceStrategyDefaultsConfig contains values that apply to builds using the diff --git a/pkg/build/admission/defaults/api/v1/swagger_doc.go b/pkg/build/admission/defaults/api/v1/swagger_doc.go index 307d4fc30a39..3e51c20495d3 100644 --- a/pkg/build/admission/defaults/api/v1/swagger_doc.go +++ b/pkg/build/admission/defaults/api/v1/swagger_doc.go @@ -13,6 +13,7 @@ var map_BuildDefaultsConfig = map[string]string{ "env": "Env is a set of default environment variables that will be applied to the build if the specified variables do not exist on the build", "sourceStrategyDefaults": "SourceStrategyDefaults are default values that apply to builds using the source strategy.", "imageLabels": "ImageLabels is a list of docker labels that are applied to the resulting image. User can override a default label by providing a label with the same name in their Build/BuildConfig.", + "resources": "Resources computes resource requirements to execute the build.", } func (BuildDefaultsConfig) SwaggerDoc() map[string]string { diff --git a/pkg/build/admission/defaults/api/v1/types.go b/pkg/build/admission/defaults/api/v1/types.go index 753a33fa32d1..587df33e41b0 100644 --- a/pkg/build/admission/defaults/api/v1/types.go +++ b/pkg/build/admission/defaults/api/v1/types.go @@ -32,6 +32,9 @@ type BuildDefaultsConfig struct { // User can override a default label by providing a label with the same name in their // Build/BuildConfig. ImageLabels []buildapi.ImageLabel `json:"imageLabels,omitempty"` + + // Resources computes resource requirements to execute the build. + Resources *kapi.ResourceRequirements `json:"resources,omitempty"` } // SourceStrategyDefaultsConfig contains values that apply to builds using the