From 61417bd9ff4048444e8a37c95e701a1e1e752d2f Mon Sep 17 00:00:00 2001 From: Michelle Au Date: Wed, 21 Feb 2018 10:10:42 -0800 Subject: [PATCH] Update kubectl describe to print out PV node affinity --- pkg/printers/internalversion/describe.go | 43 ++++++++ pkg/printers/internalversion/describe_test.go | 100 ++++++++++++++++++ 2 files changed, 143 insertions(+) diff --git a/pkg/printers/internalversion/describe.go b/pkg/printers/internalversion/describe.go index fe38c3f696d..ade3d4695c8 100644 --- a/pkg/printers/internalversion/describe.go +++ b/pkg/printers/internalversion/describe.go @@ -1134,6 +1134,48 @@ func (d *PersistentVolumeDescriber) Describe(namespace, name string, describerSe return describePersistentVolume(pv, events) } +func printVolumeNodeAffinity(w PrefixWriter, affinity *api.VolumeNodeAffinity) { + w.Write(LEVEL_0, "Node Affinity:\t") + if affinity == nil || affinity.Required == nil { + w.WriteLine("") + return + } + w.WriteLine("") + + if affinity.Required != nil { + w.Write(LEVEL_1, "Required Terms:\t") + if len(affinity.Required.NodeSelectorTerms) == 0 { + w.WriteLine("") + } else { + w.WriteLine("") + for i, term := range affinity.Required.NodeSelectorTerms { + printNodeSelectorTermsMultilineWithIndent(w, LEVEL_2, fmt.Sprintf("Term %v", i), "\t", term.MatchExpressions) + } + } + } +} + +// printLabelsMultiline prints multiple labels with a user-defined alignment. +func printNodeSelectorTermsMultilineWithIndent(w PrefixWriter, indentLevel int, title, innerIndent string, reqs []api.NodeSelectorRequirement) { + w.Write(indentLevel, "%s:%s", title, innerIndent) + + if len(reqs) == 0 { + w.WriteLine("") + return + } + + for i, req := range reqs { + if i != 0 { + w.Write(indentLevel, "%s", innerIndent) + } + exprStr := fmt.Sprintf("%s %s", req.Key, strings.ToLower(string(req.Operator))) + if len(req.Values) > 0 { + exprStr = fmt.Sprintf("%s [%s]", exprStr, strings.Join(req.Values, ", ")) + } + w.Write(LEVEL_0, "%s\n", exprStr) + } +} + func describePersistentVolume(pv *api.PersistentVolume, events *api.EventList) (string, error) { return tabbedString(func(out io.Writer) error { w := NewPrefixWriter(out) @@ -1159,6 +1201,7 @@ func describePersistentVolume(pv *api.PersistentVolume, events *api.EventList) ( } storage := pv.Spec.Capacity[api.ResourceStorage] w.Write(LEVEL_0, "Capacity:\t%s\n", storage.String()) + printVolumeNodeAffinity(w, pv.Spec.NodeAffinity) w.Write(LEVEL_0, "Message:\t%s\n", pv.Status.Message) w.Write(LEVEL_0, "Source:\n") diff --git a/pkg/printers/internalversion/describe_test.go b/pkg/printers/internalversion/describe_test.go index dc0775055e2..f4be69dd5a0 100644 --- a/pkg/printers/internalversion/describe_test.go +++ b/pkg/printers/internalversion/describe_test.go @@ -991,6 +991,106 @@ func TestPersistentVolumeDescriber(t *testing.T) { }, expectedElements: []string{"VolumeMode", "Block"}, }, + { + plugin: "local", + pv: &api.PersistentVolume{ + ObjectMeta: metav1.ObjectMeta{Name: "bar"}, + Spec: api.PersistentVolumeSpec{ + PersistentVolumeSource: api.PersistentVolumeSource{ + Local: &api.LocalVolumeSource{}, + }, + }, + }, + expectedElements: []string{"Node Affinity: "}, + unexpectedElements: []string{"Required Terms", "Term "}, + }, + { + plugin: "local", + pv: &api.PersistentVolume{ + ObjectMeta: metav1.ObjectMeta{Name: "bar"}, + Spec: api.PersistentVolumeSpec{ + PersistentVolumeSource: api.PersistentVolumeSource{ + Local: &api.LocalVolumeSource{}, + }, + NodeAffinity: &api.VolumeNodeAffinity{}, + }, + }, + expectedElements: []string{"Node Affinity: "}, + unexpectedElements: []string{"Required Terms", "Term "}, + }, + { + plugin: "local", + pv: &api.PersistentVolume{ + ObjectMeta: metav1.ObjectMeta{Name: "bar"}, + Spec: api.PersistentVolumeSpec{ + PersistentVolumeSource: api.PersistentVolumeSource{ + Local: &api.LocalVolumeSource{}, + }, + NodeAffinity: &api.VolumeNodeAffinity{ + Required: &api.NodeSelector{}, + }, + }, + }, + expectedElements: []string{"Node Affinity", "Required Terms: "}, + unexpectedElements: []string{"Term "}, + }, + { + plugin: "local", + pv: &api.PersistentVolume{ + ObjectMeta: metav1.ObjectMeta{Name: "bar"}, + Spec: api.PersistentVolumeSpec{ + PersistentVolumeSource: api.PersistentVolumeSource{ + Local: &api.LocalVolumeSource{}, + }, + NodeAffinity: &api.VolumeNodeAffinity{ + Required: &api.NodeSelector{ + NodeSelectorTerms: []api.NodeSelectorTerm{ + { + MatchExpressions: []api.NodeSelectorRequirement{}, + }, + { + MatchExpressions: []api.NodeSelectorRequirement{}, + }, + }, + }, + }, + }, + }, + expectedElements: []string{"Node Affinity", "Required Terms", "Term 0", "Term 1"}, + }, + { + plugin: "local", + pv: &api.PersistentVolume{ + ObjectMeta: metav1.ObjectMeta{Name: "bar"}, + Spec: api.PersistentVolumeSpec{ + PersistentVolumeSource: api.PersistentVolumeSource{ + Local: &api.LocalVolumeSource{}, + }, + NodeAffinity: &api.VolumeNodeAffinity{ + Required: &api.NodeSelector{ + NodeSelectorTerms: []api.NodeSelectorTerm{ + { + MatchExpressions: []api.NodeSelectorRequirement{ + { + Key: "foo", + Operator: "In", + Values: []string{"val1", "val2"}, + }, + { + Key: "foo", + Operator: "Exists", + }, + }, + }, + }, + }, + }, + }, + }, + expectedElements: []string{"Node Affinity", "Required Terms", "Term 0", + "foo in [val1, val2]", + "foo exists"}, + }, } for _, test := range testCases {