1/* 2Copyright 2014 The Kubernetes Authors. 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15*/ 16 17package v1 18 19import ( 20 "time" 21 22 "k8s.io/apimachinery/pkg/api/resource" 23 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24) 25 26// Returns string version of ResourceName. 27func (self ResourceName) String() string { 28 return string(self) 29} 30 31// Returns the CPU limit if specified. 32func (self *ResourceList) Cpu() *resource.Quantity { 33 if val, ok := (*self)[ResourceCPU]; ok { 34 return &val 35 } 36 return &resource.Quantity{Format: resource.DecimalSI} 37} 38 39// Returns the Memory limit if specified. 40func (self *ResourceList) Memory() *resource.Quantity { 41 if val, ok := (*self)[ResourceMemory]; ok { 42 return &val 43 } 44 return &resource.Quantity{Format: resource.BinarySI} 45} 46 47func (self *ResourceList) Pods() *resource.Quantity { 48 if val, ok := (*self)[ResourcePods]; ok { 49 return &val 50 } 51 return &resource.Quantity{} 52} 53 54func (self *ResourceList) NvidiaGPU() *resource.Quantity { 55 if val, ok := (*self)[ResourceNvidiaGPU]; ok { 56 return &val 57 } 58 return &resource.Quantity{} 59} 60 61func GetContainerStatus(statuses []ContainerStatus, name string) (ContainerStatus, bool) { 62 for i := range statuses { 63 if statuses[i].Name == name { 64 return statuses[i], true 65 } 66 } 67 return ContainerStatus{}, false 68} 69 70func GetExistingContainerStatus(statuses []ContainerStatus, name string) ContainerStatus { 71 for i := range statuses { 72 if statuses[i].Name == name { 73 return statuses[i] 74 } 75 } 76 return ContainerStatus{} 77} 78 79// IsPodAvailable returns true if a pod is available; false otherwise. 80// Precondition for an available pod is that it must be ready. On top 81// of that, there are two cases when a pod can be considered available: 82// 1. minReadySeconds == 0, or 83// 2. LastTransitionTime (is set) + minReadySeconds < current time 84func IsPodAvailable(pod *Pod, minReadySeconds int32, now metav1.Time) bool { 85 if !IsPodReady(pod) { 86 return false 87 } 88 89 c := GetPodReadyCondition(pod.Status) 90 minReadySecondsDuration := time.Duration(minReadySeconds) * time.Second 91 if minReadySeconds == 0 || !c.LastTransitionTime.IsZero() && c.LastTransitionTime.Add(minReadySecondsDuration).Before(now.Time) { 92 return true 93 } 94 return false 95} 96 97// IsPodReady returns true if a pod is ready; false otherwise. 98func IsPodReady(pod *Pod) bool { 99 return IsPodReadyConditionTrue(pod.Status) 100} 101 102// IsPodReady retruns true if a pod is ready; false otherwise. 103func IsPodReadyConditionTrue(status PodStatus) bool { 104 condition := GetPodReadyCondition(status) 105 return condition != nil && condition.Status == ConditionTrue 106} 107 108// Extracts the pod ready condition from the given status and returns that. 109// Returns nil if the condition is not present. 110func GetPodReadyCondition(status PodStatus) *PodCondition { 111 _, condition := GetPodCondition(&status, PodReady) 112 return condition 113} 114 115// GetPodCondition extracts the provided condition from the given status and returns that. 116// Returns nil and -1 if the condition is not present, and the index of the located condition. 117func GetPodCondition(status *PodStatus, conditionType PodConditionType) (int, *PodCondition) { 118 if status == nil { 119 return -1, nil 120 } 121 for i := range status.Conditions { 122 if status.Conditions[i].Type == conditionType { 123 return i, &status.Conditions[i] 124 } 125 } 126 return -1, nil 127} 128 129// GetNodeCondition extracts the provided condition from the given status and returns that. 130// Returns nil and -1 if the condition is not present, and the index of the located condition. 131func GetNodeCondition(status *NodeStatus, conditionType NodeConditionType) (int, *NodeCondition) { 132 if status == nil { 133 return -1, nil 134 } 135 for i := range status.Conditions { 136 if status.Conditions[i].Type == conditionType { 137 return i, &status.Conditions[i] 138 } 139 } 140 return -1, nil 141} 142 143// Updates existing pod condition or creates a new one. Sets LastTransitionTime to now if the 144// status has changed. 145// Returns true if pod condition has changed or has been added. 146func UpdatePodCondition(status *PodStatus, condition *PodCondition) bool { 147 condition.LastTransitionTime = metav1.Now() 148 // Try to find this pod condition. 149 conditionIndex, oldCondition := GetPodCondition(status, condition.Type) 150 151 if oldCondition == nil { 152 // We are adding new pod condition. 153 status.Conditions = append(status.Conditions, *condition) 154 return true 155 } else { 156 // We are updating an existing condition, so we need to check if it has changed. 157 if condition.Status == oldCondition.Status { 158 condition.LastTransitionTime = oldCondition.LastTransitionTime 159 } 160 161 isEqual := condition.Status == oldCondition.Status && 162 condition.Reason == oldCondition.Reason && 163 condition.Message == oldCondition.Message && 164 condition.LastProbeTime.Equal(oldCondition.LastProbeTime) && 165 condition.LastTransitionTime.Equal(oldCondition.LastTransitionTime) 166 167 status.Conditions[conditionIndex] = *condition 168 // Return true if one of the fields have changed. 169 return !isEqual 170 } 171} 172 173// IsNodeReady returns true if a node is ready; false otherwise. 174func IsNodeReady(node *Node) bool { 175 for _, c := range node.Status.Conditions { 176 if c.Type == NodeReady { 177 return c.Status == ConditionTrue 178 } 179 } 180 return false 181} 182 183// PodRequestsAndLimits returns a dictionary of all defined resources summed up for all 184// containers of the pod. 185func PodRequestsAndLimits(pod *Pod) (reqs map[ResourceName]resource.Quantity, limits map[ResourceName]resource.Quantity, err error) { 186 reqs, limits = map[ResourceName]resource.Quantity{}, map[ResourceName]resource.Quantity{} 187 for _, container := range pod.Spec.Containers { 188 for name, quantity := range container.Resources.Requests { 189 if value, ok := reqs[name]; !ok { 190 reqs[name] = *quantity.Copy() 191 } else { 192 value.Add(quantity) 193 reqs[name] = value 194 } 195 } 196 for name, quantity := range container.Resources.Limits { 197 if value, ok := limits[name]; !ok { 198 limits[name] = *quantity.Copy() 199 } else { 200 value.Add(quantity) 201 limits[name] = value 202 } 203 } 204 } 205 // init containers define the minimum of any resource 206 for _, container := range pod.Spec.InitContainers { 207 for name, quantity := range container.Resources.Requests { 208 value, ok := reqs[name] 209 if !ok { 210 reqs[name] = *quantity.Copy() 211 continue 212 } 213 if quantity.Cmp(value) > 0 { 214 reqs[name] = *quantity.Copy() 215 } 216 } 217 for name, quantity := range container.Resources.Limits { 218 value, ok := limits[name] 219 if !ok { 220 limits[name] = *quantity.Copy() 221 continue 222 } 223 if quantity.Cmp(value) > 0 { 224 limits[name] = *quantity.Copy() 225 } 226 } 227 } 228 return 229} 230 231// finds and returns the request for a specific resource. 232func GetResourceRequest(pod *Pod, resource ResourceName) int64 { 233 if resource == ResourcePods { 234 return 1 235 } 236 totalResources := int64(0) 237 for _, container := range pod.Spec.Containers { 238 if rQuantity, ok := container.Resources.Requests[resource]; ok { 239 if resource == ResourceCPU { 240 totalResources += rQuantity.MilliValue() 241 } else { 242 totalResources += rQuantity.Value() 243 } 244 } 245 } 246 // take max_resource(sum_pod, any_init_container) 247 for _, container := range pod.Spec.InitContainers { 248 if rQuantity, ok := container.Resources.Requests[resource]; ok { 249 if resource == ResourceCPU && rQuantity.MilliValue() > totalResources { 250 totalResources = rQuantity.MilliValue() 251 } else if rQuantity.Value() > totalResources { 252 totalResources = rQuantity.Value() 253 } 254 } 255 } 256 return totalResources 257} 258