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