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  }