Sidecar: Update printPod to show restartable init container information

This commit is contained in:
Gunju Kim 2023-05-10 02:00:47 +09:00
parent ea1eb7f8f7
commit 0a98707912
No known key found for this signature in database
GPG Key ID: 9300A528F3F0DAB7
2 changed files with 245 additions and 3 deletions

View File

@ -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
}

View File

@ -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"