Sidecar: Update printPod to show restartable init container information
This commit is contained in:
		@@ -815,9 +815,11 @@ func printPodList(podList *api.PodList, options printers.GenerateOptions) ([]met
 | 
			
		||||
 | 
			
		||||
func printPod(pod *api.Pod, options printers.GenerateOptions) ([]metav1.TableRow, error) {
 | 
			
		||||
	restarts := 0
 | 
			
		||||
	restartableInitContainerRestarts := 0
 | 
			
		||||
	totalContainers := len(pod.Spec.Containers)
 | 
			
		||||
	readyContainers := 0
 | 
			
		||||
	lastRestartDate := metav1.NewTime(time.Time{})
 | 
			
		||||
	lastRestartableInitContainerRestartDate := metav1.NewTime(time.Time{})
 | 
			
		||||
 | 
			
		||||
	reason := string(pod.Status.Phase)
 | 
			
		||||
	if pod.Status.Reason != "" {
 | 
			
		||||
@@ -842,6 +844,14 @@ func printPod(pod *api.Pod, options printers.GenerateOptions) ([]metav1.TableRow
 | 
			
		||||
		row.Conditions = podFailedConditions
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	initContainers := make(map[string]*api.Container)
 | 
			
		||||
	for i := range pod.Spec.InitContainers {
 | 
			
		||||
		initContainers[pod.Spec.InitContainers[i].Name] = &pod.Spec.InitContainers[i]
 | 
			
		||||
		if isRestartableInitContainer(&pod.Spec.InitContainers[i]) {
 | 
			
		||||
			totalContainers++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	initializing := false
 | 
			
		||||
	for i := range pod.Status.InitContainerStatuses {
 | 
			
		||||
		container := pod.Status.InitContainerStatuses[i]
 | 
			
		||||
@@ -852,9 +862,24 @@ func printPod(pod *api.Pod, options printers.GenerateOptions) ([]metav1.TableRow
 | 
			
		||||
				lastRestartDate = terminatedDate
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if isRestartableInitContainer(initContainers[container.Name]) {
 | 
			
		||||
			restartableInitContainerRestarts += int(container.RestartCount)
 | 
			
		||||
			if container.LastTerminationState.Terminated != nil {
 | 
			
		||||
				terminatedDate := container.LastTerminationState.Terminated.FinishedAt
 | 
			
		||||
				if lastRestartableInitContainerRestartDate.Before(&terminatedDate) {
 | 
			
		||||
					lastRestartableInitContainerRestartDate = terminatedDate
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		switch {
 | 
			
		||||
		case container.State.Terminated != nil && container.State.Terminated.ExitCode == 0:
 | 
			
		||||
			continue
 | 
			
		||||
		case isRestartableInitContainer(initContainers[container.Name]) &&
 | 
			
		||||
			container.Started != nil && *container.Started:
 | 
			
		||||
			if container.Ready {
 | 
			
		||||
				readyContainers++
 | 
			
		||||
			}
 | 
			
		||||
			continue
 | 
			
		||||
		case container.State.Terminated != nil:
 | 
			
		||||
			// initialization is failed
 | 
			
		||||
			if len(container.State.Terminated.Reason) == 0 {
 | 
			
		||||
@@ -876,8 +901,10 @@ func printPod(pod *api.Pod, options printers.GenerateOptions) ([]metav1.TableRow
 | 
			
		||||
		}
 | 
			
		||||
		break
 | 
			
		||||
	}
 | 
			
		||||
	if !initializing {
 | 
			
		||||
		restarts = 0
 | 
			
		||||
 | 
			
		||||
	if !initializing || isPodInitializedConditionTrue(&pod.Status) {
 | 
			
		||||
		restarts = restartableInitContainerRestarts
 | 
			
		||||
		lastRestartDate = lastRestartableInitContainerRestartDate
 | 
			
		||||
		hasRunning := false
 | 
			
		||||
		for i := len(pod.Status.ContainerStatuses) - 1; i >= 0; i-- {
 | 
			
		||||
			container := pod.Status.ContainerStatuses[i]
 | 
			
		||||
@@ -922,7 +949,7 @@ func printPod(pod *api.Pod, options printers.GenerateOptions) ([]metav1.TableRow
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	restartsStr := strconv.Itoa(restarts)
 | 
			
		||||
	if !lastRestartDate.IsZero() {
 | 
			
		||||
	if restarts != 0 && !lastRestartDate.IsZero() {
 | 
			
		||||
		restartsStr = fmt.Sprintf("%d (%s ago)", restarts, translateTimestampSince(lastRestartDate))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -2996,3 +3023,22 @@ func (list SortableResourceNames) Swap(i, j int) {
 | 
			
		||||
func (list SortableResourceNames) Less(i, j int) bool {
 | 
			
		||||
	return list[i] < list[j]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isRestartableInitContainer(initContainer *api.Container) bool {
 | 
			
		||||
	if initContainer.RestartPolicy == nil {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return *initContainer.RestartPolicy == api.ContainerRestartPolicyAlways
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isPodInitializedConditionTrue(status *api.PodStatus) bool {
 | 
			
		||||
	for _, condition := range status.Conditions {
 | 
			
		||||
		if condition.Type != api.PodInitialized {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return condition.Status == api.ConditionTrue
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -50,6 +50,8 @@ import (
 | 
			
		||||
	utilpointer "k8s.io/utils/pointer"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var containerRestartPolicyAlways = api.ContainerRestartPolicyAlways
 | 
			
		||||
 | 
			
		||||
func TestFormatResourceName(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		kind schema.GroupKind
 | 
			
		||||
@@ -1536,6 +1538,200 @@ func TestPrintPod(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestPrintPodWithRestartableInitContainer(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		pod    api.Pod
 | 
			
		||||
		expect []metav1.TableRow
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			// Test pod has 2 restartable init containers, the first one running but not started.
 | 
			
		||||
			api.Pod{
 | 
			
		||||
				ObjectMeta: metav1.ObjectMeta{Name: "test1"},
 | 
			
		||||
				Spec: api.PodSpec{
 | 
			
		||||
					InitContainers: []api.Container{
 | 
			
		||||
						{Name: "restartable-init-1", RestartPolicy: &containerRestartPolicyAlways},
 | 
			
		||||
						{Name: "restartable-init-2", RestartPolicy: &containerRestartPolicyAlways},
 | 
			
		||||
					}, Containers: make([]api.Container, 1)},
 | 
			
		||||
				Status: api.PodStatus{
 | 
			
		||||
					Phase: "Pending",
 | 
			
		||||
					InitContainerStatuses: []api.ContainerStatus{
 | 
			
		||||
						{
 | 
			
		||||
							Name:                 "restartable-init-1",
 | 
			
		||||
							Ready:                false,
 | 
			
		||||
							RestartCount:         3,
 | 
			
		||||
							State:                api.ContainerState{Running: &api.ContainerStateRunning{}},
 | 
			
		||||
							Started:              utilpointer.Bool(false),
 | 
			
		||||
							LastTerminationState: api.ContainerState{Terminated: &api.ContainerStateTerminated{FinishedAt: metav1.NewTime(time.Now().Add(-10 * time.Second))}},
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							Name:    "restartable-init-2",
 | 
			
		||||
							Ready:   false,
 | 
			
		||||
							State:   api.ContainerState{Waiting: &api.ContainerStateWaiting{}},
 | 
			
		||||
							Started: utilpointer.Bool(false),
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					ContainerStatuses: []api.ContainerStatus{
 | 
			
		||||
						{
 | 
			
		||||
							Ready:        false,
 | 
			
		||||
							RestartCount: 0,
 | 
			
		||||
							State:        api.ContainerState{Waiting: &api.ContainerStateWaiting{}},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					Conditions: []api.PodCondition{
 | 
			
		||||
						{Type: api.PodInitialized, Status: api.ConditionFalse},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			[]metav1.TableRow{{Cells: []interface{}{"test1", "0/3", "Init:0/2", "3 (10s ago)", "<unknown>"}}},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			// Test pod has 2 restartable init containers, the first one started and the second one running but not started.
 | 
			
		||||
			api.Pod{
 | 
			
		||||
				ObjectMeta: metav1.ObjectMeta{Name: "test1"},
 | 
			
		||||
				Spec: api.PodSpec{
 | 
			
		||||
					InitContainers: []api.Container{
 | 
			
		||||
						{Name: "restartable-init-1", RestartPolicy: &containerRestartPolicyAlways},
 | 
			
		||||
						{Name: "restartable-init-2", RestartPolicy: &containerRestartPolicyAlways},
 | 
			
		||||
					}, Containers: make([]api.Container, 1)},
 | 
			
		||||
				Status: api.PodStatus{
 | 
			
		||||
					Phase: "Pending",
 | 
			
		||||
					InitContainerStatuses: []api.ContainerStatus{
 | 
			
		||||
						{
 | 
			
		||||
							Name:                 "restartable-init-1",
 | 
			
		||||
							Ready:                false,
 | 
			
		||||
							RestartCount:         3,
 | 
			
		||||
							State:                api.ContainerState{Running: &api.ContainerStateRunning{}},
 | 
			
		||||
							Started:              utilpointer.Bool(true),
 | 
			
		||||
							LastTerminationState: api.ContainerState{Terminated: &api.ContainerStateTerminated{FinishedAt: metav1.NewTime(time.Now().Add(-10 * time.Second))}},
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							Name:    "restartable-init-2",
 | 
			
		||||
							Ready:   false,
 | 
			
		||||
							State:   api.ContainerState{Running: &api.ContainerStateRunning{}},
 | 
			
		||||
							Started: utilpointer.Bool(false),
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					ContainerStatuses: []api.ContainerStatus{
 | 
			
		||||
						{
 | 
			
		||||
							Ready:        false,
 | 
			
		||||
							RestartCount: 0,
 | 
			
		||||
							State:        api.ContainerState{Waiting: &api.ContainerStateWaiting{}},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					Conditions: []api.PodCondition{
 | 
			
		||||
						{Type: api.PodInitialized, Status: api.ConditionFalse},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			[]metav1.TableRow{{Cells: []interface{}{"test1", "0/3", "Init:1/2", "3 (10s ago)", "<unknown>"}}},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			// Test pod has 2 restartable init containers started and 1 container running
 | 
			
		||||
			api.Pod{
 | 
			
		||||
				ObjectMeta: metav1.ObjectMeta{Name: "test2"},
 | 
			
		||||
				Spec: api.PodSpec{
 | 
			
		||||
					InitContainers: []api.Container{
 | 
			
		||||
						{Name: "restartable-init-1", RestartPolicy: &containerRestartPolicyAlways},
 | 
			
		||||
						{Name: "restartable-init-2", RestartPolicy: &containerRestartPolicyAlways},
 | 
			
		||||
					}, Containers: make([]api.Container, 1)},
 | 
			
		||||
				Status: api.PodStatus{
 | 
			
		||||
					Phase: "Running",
 | 
			
		||||
					InitContainerStatuses: []api.ContainerStatus{
 | 
			
		||||
						{
 | 
			
		||||
							Name:                 "restartable-init-1",
 | 
			
		||||
							Ready:                false,
 | 
			
		||||
							RestartCount:         3,
 | 
			
		||||
							State:                api.ContainerState{Running: &api.ContainerStateRunning{}},
 | 
			
		||||
							Started:              utilpointer.Bool(true),
 | 
			
		||||
							LastTerminationState: api.ContainerState{Terminated: &api.ContainerStateTerminated{FinishedAt: metav1.NewTime(time.Now().Add(-10 * time.Second))}},
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							Name:    "restartable-init-2",
 | 
			
		||||
							Ready:   false,
 | 
			
		||||
							State:   api.ContainerState{Running: &api.ContainerStateRunning{}},
 | 
			
		||||
							Started: utilpointer.Bool(true),
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					ContainerStatuses: []api.ContainerStatus{
 | 
			
		||||
						{
 | 
			
		||||
							Ready:                true,
 | 
			
		||||
							RestartCount:         4,
 | 
			
		||||
							State:                api.ContainerState{Running: &api.ContainerStateRunning{}},
 | 
			
		||||
							LastTerminationState: api.ContainerState{Terminated: &api.ContainerStateTerminated{FinishedAt: metav1.NewTime(time.Now().Add(-20 * time.Second))}},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					Conditions: []api.PodCondition{
 | 
			
		||||
						{Type: api.PodInitialized, Status: api.ConditionTrue},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			[]metav1.TableRow{{Cells: []interface{}{"test2", "1/3", "Running", "7 (10s ago)", "<unknown>"}}},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			// Test pod has 2 restartable init containers completed with non-zero and 1 container completed
 | 
			
		||||
			api.Pod{
 | 
			
		||||
				ObjectMeta: metav1.ObjectMeta{Name: "test3"},
 | 
			
		||||
				Spec: api.PodSpec{
 | 
			
		||||
					InitContainers: []api.Container{
 | 
			
		||||
						{Name: "restartable-init-1", RestartPolicy: &containerRestartPolicyAlways},
 | 
			
		||||
						{Name: "restartable-init-2", RestartPolicy: &containerRestartPolicyAlways},
 | 
			
		||||
					}, Containers: make([]api.Container, 1)},
 | 
			
		||||
				Status: api.PodStatus{
 | 
			
		||||
					Phase: "Succeeded",
 | 
			
		||||
					InitContainerStatuses: []api.ContainerStatus{
 | 
			
		||||
						{
 | 
			
		||||
							Name:                 "restartable-init-1",
 | 
			
		||||
							Ready:                false,
 | 
			
		||||
							RestartCount:         3,
 | 
			
		||||
							State:                api.ContainerState{Terminated: &api.ContainerStateTerminated{Reason: "Error", ExitCode: 137}},
 | 
			
		||||
							Started:              utilpointer.Bool(false),
 | 
			
		||||
							LastTerminationState: api.ContainerState{Terminated: &api.ContainerStateTerminated{FinishedAt: metav1.NewTime(time.Now().Add(-10 * time.Second))}},
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							Name:    "restartable-init-2",
 | 
			
		||||
							Ready:   false,
 | 
			
		||||
							State:   api.ContainerState{Terminated: &api.ContainerStateTerminated{Reason: "Error", ExitCode: 137}},
 | 
			
		||||
							Started: utilpointer.Bool(false),
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					ContainerStatuses: []api.ContainerStatus{
 | 
			
		||||
						{
 | 
			
		||||
							Ready:                false,
 | 
			
		||||
							RestartCount:         4,
 | 
			
		||||
							State:                api.ContainerState{Terminated: &api.ContainerStateTerminated{Reason: "Completed", ExitCode: 0}},
 | 
			
		||||
							LastTerminationState: api.ContainerState{Terminated: &api.ContainerStateTerminated{FinishedAt: metav1.NewTime(time.Now().Add(-20 * time.Second))}},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					Conditions: []api.PodCondition{
 | 
			
		||||
						{Type: api.PodInitialized, Status: api.ConditionTrue},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			[]metav1.TableRow{
 | 
			
		||||
				{
 | 
			
		||||
					Cells: []interface{}{"test3", "0/3", "Completed", "7 (10s ago)", "<unknown>"},
 | 
			
		||||
					Conditions: []metav1.TableRowCondition{
 | 
			
		||||
						{Type: metav1.RowCompleted, Status: metav1.ConditionTrue, Reason: "Succeeded", Message: "The pod has completed successfully."},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i, test := range tests {
 | 
			
		||||
		rows, err := printPod(&test.pod, printers.GenerateOptions{})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
		for i := range rows {
 | 
			
		||||
			rows[i].Object.Object = nil
 | 
			
		||||
		}
 | 
			
		||||
		if !reflect.DeepEqual(test.expect, rows) {
 | 
			
		||||
			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expect, rows))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestPrintPodwide(t *testing.T) {
 | 
			
		||||
	condition1 := "condition1"
 | 
			
		||||
	condition2 := "condition2"
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user