mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-06 02:34:03 +00:00
Sidecar: Update printPod to show restartable init container information
This commit is contained in:
parent
ea1eb7f8f7
commit
0a98707912
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user