Merge pull request #70775 from freehan/pod-ready-cli

add readiness gates in kubectl extended output for pods
pull/58/head
k8s-ci-robot 2018-11-13 14:59:18 -08:00 committed by GitHub
commit 68b4be3e19
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 106 additions and 8 deletions

View File

@ -61,7 +61,7 @@ func TestHumanReadablePrinterSupportsExpectedOptions(t *testing.T) {
{ {
name: "\"wide\" output format prints", name: "\"wide\" output format prints",
outputFormat: "wide", outputFormat: "wide",
expectedOutput: "NAME\\ +READY\\ +STATUS\\ +RESTARTS\\ +AGE\\ +IP\\ +NODE\\ +NOMINATED NODE\nfoo\\ +0/0\\ +0\\ +<unknown>\\ +<none>\\ +<none>\\ +<none>\n", expectedOutput: "NAME\\ +READY\\ +STATUS\\ +RESTARTS\\ +AGE\\ +IP\\ +NODE\\ +NOMINATED NODE\\ +READINESS GATES\nfoo\\ +0/0\\ +0\\ +<unknown>\\ +<none>\\ +<none>\\ +<none>\\ +<none>\n",
}, },
{ {
name: "no-headers prints output with no headers", name: "no-headers prints output with no headers",
@ -72,7 +72,7 @@ func TestHumanReadablePrinterSupportsExpectedOptions(t *testing.T) {
name: "no-headers and a \"wide\" output format prints output with no headers and additional columns", name: "no-headers and a \"wide\" output format prints output with no headers and additional columns",
outputFormat: "wide", outputFormat: "wide",
noHeaders: true, noHeaders: true,
expectedOutput: "foo\\ +0/0\\ +0\\ +<unknown>\\ +<none>\\ +<none>\\ +<none>\n", expectedOutput: "foo\\ +0/0\\ +0\\ +<unknown>\\ +<none>\\ +<none>\\ +<none>\\ +<none>\n",
}, },
{ {
name: "show-kind displays the resource's kind, even when printing a single type of resource", name: "show-kind displays the resource's kind, even when printing a single type of resource",

View File

@ -679,6 +679,21 @@ func describePod(pod *api.Pod, events *api.EventList) (string, error) {
describeContainers("Init Containers", pod.Spec.InitContainers, pod.Status.InitContainerStatuses, EnvValueRetriever(pod), w, "") describeContainers("Init Containers", pod.Spec.InitContainers, pod.Status.InitContainerStatuses, EnvValueRetriever(pod), w, "")
} }
describeContainers("Containers", pod.Spec.Containers, pod.Status.ContainerStatuses, EnvValueRetriever(pod), w, "") describeContainers("Containers", pod.Spec.Containers, pod.Status.ContainerStatuses, EnvValueRetriever(pod), w, "")
if len(pod.Spec.ReadinessGates) > 0 {
w.Write(LEVEL_0, "Readiness Gates:\n Type\tStatus\n")
for _, g := range pod.Spec.ReadinessGates {
status := "<none>"
for _, c := range pod.Status.Conditions {
if c.Type == g.ConditionType {
status = fmt.Sprintf("%v", c.Status)
break
}
}
w.Write(LEVEL_1, "%v \t%v \n",
g.ConditionType,
status)
}
}
if len(pod.Status.Conditions) > 0 { if len(pod.Status.Conditions) > 0 {
w.Write(LEVEL_0, "Conditions:\n Type\tStatus\n") w.Write(LEVEL_0, "Conditions:\n Type\tStatus\n")
for _, c := range pod.Status.Conditions { for _, c := range pod.Status.Conditions {

View File

@ -55,6 +55,8 @@ type describeClient struct {
func TestDescribePod(t *testing.T) { func TestDescribePod(t *testing.T) {
deletionTimestamp := metav1.Time{Time: time.Now().UTC().AddDate(10, 0, 0)} deletionTimestamp := metav1.Time{Time: time.Now().UTC().AddDate(10, 0, 0)}
gracePeriod := int64(1234) gracePeriod := int64(1234)
condition1 := api.PodConditionType("condition1")
condition2 := api.PodConditionType("condition2")
fake := fake.NewSimpleClientset(&api.Pod{ fake := fake.NewSimpleClientset(&api.Pod{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: "bar", Name: "bar",
@ -62,6 +64,24 @@ func TestDescribePod(t *testing.T) {
DeletionTimestamp: &deletionTimestamp, DeletionTimestamp: &deletionTimestamp,
DeletionGracePeriodSeconds: &gracePeriod, DeletionGracePeriodSeconds: &gracePeriod,
}, },
Spec: api.PodSpec{
ReadinessGates: []api.PodReadinessGate{
{
ConditionType: condition1,
},
{
ConditionType: condition2,
},
},
},
Status: api.PodStatus{
Conditions: []api.PodCondition{
{
Type: condition1,
Status: api.ConditionTrue,
},
},
},
}) })
c := &describeClient{T: t, Namespace: "foo", Interface: fake} c := &describeClient{T: t, Namespace: "foo", Interface: fake}
d := PodDescriber{c} d := PodDescriber{c}

View File

@ -86,6 +86,7 @@ func AddHandlers(h printers.PrintHandler) {
{Name: "IP", Type: "string", Priority: 1, Description: apiv1.PodStatus{}.SwaggerDoc()["podIP"]}, {Name: "IP", Type: "string", Priority: 1, Description: apiv1.PodStatus{}.SwaggerDoc()["podIP"]},
{Name: "Node", Type: "string", Priority: 1, Description: apiv1.PodSpec{}.SwaggerDoc()["nodeName"]}, {Name: "Node", Type: "string", Priority: 1, Description: apiv1.PodSpec{}.SwaggerDoc()["nodeName"]},
{Name: "Nominated Node", Type: "string", Priority: 1, Description: apiv1.PodStatus{}.SwaggerDoc()["nominatedNodeName"]}, {Name: "Nominated Node", Type: "string", Priority: 1, Description: apiv1.PodStatus{}.SwaggerDoc()["nominatedNodeName"]},
{Name: "Readiness Gates", Type: "string", Priority: 1, Description: apiv1.PodSpec{}.SwaggerDoc()["readinessGates"]},
} }
h.TableHandler(podColumnDefinitions, printPodList) h.TableHandler(podColumnDefinitions, printPodList)
h.TableHandler(podColumnDefinitions, printPod) h.TableHandler(podColumnDefinitions, printPod)
@ -661,11 +662,11 @@ func printPod(pod *api.Pod, options printers.PrintOptions) ([]metav1beta1.TableR
} }
row.Cells = append(row.Cells, pod.Name, fmt.Sprintf("%d/%d", readyContainers, totalContainers), reason, int64(restarts), translateTimestampSince(pod.CreationTimestamp)) row.Cells = append(row.Cells, pod.Name, fmt.Sprintf("%d/%d", readyContainers, totalContainers), reason, int64(restarts), translateTimestampSince(pod.CreationTimestamp))
if options.Wide { if options.Wide {
nodeName := pod.Spec.NodeName nodeName := pod.Spec.NodeName
nominatedNodeName := pod.Status.NominatedNodeName nominatedNodeName := pod.Status.NominatedNodeName
podIP := pod.Status.PodIP podIP := pod.Status.PodIP
if podIP == "" { if podIP == "" {
podIP = "<none>" podIP = "<none>"
} }
@ -675,7 +676,24 @@ func printPod(pod *api.Pod, options printers.PrintOptions) ([]metav1beta1.TableR
if nominatedNodeName == "" { if nominatedNodeName == "" {
nominatedNodeName = "<none>" nominatedNodeName = "<none>"
} }
row.Cells = append(row.Cells, podIP, nodeName, nominatedNodeName)
readinessGates := "<none>"
if len(pod.Spec.ReadinessGates) > 0 {
trueConditions := 0
for _, readinessGate := range pod.Spec.ReadinessGates {
conditionType := readinessGate.ConditionType
for _, condition := range pod.Status.Conditions {
if condition.Type == conditionType {
if condition.Status == api.ConditionTrue {
trueConditions += 1
}
break
}
}
}
readinessGates = fmt.Sprintf("%d/%d", trueConditions, len(pod.Spec.ReadinessGates))
}
row.Cells = append(row.Cells, podIP, nodeName, nominatedNodeName, readinessGates)
} }
return []metav1beta1.TableRow{row}, nil return []metav1beta1.TableRow{row}, nil

View File

@ -1649,6 +1649,9 @@ func TestPrintPod(t *testing.T) {
} }
func TestPrintPodwide(t *testing.T) { func TestPrintPodwide(t *testing.T) {
condition1 := "condition1"
condition2 := "condition2"
condition3 := "condition3"
tests := []struct { tests := []struct {
pod api.Pod pod api.Pod
expect []metav1beta1.TableRow expect []metav1beta1.TableRow
@ -1660,8 +1663,29 @@ func TestPrintPodwide(t *testing.T) {
Spec: api.PodSpec{ Spec: api.PodSpec{
Containers: make([]api.Container, 2), Containers: make([]api.Container, 2),
NodeName: "test1", NodeName: "test1",
ReadinessGates: []api.PodReadinessGate{
{
ConditionType: api.PodConditionType(condition1),
},
{
ConditionType: api.PodConditionType(condition2),
},
{
ConditionType: api.PodConditionType(condition3),
},
},
}, },
Status: api.PodStatus{ Status: api.PodStatus{
Conditions: []api.PodCondition{
{
Type: api.PodConditionType(condition1),
Status: api.ConditionFalse,
},
{
Type: api.PodConditionType(condition2),
Status: api.ConditionTrue,
},
},
Phase: "podPhase", Phase: "podPhase",
PodIP: "1.1.1.1", PodIP: "1.1.1.1",
ContainerStatuses: []api.ContainerStatus{ ContainerStatuses: []api.ContainerStatus{
@ -1671,7 +1695,7 @@ func TestPrintPodwide(t *testing.T) {
NominatedNodeName: "node1", NominatedNodeName: "node1",
}, },
}, },
[]metav1beta1.TableRow{{Cells: []interface{}{"test1", "1/2", "podPhase", int64(6), "<unknown>", "1.1.1.1", "test1", "node1"}}}, []metav1beta1.TableRow{{Cells: []interface{}{"test1", "1/2", "podPhase", int64(6), "<unknown>", "1.1.1.1", "test1", "node1", "1/3"}}},
}, },
{ {
// Test when the NodeName and PodIP are none // Test when the NodeName and PodIP are none
@ -1690,7 +1714,7 @@ func TestPrintPodwide(t *testing.T) {
}, },
}, },
}, },
[]metav1beta1.TableRow{{Cells: []interface{}{"test2", "1/2", "ContainerWaitingReason", int64(6), "<unknown>", "<none>", "<none>", "<none>"}}}, []metav1beta1.TableRow{{Cells: []interface{}{"test2", "1/2", "ContainerWaitingReason", int64(6), "<unknown>", "<none>", "<none>", "<none>", "<none>"}}},
}, },
} }

View File

@ -421,8 +421,11 @@ func TestConvertToTableList(t *testing.T) {
{Name: "IP", Type: "string", Priority: 1, Description: v1.PodStatus{}.SwaggerDoc()["podIP"]}, {Name: "IP", Type: "string", Priority: 1, Description: v1.PodStatus{}.SwaggerDoc()["podIP"]},
{Name: "Node", Type: "string", Priority: 1, Description: v1.PodSpec{}.SwaggerDoc()["nodeName"]}, {Name: "Node", Type: "string", Priority: 1, Description: v1.PodSpec{}.SwaggerDoc()["nodeName"]},
{Name: "Nominated Node", Type: "string", Priority: 1, Description: v1.PodStatus{}.SwaggerDoc()["nominatedNodeName"]}, {Name: "Nominated Node", Type: "string", Priority: 1, Description: v1.PodStatus{}.SwaggerDoc()["nominatedNodeName"]},
{Name: "Readiness Gates", Type: "string", Priority: 1, Description: v1.PodSpec{}.SwaggerDoc()["readinessGates"]},
} }
condition1 := "condition1"
condition2 := "condition2"
pod1 := &api.Pod{ pod1 := &api.Pod{
ObjectMeta: metav1.ObjectMeta{Namespace: "test", Name: "foo", CreationTimestamp: metav1.NewTime(time.Now().Add(-370 * 24 * time.Hour))}, ObjectMeta: metav1.ObjectMeta{Namespace: "test", Name: "foo", CreationTimestamp: metav1.NewTime(time.Now().Add(-370 * 24 * time.Hour))},
Spec: api.PodSpec{ Spec: api.PodSpec{
@ -431,8 +434,26 @@ func TestConvertToTableList(t *testing.T) {
{Name: "ctr2", Ports: []api.ContainerPort{{ContainerPort: 9376}}}, {Name: "ctr2", Ports: []api.ContainerPort{{ContainerPort: 9376}}},
}, },
NodeName: "test-node", NodeName: "test-node",
ReadinessGates: []api.PodReadinessGate{
{
ConditionType: api.PodConditionType(condition1),
},
{
ConditionType: api.PodConditionType(condition2),
},
},
}, },
Status: api.PodStatus{ Status: api.PodStatus{
Conditions: []api.PodCondition{
{
Type: api.PodConditionType(condition1),
Status: api.ConditionFalse,
},
{
Type: api.PodConditionType(condition2),
Status: api.ConditionTrue,
},
},
PodIP: "10.1.2.3", PodIP: "10.1.2.3",
Phase: api.PodPending, Phase: api.PodPending,
ContainerStatuses: []api.ContainerStatus{ ContainerStatuses: []api.ContainerStatus{
@ -457,7 +478,7 @@ func TestConvertToTableList(t *testing.T) {
out: &metav1beta1.Table{ out: &metav1beta1.Table{
ColumnDefinitions: columns, ColumnDefinitions: columns,
Rows: []metav1beta1.TableRow{ Rows: []metav1beta1.TableRow{
{Cells: []interface{}{"", "0/0", "", int64(0), "<unknown>", "<none>", "<none>", "<none>"}, Object: runtime.RawExtension{Object: &api.Pod{}}}, {Cells: []interface{}{"", "0/0", "", int64(0), "<unknown>", "<none>", "<none>", "<none>", "<none>"}, Object: runtime.RawExtension{Object: &api.Pod{}}},
}, },
}, },
}, },
@ -466,7 +487,7 @@ func TestConvertToTableList(t *testing.T) {
out: &metav1beta1.Table{ out: &metav1beta1.Table{
ColumnDefinitions: columns, ColumnDefinitions: columns,
Rows: []metav1beta1.TableRow{ Rows: []metav1beta1.TableRow{
{Cells: []interface{}{"foo", "1/2", "Pending", int64(10), "370d", "10.1.2.3", "test-node", "nominated-node"}, Object: runtime.RawExtension{Object: pod1}}, {Cells: []interface{}{"foo", "1/2", "Pending", int64(10), "370d", "10.1.2.3", "test-node", "nominated-node", "1/2"}, Object: runtime.RawExtension{Object: pod1}},
}, },
}, },
}, },