Change:
return status } <del> // Make the latest container status comes first. sort.Sort(sort.Reverse(kubecontainer.SortContainerStatusesByCreationTime(podStatus.ContainerStatuses))) </del> <ins> // Fetch old containers statuses from old pod status. oldStatuses := make(map[string]api.ContainerStatus, len(pod.Spec.Containers)) for _, status := range pod.Status.ContainerStatuses { oldStatuses[status.Name] = status } // Set all container statuses to default waiting state </ins> statuses := make(map[string]*api.ContainerStatus, len(pod.Spec.Containers)) <del> // Create a map of expected containers based on the pod spec. expectedContainers := make(map[string]api.Container) </del> <ins> defaultWaitingState := api.ContainerState{Waiting: &api.ContainerStateWaiting{Reason: "ContainerCreating"}} </ins> for _, container := range pod.Spec.Containers { <del> expectedContainers[container.Name] = container </del> <ins> status := &api.ContainerStatus{ Name:  container.Name, Image: container.Image, State: defaultWaitingState, } // Apply some values from the old statuses as the default values. if oldStatus, found := oldStatuses[container.Name]; found { status.RestartCount = oldStatus.RestartCount status.LastTerminationState = oldStatus.LastTerminationState } statuses[container.Name] = status </ins> } <del> containerDone := sets.NewString() apiPodStatus.PodIP = podStatus.IP for _, containerStatus := range podStatus.ContainerStatuses { cName := containerStatus.Name if _, ok := expectedContainers[cName]; !ok { </del> <ins> // Make the latest container status comes first. sort.Sort(sort.Reverse(kubecontainer.SortContainerStatusesByCreationTime(podStatus.ContainerStatuses))) // Set container statuses according to the statuses seen in pod status containerSeen := map[string]int{} for _, cStatus := range podStatus.ContainerStatuses { cName := cStatus.Name if _, ok := statuses[cName]; !ok { </ins> // This would also ignore the infra container. continue } <del> if containerDone.Has(cName) { </del> <ins> if containerSeen[cName] >= 2 { </ins> continue } <del> status := convertContainerStatus(containerStatus) if existing, found := statuses[cName]; found { existing.LastTerminationState = status.State containerDone.Insert(cName) } else { </del> <ins> status := convertContainerStatus(cStatus) if containerSeen[cName] == 0 { </ins> statuses[cName] = status <ins> } else { statuses[cName].LastTerminationState = status.State </ins> } <ins> containerSeen[cName] = containerSeen[cName] + 1 </ins> } <del> // Handle the containers for which we cannot find any associated active or dead containers or are in restart backoff // Fetch old containers statuses from old pod status. // TODO(random-liu) Maybe it's better to get status from status manager, because it takes the newest status and there is not // status in api.Pod of static pod. oldStatuses := make(map[string]api.ContainerStatus, len(pod.Spec.Containers)) for _, status := range pod.Status.ContainerStatuses { oldStatuses[status.Name] = status } </del> <ins> // Handle the containers failed to be started, which should be in Waiting state. </ins> for _, container := range pod.Spec.Containers { <del> // TODO(random-liu): We should define "Waiting" state better. And cleanup the following code. if containerStatus, found := statuses[container.Name]; found { reason, message, ok := kl.reasonCache.Get(uid, container.Name) if ok && reason == kubecontainer.ErrCrashLoopBackOff { containerStatus.LastTerminationState = containerStatus.State containerStatus.State = api.ContainerState{ Waiting: &api.ContainerStateWaiting{ Reason:  reason.Error(), Message: message, }, } } </del> <ins> // If a container should be restarted in next syncpod, it is *Waiting*. if !kubecontainer.ShouldContainerBeRestarted(&container, pod, podStatus) { </ins> continue } <del> var containerStatus api.ContainerStatus containerStatus.Name = container.Name containerStatus.Image = container.Image if oldStatus, found := oldStatuses[container.Name]; found { // Some states may be lost due to GC; apply the last observed // values if possible. containerStatus.RestartCount = oldStatus.RestartCount containerStatus.LastTerminationState = oldStatus.LastTerminationState } reason, _, ok := kl.reasonCache.Get(uid, container.Name) </del> <ins> status := statuses[container.Name] reason, message, ok := kl.reasonCache.Get(uid, container.Name) </ins> if !ok { <del> // default position for a container // At this point there are no active or dead containers, the reasonCache is empty (no entry or the entry has expired) // its reasonable to say the container is being created till a more accurate reason is logged containerStatus.State = api.ContainerState{ Waiting: &api.ContainerStateWaiting{ Reason:  fmt.Sprintf("ContainerCreating"), Message: fmt.Sprintf("Image: %s is ready, container is creating", container.Image), }, } } else if reason == kubecontainer.ErrImagePullBackOff || reason == kubecontainer.ErrImageInspect || reason == kubecontainer.ErrImagePull || reason == kubecontainer.ErrImageNeverPull || reason == kubecontainer.RegistryUnavailable { // mark it as waiting, reason will be filled bellow containerStatus.State = api.ContainerState{Waiting: &api.ContainerStateWaiting{}} } else if reason == kubecontainer.ErrRunContainer { // mark it as waiting, reason will be filled bellow containerStatus.State = api.ContainerState{Waiting: &api.ContainerStateWaiting{}} </del> <ins> // In fact, we could also apply Waiting state here, but it is less informative, // and the container will be restarted soon, so we prefer the original state here. // Note that with the current implementation of ShouldContainerBeRestarted the original state here // could be: //   * Waiting: There is no associated historical container and start failure reason record. //   * Terminated: The container is terminated. continue } if status.State.Terminated != nil { status.LastTerminationState = status.State </ins> } <del> statuses[container.Name] = &containerStatus </del> <ins> status.State = api.ContainerState{ Waiting: &api.ContainerStateWaiting{ Reason:  reason.Error(), Message: message, }, } statuses[container.Name] = status </ins> } apiPodStatus.ContainerStatuses = make([]api.ContainerStatus, 0) <del> for containerName, status := range statuses { if status.State.Waiting != nil { status.State.Running = nil // For containers in the waiting state, fill in a specific reason if it is recorded. if reason, message, ok := kl.reasonCache.Get(uid, containerName); ok { status.State.Waiting.Reason = reason.Error() status.State.Waiting.Message = message } } </del> <ins> for _, status := range statuses { </ins> apiPodStatus.ContainerStatuses = append(apiPodStatus.ContainerStatuses, *status) }

Discussion:
We moved reason cache and status convert function from to by , , and . After this, we should also migrate related old unit test to kubelet level. For we should add test for: [x] Different image pull policy. [x] Pod creation failure. [x] Pod image failure. For we should add test for: [x] ContainerStatuses order. [x] Last termination state. [x] How reason cache will affect the PodPhase: [x] Pull image failure reason [x] Pod creation failure reason
Time to close this issue? :)
Not yet, The last 3 tests are in . :)