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 status
18
19import (
20	"fmt"
21	"strings"
22
23	"k8s.io/api/core/v1"
24	podutil "k8s.io/kubernetes/pkg/api/v1/pod"
25)
26
27const (
28	// UnknownContainerStatuses says that all container statuses are unknown.
29	UnknownContainerStatuses = "UnknownContainerStatuses"
30	// PodCompleted says that all related containers have succeeded.
31	PodCompleted = "PodCompleted"
32	// ContainersNotReady says that one or more containers are not ready.
33	ContainersNotReady = "ContainersNotReady"
34	// ContainersNotInitialized says that one or more init containers have not succeeded.
35	ContainersNotInitialized = "ContainersNotInitialized"
36	// ReadinessGatesNotReady says that one or more pod readiness gates are not ready.
37	ReadinessGatesNotReady = "ReadinessGatesNotReady"
38)
39
40// GenerateContainersReadyCondition returns the status of "ContainersReady" condition.
41// The status of "ContainersReady" condition is true when all containers are ready.
42func GenerateContainersReadyCondition(spec *v1.PodSpec, containerStatuses []v1.ContainerStatus, podPhase v1.PodPhase) v1.PodCondition {
43	// Find if all containers are ready or not.
44	if containerStatuses == nil {
45		return v1.PodCondition{
46			Type:   v1.ContainersReady,
47			Status: v1.ConditionFalse,
48			Reason: UnknownContainerStatuses,
49		}
50	}
51	unknownContainers := []string{}
52	unreadyContainers := []string{}
53	for _, container := range spec.Containers {
54		if containerStatus, ok := podutil.GetContainerStatus(containerStatuses, container.Name); ok {
55			if !containerStatus.Ready {
56				unreadyContainers = append(unreadyContainers, container.Name)
57			}
58		} else {
59			unknownContainers = append(unknownContainers, container.Name)
60		}
61	}
62
63	// If all containers are known and succeeded, just return PodCompleted.
64	if podPhase == v1.PodSucceeded && len(unknownContainers) == 0 {
65		return v1.PodCondition{
66			Type:   v1.ContainersReady,
67			Status: v1.ConditionFalse,
68			Reason: PodCompleted,
69		}
70	}
71
72	// Generate message for containers in unknown condition.
73	unreadyMessages := []string{}
74	if len(unknownContainers) > 0 {
75		unreadyMessages = append(unreadyMessages, fmt.Sprintf("containers with unknown status: %s", unknownContainers))
76	}
77	if len(unreadyContainers) > 0 {
78		unreadyMessages = append(unreadyMessages, fmt.Sprintf("containers with unready status: %s", unreadyContainers))
79	}
80	unreadyMessage := strings.Join(unreadyMessages, ", ")
81	if unreadyMessage != "" {
82		return v1.PodCondition{
83			Type:    v1.ContainersReady,
84			Status:  v1.ConditionFalse,
85			Reason:  ContainersNotReady,
86			Message: unreadyMessage,
87		}
88	}
89
90	return v1.PodCondition{
91		Type:   v1.ContainersReady,
92		Status: v1.ConditionTrue,
93	}
94}
95
96// GeneratePodReadyCondition returns "Ready" condition of a pod.
97// The status of "Ready" condition is "True", if all containers in a pod are ready
98// AND all matching conditions specified in the ReadinessGates have status equal to "True".
99func GeneratePodReadyCondition(spec *v1.PodSpec, conditions []v1.PodCondition, containerStatuses []v1.ContainerStatus, podPhase v1.PodPhase) v1.PodCondition {
100	containersReady := GenerateContainersReadyCondition(spec, containerStatuses, podPhase)
101	// If the status of ContainersReady is not True, return the same status, reason and message as ContainersReady.
102	if containersReady.Status != v1.ConditionTrue {
103		return v1.PodCondition{
104			Type:    v1.PodReady,
105			Status:  containersReady.Status,
106			Reason:  containersReady.Reason,
107			Message: containersReady.Message,
108		}
109	}
110
111	// Evaluate corresponding conditions specified in readiness gate
112	// Generate message if any readiness gate is not satisfied.
113	unreadyMessages := []string{}
114	for _, rg := range spec.ReadinessGates {
115		_, c := podutil.GetPodConditionFromList(conditions, rg.ConditionType)
116		if c == nil {
117			unreadyMessages = append(unreadyMessages, fmt.Sprintf("corresponding condition of pod readiness gate %q does not exist.", string(rg.ConditionType)))
118		} else if c.Status != v1.ConditionTrue {
119			unreadyMessages = append(unreadyMessages, fmt.Sprintf("the status of pod readiness gate %q is not \"True\", but %v", string(rg.ConditionType), c.Status))
120		}
121	}
122
123	// Set "Ready" condition to "False" if any readiness gate is not ready.
124	if len(unreadyMessages) != 0 {
125		unreadyMessage := strings.Join(unreadyMessages, ", ")
126		return v1.PodCondition{
127			Type:    v1.PodReady,
128			Status:  v1.ConditionFalse,
129			Reason:  ReadinessGatesNotReady,
130			Message: unreadyMessage,
131		}
132	}
133
134	return v1.PodCondition{
135		Type:   v1.PodReady,
136		Status: v1.ConditionTrue,
137	}
138}
139
140// GeneratePodInitializedCondition returns initialized condition if all init containers in a pod are ready, else it
141// returns an uninitialized condition.
142func GeneratePodInitializedCondition(spec *v1.PodSpec, containerStatuses []v1.ContainerStatus, podPhase v1.PodPhase) v1.PodCondition {
143	// Find if all containers are ready or not.
144	if containerStatuses == nil && len(spec.InitContainers) > 0 {
145		return v1.PodCondition{
146			Type:   v1.PodInitialized,
147			Status: v1.ConditionFalse,
148			Reason: UnknownContainerStatuses,
149		}
150	}
151	unknownContainers := []string{}
152	unreadyContainers := []string{}
153	for _, container := range spec.InitContainers {
154		if containerStatus, ok := podutil.GetContainerStatus(containerStatuses, container.Name); ok {
155			if !containerStatus.Ready {
156				unreadyContainers = append(unreadyContainers, container.Name)
157			}
158		} else {
159			unknownContainers = append(unknownContainers, container.Name)
160		}
161	}
162
163	// If all init containers are known and succeeded, just return PodCompleted.
164	if podPhase == v1.PodSucceeded && len(unknownContainers) == 0 {
165		return v1.PodCondition{
166			Type:   v1.PodInitialized,
167			Status: v1.ConditionTrue,
168			Reason: PodCompleted,
169		}
170	}
171
172	unreadyMessages := []string{}
173	if len(unknownContainers) > 0 {
174		unreadyMessages = append(unreadyMessages, fmt.Sprintf("containers with unknown status: %s", unknownContainers))
175	}
176	if len(unreadyContainers) > 0 {
177		unreadyMessages = append(unreadyMessages, fmt.Sprintf("containers with incomplete status: %s", unreadyContainers))
178	}
179	unreadyMessage := strings.Join(unreadyMessages, ", ")
180	if unreadyMessage != "" {
181		return v1.PodCondition{
182			Type:    v1.PodInitialized,
183			Status:  v1.ConditionFalse,
184			Reason:  ContainersNotInitialized,
185			Message: unreadyMessage,
186		}
187	}
188
189	return v1.PodCondition{
190		Type:   v1.PodInitialized,
191		Status: v1.ConditionTrue,
192	}
193}
194