From 9837a98564b06ee1f8662b83960051463e8e3c36 Mon Sep 17 00:00:00 2001 From: Chao Xu Date: Mon, 10 Apr 2017 15:16:45 -0700 Subject: [PATCH] move resource_helpers.go to a subpackage --- pkg/api/BUILD | 11 +- pkg/api/pod/BUILD | 5 +- pkg/api/pod/util.go | 66 ++++- pkg/api/resource.go | 55 ++++ pkg/api/resource/BUILD | 19 +- pkg/api/resource/doc.go | 21 -- pkg/api/resource/resource_helpers.go | 112 ++++++++ .../{ => resource}/resource_helpers_test.go | 64 +---- pkg/api/resource_helpers.go | 270 ------------------ pkg/client/unversioned/BUILD | 1 + pkg/client/unversioned/conditions.go | 3 +- pkg/printers/internalversion/BUILD | 1 + pkg/printers/internalversion/describe.go | 7 +- pkg/registry/core/pod/storage/BUILD | 1 + pkg/registry/core/pod/storage/storage.go | 3 +- .../src/k8s.io/client-go/pkg/api/resource.go | 55 ++++ .../client-go/pkg/api/resource_helpers.go | 270 ------------------ vendor/BUILD | 2 +- 18 files changed, 337 insertions(+), 629 deletions(-) create mode 100644 pkg/api/resource.go delete mode 100644 pkg/api/resource/doc.go create mode 100644 pkg/api/resource/resource_helpers.go rename pkg/api/{ => resource}/resource_helpers_test.go (62%) delete mode 100644 pkg/api/resource_helpers.go create mode 100644 staging/src/k8s.io/client-go/pkg/api/resource.go delete mode 100644 staging/src/k8s.io/client-go/pkg/api/resource_helpers.go diff --git a/pkg/api/BUILD b/pkg/api/BUILD index 9ba133e16e..048fc801f0 100644 --- a/pkg/api/BUILD +++ b/pkg/api/BUILD @@ -18,7 +18,7 @@ go_library( "json.go", "objectreference.go", "register.go", - "resource_helpers.go", + "resource.go", "taint.go", "toleration.go", "types.go", @@ -43,16 +43,9 @@ go_library( go_test( name = "go_default_test", - srcs = [ - "resource_helpers_test.go", - "taint_test.go", - ], + srcs = ["taint_test.go"], library = ":go_default_library", tags = ["automanaged"], - deps = [ - "//vendor:k8s.io/apimachinery/pkg/api/resource", - "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", - ], ) go_test( diff --git a/pkg/api/pod/BUILD b/pkg/api/pod/BUILD index 01caf79b27..c0c3201022 100644 --- a/pkg/api/pod/BUILD +++ b/pkg/api/pod/BUILD @@ -12,7 +12,10 @@ go_library( name = "go_default_library", srcs = ["util.go"], tags = ["automanaged"], - deps = ["//pkg/api:go_default_library"], + deps = [ + "//pkg/api:go_default_library", + "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", + ], ) filegroup( diff --git a/pkg/api/pod/util.go b/pkg/api/pod/util.go index 33757290ec..ec91c03276 100644 --- a/pkg/api/pod/util.go +++ b/pkg/api/pod/util.go @@ -16,7 +16,10 @@ limitations under the License. package pod -import "k8s.io/kubernetes/pkg/api" +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/kubernetes/pkg/api" +) // VisitPodSecretNames invokes the visitor function with the name of every secret // referenced by the pod spec. If visitor returns false, visiting is short-circuited. @@ -100,3 +103,64 @@ func visitContainerSecretNames(container *api.Container, visitor func(string) bo } return true } + +// IsPodReady returns true if a pod is ready; false otherwise. +func IsPodReady(pod *api.Pod) bool { + return IsPodReadyConditionTrue(pod.Status) +} + +// IsPodReadyConditionTrue retruns true if a pod is ready; false otherwise. +func IsPodReadyConditionTrue(status api.PodStatus) bool { + condition := GetPodReadyCondition(status) + return condition != nil && condition.Status == api.ConditionTrue +} + +// GetPodReadyCondition extracts the pod ready condition from the given status and returns that. +// Returns nil if the condition is not present. +func GetPodReadyCondition(status api.PodStatus) *api.PodCondition { + _, condition := GetPodCondition(&status, api.PodReady) + return condition +} + +// GetPodCondition extracts the provided condition from the given status and returns that. +// Returns nil and -1 if the condition is not present, and the index of the located condition. +func GetPodCondition(status *api.PodStatus, conditionType api.PodConditionType) (int, *api.PodCondition) { + if status == nil { + return -1, nil + } + for i := range status.Conditions { + if status.Conditions[i].Type == conditionType { + return i, &status.Conditions[i] + } + } + return -1, nil +} + +// UpdatePodCondition updates existing pod condition or creates a new one. Sets LastTransitionTime to now if the +// status has changed. +// Returns true if pod condition has changed or has been added. +func UpdatePodCondition(status *api.PodStatus, condition *api.PodCondition) bool { + condition.LastTransitionTime = metav1.Now() + // Try to find this pod condition. + conditionIndex, oldCondition := GetPodCondition(status, condition.Type) + + if oldCondition == nil { + // We are adding new pod condition. + status.Conditions = append(status.Conditions, *condition) + return true + } + // We are updating an existing condition, so we need to check if it has changed. + if condition.Status == oldCondition.Status { + condition.LastTransitionTime = oldCondition.LastTransitionTime + } + + isEqual := condition.Status == oldCondition.Status && + condition.Reason == oldCondition.Reason && + condition.Message == oldCondition.Message && + condition.LastProbeTime.Equal(oldCondition.LastProbeTime) && + condition.LastTransitionTime.Equal(oldCondition.LastTransitionTime) + + status.Conditions[conditionIndex] = *condition + // Return true if one of the fields have changed. + return !isEqual +} diff --git a/pkg/api/resource.go b/pkg/api/resource.go new file mode 100644 index 0000000000..1ea96d16e3 --- /dev/null +++ b/pkg/api/resource.go @@ -0,0 +1,55 @@ +/* +Copyright 2017 The Kubernetes Authors. + +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 api + +import ( + "k8s.io/apimachinery/pkg/api/resource" +) + +func (self ResourceName) String() string { + return string(self) +} + +// Returns the CPU limit if specified. +func (self *ResourceList) Cpu() *resource.Quantity { + if val, ok := (*self)[ResourceCPU]; ok { + return &val + } + return &resource.Quantity{Format: resource.DecimalSI} +} + +// Returns the Memory limit if specified. +func (self *ResourceList) Memory() *resource.Quantity { + if val, ok := (*self)[ResourceMemory]; ok { + return &val + } + return &resource.Quantity{Format: resource.BinarySI} +} + +func (self *ResourceList) Pods() *resource.Quantity { + if val, ok := (*self)[ResourcePods]; ok { + return &val + } + return &resource.Quantity{} +} + +func (self *ResourceList) NvidiaGPU() *resource.Quantity { + if val, ok := (*self)[ResourceNvidiaGPU]; ok { + return &val + } + return &resource.Quantity{} +} diff --git a/pkg/api/resource/BUILD b/pkg/api/resource/BUILD index deefdcef3c..e457c97eba 100644 --- a/pkg/api/resource/BUILD +++ b/pkg/api/resource/BUILD @@ -5,12 +5,17 @@ licenses(["notice"]) load( "@io_bazel_rules_go//go:def.bzl", "go_library", + "go_test", ) go_library( name = "go_default_library", - srcs = ["doc.go"], + srcs = ["resource_helpers.go"], tags = ["automanaged"], + deps = [ + "//pkg/api:go_default_library", + "//vendor:k8s.io/apimachinery/pkg/api/resource", + ], ) filegroup( @@ -25,3 +30,15 @@ filegroup( srcs = [":package-srcs"], tags = ["automanaged"], ) + +go_test( + name = "go_default_test", + srcs = ["resource_helpers_test.go"], + library = ":go_default_library", + tags = ["automanaged"], + deps = [ + "//pkg/api:go_default_library", + "//vendor:k8s.io/apimachinery/pkg/api/resource", + "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", + ], +) diff --git a/pkg/api/resource/doc.go b/pkg/api/resource/doc.go deleted file mode 100644 index 80c5e64bf8..0000000000 --- a/pkg/api/resource/doc.go +++ /dev/null @@ -1,21 +0,0 @@ -/* -Copyright 2017 The Kubernetes Authors. - -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 only exists until heapster rebases -// TODO genericapiserver remove this empty package. Godep fails without this because heapster relies -// on this package. This will allow us to start splitting packages, but will force -// heapster to update on their next kube rebase. -package resource diff --git a/pkg/api/resource/resource_helpers.go b/pkg/api/resource/resource_helpers.go new file mode 100644 index 0000000000..debdbe5281 --- /dev/null +++ b/pkg/api/resource/resource_helpers.go @@ -0,0 +1,112 @@ +/* +Copyright 2014 The Kubernetes Authors. + +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 ( + "fmt" + "math" + "strconv" + + "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/kubernetes/pkg/api" +) + +// PodRequestsAndLimits returns a dictionary of all defined resources summed up for all +// containers of the pod. +func PodRequestsAndLimits(pod *api.Pod) (reqs map[api.ResourceName]resource.Quantity, limits map[api.ResourceName]resource.Quantity, err error) { + reqs, limits = map[api.ResourceName]resource.Quantity{}, map[api.ResourceName]resource.Quantity{} + for _, container := range pod.Spec.Containers { + for name, quantity := range container.Resources.Requests { + if value, ok := reqs[name]; !ok { + reqs[name] = *quantity.Copy() + } else { + value.Add(quantity) + reqs[name] = value + } + } + for name, quantity := range container.Resources.Limits { + if value, ok := limits[name]; !ok { + limits[name] = *quantity.Copy() + } else { + value.Add(quantity) + limits[name] = value + } + } + } + // init containers define the minimum of any resource + for _, container := range pod.Spec.InitContainers { + for name, quantity := range container.Resources.Requests { + value, ok := reqs[name] + if !ok { + reqs[name] = *quantity.Copy() + continue + } + if quantity.Cmp(value) > 0 { + reqs[name] = *quantity.Copy() + } + } + for name, quantity := range container.Resources.Limits { + value, ok := limits[name] + if !ok { + limits[name] = *quantity.Copy() + continue + } + if quantity.Cmp(value) > 0 { + limits[name] = *quantity.Copy() + } + } + } + return +} + +// ExtractContainerResourceValue extracts the value of a resource +// in an already known container +func ExtractContainerResourceValue(fs *api.ResourceFieldSelector, container *api.Container) (string, error) { + divisor := resource.Quantity{} + if divisor.Cmp(fs.Divisor) == 0 { + divisor = resource.MustParse("1") + } else { + divisor = fs.Divisor + } + + switch fs.Resource { + case "limits.cpu": + return convertResourceCPUToString(container.Resources.Limits.Cpu(), divisor) + case "limits.memory": + return convertResourceMemoryToString(container.Resources.Limits.Memory(), divisor) + case "requests.cpu": + return convertResourceCPUToString(container.Resources.Requests.Cpu(), divisor) + case "requests.memory": + return convertResourceMemoryToString(container.Resources.Requests.Memory(), divisor) + } + + return "", fmt.Errorf("unsupported container resource : %v", fs.Resource) +} + +// convertResourceCPUToString converts cpu value to the format of divisor and returns +// ceiling of the value. +func convertResourceCPUToString(cpu *resource.Quantity, divisor resource.Quantity) (string, error) { + c := int64(math.Ceil(float64(cpu.MilliValue()) / float64(divisor.MilliValue()))) + return strconv.FormatInt(c, 10), nil +} + +// convertResourceMemoryToString converts memory value to the format of divisor and returns +// ceiling of the value. +func convertResourceMemoryToString(memory *resource.Quantity, divisor resource.Quantity) (string, error) { + m := int64(math.Ceil(float64(memory.Value()) / float64(divisor.Value()))) + return strconv.FormatInt(m, 10), nil +} diff --git a/pkg/api/resource_helpers_test.go b/pkg/api/resource/resource_helpers_test.go similarity index 62% rename from pkg/api/resource_helpers_test.go rename to pkg/api/resource/resource_helpers_test.go index a52bab14be..fbcc3688b6 100644 --- a/pkg/api/resource_helpers_test.go +++ b/pkg/api/resource/resource_helpers_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package api +package resource import ( "testing" @@ -22,13 +22,14 @@ import ( "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/kubernetes/pkg/api" ) func TestResourceHelpers(t *testing.T) { cpuLimit := resource.MustParse("10") memoryLimit := resource.MustParse("10G") - resourceSpec := ResourceRequirements{ - Limits: ResourceList{ + resourceSpec := api.ResourceRequirements{ + Limits: api.ResourceList{ "cpu": cpuLimit, "memory": memoryLimit, "kube.io/storage": memoryLimit, @@ -40,8 +41,8 @@ func TestResourceHelpers(t *testing.T) { if res := resourceSpec.Limits.Memory(); res.Cmp(memoryLimit) != 0 { t.Errorf("expected memorylimit %v, got %v", memoryLimit, res) } - resourceSpec = ResourceRequirements{ - Limits: ResourceList{ + resourceSpec = api.ResourceRequirements{ + Limits: api.ResourceList{ "memory": memoryLimit, "kube.io/storage": memoryLimit, }, @@ -55,7 +56,7 @@ func TestResourceHelpers(t *testing.T) { } func TestDefaultResourceHelpers(t *testing.T) { - resourceList := ResourceList{} + resourceList := api.ResourceList{} if resourceList.Cpu().Format != resource.DecimalSI { t.Errorf("expected %v, actual %v", resource.DecimalSI, resourceList.Cpu().Format) } @@ -64,16 +65,16 @@ func TestDefaultResourceHelpers(t *testing.T) { } } -func newPod(now metav1.Time, ready bool, beforeSec int) *Pod { - conditionStatus := ConditionFalse +func newPod(now metav1.Time, ready bool, beforeSec int) *api.Pod { + conditionStatus := api.ConditionFalse if ready { - conditionStatus = ConditionTrue + conditionStatus = api.ConditionTrue } - return &Pod{ - Status: PodStatus{ - Conditions: []PodCondition{ + return &api.Pod{ + Status: api.PodStatus{ + Conditions: []api.PodCondition{ { - Type: PodReady, + Type: api.PodReady, LastTransitionTime: metav1.NewTime(now.Time.Add(-1 * time.Duration(beforeSec) * time.Second)), Status: conditionStatus, }, @@ -81,40 +82,3 @@ func newPod(now metav1.Time, ready bool, beforeSec int) *Pod { }, } } - -func TestIsPodAvailable(t *testing.T) { - now := metav1.Now() - tests := []struct { - pod *Pod - minReadySeconds int32 - expected bool - }{ - { - pod: newPod(now, false, 0), - minReadySeconds: 0, - expected: false, - }, - { - pod: newPod(now, true, 0), - minReadySeconds: 1, - expected: false, - }, - { - pod: newPod(now, true, 0), - minReadySeconds: 0, - expected: true, - }, - { - pod: newPod(now, true, 51), - minReadySeconds: 50, - expected: true, - }, - } - - for i, test := range tests { - isAvailable := IsPodAvailable(test.pod, test.minReadySeconds, now) - if isAvailable != test.expected { - t.Errorf("[tc #%d] expected available pod: %t, got: %t", i, test.expected, isAvailable) - } - } -} diff --git a/pkg/api/resource_helpers.go b/pkg/api/resource_helpers.go deleted file mode 100644 index 74da82ef62..0000000000 --- a/pkg/api/resource_helpers.go +++ /dev/null @@ -1,270 +0,0 @@ -/* -Copyright 2014 The Kubernetes Authors. - -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 api - -import ( - "fmt" - "math" - "strconv" - "time" - - "k8s.io/apimachinery/pkg/api/resource" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// Returns string version of ResourceName. -func (self ResourceName) String() string { - return string(self) -} - -// Returns the CPU limit if specified. -func (self *ResourceList) Cpu() *resource.Quantity { - if val, ok := (*self)[ResourceCPU]; ok { - return &val - } - return &resource.Quantity{Format: resource.DecimalSI} -} - -// Returns the Memory limit if specified. -func (self *ResourceList) Memory() *resource.Quantity { - if val, ok := (*self)[ResourceMemory]; ok { - return &val - } - return &resource.Quantity{Format: resource.BinarySI} -} - -func (self *ResourceList) Pods() *resource.Quantity { - if val, ok := (*self)[ResourcePods]; ok { - return &val - } - return &resource.Quantity{} -} - -func (self *ResourceList) NvidiaGPU() *resource.Quantity { - if val, ok := (*self)[ResourceNvidiaGPU]; ok { - return &val - } - return &resource.Quantity{} -} - -func GetContainerStatus(statuses []ContainerStatus, name string) (ContainerStatus, bool) { - for i := range statuses { - if statuses[i].Name == name { - return statuses[i], true - } - } - return ContainerStatus{}, false -} - -func GetExistingContainerStatus(statuses []ContainerStatus, name string) ContainerStatus { - for i := range statuses { - if statuses[i].Name == name { - return statuses[i] - } - } - return ContainerStatus{} -} - -// IsPodAvailable returns true if a pod is available; false otherwise. -// Precondition for an available pod is that it must be ready. On top -// of that, there are two cases when a pod can be considered available: -// 1. minReadySeconds == 0, or -// 2. LastTransitionTime (is set) + minReadySeconds < current time -func IsPodAvailable(pod *Pod, minReadySeconds int32, now metav1.Time) bool { - if !IsPodReady(pod) { - return false - } - - c := GetPodReadyCondition(pod.Status) - minReadySecondsDuration := time.Duration(minReadySeconds) * time.Second - if minReadySeconds == 0 || !c.LastTransitionTime.IsZero() && c.LastTransitionTime.Add(minReadySecondsDuration).Before(now.Time) { - return true - } - return false -} - -// IsPodReady returns true if a pod is ready; false otherwise. -func IsPodReady(pod *Pod) bool { - return IsPodReadyConditionTrue(pod.Status) -} - -// IsPodReady retruns true if a pod is ready; false otherwise. -func IsPodReadyConditionTrue(status PodStatus) bool { - condition := GetPodReadyCondition(status) - return condition != nil && condition.Status == ConditionTrue -} - -// Extracts the pod ready condition from the given status and returns that. -// Returns nil if the condition is not present. -func GetPodReadyCondition(status PodStatus) *PodCondition { - _, condition := GetPodCondition(&status, PodReady) - return condition -} - -// GetPodCondition extracts the provided condition from the given status and returns that. -// Returns nil and -1 if the condition is not present, and the index of the located condition. -func GetPodCondition(status *PodStatus, conditionType PodConditionType) (int, *PodCondition) { - if status == nil { - return -1, nil - } - for i := range status.Conditions { - if status.Conditions[i].Type == conditionType { - return i, &status.Conditions[i] - } - } - return -1, nil -} - -// GetNodeCondition extracts the provided condition from the given status and returns that. -// Returns nil and -1 if the condition is not present, and the index of the located condition. -func GetNodeCondition(status *NodeStatus, conditionType NodeConditionType) (int, *NodeCondition) { - if status == nil { - return -1, nil - } - for i := range status.Conditions { - if status.Conditions[i].Type == conditionType { - return i, &status.Conditions[i] - } - } - return -1, nil -} - -// Updates existing pod condition or creates a new one. Sets LastTransitionTime to now if the -// status has changed. -// Returns true if pod condition has changed or has been added. -func UpdatePodCondition(status *PodStatus, condition *PodCondition) bool { - condition.LastTransitionTime = metav1.Now() - // Try to find this pod condition. - conditionIndex, oldCondition := GetPodCondition(status, condition.Type) - - if oldCondition == nil { - // We are adding new pod condition. - status.Conditions = append(status.Conditions, *condition) - return true - } else { - // We are updating an existing condition, so we need to check if it has changed. - if condition.Status == oldCondition.Status { - condition.LastTransitionTime = oldCondition.LastTransitionTime - } - - isEqual := condition.Status == oldCondition.Status && - condition.Reason == oldCondition.Reason && - condition.Message == oldCondition.Message && - condition.LastProbeTime.Equal(oldCondition.LastProbeTime) && - condition.LastTransitionTime.Equal(oldCondition.LastTransitionTime) - - status.Conditions[conditionIndex] = *condition - // Return true if one of the fields have changed. - return !isEqual - } -} - -// IsNodeReady returns true if a node is ready; false otherwise. -func IsNodeReady(node *Node) bool { - for _, c := range node.Status.Conditions { - if c.Type == NodeReady { - return c.Status == ConditionTrue - } - } - return false -} - -// PodRequestsAndLimits returns a dictionary of all defined resources summed up for all -// containers of the pod. -func PodRequestsAndLimits(pod *Pod) (reqs map[ResourceName]resource.Quantity, limits map[ResourceName]resource.Quantity, err error) { - reqs, limits = map[ResourceName]resource.Quantity{}, map[ResourceName]resource.Quantity{} - for _, container := range pod.Spec.Containers { - for name, quantity := range container.Resources.Requests { - if value, ok := reqs[name]; !ok { - reqs[name] = *quantity.Copy() - } else { - value.Add(quantity) - reqs[name] = value - } - } - for name, quantity := range container.Resources.Limits { - if value, ok := limits[name]; !ok { - limits[name] = *quantity.Copy() - } else { - value.Add(quantity) - limits[name] = value - } - } - } - // init containers define the minimum of any resource - for _, container := range pod.Spec.InitContainers { - for name, quantity := range container.Resources.Requests { - value, ok := reqs[name] - if !ok { - reqs[name] = *quantity.Copy() - continue - } - if quantity.Cmp(value) > 0 { - reqs[name] = *quantity.Copy() - } - } - for name, quantity := range container.Resources.Limits { - value, ok := limits[name] - if !ok { - limits[name] = *quantity.Copy() - continue - } - if quantity.Cmp(value) > 0 { - limits[name] = *quantity.Copy() - } - } - } - return -} - -// ExtractContainerResourceValue extracts the value of a resource -// in an already known container -func ExtractContainerResourceValue(fs *ResourceFieldSelector, container *Container) (string, error) { - divisor := resource.Quantity{} - if divisor.Cmp(fs.Divisor) == 0 { - divisor = resource.MustParse("1") - } else { - divisor = fs.Divisor - } - - switch fs.Resource { - case "limits.cpu": - return convertResourceCPUToString(container.Resources.Limits.Cpu(), divisor) - case "limits.memory": - return convertResourceMemoryToString(container.Resources.Limits.Memory(), divisor) - case "requests.cpu": - return convertResourceCPUToString(container.Resources.Requests.Cpu(), divisor) - case "requests.memory": - return convertResourceMemoryToString(container.Resources.Requests.Memory(), divisor) - } - - return "", fmt.Errorf("unsupported container resource : %v", fs.Resource) -} - -// convertResourceCPUToString converts cpu value to the format of divisor and returns -// ceiling of the value. -func convertResourceCPUToString(cpu *resource.Quantity, divisor resource.Quantity) (string, error) { - c := int64(math.Ceil(float64(cpu.MilliValue()) / float64(divisor.MilliValue()))) - return strconv.FormatInt(c, 10), nil -} - -// convertResourceMemoryToString converts memory value to the format of divisor and returns -// ceiling of the value. -func convertResourceMemoryToString(memory *resource.Quantity, divisor resource.Quantity) (string, error) { - m := int64(math.Ceil(float64(memory.Value()) / float64(divisor.Value()))) - return strconv.FormatInt(m, 10), nil -} diff --git a/pkg/client/unversioned/BUILD b/pkg/client/unversioned/BUILD index 7eab27bc84..09a80f4e06 100644 --- a/pkg/client/unversioned/BUILD +++ b/pkg/client/unversioned/BUILD @@ -17,6 +17,7 @@ go_library( tags = ["automanaged"], deps = [ "//pkg/api:go_default_library", + "//pkg/api/pod:go_default_library", "//pkg/apis/apps:go_default_library", "//pkg/apis/batch:go_default_library", "//pkg/apis/extensions:go_default_library", diff --git a/pkg/client/unversioned/conditions.go b/pkg/client/unversioned/conditions.go index a8fbf9d8e9..7c1994638f 100644 --- a/pkg/client/unversioned/conditions.go +++ b/pkg/client/unversioned/conditions.go @@ -25,6 +25,7 @@ import ( "k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/watch" "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/pod" "k8s.io/kubernetes/pkg/apis/apps" "k8s.io/kubernetes/pkg/apis/batch" "k8s.io/kubernetes/pkg/apis/extensions" @@ -192,7 +193,7 @@ func PodRunningAndReady(event watch.Event) (bool, error) { case api.PodFailed, api.PodSucceeded: return false, ErrPodCompleted case api.PodRunning: - return api.IsPodReady(t), nil + return pod.IsPodReady(t), nil } } return false, nil diff --git a/pkg/printers/internalversion/BUILD b/pkg/printers/internalversion/BUILD index 2b73b4d3ce..f80f69d427 100644 --- a/pkg/printers/internalversion/BUILD +++ b/pkg/printers/internalversion/BUILD @@ -64,6 +64,7 @@ go_library( "//pkg/api/events:go_default_library", "//pkg/api/helper:go_default_library", "//pkg/api/ref:go_default_library", + "//pkg/api/resource:go_default_library", "//pkg/apis/apps:go_default_library", "//pkg/apis/autoscaling:go_default_library", "//pkg/apis/batch:go_default_library", diff --git a/pkg/printers/internalversion/describe.go b/pkg/printers/internalversion/describe.go index e436c40e2e..1d4efa4d51 100644 --- a/pkg/printers/internalversion/describe.go +++ b/pkg/printers/internalversion/describe.go @@ -49,6 +49,7 @@ import ( "k8s.io/kubernetes/pkg/api/events" "k8s.io/kubernetes/pkg/api/helper" "k8s.io/kubernetes/pkg/api/ref" + resourcehelper "k8s.io/kubernetes/pkg/api/resource" "k8s.io/kubernetes/pkg/apis/apps" "k8s.io/kubernetes/pkg/apis/autoscaling" "k8s.io/kubernetes/pkg/apis/batch" @@ -1115,7 +1116,7 @@ func describeContainerEnvVars(container api.Container, resolverFn EnvVarResolver } w.Write(LEVEL_3, "%s:\t%s (%s:%s)\n", e.Name, valueFrom, e.ValueFrom.FieldRef.APIVersion, e.ValueFrom.FieldRef.FieldPath) case e.ValueFrom.ResourceFieldRef != nil: - valueFrom, err := api.ExtractContainerResourceValue(e.ValueFrom.ResourceFieldRef, &container) + valueFrom, err := resourcehelper.ExtractContainerResourceValue(e.ValueFrom.ResourceFieldRef, &container) if err != nil { valueFrom = "" } @@ -2337,7 +2338,7 @@ func describeNodeResource(nodeNonTerminatedPodsList *api.PodList, node *api.Node } for _, pod := range nodeNonTerminatedPodsList.Items { - req, limit, err := api.PodRequestsAndLimits(&pod) + req, limit, err := resourcehelper.PodRequestsAndLimits(&pod) if err != nil { return err } @@ -2385,7 +2386,7 @@ func filterTerminatedPods(pods []*api.Pod) []*api.Pod { func getPodsTotalRequestsAndLimits(podList *api.PodList) (reqs map[api.ResourceName]resource.Quantity, limits map[api.ResourceName]resource.Quantity, err error) { reqs, limits = map[api.ResourceName]resource.Quantity{}, map[api.ResourceName]resource.Quantity{} for _, pod := range podList.Items { - podReqs, podLimits, err := api.PodRequestsAndLimits(&pod) + podReqs, podLimits, err := resourcehelper.PodRequestsAndLimits(&pod) if err != nil { return nil, nil, err } diff --git a/pkg/registry/core/pod/storage/BUILD b/pkg/registry/core/pod/storage/BUILD index 763ff40f3b..7cbcabaed1 100644 --- a/pkg/registry/core/pod/storage/BUILD +++ b/pkg/registry/core/pod/storage/BUILD @@ -43,6 +43,7 @@ go_library( tags = ["automanaged"], deps = [ "//pkg/api:go_default_library", + "//pkg/api/pod:go_default_library", "//pkg/api/validation:go_default_library", "//pkg/apis/policy:go_default_library", "//pkg/client/clientset_generated/internalclientset/typed/policy/internalversion:go_default_library", diff --git a/pkg/registry/core/pod/storage/storage.go b/pkg/registry/core/pod/storage/storage.go index 8de42e86dd..ab57de501c 100644 --- a/pkg/registry/core/pod/storage/storage.go +++ b/pkg/registry/core/pod/storage/storage.go @@ -31,6 +31,7 @@ import ( "k8s.io/apiserver/pkg/storage" storeerr "k8s.io/apiserver/pkg/storage/errors" "k8s.io/kubernetes/pkg/api" + podutil "k8s.io/kubernetes/pkg/api/pod" "k8s.io/kubernetes/pkg/api/validation" policyclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/policy/internalversion" "k8s.io/kubernetes/pkg/kubelet/client" @@ -165,7 +166,7 @@ func (r *BindingREST) setPodHostAndAnnotations(ctx genericapirequest.Context, po for k, v := range annotations { pod.Annotations[k] = v } - api.UpdatePodCondition(&pod.Status, &api.PodCondition{ + podutil.UpdatePodCondition(&pod.Status, &api.PodCondition{ Type: api.PodScheduled, Status: api.ConditionTrue, }) diff --git a/staging/src/k8s.io/client-go/pkg/api/resource.go b/staging/src/k8s.io/client-go/pkg/api/resource.go new file mode 100644 index 0000000000..1ea96d16e3 --- /dev/null +++ b/staging/src/k8s.io/client-go/pkg/api/resource.go @@ -0,0 +1,55 @@ +/* +Copyright 2017 The Kubernetes Authors. + +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 api + +import ( + "k8s.io/apimachinery/pkg/api/resource" +) + +func (self ResourceName) String() string { + return string(self) +} + +// Returns the CPU limit if specified. +func (self *ResourceList) Cpu() *resource.Quantity { + if val, ok := (*self)[ResourceCPU]; ok { + return &val + } + return &resource.Quantity{Format: resource.DecimalSI} +} + +// Returns the Memory limit if specified. +func (self *ResourceList) Memory() *resource.Quantity { + if val, ok := (*self)[ResourceMemory]; ok { + return &val + } + return &resource.Quantity{Format: resource.BinarySI} +} + +func (self *ResourceList) Pods() *resource.Quantity { + if val, ok := (*self)[ResourcePods]; ok { + return &val + } + return &resource.Quantity{} +} + +func (self *ResourceList) NvidiaGPU() *resource.Quantity { + if val, ok := (*self)[ResourceNvidiaGPU]; ok { + return &val + } + return &resource.Quantity{} +} diff --git a/staging/src/k8s.io/client-go/pkg/api/resource_helpers.go b/staging/src/k8s.io/client-go/pkg/api/resource_helpers.go deleted file mode 100644 index 74da82ef62..0000000000 --- a/staging/src/k8s.io/client-go/pkg/api/resource_helpers.go +++ /dev/null @@ -1,270 +0,0 @@ -/* -Copyright 2014 The Kubernetes Authors. - -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 api - -import ( - "fmt" - "math" - "strconv" - "time" - - "k8s.io/apimachinery/pkg/api/resource" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// Returns string version of ResourceName. -func (self ResourceName) String() string { - return string(self) -} - -// Returns the CPU limit if specified. -func (self *ResourceList) Cpu() *resource.Quantity { - if val, ok := (*self)[ResourceCPU]; ok { - return &val - } - return &resource.Quantity{Format: resource.DecimalSI} -} - -// Returns the Memory limit if specified. -func (self *ResourceList) Memory() *resource.Quantity { - if val, ok := (*self)[ResourceMemory]; ok { - return &val - } - return &resource.Quantity{Format: resource.BinarySI} -} - -func (self *ResourceList) Pods() *resource.Quantity { - if val, ok := (*self)[ResourcePods]; ok { - return &val - } - return &resource.Quantity{} -} - -func (self *ResourceList) NvidiaGPU() *resource.Quantity { - if val, ok := (*self)[ResourceNvidiaGPU]; ok { - return &val - } - return &resource.Quantity{} -} - -func GetContainerStatus(statuses []ContainerStatus, name string) (ContainerStatus, bool) { - for i := range statuses { - if statuses[i].Name == name { - return statuses[i], true - } - } - return ContainerStatus{}, false -} - -func GetExistingContainerStatus(statuses []ContainerStatus, name string) ContainerStatus { - for i := range statuses { - if statuses[i].Name == name { - return statuses[i] - } - } - return ContainerStatus{} -} - -// IsPodAvailable returns true if a pod is available; false otherwise. -// Precondition for an available pod is that it must be ready. On top -// of that, there are two cases when a pod can be considered available: -// 1. minReadySeconds == 0, or -// 2. LastTransitionTime (is set) + minReadySeconds < current time -func IsPodAvailable(pod *Pod, minReadySeconds int32, now metav1.Time) bool { - if !IsPodReady(pod) { - return false - } - - c := GetPodReadyCondition(pod.Status) - minReadySecondsDuration := time.Duration(minReadySeconds) * time.Second - if minReadySeconds == 0 || !c.LastTransitionTime.IsZero() && c.LastTransitionTime.Add(minReadySecondsDuration).Before(now.Time) { - return true - } - return false -} - -// IsPodReady returns true if a pod is ready; false otherwise. -func IsPodReady(pod *Pod) bool { - return IsPodReadyConditionTrue(pod.Status) -} - -// IsPodReady retruns true if a pod is ready; false otherwise. -func IsPodReadyConditionTrue(status PodStatus) bool { - condition := GetPodReadyCondition(status) - return condition != nil && condition.Status == ConditionTrue -} - -// Extracts the pod ready condition from the given status and returns that. -// Returns nil if the condition is not present. -func GetPodReadyCondition(status PodStatus) *PodCondition { - _, condition := GetPodCondition(&status, PodReady) - return condition -} - -// GetPodCondition extracts the provided condition from the given status and returns that. -// Returns nil and -1 if the condition is not present, and the index of the located condition. -func GetPodCondition(status *PodStatus, conditionType PodConditionType) (int, *PodCondition) { - if status == nil { - return -1, nil - } - for i := range status.Conditions { - if status.Conditions[i].Type == conditionType { - return i, &status.Conditions[i] - } - } - return -1, nil -} - -// GetNodeCondition extracts the provided condition from the given status and returns that. -// Returns nil and -1 if the condition is not present, and the index of the located condition. -func GetNodeCondition(status *NodeStatus, conditionType NodeConditionType) (int, *NodeCondition) { - if status == nil { - return -1, nil - } - for i := range status.Conditions { - if status.Conditions[i].Type == conditionType { - return i, &status.Conditions[i] - } - } - return -1, nil -} - -// Updates existing pod condition or creates a new one. Sets LastTransitionTime to now if the -// status has changed. -// Returns true if pod condition has changed or has been added. -func UpdatePodCondition(status *PodStatus, condition *PodCondition) bool { - condition.LastTransitionTime = metav1.Now() - // Try to find this pod condition. - conditionIndex, oldCondition := GetPodCondition(status, condition.Type) - - if oldCondition == nil { - // We are adding new pod condition. - status.Conditions = append(status.Conditions, *condition) - return true - } else { - // We are updating an existing condition, so we need to check if it has changed. - if condition.Status == oldCondition.Status { - condition.LastTransitionTime = oldCondition.LastTransitionTime - } - - isEqual := condition.Status == oldCondition.Status && - condition.Reason == oldCondition.Reason && - condition.Message == oldCondition.Message && - condition.LastProbeTime.Equal(oldCondition.LastProbeTime) && - condition.LastTransitionTime.Equal(oldCondition.LastTransitionTime) - - status.Conditions[conditionIndex] = *condition - // Return true if one of the fields have changed. - return !isEqual - } -} - -// IsNodeReady returns true if a node is ready; false otherwise. -func IsNodeReady(node *Node) bool { - for _, c := range node.Status.Conditions { - if c.Type == NodeReady { - return c.Status == ConditionTrue - } - } - return false -} - -// PodRequestsAndLimits returns a dictionary of all defined resources summed up for all -// containers of the pod. -func PodRequestsAndLimits(pod *Pod) (reqs map[ResourceName]resource.Quantity, limits map[ResourceName]resource.Quantity, err error) { - reqs, limits = map[ResourceName]resource.Quantity{}, map[ResourceName]resource.Quantity{} - for _, container := range pod.Spec.Containers { - for name, quantity := range container.Resources.Requests { - if value, ok := reqs[name]; !ok { - reqs[name] = *quantity.Copy() - } else { - value.Add(quantity) - reqs[name] = value - } - } - for name, quantity := range container.Resources.Limits { - if value, ok := limits[name]; !ok { - limits[name] = *quantity.Copy() - } else { - value.Add(quantity) - limits[name] = value - } - } - } - // init containers define the minimum of any resource - for _, container := range pod.Spec.InitContainers { - for name, quantity := range container.Resources.Requests { - value, ok := reqs[name] - if !ok { - reqs[name] = *quantity.Copy() - continue - } - if quantity.Cmp(value) > 0 { - reqs[name] = *quantity.Copy() - } - } - for name, quantity := range container.Resources.Limits { - value, ok := limits[name] - if !ok { - limits[name] = *quantity.Copy() - continue - } - if quantity.Cmp(value) > 0 { - limits[name] = *quantity.Copy() - } - } - } - return -} - -// ExtractContainerResourceValue extracts the value of a resource -// in an already known container -func ExtractContainerResourceValue(fs *ResourceFieldSelector, container *Container) (string, error) { - divisor := resource.Quantity{} - if divisor.Cmp(fs.Divisor) == 0 { - divisor = resource.MustParse("1") - } else { - divisor = fs.Divisor - } - - switch fs.Resource { - case "limits.cpu": - return convertResourceCPUToString(container.Resources.Limits.Cpu(), divisor) - case "limits.memory": - return convertResourceMemoryToString(container.Resources.Limits.Memory(), divisor) - case "requests.cpu": - return convertResourceCPUToString(container.Resources.Requests.Cpu(), divisor) - case "requests.memory": - return convertResourceMemoryToString(container.Resources.Requests.Memory(), divisor) - } - - return "", fmt.Errorf("unsupported container resource : %v", fs.Resource) -} - -// convertResourceCPUToString converts cpu value to the format of divisor and returns -// ceiling of the value. -func convertResourceCPUToString(cpu *resource.Quantity, divisor resource.Quantity) (string, error) { - c := int64(math.Ceil(float64(cpu.MilliValue()) / float64(divisor.MilliValue()))) - return strconv.FormatInt(c, 10), nil -} - -// convertResourceMemoryToString converts memory value to the format of divisor and returns -// ceiling of the value. -func convertResourceMemoryToString(memory *resource.Quantity, divisor resource.Quantity) (string, error) { - m := int64(math.Ceil(float64(memory.Value()) / float64(divisor.Value()))) - return strconv.FormatInt(m, 10), nil -} diff --git a/vendor/BUILD b/vendor/BUILD index ee45616181..20b88ebf52 100644 --- a/vendor/BUILD +++ b/vendor/BUILD @@ -13905,7 +13905,7 @@ go_library( "k8s.io/client-go/pkg/api/json.go", "k8s.io/client-go/pkg/api/objectreference.go", "k8s.io/client-go/pkg/api/register.go", - "k8s.io/client-go/pkg/api/resource_helpers.go", + "k8s.io/client-go/pkg/api/resource.go", "k8s.io/client-go/pkg/api/taint.go", "k8s.io/client-go/pkg/api/toleration.go", "k8s.io/client-go/pkg/api/types.go",