mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-10-31 02:08:13 +00:00 
			
		
		
		
	Add resource requests support
This commit is contained in:
		| @@ -22,11 +22,9 @@ import ( | ||||
| 	"github.com/gogo/protobuf/proto" | ||||
| 	mesos "github.com/mesos/mesos-go/mesosproto" | ||||
| 	mutil "github.com/mesos/mesos-go/mesosutil" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"k8s.io/kubernetes/contrib/mesos/pkg/node" | ||||
| 	mresource "k8s.io/kubernetes/contrib/mesos/pkg/scheduler/resource" | ||||
| 	"k8s.io/kubernetes/pkg/api" | ||||
| 	"k8s.io/kubernetes/pkg/api/resource" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| @@ -43,93 +41,6 @@ func fakePodTask(id string) (*T, error) { | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func TestUnlimitedResources(t *testing.T) { | ||||
| 	assert := assert.New(t) | ||||
|  | ||||
| 	task, _ := fakePodTask("unlimited") | ||||
| 	pod := &task.Pod | ||||
| 	pod.Spec = api.PodSpec{ | ||||
| 		Containers: []api.Container{{ | ||||
| 			Name: "a", | ||||
| 			Ports: []api.ContainerPort{{ | ||||
| 				HostPort: 123, | ||||
| 			}}, | ||||
| 			Resources: api.ResourceRequirements{ | ||||
| 				Limits: api.ResourceList{ | ||||
| 					api.ResourceCPU:    *resource.NewQuantity(3, resource.DecimalSI), | ||||
| 					api.ResourceMemory: *resource.NewQuantity(768*1024*1024, resource.BinarySI), | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, { | ||||
| 			Name: "b", | ||||
| 		}, { | ||||
| 			Name: "c", | ||||
| 		}}, | ||||
| 	} | ||||
|  | ||||
| 	beforeLimitingCPU := mresource.CPUForPod(pod, mresource.DefaultDefaultContainerCPULimit) | ||||
| 	beforeLimitingMem := mresource.MemForPod(pod, mresource.DefaultDefaultContainerMemLimit) | ||||
|  | ||||
| 	unboundedCPU := mresource.LimitPodCPU(pod, mresource.DefaultDefaultContainerCPULimit) | ||||
| 	unboundedMem := mresource.LimitPodMem(pod, mresource.DefaultDefaultContainerMemLimit) | ||||
|  | ||||
| 	cpu := mresource.PodCPULimit(pod) | ||||
| 	mem := mresource.PodMemLimit(pod) | ||||
|  | ||||
| 	assert.True(unboundedCPU, "CPU resources are defined as unlimited") | ||||
| 	assert.True(unboundedMem, "mem resources are defined as unlimited") | ||||
|  | ||||
| 	assert.Equal(2*float64(mresource.DefaultDefaultContainerCPULimit)+3.0, float64(cpu)) | ||||
| 	assert.Equal(2*float64(mresource.DefaultDefaultContainerMemLimit)+768.0, float64(mem)) | ||||
|  | ||||
| 	assert.Equal(cpu, beforeLimitingCPU) | ||||
| 	assert.Equal(mem, beforeLimitingMem) | ||||
| } | ||||
|  | ||||
| func TestLimitedResources(t *testing.T) { | ||||
| 	assert := assert.New(t) | ||||
|  | ||||
| 	task, _ := fakePodTask("limited") | ||||
| 	pod := &task.Pod | ||||
| 	pod.Spec = api.PodSpec{ | ||||
| 		Containers: []api.Container{{ | ||||
| 			Name: "a", | ||||
| 			Resources: api.ResourceRequirements{ | ||||
| 				Limits: api.ResourceList{ | ||||
| 					api.ResourceCPU:    *resource.NewQuantity(1, resource.DecimalSI), | ||||
| 					api.ResourceMemory: *resource.NewQuantity(256*1024*1024, resource.BinarySI), | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, { | ||||
| 			Name: "b", | ||||
| 			Resources: api.ResourceRequirements{ | ||||
| 				Limits: api.ResourceList{ | ||||
| 					api.ResourceCPU:    *resource.NewQuantity(2, resource.DecimalSI), | ||||
| 					api.ResourceMemory: *resource.NewQuantity(512*1024*1024, resource.BinarySI), | ||||
| 				}, | ||||
| 			}, | ||||
| 		}}, | ||||
| 	} | ||||
|  | ||||
| 	beforeLimitingCPU := mresource.CPUForPod(pod, mresource.DefaultDefaultContainerCPULimit) | ||||
| 	beforeLimitingMem := mresource.MemForPod(pod, mresource.DefaultDefaultContainerMemLimit) | ||||
|  | ||||
| 	unboundedCPU := mresource.LimitPodCPU(pod, mresource.DefaultDefaultContainerCPULimit) | ||||
| 	unboundedMem := mresource.LimitPodMem(pod, mresource.DefaultDefaultContainerMemLimit) | ||||
|  | ||||
| 	cpu := mresource.PodCPULimit(pod) | ||||
| 	mem := mresource.PodMemLimit(pod) | ||||
|  | ||||
| 	assert.False(unboundedCPU, "CPU resources are defined as limited") | ||||
| 	assert.False(unboundedMem, "mem resources are defined as limited") | ||||
|  | ||||
| 	assert.Equal(3.0, float64(cpu)) | ||||
| 	assert.Equal(768.0, float64(mem)) | ||||
|  | ||||
| 	assert.Equal(cpu, beforeLimitingCPU) | ||||
| 	assert.Equal(mem, beforeLimitingMem) | ||||
| } | ||||
|  | ||||
| func TestEmptyOffer(t *testing.T) { | ||||
| 	t.Parallel() | ||||
| 	task, err := fakePodTask("foo") | ||||
|   | ||||
| @@ -190,7 +190,7 @@ func TestWildcardHostPortMatching(t *testing.T) { | ||||
| 			}}, | ||||
| 		}}, | ||||
| 	} | ||||
| 	task, err = New(api.NewDefaultContext(), "", *pod, &mesos.ExecutorInfo{}) | ||||
| 	task, err = New(api.NewDefaultContext(), "", pod) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
|   | ||||
| @@ -100,8 +100,15 @@ func NewPodFitsResourcesPredicate(c mresource.CPUShares, m mresource.MegaBytes) | ||||
| 		// calculate cpu and mem sum over all containers of the pod | ||||
| 		// TODO (@sttts): also support pod.spec.resources.limit.request | ||||
| 		// TODO (@sttts): take into account the executor resources | ||||
| 		cpu := mresource.CPUForPod(&t.Pod, c) | ||||
| 		mem := mresource.MemForPod(&t.Pod, m) | ||||
| 		_, cpu, _, err := mresource.CPUForPod(&t.Pod, c) | ||||
| 		if err != nil { | ||||
| 			return false | ||||
| 		} | ||||
| 		_, mem, _, err := mresource.MemForPod(&t.Pod, m) | ||||
| 		if err != nil { | ||||
| 			return false | ||||
| 		} | ||||
|  | ||||
| 		log.V(4).Infof("trying to match offer with pod %v/%v: cpus: %.2f mem: %.2f MB", t.Pod.Namespace, t.Pod.Name, cpu, mem) | ||||
| 		if (cpu > offeredCpus) || (mem > offeredMem) { | ||||
| 			log.V(3).Infof("not enough resources for pod %v/%v: cpus: %.2f mem: %.2f MB", t.Pod.Namespace, t.Pod.Name, cpu, mem) | ||||
|   | ||||
| @@ -27,15 +27,14 @@ import ( | ||||
| // k8s api.Pod.Spec's that don't declare resources (all containers in k8s-mesos require cpu | ||||
| // and memory limits). | ||||
| func NewDefaultProcurement(c mresource.CPUShares, m mresource.MegaBytes) Procurement { | ||||
| 	requireSome := &RequireSomePodResources{ | ||||
| 	resourceProcurer := &RequirePodResources{ | ||||
| 		defaultContainerCPULimit: c, | ||||
| 		defaultContainerMemLimit: m, | ||||
| 	} | ||||
| 	return AllOrNothingProcurement([]Procurement{ | ||||
| 		ValidateProcurement, | ||||
| 		NodeProcurement, | ||||
| 		requireSome.Procure, | ||||
| 		PodResourcesProcurement, | ||||
| 		resourceProcurer.Procure, | ||||
| 		PortsProcurement, | ||||
| 	}).Procure | ||||
| } | ||||
| @@ -80,37 +79,33 @@ func NodeProcurement(t *T, offer *mesos.Offer) error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| type RequireSomePodResources struct { | ||||
| type RequirePodResources struct { | ||||
| 	defaultContainerCPULimit mresource.CPUShares | ||||
| 	defaultContainerMemLimit mresource.MegaBytes | ||||
| } | ||||
|  | ||||
| func (r *RequireSomePodResources) Procure(t *T, offer *mesos.Offer) error { | ||||
| func (r *RequirePodResources) Procure(t *T, offer *mesos.Offer) error { | ||||
| 	// write resource limits into the pod spec which is transferred to the executor. From here | ||||
| 	// on we can expect that the pod spec of a task has proper limits for CPU and memory. | ||||
| 	// TODO(sttts): For a later separation of the kubelet and the executor also patch the pod on the apiserver | ||||
| 	// TODO(sttts): fall back to requested resources if resource limit cannot be fulfilled by the offer | ||||
| 	// TODO(jdef): changing the state of t.Pod here feels dirty, especially since we don't use a kosher | ||||
| 	// method to clone the api.Pod state in T.Clone(). This needs some love. | ||||
| 	if unlimitedCPU := mresource.LimitPodCPU(&t.Pod, r.defaultContainerCPULimit); unlimitedCPU { | ||||
| 		log.V(2).Infof("Pod %s/%s without cpu limits is admitted %.2f cpu shares", t.Pod.Namespace, t.Pod.Name, mresource.PodCPULimit(&t.Pod)) | ||||
| 	_, cpuLimit, _, err := mresource.LimitPodCPU(&t.Pod, r.defaultContainerCPULimit) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if unlimitedMem := mresource.LimitPodMem(&t.Pod, r.defaultContainerMemLimit); unlimitedMem { | ||||
| 		log.V(2).Infof("Pod %s/%s without memory limits is admitted %.2f MB", t.Pod.Namespace, t.Pod.Name, mresource.PodMemLimit(&t.Pod)) | ||||
|  | ||||
| 	_, memLimit, _, err := mresource.LimitPodMem(&t.Pod, r.defaultContainerMemLimit) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // PodResourcesProcurement converts k8s pod cpu and memory resource requirements into | ||||
| // mesos resource allocations. | ||||
| func PodResourcesProcurement(t *T, offer *mesos.Offer) error { | ||||
| 	// compute used resources | ||||
| 	cpu := mresource.PodCPULimit(&t.Pod) | ||||
| 	mem := mresource.PodMemLimit(&t.Pod) | ||||
| 	log.V(3).Infof("Recording offer(s) %s/%s against pod %v: cpu: %.2f, mem: %.2f MB", offer.Id, t.Pod.Namespace, t.Pod.Name, cpuLimit, memLimit) | ||||
|  | ||||
| 	log.V(3).Infof("Recording offer(s) %s/%s against pod %v: cpu: %.2f, mem: %.2f MB", offer.Id, t.Pod.Namespace, t.Pod.Name, cpu, mem) | ||||
| 	t.Spec.CPU = cpuLimit | ||||
| 	t.Spec.Memory = memLimit | ||||
|  | ||||
| 	t.Spec.CPU = cpu | ||||
| 	t.Spec.Memory = mem | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -19,87 +19,126 @@ package resource | ||||
| import ( | ||||
| 	"k8s.io/kubernetes/pkg/api" | ||||
| 	"k8s.io/kubernetes/pkg/api/resource" | ||||
| 	resourcequotacontroller "k8s.io/kubernetes/pkg/controller/resourcequota" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	DefaultDefaultContainerCPULimit = CPUShares(0.25) // CPUs allocated for pods without CPU limit | ||||
| 	DefaultDefaultContainerMemLimit = MegaBytes(64.0) // memory allocated for pods without memory limit | ||||
| 	MinimumContainerCPU             = CPUShares(0.01) // minimum CPUs allowed by Mesos | ||||
| 	MinimumContainerMem             = MegaBytes(32.0) // minimum memory allowed by Mesos | ||||
| ) | ||||
|  | ||||
| // CPUFromPodSpec computes the cpu shares that the pod is admitted to use. Containers | ||||
| // without CPU limit are NOT taken into account. | ||||
| func PodCPULimit(pod *api.Pod) CPUShares { | ||||
| 	cpuQuantity := resourcequotacontroller.PodCPU(pod) | ||||
| 	return CPUShares(float64(cpuQuantity.MilliValue()) / 1000.0) | ||||
| } | ||||
| var ( | ||||
| 	zero = *resource.NewQuantity(0, resource.BinarySI) | ||||
| ) | ||||
|  | ||||
| // MemFromPodSpec computes the amount of memory that the pod is admitted to use. Containers | ||||
| // without memory limit are NOT taken into account. | ||||
| func PodMemLimit(pod *api.Pod) MegaBytes { | ||||
| 	memQuantity := resourcequotacontroller.PodMemory(pod) | ||||
| 	return MegaBytes(float64(memQuantity.Value()) / 1024.0 / 1024.0) | ||||
| } | ||||
| // podResource computes requested resources and the limit. If write is true, | ||||
| // it will also write missing requests and limits into the pod. | ||||
| func podResources(pod *api.Pod, resourceName api.ResourceName, def, min resource.Quantity, write bool) ( | ||||
| 	requestSum *resource.Quantity, | ||||
| 	limitSum *resource.Quantity, | ||||
| 	modified bool, | ||||
| 	err error, | ||||
| ) { | ||||
| 	requestSum = (&zero).Copy() | ||||
| 	limitSum = (&zero).Copy() | ||||
| 	modified = false | ||||
| 	err = nil | ||||
|  | ||||
| // limitPodResource sets the given default resource limit for each container that | ||||
| // does not limit the given resource yet. limitPodResource returns true if and only if | ||||
| // at least one container had no limit for that resource. | ||||
| func limitPodResource(pod *api.Pod, resourceName api.ResourceName, defaultLimit resource.Quantity) bool { | ||||
| 	unlimited := false | ||||
| 	for j := range pod.Spec.Containers { | ||||
| 		container := &pod.Spec.Containers[j] | ||||
|  | ||||
| 		// create maps | ||||
| 		if container.Resources.Limits == nil { | ||||
| 			container.Resources.Limits = api.ResourceList{} | ||||
| 		} | ||||
| 		_, ok := container.Resources.Limits[resourceName] | ||||
| 		if !ok { | ||||
| 			container.Resources.Limits[resourceName] = defaultLimit | ||||
| 			unlimited = true | ||||
| 		if container.Resources.Requests == nil { | ||||
| 			container.Resources.Requests = api.ResourceList{} | ||||
| 		} | ||||
|  | ||||
| 		// request and limit defined? | ||||
| 		request, requestFound := container.Resources.Requests[resourceName] | ||||
| 		limit, limitFound := container.Resources.Limits[resourceName] | ||||
|  | ||||
| 		// fill-in missing request and/or limit | ||||
| 		if !requestFound && !limitFound { | ||||
| 			limit = def | ||||
| 			request = def | ||||
| 			modified = true | ||||
| 		} else if requestFound && !limitFound { | ||||
| 			limit = request | ||||
| 			modified = true | ||||
| 		} else if !requestFound && limitFound { | ||||
| 			// TODO(sttts): possibly use the default here? | ||||
| 			request = limit | ||||
| 			modified = true | ||||
| 		} | ||||
|  | ||||
| 		// make request and limit at least as big as min | ||||
| 		if (&request).Cmp(min) < 0 { | ||||
| 			request = *(&min).Copy() | ||||
| 			modified = true | ||||
| 		} | ||||
| 		if (&limit).Cmp(min) < 0 { | ||||
| 			limit = *(&min).Copy() | ||||
| 			modified = true | ||||
| 		} | ||||
|  | ||||
| 		// add up the request and limit sum for all containers | ||||
| 		err = requestSum.Add(request) | ||||
| 		if err != nil { | ||||
| 			return | ||||
| 		} | ||||
| 		err = limitSum.Add(limit) | ||||
| 		if err != nil { | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		// optionally write request and limit back | ||||
| 		if write { | ||||
| 			container.Resources.Requests[resourceName] = request | ||||
| 			container.Resources.Limits[resourceName] = limit | ||||
| 		} | ||||
| 	} | ||||
| 	return unlimited | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // unlimitedPodResources counts how many containers in the pod have no limit for the given resource | ||||
| func unlimitedCountainerNum(pod *api.Pod, resourceName api.ResourceName) int { | ||||
| 	unlimited := 0 | ||||
| 	for j := range pod.Spec.Containers { | ||||
| 		container := &pod.Spec.Containers[j] | ||||
|  | ||||
| 		if container.Resources.Limits == nil { | ||||
| 			unlimited += 1 | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		if _, ok := container.Resources.Limits[resourceName]; !ok { | ||||
| 			unlimited += 1 | ||||
| 		} | ||||
| // LimitPodCPU sets default CPU requests and limits of each container that | ||||
| // does not limit its CPU resource yet. LimitPodCPU returns the new request, | ||||
| // limit and whether the pod was modified. | ||||
| func LimitPodCPU(pod *api.Pod, defaultLimit CPUShares) (request, limit CPUShares, modified bool, err error) { | ||||
| 	r, l, m, err := podResources(pod, api.ResourceCPU, *defaultLimit.Quantity(), *MinimumContainerCPU.Quantity(), true) | ||||
| 	if err != nil { | ||||
| 		return 0.0, 0.0, false, err | ||||
| 	} | ||||
| 	return unlimited | ||||
| 	return NewCPUShares(*r), NewCPUShares(*l), m, nil | ||||
| } | ||||
|  | ||||
| // limitPodCPU sets DefaultContainerCPUs for the CPU limit of each container that | ||||
| // does not limit its CPU resource yet. limitPodCPU returns true if and only if | ||||
| // at least one container had no CPU limit set. | ||||
| func LimitPodCPU(pod *api.Pod, defaultLimit CPUShares) bool { | ||||
| 	defaultCPUQuantity := resource.NewMilliQuantity(int64(float64(defaultLimit)*1000.0), resource.DecimalSI) | ||||
| 	return limitPodResource(pod, api.ResourceCPU, *defaultCPUQuantity) | ||||
| // LimitPodMem sets default memory requests and limits of each container that | ||||
| // does not limit its memory resource yet. LimitPodMem returns the new request, | ||||
| // limit and whether the pod was modified. | ||||
| func LimitPodMem(pod *api.Pod, defaultLimit MegaBytes) (request, limit MegaBytes, modified bool, err error) { | ||||
| 	r, l, m, err := podResources(pod, api.ResourceMemory, *defaultLimit.Quantity(), *MinimumContainerMem.Quantity(), true) | ||||
| 	if err != nil { | ||||
| 		return 0.0, 0.0, false, err | ||||
| 	} | ||||
| 	return NewMegaBytes(*r), NewMegaBytes(*l), m, nil | ||||
| } | ||||
|  | ||||
| // limitPodMem sets DefaultContainerMem for the memory limit of each container that | ||||
| // does not limit its memory resource yet. limitPodMem returns true if and only if | ||||
| // at least one container had no memory limit set. | ||||
| func LimitPodMem(pod *api.Pod, defaultLimit MegaBytes) bool { | ||||
| 	defaultMemQuantity := resource.NewQuantity(int64(float64(defaultLimit)*1024.0*1024.0), resource.BinarySI) | ||||
| 	return limitPodResource(pod, api.ResourceMemory, *defaultMemQuantity) | ||||
| // CPUForPod computes the limits from the spec plus the default CPU limit difference for unlimited containers | ||||
| func CPUForPod(pod *api.Pod, defaultLimit CPUShares) (request, limit CPUShares, modified bool, err error) { | ||||
| 	r, l, m, err := podResources(pod, api.ResourceCPU, *defaultLimit.Quantity(), *MinimumContainerCPU.Quantity(), false) | ||||
| 	if err != nil { | ||||
| 		return 0.0, 0.0, false, err | ||||
| 	} | ||||
| 	return NewCPUShares(*r), NewCPUShares(*l), m, nil | ||||
| } | ||||
|  | ||||
| // CPUForPod computes the limits from the spec plus the default CPU limit for unlimited containers | ||||
| func CPUForPod(pod *api.Pod, defaultLimit CPUShares) CPUShares { | ||||
| 	return PodCPULimit(pod) + CPUShares(unlimitedCountainerNum(pod, api.ResourceCPU))*defaultLimit | ||||
| } | ||||
|  | ||||
| // MemForPod computes the limits from the spec plus the default memory limit for unlimited containers | ||||
| func MemForPod(pod *api.Pod, defaultLimit MegaBytes) MegaBytes { | ||||
| 	return PodMemLimit(pod) + MegaBytes(unlimitedCountainerNum(pod, api.ResourceMemory))*defaultLimit | ||||
| // MemForPod computes the limits from the spec plus the default memory limit difference for unlimited containers | ||||
| func MemForPod(pod *api.Pod, defaultLimit MegaBytes) (request, limit MegaBytes, modified bool, err error) { | ||||
| 	r, l, m, err := podResources(pod, api.ResourceMemory, *defaultLimit.Quantity(), *MinimumContainerMem.Quantity(), true) | ||||
| 	if err != nil { | ||||
| 		return 0.0, 0.0, false, err | ||||
| 	} | ||||
| 	return NewMegaBytes(*r), NewMegaBytes(*l), m, nil | ||||
| } | ||||
|   | ||||
							
								
								
									
										113
									
								
								contrib/mesos/pkg/scheduler/resource/resource_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								contrib/mesos/pkg/scheduler/resource/resource_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,113 @@ | ||||
| /* | ||||
| Copyright 2015 The Kubernetes Authors All rights reserved. | ||||
|  | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
|  | ||||
| package resource | ||||
|  | ||||
| import ( | ||||
| 	"math" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"k8s.io/kubernetes/pkg/api" | ||||
| 	"k8s.io/kubernetes/pkg/controller/resourcequota" | ||||
| ) | ||||
|  | ||||
| type resources struct { | ||||
| 	cpuR, cpuL, memR, memL float64 | ||||
| } | ||||
|  | ||||
| func TestResources(tst *testing.T) { | ||||
| 	assert := assert.New(tst) | ||||
|  | ||||
| 	const ( | ||||
| 		defCpu = float64(DefaultDefaultContainerCPULimit) | ||||
| 		defMem = float64(DefaultDefaultContainerMemLimit) | ||||
| 		minCpu = float64(MinimumContainerCPU) | ||||
| 		minMem = float64(MinimumContainerMem) | ||||
| 	) | ||||
| 	undef := math.NaN() | ||||
| 	defined := func(f float64) bool { return !math.IsNaN(f) } | ||||
|  | ||||
| 	for _, t := range []struct { | ||||
| 		input, want resources | ||||
| 	}{ | ||||
| 		{resources{undef, 3.0, undef, 768.0}, resources{defCpu + 3.0, defCpu + 3.0, defMem + 768.0, defMem + 768.0}}, | ||||
| 		{resources{0.0, 3.0, 0.0, 768.0}, resources{defCpu + minCpu, defCpu + 3.0, defMem + minMem, defMem + 768.0}}, | ||||
| 		{resources{undef, undef, undef, undef}, resources{2 * defCpu, 2 * defCpu, 2 * defMem, 2 * defMem}}, | ||||
| 		{resources{0.0, 0.0, 0.0, 0.0}, resources{minCpu + defCpu, minCpu + defCpu, minMem + defMem, minMem + defMem}}, | ||||
| 		{resources{2.0, 3.0, undef, 768.0}, resources{defCpu + 2.0, defCpu + 3.0, defMem + 768.0, defMem + 768.0}}, | ||||
| 		{resources{2.0, 3.0, 256.0, 768.0}, resources{defCpu + 2.0, defCpu + 3.0, defMem + 256.0, defMem + 768.0}}, | ||||
| 	} { | ||||
| 		pod := &api.Pod{ | ||||
| 			Spec: api.PodSpec{ | ||||
| 				Containers: []api.Container{{ | ||||
| 					Name: "a", | ||||
| 				}, { | ||||
| 					Name: "b", | ||||
| 				}}, | ||||
| 			}, | ||||
| 		} | ||||
|  | ||||
| 		if defined(t.input.cpuR) || defined(t.input.memR) { | ||||
| 			pod.Spec.Containers[0].Resources.Requests = api.ResourceList{} | ||||
| 			if defined(t.input.cpuR) { | ||||
| 				pod.Spec.Containers[0].Resources.Requests[api.ResourceCPU] = *CPUShares(t.input.cpuR).Quantity() | ||||
| 			} | ||||
| 			if defined(t.input.memR) { | ||||
| 				pod.Spec.Containers[0].Resources.Requests[api.ResourceMemory] = *MegaBytes(t.input.memR).Quantity() | ||||
| 			} | ||||
| 		} | ||||
| 		if defined(t.input.cpuL) || defined(t.input.memL) { | ||||
| 			pod.Spec.Containers[0].Resources.Limits = api.ResourceList{} | ||||
| 			if defined(t.input.cpuL) { | ||||
| 				pod.Spec.Containers[0].Resources.Limits[api.ResourceCPU] = *CPUShares(t.input.cpuL).Quantity() | ||||
| 			} | ||||
| 			if defined(t.input.memL) { | ||||
| 				pod.Spec.Containers[0].Resources.Limits[api.ResourceMemory] = *MegaBytes(t.input.memL).Quantity() | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		tst.Logf("Testing resource computation for %v => request=%v limit=%v", t, pod.Spec.Containers[0].Resources.Requests, pod.Spec.Containers[0].Resources.Limits) | ||||
| 		tst.Logf("hasRequests: cpu => %v, mem => %v", resourcequota.PodHasRequests(pod, api.ResourceCPU), resourcequota.PodHasRequests(pod, api.ResourceMemory)) | ||||
|  | ||||
| 		beforeCpuR, beforeCpuL, _, err := CPUForPod(pod, DefaultDefaultContainerCPULimit) | ||||
| 		assert.NoError(err, "CPUForPod should not return an error") | ||||
|  | ||||
| 		beforeMemR, beforeMemL, _, err := MemForPod(pod, DefaultDefaultContainerMemLimit) | ||||
| 		assert.NoError(err, "MemForPod should not return an error") | ||||
|  | ||||
| 		cpuR, cpuL, _, err := LimitPodCPU(pod, DefaultDefaultContainerCPULimit) | ||||
| 		assert.NoError(err, "LimitPodCPU should not return an error") | ||||
|  | ||||
| 		memR, memL, _, err := LimitPodMem(pod, DefaultDefaultContainerMemLimit) | ||||
| 		assert.NoError(err, "LimitPodMem should not return an error") | ||||
|  | ||||
| 		tst.Logf("New resources container 0: request=%v limit=%v", pod.Spec.Containers[0].Resources.Requests, pod.Spec.Containers[0].Resources.Limits) | ||||
| 		tst.Logf("New resources container 1: request=%v limit=%v", pod.Spec.Containers[1].Resources.Requests, pod.Spec.Containers[1].Resources.Limits) | ||||
|  | ||||
| 		assert.Equal(t.want.cpuR, float64(beforeCpuR), "cpu request before modifiation is wrong") | ||||
| 		assert.Equal(t.want.cpuL, float64(beforeCpuL), "cpu limit before modifiation is wrong") | ||||
|  | ||||
| 		assert.Equal(t.want.memR, float64(beforeMemR), "mem request before modifiation is wrong") | ||||
| 		assert.Equal(t.want.memL, float64(beforeMemL), "mem limit before modifiation is wrong") | ||||
|  | ||||
| 		assert.Equal(t.want.cpuR, float64(cpuR), "cpu request is wrong") | ||||
| 		assert.Equal(t.want.cpuL, float64(cpuL), "cpu limit is wrong") | ||||
|  | ||||
| 		assert.Equal(t.want.memR, float64(memR), "mem request is wrong") | ||||
| 		assert.Equal(t.want.memL, float64(memL), "mem limit is wrong") | ||||
| 	} | ||||
| } | ||||
| @@ -19,6 +19,8 @@ package resource | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strconv" | ||||
|  | ||||
| 	"k8s.io/kubernetes/pkg/api/resource" | ||||
| ) | ||||
|  | ||||
| type MegaBytes float64 | ||||
| @@ -30,11 +32,11 @@ func (f *CPUShares) Set(s string) error { | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| func (f *CPUShares) Type() string { | ||||
| func (f CPUShares) Type() string { | ||||
| 	return "float64" | ||||
| } | ||||
|  | ||||
| func (f *CPUShares) String() string { return fmt.Sprintf("%v", *f) } | ||||
| func (f CPUShares) String() string { return fmt.Sprintf("%v", float64(f)) } | ||||
|  | ||||
| func (f *MegaBytes) Set(s string) error { | ||||
| 	v, err := strconv.ParseFloat(s, 64) | ||||
| @@ -42,8 +44,24 @@ func (f *MegaBytes) Set(s string) error { | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| func (f *MegaBytes) Type() string { | ||||
| func (f MegaBytes) Type() string { | ||||
| 	return "float64" | ||||
| } | ||||
|  | ||||
| func (f *MegaBytes) String() string { return fmt.Sprintf("%v", *f) } | ||||
| func (f MegaBytes) String() string { return fmt.Sprintf("%v", float64(f)) } | ||||
|  | ||||
| func (f MegaBytes) Quantity() *resource.Quantity { | ||||
| 	return resource.NewQuantity(int64(float64(f)*1024.0*1024.0), resource.BinarySI) | ||||
| } | ||||
|  | ||||
| func (f CPUShares) Quantity() *resource.Quantity { | ||||
| 	return resource.NewMilliQuantity(int64(float64(f)*1000.0), resource.DecimalSI) | ||||
| } | ||||
|  | ||||
| func NewCPUShares(q resource.Quantity) CPUShares { | ||||
| 	return CPUShares(float64(q.MilliValue()) / 1000.0) | ||||
| } | ||||
|  | ||||
| func NewMegaBytes(q resource.Quantity) MegaBytes { | ||||
| 	return MegaBytes(float64(q.Value()) / 1024.0 / 1024.0) | ||||
| } | ||||
|   | ||||
| @@ -453,18 +453,15 @@ func (s *SchedulerServer) prepareExecutorInfo(hks hyperkube.Interface) (*mesos.E | ||||
| 				return nil, nil, fmt.Errorf("error parsing static pod spec at %v: %v", podPath, err) | ||||
| 			} | ||||
|  | ||||
| 			// TODO(sttts): allow unlimited static pods as well and patch in the default resource limits | ||||
| 			unlimitedCPU := mresource.LimitPodCPU(&pod, s.defaultContainerCPULimit) | ||||
| 			unlimitedMem := mresource.LimitPodMem(&pod, s.defaultContainerMemLimit) | ||||
| 			if unlimitedCPU { | ||||
| 				return nil, nil, fmt.Errorf("found static pod without limit on cpu resources: %v", podPath) | ||||
| 			_, cpu, _, err := mresource.LimitPodCPU(&pod, s.defaultContainerCPULimit) | ||||
| 			if err != nil { | ||||
| 				return nil, nil, fmt.Errorf("cannot derive cpu limit for static pod: %v", podPath) | ||||
| 			} | ||||
| 			if unlimitedMem { | ||||
| 				return nil, nil, fmt.Errorf("found static pod without limit on memory resources: %v", podPath) | ||||
| 			_, mem, _, err := mresource.LimitPodMem(&pod, s.defaultContainerMemLimit) | ||||
| 			if err != nil { | ||||
| 				return nil, nil, fmt.Errorf("cannot derive memory limit for static pod: %v", podPath) | ||||
| 			} | ||||
|  | ||||
| 			cpu := mresource.PodCPULimit(&pod) | ||||
| 			mem := mresource.PodMemLimit(&pod) | ||||
| 			log.V(2).Infof("reserving %.2f cpu shares and %.2f MB of memory to static pod %s", cpu, mem, pod.Name) | ||||
|  | ||||
| 			staticPodCPUs += float64(cpu) | ||||
|   | ||||
| @@ -281,23 +281,3 @@ func PodHasRequests(pod *api.Pod, resourceName api.ResourceName) bool { | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| // PodCPU computes total cpu limit across all containers in pod | ||||
| // TODO: Remove this once the mesos scheduler becomes request aware | ||||
| func PodCPU(pod *api.Pod) *resource.Quantity { | ||||
| 	val := int64(0) | ||||
| 	for j := range pod.Spec.Containers { | ||||
| 		val = val + pod.Spec.Containers[j].Resources.Limits.Cpu().MilliValue() | ||||
| 	} | ||||
| 	return resource.NewMilliQuantity(int64(val), resource.DecimalSI) | ||||
| } | ||||
|  | ||||
| // PodMemory computes total memory limit across all containers in a pod | ||||
| // TODO: Remove this once the mesos scheduler becomes request aware | ||||
| func PodMemory(pod *api.Pod) *resource.Quantity { | ||||
| 	val := int64(0) | ||||
| 	for j := range pod.Spec.Containers { | ||||
| 		val = val + pod.Spec.Containers[j].Resources.Limits.Memory().Value() | ||||
| 	} | ||||
| 	return resource.NewQuantity(int64(val), resource.DecimalSI) | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Dr. Stefan Schimanski
					Dr. Stefan Schimanski