Include Conditions in kubectl describe namespace

If a namespace deletion is blocked, finding the reason why can require
reading the Conditions on the namespace.

Currently, `kubectl describe namespace` does not include Conditions.
This change adds Conditions to the output. Example output:

```
Name:         example
Labels:       <none>
Annotations:  <none>
Status:	      Terminating
Conditions:
  Type                             Status  LastTransitionTime               Reason          Message
  ----                             ------  ------------------               ------          -------
  NamespaceDeletionContentFailure  True    Wed, 15 Jan 2014 00:00:00 +0000  example reason  example message

No resource quota.

No LimitRange resource.
```
This commit is contained in:
Daniel Lipovetsky 2021-11-07 21:39:56 -08:00 committed by Daniel Lipovetsky
parent fa040a9945
commit eb6e8ce00a
No known key found for this signature in database
GPG Key ID: 559B3DEDDDF8FF82
2 changed files with 97 additions and 11 deletions

View File

@ -451,14 +451,31 @@ func describeNamespace(namespace *corev1.Namespace, resourceQuotaList *corev1.Re
printLabelsMultiline(w, "Labels", namespace.Labels)
printAnnotationsMultiline(w, "Annotations", namespace.Annotations)
w.Write(LEVEL_0, "Status:\t%s\n", string(namespace.Status.Phase))
if len(namespace.Status.Conditions) > 0 {
w.Write(LEVEL_0, "Conditions:\n")
w.Write(LEVEL_1, "Type\tStatus\tLastTransitionTime\tReason\tMessage\n")
w.Write(LEVEL_1, "----\t------\t------------------\t------\t-------\n")
for _, c := range namespace.Status.Conditions {
w.Write(LEVEL_1, "%v\t%v\t%s\t%v\t%v\n",
c.Type,
c.Status,
c.LastTransitionTime.Time.Format(time.RFC1123Z),
c.Reason,
c.Message)
}
}
if resourceQuotaList != nil {
w.Write(LEVEL_0, "\n")
DescribeResourceQuotas(resourceQuotaList, w)
}
if limitRangeList != nil {
w.Write(LEVEL_0, "\n")
DescribeLimitRanges(limitRangeList, w)
}
return nil
})
}

View File

@ -236,19 +236,88 @@ func TestDescribeSecret(t *testing.T) {
}
func TestDescribeNamespace(t *testing.T) {
fake := fake.NewSimpleClientset(&corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "myns",
exampleNamespaceName := "example"
testCases := []struct {
name string
namespace *corev1.Namespace
expect []string
}{
{
name: "no quotas or limit ranges",
namespace: &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: exampleNamespaceName,
},
Status: corev1.NamespaceStatus{
Phase: corev1.NamespaceActive,
},
},
expect: []string{
"Name",
exampleNamespaceName,
"Status",
string(corev1.NamespaceActive),
"No resource quota",
"No LimitRange resource.",
},
},
{
name: "has conditions",
namespace: &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: exampleNamespaceName,
},
Status: corev1.NamespaceStatus{
Phase: corev1.NamespaceTerminating,
Conditions: []corev1.NamespaceCondition{
{
LastTransitionTime: metav1.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)),
Message: "example message",
Reason: "example reason",
Status: corev1.ConditionTrue,
Type: corev1.NamespaceDeletionContentFailure,
},
},
},
},
expect: []string{
"Name",
exampleNamespaceName,
"Status",
string(corev1.NamespaceTerminating),
"Conditions",
"Type",
string(corev1.NamespaceDeletionContentFailure),
"Status",
string(corev1.ConditionTrue),
"Reason",
"example reason",
"Message",
"example message",
"No resource quota",
"No LimitRange resource.",
},
},
})
c := &describeClient{T: t, Namespace: "", Interface: fake}
d := NamespaceDescriber{c}
out, err := d.Describe("", "myns", DescriberSettings{ShowEvents: true})
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if !strings.Contains(out, "myns") {
t.Errorf("unexpected out: %s", out)
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
fake := fake.NewSimpleClientset(testCase.namespace)
c := &describeClient{T: t, Namespace: "", Interface: fake}
d := NamespaceDescriber{c}
out, err := d.Describe("", testCase.namespace.Name, DescriberSettings{ShowEvents: true})
if err != nil {
t.Errorf("unexpected error: %v", err)
}
for _, expected := range testCase.expect {
if !strings.Contains(out, expected) {
t.Errorf("expected to find %q in output: %q", expected, out)
}
}
})
}
}