Commit 46504c20 authored by k8s-merge-robot's avatar k8s-merge-robot

Merge pull request #14943 from vishh/qos

Automatic merge from submit-queue Updaing QoS policy to be at the pod level Quality of Service will be derived from an entire Pod Spec, instead of being derived from resource specifications of individual resources per-container. A Pod is `Guaranteed` iff all its containers have limits == requests for all the first-class resources (cpu, memory as of now). A Pod is `BestEffort` iff requests & limits are not specified for any resource across all containers. A Pod is `Burstable` otherwise. Note: Existing pods might be more susceptible to OOM Kills on the node due to this PR! To protect pods from being OOM killed on the node, set `limits` for all resources across all containers in a pod. <!-- Reviewable:start --> --- This change is [<img src="http://reviewable.k8s.io/review_button.svg" height="35" align="absmiddle" alt="Reviewable"/>](http://reviewable.k8s.io/reviews/kubernetes/kubernetes/14943) <!-- Reviewable:end -->
parents 45514f72 a64fe657
......@@ -42,7 +42,7 @@ The purpose of filtering the nodes is to filter out the nodes that do not meet c
- `NoDiskConflict`: Evaluate if a pod can fit due to the volumes it requests, and those that are already mounted.
- `NoVolumeZoneConflict`: Evaluate if the volumes a pod requests are available on the node, given the Zone restrictions.
- `PodFitsResources`: Check if the free resource (CPU and Memory) meets the requirement of the Pod. The free resource is measured by the capacity minus the sum of requests of all Pods on the node. To learn more about the resource QoS in Kubernetes, please check [QoS proposal](../proposals/resource-qos.md).
- `PodFitsResources`: Check if the free resource (CPU and Memory) meets the requirement of the Pod. The free resource is measured by the capacity minus the sum of requests of all Pods on the node. To learn more about the resource QoS in Kubernetes, please check [QoS proposal](../design/resource-qos.md).
- `PodFitsHostPorts`: Check if any HostPort required by the Pod is already occupied on the node.
- `HostName`: Filter out all nodes except the one specified in the PodSpec's NodeName field.
- `MatchNodeSelector`: Check if the labels of the node match the labels specified in the Pod's `nodeSelector` field and, as of Kubernetes v1.2, also match the `scheduler.alpha.kubernetes.io/affinity` pod annotation if present. See [here](../user-guide/node-selection/) for more details on both.
......
......@@ -39,11 +39,10 @@ and set them before the container is run. This document describes design of the
## Motivation
Since we want to make Kubernetes as simple as possible for its users we don’t want to require setting
[Resources](resource-qos.md#resource-specifications)
for container by its owner. On the other hand having Resources filled is critical for scheduling decisions.
Current solution to set up Resources to hardcoded value has obvious drawbacks. We need to implement a component
which will set initial Resources to a reasonable value.
Since we want to make Kubernetes as simple as possible for its users we don’t want to require setting [Resources](../design/resource-qos.md) for container by its owner.
On the other hand having Resources filled is critical for scheduling decisions.
Current solution to set up Resources to hardcoded value has obvious drawbacks.
We need to implement a component which will set initial Resources to a reasonable value.
## Design
......@@ -51,11 +50,9 @@ InitialResources component will be implemented as an [admission plugin](../../pl
[LimitRanger](https://github.com/kubernetes/kubernetes/blob/7c9bbef96ed7f2a192a1318aa312919b861aee00/cluster/gce/config-default.sh#L91).
For every container without Resources specified it will try to predict amount of resources that should be sufficient for it.
So that a pod without specified resources will be treated as
[Burstable](resource-qos.md#qos-classes).
.
InitialResources will set only [request](resource-qos.md#resource-specifications)
(independently for each resource type: cpu, memory)
field in the first version to avoid killing containers due to OOM (however the container still may be killed if exceeds requested resources).
InitialResources will set only [request](../design/resource-qos.md#requests-and-limits) (independently for each resource type: cpu, memory) field in the first version to avoid killing containers due to OOM (however the container still may be killed if exceeds requested resources).
To make the component work with LimitRanger the estimated value will be capped by min and max possible values if defined.
It will prevent from situation when the pod is rejected due to too low or too high estimation.
......
......@@ -72,9 +72,9 @@ hack/test-update-storage-objects.sh: local storage_versions=${1:-""}
hack/test-update-storage-objects.sh: source_file=${test_data[0]}
hack/test-update-storage-objects.sh:# source_file,resource,namespace,name,old_version,new_version
pkg/kubelet/network/hairpin/hairpin.go: hairpinModeRelativePath = "hairpin_mode"
pkg/kubelet/qos/memory_policy_test.go: t.Errorf("oom_score_adj should be between %d and %d, but was %d", test.lowOOMScoreAdj, test.highOOMScoreAdj, oomScoreAdj)
pkg/kubelet/qos/memory_policy_test.go: highOOMScoreAdj int // The min oom_score_adj score the container should be assigned.
pkg/kubelet/qos/memory_policy_test.go: lowOOMScoreAdj int // The max oom_score_adj score the container should be assigned.
pkg/kubelet/qos/policy_test.go: t.Errorf("oom_score_adj should be between %d and %d, but was %d", test.lowOOMScoreAdj, test.highOOMScoreAdj, oomScoreAdj)
pkg/kubelet/qos/policy_test.go: highOOMScoreAdj int // The min oom_score_adj score the container should be assigned.
pkg/kubelet/qos/policy_test.go: lowOOMScoreAdj int // The max oom_score_adj score the container should be assigned.
pkg/util/oom/oom_linux.go: return fmt.Errorf("invalid PID %d specified for oom_score_adj", pid)
pkg/util/oom/oom_linux.go: oomScoreAdjPath := path.Join("/proc", pidStr, "oom_score_adj")
pkg/util/oom/oom_linux.go:// Writes 'value' to /proc/<pid>/oom_score_adj for all processes in cgroup cgroupName.
......
......@@ -1412,7 +1412,7 @@ func containerAndPodFromLabels(inspect *dockertypes.ContainerJSON) (pod *api.Pod
return
}
func (dm *DockerManager) applyOOMScoreAdj(container *api.Container, containerInfo *dockertypes.ContainerJSON) error {
func (dm *DockerManager) applyOOMScoreAdj(pod *api.Pod, container *api.Container, containerInfo *dockertypes.ContainerJSON) error {
if containerInfo.State.Pid == 0 {
// Container exited. We cannot do anything about it. Ignore this error.
glog.V(2).Infof("Failed to apply OOM score adj on container %q with ID %q. Init process does not exist.", containerInfo.Name, containerInfo.ID)
......@@ -1428,7 +1428,7 @@ func (dm *DockerManager) applyOOMScoreAdj(container *api.Container, containerInf
}
return err
}
oomScoreAdj := dm.calculateOomScoreAdj(container)
oomScoreAdj := dm.calculateOomScoreAdj(pod, container)
if err = dm.oomAdjuster.ApplyOOMScoreAdjContainer(cgroupName, oomScoreAdj, 5); err != nil {
if err == os.ErrNotExist {
// Container exited. We cannot do anything about it. Ignore this error.
......@@ -1464,7 +1464,7 @@ func (dm *DockerManager) runContainerInPod(pod *api.Pod, container *api.Containe
utsMode = namespaceModeHost
}
oomScoreAdj := dm.calculateOomScoreAdj(container)
oomScoreAdj := dm.calculateOomScoreAdj(pod, container)
id, err := dm.runContainer(pod, container, opts, ref, netMode, ipcMode, utsMode, pidMode, restartCount, oomScoreAdj)
if err != nil {
......@@ -1503,7 +1503,7 @@ func (dm *DockerManager) runContainerInPod(pod *api.Pod, container *api.Containe
// Check if current docker version is higher than 1.10. Otherwise, we have to apply OOMScoreAdj instead of using docker API.
// TODO: Remove this logic after we stop supporting docker version < 1.10.
if err := dm.applyOOMScoreAdjIfNeeded(container, containerInfo); err != nil {
if err = dm.applyOOMScoreAdjIfNeeded(pod, container, containerInfo); err != nil {
return kubecontainer.ContainerID{}, err
}
......@@ -1521,7 +1521,7 @@ func (dm *DockerManager) runContainerInPod(pod *api.Pod, container *api.Containe
return id, err
}
func (dm *DockerManager) applyOOMScoreAdjIfNeeded(container *api.Container, containerInfo *dockertypes.ContainerJSON) error {
func (dm *DockerManager) applyOOMScoreAdjIfNeeded(pod *api.Pod, container *api.Container, containerInfo *dockertypes.ContainerJSON) error {
// Compare current API version with expected api version.
result, err := dm.checkDockerAPIVersion(dockerv110APIVersion)
if err != nil {
......@@ -1529,7 +1529,7 @@ func (dm *DockerManager) applyOOMScoreAdjIfNeeded(container *api.Container, cont
}
// If current api version is older than OOMScoreAdj requested, use the old way.
if result < 0 {
if err := dm.applyOOMScoreAdj(container, containerInfo); err != nil {
if err := dm.applyOOMScoreAdj(pod, container, containerInfo); err != nil {
return fmt.Errorf("Failed to apply oom-score-adj to container %q- %v", err, containerInfo.Name)
}
}
......@@ -1537,7 +1537,7 @@ func (dm *DockerManager) applyOOMScoreAdjIfNeeded(container *api.Container, cont
return nil
}
func (dm *DockerManager) calculateOomScoreAdj(container *api.Container) int {
func (dm *DockerManager) calculateOomScoreAdj(pod *api.Pod, container *api.Container) int {
// Set OOM score of the container based on the priority of the container.
// Processes in lower-priority pods should be killed first if the system runs out of memory.
// The main pod infrastructure container is considered high priority, since if it is killed the
......@@ -1546,7 +1546,7 @@ func (dm *DockerManager) calculateOomScoreAdj(container *api.Container) int {
if container.Name == PodInfraContainerName {
oomScoreAdj = qos.PodInfraOOMAdj
} else {
oomScoreAdj = qos.GetContainerOOMScoreAdjust(container, int64(dm.machineInfo.MemoryCapacity))
oomScoreAdj = qos.GetContainerOOMScoreAdjust(pod, container, int64(dm.machineInfo.MemoryCapacity))
}
......
......@@ -18,58 +18,50 @@ package qos
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/kubelet/qos/util"
)
const (
PodInfraOOMAdj int = -999
KubeletOOMScoreAdj int = -999
KubeProxyOOMScoreAdj int = -999
PodInfraOOMAdj int = -999
KubeletOOMScoreAdj int = -999
KubeProxyOOMScoreAdj int = -999
guaranteedOOMScoreAdj int = -998
besteffortOOMScoreAdj int = 1000
)
// isMemoryBestEffort returns true if the container's memory requirements are best-effort.
func isMemoryBestEffort(container *api.Container) bool {
// A container is memory best-effort if its memory request is unspecified or 0.
// If a request is specified, then the user expects some kind of resource guarantee.
return container.Resources.Requests.Memory().Value() == 0
}
// isMemoryGuaranteed returns true if the container's memory requirements are Guaranteed.
func isMemoryGuaranteed(container *api.Container) bool {
// A container is memory guaranteed if its memory request == memory limit.
// If memory request == memory limit, the user is very confident of resource consumption.
memoryRequest := container.Resources.Requests.Memory()
memoryLimit := container.Resources.Limits.Memory()
return (*memoryRequest).Cmp(*memoryLimit) == 0 && memoryRequest.Value() != 0
}
// GetContainerOOMAdjust returns the amount by which the OOM score of all processes in the
// container should be adjusted. The OOM score of a process is the percentage of memory it consumes
// container should be adjusted.
// The OOM score of a process is the percentage of memory it consumes
// multiplied by 10 (barring exceptional cases) + a configurable quantity which is between -1000
// and 1000. Containers with higher OOM scores are killed if the system runs out of memory.
// See https://lwn.net/Articles/391222/ for more information.
func GetContainerOOMScoreAdjust(container *api.Container, memoryCapacity int64) int {
if isMemoryGuaranteed(container) {
// Memory guaranteed containers should be the last to get killed.
return -999
} else if isMemoryBestEffort(container) {
// Memory best-effort containers should be the first to be killed.
return 1000
} else {
// Burstable containers are a middle tier, between Guaranteed and Best-Effort. Ideally,
// we want to protect Burstable containers that consume less memory than requested.
// The formula below is a heuristic. A container requesting for 10% of a system's
// memory will have an oom score adjust of 900. If a process in container Y
// uses over 10% of memory, its OOM score will be 1000. The idea is that containers
// which use more than their request will have an OOM score of 1000 and will be prime
// targets for OOM kills.
// Note that this is a heuristic, it won't work if a container has many small processes.
memoryRequest := container.Resources.Requests.Memory().Value()
oomScoreAdjust := 1000 - (1000*memoryRequest)/memoryCapacity
// A memory guaranteed container using 100% of memory can have an OOM score of 1. Ensure
// that memory burstable containers have a higher OOM score.
if oomScoreAdjust < 2 {
return 2
}
return int(oomScoreAdjust)
func GetContainerOOMScoreAdjust(pod *api.Pod, container *api.Container, memoryCapacity int64) int {
switch util.GetPodQos(pod) {
case util.Guaranteed:
// Guaranteed containers should be the last to get killed.
return guaranteedOOMScoreAdj
case util.BestEffort:
return besteffortOOMScoreAdj
}
// Burstable containers are a middle tier, between Guaranteed and Best-Effort. Ideally,
// we want to protect Burstable containers that consume less memory than requested.
// The formula below is a heuristic. A container requesting for 10% of a system's
// memory will have an OOM score adjust of 900. If a process in container Y
// uses over 10% of memory, its OOM score will be 1000. The idea is that containers
// which use more than their request will have an OOM score of 1000 and will be prime
// targets for OOM kills.
// Note that this is a heuristic, it won't work if a container has many small processes.
memoryRequest := container.Resources.Requests.Memory().Value()
oomScoreAdjust := 1000 - (1000*memoryRequest)/memoryCapacity
// A guaranteed pod using 100% of memory can have an OOM score of 1. Ensure
// that burstable pods have a higher OOM score adjustment.
if oomScoreAdjust < 2 {
return 2
}
// Give burstable pods a higher chance of survival over besteffort pods.
if int(oomScoreAdjust) == besteffortOOMScoreAdj {
return int(oomScoreAdjust - 1)
}
return int(oomScoreAdjust)
}
......@@ -29,157 +29,168 @@ const (
)
var (
zeroRequestMemoryBestEffort = api.Container{
Resources: api.ResourceRequirements{
Requests: api.ResourceList{
api.ResourceName(api.ResourceCPU): resource.MustParse("5m"),
api.ResourceName(api.ResourceMemory): resource.MustParse("0G"),
},
Limits: api.ResourceList{
api.ResourceName(api.ResourceCPU): resource.MustParse("5m"),
api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
cpuLimit = api.Pod{
Spec: api.PodSpec{
Containers: []api.Container{
{
Resources: api.ResourceRequirements{
Limits: api.ResourceList{
api.ResourceName(api.ResourceCPU): resource.MustParse("10"),
},
},
},
},
},
}
edgeMemoryBestEffort = api.Container{
Resources: api.ResourceRequirements{
Requests: api.ResourceList{
api.ResourceName(api.ResourceMemory): resource.MustParse("0G"),
},
Limits: api.ResourceList{
api.ResourceName(api.ResourceMemory): resource.MustParse("0G"),
memoryLimitCPURequest = api.Pod{
Spec: api.PodSpec{
Containers: []api.Container{
{
Resources: api.ResourceRequirements{
Requests: api.ResourceList{
api.ResourceName(api.ResourceCPU): resource.MustParse("0"),
},
Limits: api.ResourceList{
api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
},
},
},
},
},
}
noRequestMemoryBestEffort = api.Container{
Resources: api.ResourceRequirements{
Limits: api.ResourceList{
api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
zeroMemoryLimit = api.Pod{
Spec: api.PodSpec{
Containers: []api.Container{
{
Resources: api.ResourceRequirements{
Limits: api.ResourceList{
api.ResourceName(api.ResourceMemory): resource.MustParse("0"),
},
},
},
},
},
}
noLimitMemoryBestEffort = api.Container{}
memoryGuaranteed = api.Container{
Resources: api.ResourceRequirements{
Requests: api.ResourceList{
api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
},
Limits: api.ResourceList{
api.ResourceName(api.ResourceCPU): resource.MustParse("5m"),
api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
noRequestLimit = api.Pod{
Spec: api.PodSpec{
Containers: []api.Container{
{
Resources: api.ResourceRequirements{},
},
},
},
}
memoryBurstable = api.Container{
Resources: api.ResourceRequirements{
Requests: api.ResourceList{
api.ResourceName(api.ResourceMemory): resource.MustParse(strconv.Itoa(standardMemoryAmount / 2)),
},
Limits: api.ResourceList{
api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
equalRequestLimitCPUMemory = api.Pod{
Spec: api.PodSpec{
Containers: []api.Container{
{
Resources: api.ResourceRequirements{
Requests: api.ResourceList{
api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
api.ResourceName(api.ResourceCPU): resource.MustParse("5m"),
},
Limits: api.ResourceList{
api.ResourceName(api.ResourceCPU): resource.MustParse("5m"),
api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
},
},
},
},
},
}
memoryBurstableNoLimit = api.Container{
Resources: api.ResourceRequirements{
Requests: api.ResourceList{
api.ResourceName(api.ResourceMemory): resource.MustParse(strconv.Itoa(standardMemoryAmount - 1)),
cpuUnlimitedMemoryLimitedWithRequests = api.Pod{
Spec: api.PodSpec{
Containers: []api.Container{
{
Resources: api.ResourceRequirements{
Requests: api.ResourceList{
api.ResourceName(api.ResourceMemory): resource.MustParse(strconv.Itoa(standardMemoryAmount / 2)),
api.ResourceName(api.ResourceCPU): resource.MustParse("5m"),
},
Limits: api.ResourceList{
api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
},
},
},
},
},
}
)
func TestIsMemoryBestEffort(t *testing.T) {
validCases := []api.Container{zeroRequestMemoryBestEffort, noRequestMemoryBestEffort, noLimitMemoryBestEffort, edgeMemoryBestEffort}
for _, container := range validCases {
if !isMemoryBestEffort(&container) {
t.Errorf("container %+v is memory best-effort", container)
}
}
invalidCases := []api.Container{memoryGuaranteed, memoryBurstable}
for _, container := range invalidCases {
if isMemoryBestEffort(&container) {
t.Errorf("container %+v is not memory best-effort", container)
}
}
}
func TestIsMemoryGuaranteed(t *testing.T) {
validCases := []api.Container{memoryGuaranteed}
for _, container := range validCases {
if !isMemoryGuaranteed(&container) {
t.Errorf("container %+v is memory guaranteed", container)
}
}
invalidCases := []api.Container{zeroRequestMemoryBestEffort, noRequestMemoryBestEffort, noLimitMemoryBestEffort, edgeMemoryBestEffort, memoryBurstable}
for _, container := range invalidCases {
if isMemoryGuaranteed(&container) {
t.Errorf("container %+v is not memory guaranteed", container)
}
requestNoLimit = api.Pod{
Spec: api.PodSpec{
Containers: []api.Container{
{
Resources: api.ResourceRequirements{
Requests: api.ResourceList{
api.ResourceName(api.ResourceMemory): resource.MustParse(strconv.Itoa(standardMemoryAmount - 1)),
api.ResourceName(api.ResourceCPU): resource.MustParse("5m"),
},
},
},
},
},
}
}
)
type oomTest struct {
container *api.Container
pod *api.Pod
memoryCapacity int64
lowOOMScoreAdj int // The max oom_score_adj score the container should be assigned.
highOOMScoreAdj int // The min oom_score_adj score the container should be assigned.
}
func TestGetContainerOOMScoreAdjust(t *testing.T) {
oomTests := []oomTest{
{
container: &zeroRequestMemoryBestEffort,
pod: &cpuLimit,
memoryCapacity: 4000000000,
lowOOMScoreAdj: 1000,
highOOMScoreAdj: 1000,
lowOOMScoreAdj: 999,
highOOMScoreAdj: 999,
},
{
container: &edgeMemoryBestEffort,
pod: &memoryLimitCPURequest,
memoryCapacity: 8000000000,
lowOOMScoreAdj: 1000,
highOOMScoreAdj: 1000,
lowOOMScoreAdj: 999,
highOOMScoreAdj: 999,
},
{
container: &noRequestMemoryBestEffort,
pod: &zeroMemoryLimit,
memoryCapacity: 7230457451,
lowOOMScoreAdj: 1000,
highOOMScoreAdj: 1000,
},
{
container: &noLimitMemoryBestEffort,
pod: &noRequestLimit,
memoryCapacity: 4000000000,
lowOOMScoreAdj: 1000,
highOOMScoreAdj: 1000,
},
{
container: &memoryGuaranteed,
pod: &equalRequestLimitCPUMemory,
memoryCapacity: 123456789,
lowOOMScoreAdj: -999,
highOOMScoreAdj: -999,
lowOOMScoreAdj: -998,
highOOMScoreAdj: -998,
},
{
container: &memoryBurstable,
pod: &cpuUnlimitedMemoryLimitedWithRequests,
memoryCapacity: standardMemoryAmount,
lowOOMScoreAdj: 495,
highOOMScoreAdj: 505,
},
{
container: &memoryBurstableNoLimit,
pod: &requestNoLimit,
memoryCapacity: standardMemoryAmount,
lowOOMScoreAdj: 2,
highOOMScoreAdj: 2,
},
}
for _, test := range oomTests {
oomScoreAdj := GetContainerOOMScoreAdjust(test.container, test.memoryCapacity)
oomScoreAdj := GetContainerOOMScoreAdjust(test.pod, &test.pod.Spec.Containers[0], test.memoryCapacity)
if oomScoreAdj < test.lowOOMScoreAdj || oomScoreAdj > test.highOOMScoreAdj {
t.Errorf("oom_score_adj should be between %d and %d, but was %d", test.lowOOMScoreAdj, test.highOOMScoreAdj, oomScoreAdj)
}
......
......@@ -18,7 +18,7 @@ package util
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/util/sets"
"k8s.io/kubernetes/pkg/api/resource"
)
const (
......@@ -48,22 +48,61 @@ func isResourceBestEffort(container *api.Container, resource api.ResourceName) b
}
// GetPodQos returns the QoS class of a pod.
// The QoS class of a pod is the lowest QoS class for each resource in each container.
// A pod is besteffort if none of its containers have specified any requests or limits.
// A pod is guaranteed only when requests and limits are specified for all the containers and they are equal.
// A pod is burstable if limits and requests do not match across all containers.
func GetPodQos(pod *api.Pod) string {
qosValues := sets.NewString()
requests := api.ResourceList{}
limits := api.ResourceList{}
zeroQuantity := resource.MustParse("0")
isGuaranteed := true
for _, container := range pod.Spec.Containers {
qosPerResource := GetQoS(&container)
for _, qosValue := range qosPerResource {
qosValues.Insert(qosValue)
// process requests
for name, quantity := range container.Resources.Requests {
if quantity.Cmp(zeroQuantity) == 1 {
delta := quantity.Copy()
if _, exists := requests[name]; !exists {
requests[name] = *delta
} else {
delta.Add(requests[name])
requests[name] = *delta
}
}
}
// process limits
for name, quantity := range container.Resources.Limits {
if quantity.Cmp(zeroQuantity) == 1 {
delta := quantity.Copy()
if _, exists := limits[name]; !exists {
limits[name] = *delta
} else {
delta.Add(limits[name])
limits[name] = *delta
}
}
}
if len(container.Resources.Limits) != len(supportedComputeResources) {
isGuaranteed = false
}
}
if qosValues.Has(BestEffort) {
if len(requests) == 0 && len(limits) == 0 {
return BestEffort
}
if qosValues.Has(Burstable) {
return Burstable
// Check is requests match limits for all resources.
if isGuaranteed {
for name, req := range requests {
if lim, exists := limits[name]; !exists || lim.Cmp(req) != 0 {
isGuaranteed = false
break
}
}
}
if isGuaranteed &&
len(requests) == len(limits) &&
len(limits) == len(supportedComputeResources) {
return Guaranteed
}
return Guaranteed
return Burstable
}
// GetQos returns a mapping of resource name to QoS class of a container
......
......@@ -65,35 +65,63 @@ func TestGetPodQos(t *testing.T) {
expected string
}{
{
pod: newPod("guaranteed", []api.Container{
newContainer("guaranteed", getResourceList("100m", "100Mi"), getResourceList("100m", "100Mi")),
}),
expected: Guaranteed,
},
{
pod: newPod("guaranteed-guaranteed", []api.Container{
newContainer("guaranteed", getResourceList("100m", "100Mi"), getResourceList("100m", "100Mi")),
newContainer("guaranteed", getResourceList("100m", "100Mi"), getResourceList("100m", "100Mi")),
}),
expected: Guaranteed,
},
{
pod: newPod("best-effort-best-effort", []api.Container{
newContainer("best-effort", getResourceList("", ""), getResourceList("", "")),
newContainer("best-effort", getResourceList("", ""), getResourceList("", "")),
}),
expected: BestEffort,
},
{
pod: newPod("best-effort", []api.Container{
newContainer("best-effort", getResourceList("", ""), getResourceList("", "")),
}),
expected: BestEffort,
},
{
pod: newPod("best-effort-burstable", []api.Container{
newContainer("best-effort", getResourceList("", ""), getResourceList("", "")),
newContainer("burstable", getResourceList("1", ""), getResourceList("2", "")),
}),
expected: Burstable,
},
{
pod: newPod("best-effort-guaranteed", []api.Container{
newContainer("best-effort", getResourceList("", ""), getResourceList("", "")),
newContainer("guaranteed", getResourceList("10m", "100Mi"), getResourceList("10m", "100Mi")),
}),
expected: BestEffort,
expected: Burstable,
},
{
pod: newPod("best-effort-cpu-guaranteed-memory", []api.Container{
newContainer("best-effort", getResourceList("", "100Mi"), getResourceList("", "100Mi")),
pod: newPod("burstable-cpu-guaranteed-memory", []api.Container{
newContainer("burstable", getResourceList("", "100Mi"), getResourceList("", "100Mi")),
}),
expected: BestEffort,
expected: Burstable,
},
{
pod: newPod("burstable", []api.Container{
newContainer("burstable", getResourceList("10m", "100Mi"), getResourceList("100m", "200Mi")),
pod: newPod("burstable-guaranteed", []api.Container{
newContainer("burstable", getResourceList("1", "100Mi"), getResourceList("2", "100Mi")),
newContainer("guaranteed", getResourceList("100m", "100Mi"), getResourceList("100m", "100Mi")),
}),
expected: Burstable,
},
{
pod: newPod("guaranteed", []api.Container{
newContainer("guaranteed", getResourceList("100m", "100Mi"), getResourceList("100m", "100Mi")),
pod: newPod("burstable", []api.Container{
newContainer("burstable", getResourceList("10m", "100Mi"), getResourceList("100m", "200Mi")),
}),
expected: Guaranteed,
expected: Burstable,
},
}
for _, testCase := range testCases {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment