mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-01-19 11:19:30 +00:00
288 lines
9.7 KiB
Go
288 lines
9.7 KiB
Go
|
/*
|
||
|
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 status
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"strings"
|
||
|
|
||
|
v1 "k8s.io/api/core/v1"
|
||
|
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
|
||
|
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||
|
runtimeutil "k8s.io/kubernetes/pkg/kubelet/kuberuntime/util"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
// UnknownContainerStatuses says that all container statuses are unknown.
|
||
|
UnknownContainerStatuses = "UnknownContainerStatuses"
|
||
|
// PodCompleted says that all related containers have succeeded.
|
||
|
PodCompleted = "PodCompleted"
|
||
|
// PodFailed says that the pod has failed and as such the containers have failed.
|
||
|
PodFailed = "PodFailed"
|
||
|
// ContainersNotReady says that one or more containers are not ready.
|
||
|
ContainersNotReady = "ContainersNotReady"
|
||
|
// ContainersNotInitialized says that one or more init containers have not succeeded.
|
||
|
ContainersNotInitialized = "ContainersNotInitialized"
|
||
|
// ReadinessGatesNotReady says that one or more pod readiness gates are not ready.
|
||
|
ReadinessGatesNotReady = "ReadinessGatesNotReady"
|
||
|
)
|
||
|
|
||
|
// GenerateContainersReadyCondition returns the status of "ContainersReady" condition.
|
||
|
// The status of "ContainersReady" condition is true when all containers are ready.
|
||
|
func GenerateContainersReadyCondition(spec *v1.PodSpec, containerStatuses []v1.ContainerStatus, podPhase v1.PodPhase) v1.PodCondition {
|
||
|
// Find if all containers are ready or not.
|
||
|
if containerStatuses == nil {
|
||
|
return v1.PodCondition{
|
||
|
Type: v1.ContainersReady,
|
||
|
Status: v1.ConditionFalse,
|
||
|
Reason: UnknownContainerStatuses,
|
||
|
}
|
||
|
}
|
||
|
unknownContainers := []string{}
|
||
|
unreadyContainers := []string{}
|
||
|
|
||
|
for _, container := range spec.InitContainers {
|
||
|
if !podutil.IsRestartableInitContainer(&container) {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
if containerStatus, ok := podutil.GetContainerStatus(containerStatuses, container.Name); ok {
|
||
|
if !containerStatus.Ready {
|
||
|
unreadyContainers = append(unreadyContainers, container.Name)
|
||
|
}
|
||
|
} else {
|
||
|
unknownContainers = append(unknownContainers, container.Name)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for _, container := range spec.Containers {
|
||
|
if containerStatus, ok := podutil.GetContainerStatus(containerStatuses, container.Name); ok {
|
||
|
if !containerStatus.Ready {
|
||
|
unreadyContainers = append(unreadyContainers, container.Name)
|
||
|
}
|
||
|
} else {
|
||
|
unknownContainers = append(unknownContainers, container.Name)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If all containers are known and succeeded, just return PodCompleted.
|
||
|
if podPhase == v1.PodSucceeded && len(unknownContainers) == 0 {
|
||
|
return generateContainersReadyConditionForTerminalPhase(podPhase)
|
||
|
}
|
||
|
|
||
|
// If the pod phase is failed, explicitly set the ready condition to false for containers since they may be in progress of terminating.
|
||
|
if podPhase == v1.PodFailed {
|
||
|
return generateContainersReadyConditionForTerminalPhase(podPhase)
|
||
|
}
|
||
|
|
||
|
// Generate message for containers in unknown condition.
|
||
|
unreadyMessages := []string{}
|
||
|
if len(unknownContainers) > 0 {
|
||
|
unreadyMessages = append(unreadyMessages, fmt.Sprintf("containers with unknown status: %s", unknownContainers))
|
||
|
}
|
||
|
if len(unreadyContainers) > 0 {
|
||
|
unreadyMessages = append(unreadyMessages, fmt.Sprintf("containers with unready status: %s", unreadyContainers))
|
||
|
}
|
||
|
unreadyMessage := strings.Join(unreadyMessages, ", ")
|
||
|
if unreadyMessage != "" {
|
||
|
return v1.PodCondition{
|
||
|
Type: v1.ContainersReady,
|
||
|
Status: v1.ConditionFalse,
|
||
|
Reason: ContainersNotReady,
|
||
|
Message: unreadyMessage,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return v1.PodCondition{
|
||
|
Type: v1.ContainersReady,
|
||
|
Status: v1.ConditionTrue,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// GeneratePodReadyCondition returns "Ready" condition of a pod.
|
||
|
// The status of "Ready" condition is "True", if all containers in a pod are ready
|
||
|
// AND all matching conditions specified in the ReadinessGates have status equal to "True".
|
||
|
func GeneratePodReadyCondition(spec *v1.PodSpec, conditions []v1.PodCondition, containerStatuses []v1.ContainerStatus, podPhase v1.PodPhase) v1.PodCondition {
|
||
|
containersReady := GenerateContainersReadyCondition(spec, containerStatuses, podPhase)
|
||
|
// If the status of ContainersReady is not True, return the same status, reason and message as ContainersReady.
|
||
|
if containersReady.Status != v1.ConditionTrue {
|
||
|
return v1.PodCondition{
|
||
|
Type: v1.PodReady,
|
||
|
Status: containersReady.Status,
|
||
|
Reason: containersReady.Reason,
|
||
|
Message: containersReady.Message,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Evaluate corresponding conditions specified in readiness gate
|
||
|
// Generate message if any readiness gate is not satisfied.
|
||
|
unreadyMessages := []string{}
|
||
|
for _, rg := range spec.ReadinessGates {
|
||
|
_, c := podutil.GetPodConditionFromList(conditions, rg.ConditionType)
|
||
|
if c == nil {
|
||
|
unreadyMessages = append(unreadyMessages, fmt.Sprintf("corresponding condition of pod readiness gate %q does not exist.", string(rg.ConditionType)))
|
||
|
} else if c.Status != v1.ConditionTrue {
|
||
|
unreadyMessages = append(unreadyMessages, fmt.Sprintf("the status of pod readiness gate %q is not \"True\", but %v", string(rg.ConditionType), c.Status))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Set "Ready" condition to "False" if any readiness gate is not ready.
|
||
|
if len(unreadyMessages) != 0 {
|
||
|
unreadyMessage := strings.Join(unreadyMessages, ", ")
|
||
|
return v1.PodCondition{
|
||
|
Type: v1.PodReady,
|
||
|
Status: v1.ConditionFalse,
|
||
|
Reason: ReadinessGatesNotReady,
|
||
|
Message: unreadyMessage,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return v1.PodCondition{
|
||
|
Type: v1.PodReady,
|
||
|
Status: v1.ConditionTrue,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func isInitContainerInitialized(initContainer *v1.Container, containerStatus *v1.ContainerStatus) bool {
|
||
|
if podutil.IsRestartableInitContainer(initContainer) {
|
||
|
if containerStatus.Started == nil || !*containerStatus.Started {
|
||
|
return false
|
||
|
}
|
||
|
} else { // regular init container
|
||
|
if !containerStatus.Ready {
|
||
|
return false
|
||
|
}
|
||
|
}
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
// GeneratePodInitializedCondition returns initialized condition if all init containers in a pod are ready, else it
|
||
|
// returns an uninitialized condition.
|
||
|
func GeneratePodInitializedCondition(spec *v1.PodSpec, containerStatuses []v1.ContainerStatus, podPhase v1.PodPhase) v1.PodCondition {
|
||
|
// Find if all containers are ready or not.
|
||
|
if containerStatuses == nil && len(spec.InitContainers) > 0 {
|
||
|
return v1.PodCondition{
|
||
|
Type: v1.PodInitialized,
|
||
|
Status: v1.ConditionFalse,
|
||
|
Reason: UnknownContainerStatuses,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
unknownContainers := []string{}
|
||
|
incompleteContainers := []string{}
|
||
|
for _, container := range spec.InitContainers {
|
||
|
containerStatus, ok := podutil.GetContainerStatus(containerStatuses, container.Name)
|
||
|
if !ok {
|
||
|
unknownContainers = append(unknownContainers, container.Name)
|
||
|
continue
|
||
|
}
|
||
|
if !isInitContainerInitialized(&container, &containerStatus) {
|
||
|
incompleteContainers = append(incompleteContainers, container.Name)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If all init containers are known and succeeded, just return PodCompleted.
|
||
|
if podPhase == v1.PodSucceeded && len(unknownContainers) == 0 {
|
||
|
return v1.PodCondition{
|
||
|
Type: v1.PodInitialized,
|
||
|
Status: v1.ConditionTrue,
|
||
|
Reason: PodCompleted,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If there is any regular container that has started, then the pod has
|
||
|
// been initialized before.
|
||
|
// This is needed to handle the case where the pod has been initialized but
|
||
|
// the restartable init containers are restarting.
|
||
|
if kubecontainer.HasAnyRegularContainerStarted(spec, containerStatuses) {
|
||
|
return v1.PodCondition{
|
||
|
Type: v1.PodInitialized,
|
||
|
Status: v1.ConditionTrue,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
unreadyMessages := make([]string, 0, len(unknownContainers)+len(incompleteContainers))
|
||
|
if len(unknownContainers) > 0 {
|
||
|
unreadyMessages = append(unreadyMessages, fmt.Sprintf("containers with unknown status: %s", unknownContainers))
|
||
|
}
|
||
|
if len(incompleteContainers) > 0 {
|
||
|
unreadyMessages = append(unreadyMessages, fmt.Sprintf("containers with incomplete status: %s", incompleteContainers))
|
||
|
}
|
||
|
unreadyMessage := strings.Join(unreadyMessages, ", ")
|
||
|
if unreadyMessage != "" {
|
||
|
return v1.PodCondition{
|
||
|
Type: v1.PodInitialized,
|
||
|
Status: v1.ConditionFalse,
|
||
|
Reason: ContainersNotInitialized,
|
||
|
Message: unreadyMessage,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return v1.PodCondition{
|
||
|
Type: v1.PodInitialized,
|
||
|
Status: v1.ConditionTrue,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func GeneratePodReadyToStartContainersCondition(pod *v1.Pod, podStatus *kubecontainer.PodStatus) v1.PodCondition {
|
||
|
newSandboxNeeded, _, _ := runtimeutil.PodSandboxChanged(pod, podStatus)
|
||
|
// if a new sandbox does not need to be created for a pod, it indicates that
|
||
|
// a sandbox for the pod with networking configured already exists.
|
||
|
// Otherwise, the kubelet needs to invoke the container runtime to create a
|
||
|
// fresh sandbox and configure networking for the sandbox.
|
||
|
if !newSandboxNeeded {
|
||
|
return v1.PodCondition{
|
||
|
Type: v1.PodReadyToStartContainers,
|
||
|
Status: v1.ConditionTrue,
|
||
|
}
|
||
|
}
|
||
|
return v1.PodCondition{
|
||
|
Type: v1.PodReadyToStartContainers,
|
||
|
Status: v1.ConditionFalse,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func generateContainersReadyConditionForTerminalPhase(podPhase v1.PodPhase) v1.PodCondition {
|
||
|
condition := v1.PodCondition{
|
||
|
Type: v1.ContainersReady,
|
||
|
Status: v1.ConditionFalse,
|
||
|
}
|
||
|
|
||
|
if podPhase == v1.PodFailed {
|
||
|
condition.Reason = PodFailed
|
||
|
} else if podPhase == v1.PodSucceeded {
|
||
|
condition.Reason = PodCompleted
|
||
|
}
|
||
|
|
||
|
return condition
|
||
|
}
|
||
|
|
||
|
func generatePodReadyConditionForTerminalPhase(podPhase v1.PodPhase) v1.PodCondition {
|
||
|
condition := v1.PodCondition{
|
||
|
Type: v1.PodReady,
|
||
|
Status: v1.ConditionFalse,
|
||
|
}
|
||
|
|
||
|
if podPhase == v1.PodFailed {
|
||
|
condition.Reason = PodFailed
|
||
|
} else if podPhase == v1.PodSucceeded {
|
||
|
condition.Reason = PodCompleted
|
||
|
}
|
||
|
|
||
|
return condition
|
||
|
}
|