k8s.io/kubernetes@v1.29.3/pkg/kubelet/status/generate.go (about) 1 /* 2 Copyright 2014 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package status 18 19 import ( 20 "fmt" 21 "strings" 22 23 v1 "k8s.io/api/core/v1" 24 podutil "k8s.io/kubernetes/pkg/api/v1/pod" 25 kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" 26 runtimeutil "k8s.io/kubernetes/pkg/kubelet/kuberuntime/util" 27 kubetypes "k8s.io/kubernetes/pkg/kubelet/types" 28 ) 29 30 const ( 31 // UnknownContainerStatuses says that all container statuses are unknown. 32 UnknownContainerStatuses = "UnknownContainerStatuses" 33 // PodCompleted says that all related containers have succeeded. 34 PodCompleted = "PodCompleted" 35 // PodFailed says that the pod has failed and as such the containers have failed. 36 PodFailed = "PodFailed" 37 // ContainersNotReady says that one or more containers are not ready. 38 ContainersNotReady = "ContainersNotReady" 39 // ContainersNotInitialized says that one or more init containers have not succeeded. 40 ContainersNotInitialized = "ContainersNotInitialized" 41 // ReadinessGatesNotReady says that one or more pod readiness gates are not ready. 42 ReadinessGatesNotReady = "ReadinessGatesNotReady" 43 ) 44 45 // GenerateContainersReadyCondition returns the status of "ContainersReady" condition. 46 // The status of "ContainersReady" condition is true when all containers are ready. 47 func GenerateContainersReadyCondition(spec *v1.PodSpec, containerStatuses []v1.ContainerStatus, podPhase v1.PodPhase) v1.PodCondition { 48 // Find if all containers are ready or not. 49 if containerStatuses == nil { 50 return v1.PodCondition{ 51 Type: v1.ContainersReady, 52 Status: v1.ConditionFalse, 53 Reason: UnknownContainerStatuses, 54 } 55 } 56 unknownContainers := []string{} 57 unreadyContainers := []string{} 58 59 for _, container := range spec.InitContainers { 60 if !kubetypes.IsRestartableInitContainer(&container) { 61 continue 62 } 63 64 if containerStatus, ok := podutil.GetContainerStatus(containerStatuses, container.Name); ok { 65 if !containerStatus.Ready { 66 unreadyContainers = append(unreadyContainers, container.Name) 67 } 68 } else { 69 unknownContainers = append(unknownContainers, container.Name) 70 } 71 } 72 73 for _, container := range spec.Containers { 74 if containerStatus, ok := podutil.GetContainerStatus(containerStatuses, container.Name); ok { 75 if !containerStatus.Ready { 76 unreadyContainers = append(unreadyContainers, container.Name) 77 } 78 } else { 79 unknownContainers = append(unknownContainers, container.Name) 80 } 81 } 82 83 // If all containers are known and succeeded, just return PodCompleted. 84 if podPhase == v1.PodSucceeded && len(unknownContainers) == 0 { 85 return generateContainersReadyConditionForTerminalPhase(podPhase) 86 } 87 88 // If the pod phase is failed, explicitly set the ready condition to false for containers since they may be in progress of terminating. 89 if podPhase == v1.PodFailed { 90 return generateContainersReadyConditionForTerminalPhase(podPhase) 91 } 92 93 // Generate message for containers in unknown condition. 94 unreadyMessages := []string{} 95 if len(unknownContainers) > 0 { 96 unreadyMessages = append(unreadyMessages, fmt.Sprintf("containers with unknown status: %s", unknownContainers)) 97 } 98 if len(unreadyContainers) > 0 { 99 unreadyMessages = append(unreadyMessages, fmt.Sprintf("containers with unready status: %s", unreadyContainers)) 100 } 101 unreadyMessage := strings.Join(unreadyMessages, ", ") 102 if unreadyMessage != "" { 103 return v1.PodCondition{ 104 Type: v1.ContainersReady, 105 Status: v1.ConditionFalse, 106 Reason: ContainersNotReady, 107 Message: unreadyMessage, 108 } 109 } 110 111 return v1.PodCondition{ 112 Type: v1.ContainersReady, 113 Status: v1.ConditionTrue, 114 } 115 } 116 117 // GeneratePodReadyCondition returns "Ready" condition of a pod. 118 // The status of "Ready" condition is "True", if all containers in a pod are ready 119 // AND all matching conditions specified in the ReadinessGates have status equal to "True". 120 func GeneratePodReadyCondition(spec *v1.PodSpec, conditions []v1.PodCondition, containerStatuses []v1.ContainerStatus, podPhase v1.PodPhase) v1.PodCondition { 121 containersReady := GenerateContainersReadyCondition(spec, containerStatuses, podPhase) 122 // If the status of ContainersReady is not True, return the same status, reason and message as ContainersReady. 123 if containersReady.Status != v1.ConditionTrue { 124 return v1.PodCondition{ 125 Type: v1.PodReady, 126 Status: containersReady.Status, 127 Reason: containersReady.Reason, 128 Message: containersReady.Message, 129 } 130 } 131 132 // Evaluate corresponding conditions specified in readiness gate 133 // Generate message if any readiness gate is not satisfied. 134 unreadyMessages := []string{} 135 for _, rg := range spec.ReadinessGates { 136 _, c := podutil.GetPodConditionFromList(conditions, rg.ConditionType) 137 if c == nil { 138 unreadyMessages = append(unreadyMessages, fmt.Sprintf("corresponding condition of pod readiness gate %q does not exist.", string(rg.ConditionType))) 139 } else if c.Status != v1.ConditionTrue { 140 unreadyMessages = append(unreadyMessages, fmt.Sprintf("the status of pod readiness gate %q is not \"True\", but %v", string(rg.ConditionType), c.Status)) 141 } 142 } 143 144 // Set "Ready" condition to "False" if any readiness gate is not ready. 145 if len(unreadyMessages) != 0 { 146 unreadyMessage := strings.Join(unreadyMessages, ", ") 147 return v1.PodCondition{ 148 Type: v1.PodReady, 149 Status: v1.ConditionFalse, 150 Reason: ReadinessGatesNotReady, 151 Message: unreadyMessage, 152 } 153 } 154 155 return v1.PodCondition{ 156 Type: v1.PodReady, 157 Status: v1.ConditionTrue, 158 } 159 } 160 161 func isInitContainerInitialized(initContainer *v1.Container, containerStatus *v1.ContainerStatus) bool { 162 if kubetypes.IsRestartableInitContainer(initContainer) { 163 if containerStatus.Started == nil || !*containerStatus.Started { 164 return false 165 } 166 } else { // regular init container 167 if !containerStatus.Ready { 168 return false 169 } 170 } 171 return true 172 } 173 174 // GeneratePodInitializedCondition returns initialized condition if all init containers in a pod are ready, else it 175 // returns an uninitialized condition. 176 func GeneratePodInitializedCondition(spec *v1.PodSpec, containerStatuses []v1.ContainerStatus, podPhase v1.PodPhase) v1.PodCondition { 177 // Find if all containers are ready or not. 178 if containerStatuses == nil && len(spec.InitContainers) > 0 { 179 return v1.PodCondition{ 180 Type: v1.PodInitialized, 181 Status: v1.ConditionFalse, 182 Reason: UnknownContainerStatuses, 183 } 184 } 185 186 unknownContainers := []string{} 187 incompleteContainers := []string{} 188 for _, container := range spec.InitContainers { 189 containerStatus, ok := podutil.GetContainerStatus(containerStatuses, container.Name) 190 if !ok { 191 unknownContainers = append(unknownContainers, container.Name) 192 continue 193 } 194 if !isInitContainerInitialized(&container, &containerStatus) { 195 incompleteContainers = append(incompleteContainers, container.Name) 196 } 197 } 198 199 // If all init containers are known and succeeded, just return PodCompleted. 200 if podPhase == v1.PodSucceeded && len(unknownContainers) == 0 { 201 return v1.PodCondition{ 202 Type: v1.PodInitialized, 203 Status: v1.ConditionTrue, 204 Reason: PodCompleted, 205 } 206 } 207 208 // If there is any regular container that has started, then the pod has 209 // been initialized before. 210 // This is needed to handle the case where the pod has been initialized but 211 // the restartable init containers are restarting. 212 if kubecontainer.HasAnyRegularContainerStarted(spec, containerStatuses) { 213 return v1.PodCondition{ 214 Type: v1.PodInitialized, 215 Status: v1.ConditionTrue, 216 } 217 } 218 219 unreadyMessages := make([]string, 0, len(unknownContainers)+len(incompleteContainers)) 220 if len(unknownContainers) > 0 { 221 unreadyMessages = append(unreadyMessages, fmt.Sprintf("containers with unknown status: %s", unknownContainers)) 222 } 223 if len(incompleteContainers) > 0 { 224 unreadyMessages = append(unreadyMessages, fmt.Sprintf("containers with incomplete status: %s", incompleteContainers)) 225 } 226 unreadyMessage := strings.Join(unreadyMessages, ", ") 227 if unreadyMessage != "" { 228 return v1.PodCondition{ 229 Type: v1.PodInitialized, 230 Status: v1.ConditionFalse, 231 Reason: ContainersNotInitialized, 232 Message: unreadyMessage, 233 } 234 } 235 236 return v1.PodCondition{ 237 Type: v1.PodInitialized, 238 Status: v1.ConditionTrue, 239 } 240 } 241 242 func GeneratePodReadyToStartContainersCondition(pod *v1.Pod, podStatus *kubecontainer.PodStatus) v1.PodCondition { 243 newSandboxNeeded, _, _ := runtimeutil.PodSandboxChanged(pod, podStatus) 244 // if a new sandbox does not need to be created for a pod, it indicates that 245 // a sandbox for the pod with networking configured already exists. 246 // Otherwise, the kubelet needs to invoke the container runtime to create a 247 // fresh sandbox and configure networking for the sandbox. 248 if !newSandboxNeeded { 249 return v1.PodCondition{ 250 Type: v1.PodReadyToStartContainers, 251 Status: v1.ConditionTrue, 252 } 253 } 254 return v1.PodCondition{ 255 Type: v1.PodReadyToStartContainers, 256 Status: v1.ConditionFalse, 257 } 258 } 259 260 func generateContainersReadyConditionForTerminalPhase(podPhase v1.PodPhase) v1.PodCondition { 261 condition := v1.PodCondition{ 262 Type: v1.ContainersReady, 263 Status: v1.ConditionFalse, 264 } 265 266 if podPhase == v1.PodFailed { 267 condition.Reason = PodFailed 268 } else if podPhase == v1.PodSucceeded { 269 condition.Reason = PodCompleted 270 } 271 272 return condition 273 } 274 275 func generatePodReadyConditionForTerminalPhase(podPhase v1.PodPhase) v1.PodCondition { 276 condition := v1.PodCondition{ 277 Type: v1.PodReady, 278 Status: v1.ConditionFalse, 279 } 280 281 if podPhase == v1.PodFailed { 282 condition.Reason = PodFailed 283 } else if podPhase == v1.PodSucceeded { 284 condition.Reason = PodCompleted 285 } 286 287 return condition 288 }