mirror of https://github.com/k3s-io/k3s
Smarter describer for generic resources
parent
85bd965219
commit
151770c8fd
|
@ -87,6 +87,8 @@ go_library(
|
|||
"//pkg/kubelet/qos:go_default_library",
|
||||
"//pkg/printers:go_default_library",
|
||||
"//pkg/util/node:go_default_library",
|
||||
"//pkg/util/slice:go_default_library",
|
||||
"//vendor/github.com/fatih/camelcase:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||
|
|
|
@ -31,6 +31,7 @@ import (
|
|||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"github.com/fatih/camelcase"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
|
@ -70,6 +71,7 @@ import (
|
|||
"k8s.io/kubernetes/pkg/fieldpath"
|
||||
"k8s.io/kubernetes/pkg/kubelet/qos"
|
||||
"k8s.io/kubernetes/pkg/printers"
|
||||
"k8s.io/kubernetes/pkg/util/slice"
|
||||
)
|
||||
|
||||
// Each level has 2 spaces for PrefixWriter
|
||||
|
@ -198,6 +200,8 @@ func (g *genericDescriber) Describe(namespace, name string, describerSettings pr
|
|||
w.Write(LEVEL_0, "Name:\t%s\n", obj.GetName())
|
||||
w.Write(LEVEL_0, "Namespace:\t%s\n", obj.GetNamespace())
|
||||
printLabelsMultiline(w, "Labels", obj.GetLabels())
|
||||
printAnnotationsMultiline(w, "Annotations", obj.GetAnnotations())
|
||||
printUnstructuredContent(w, LEVEL_0, obj.UnstructuredContent(), "", ".metadata.name", ".metadata.namespace", ".metadata.labels", ".metadata.annotations")
|
||||
if events != nil {
|
||||
DescribeEvents(events, w)
|
||||
}
|
||||
|
@ -205,6 +209,67 @@ func (g *genericDescriber) Describe(namespace, name string, describerSettings pr
|
|||
})
|
||||
}
|
||||
|
||||
func printUnstructuredContent(w PrefixWriter, level int, content map[string]interface{}, skipPrefix string, skip ...string) {
|
||||
fields := []string{}
|
||||
for field := range content {
|
||||
fields = append(fields, field)
|
||||
}
|
||||
sort.Strings(fields)
|
||||
|
||||
for _, field := range fields {
|
||||
value := content[field]
|
||||
switch typedValue := value.(type) {
|
||||
case map[string]interface{}:
|
||||
skipExpr := fmt.Sprintf("%s.%s", skipPrefix, field)
|
||||
if slice.ContainsString(skip, skipExpr, nil) {
|
||||
continue
|
||||
}
|
||||
w.Write(level, fmt.Sprintf("%s:\n", smartLabelFor(field)))
|
||||
printUnstructuredContent(w, level+1, typedValue, skipExpr, skip...)
|
||||
|
||||
case []interface{}:
|
||||
skipExpr := fmt.Sprintf("%s.%s", skipPrefix, field)
|
||||
if slice.ContainsString(skip, skipExpr, nil) {
|
||||
continue
|
||||
}
|
||||
w.Write(level, fmt.Sprintf("%s:\n", smartLabelFor(field)))
|
||||
for _, child := range typedValue {
|
||||
switch typedChild := child.(type) {
|
||||
case map[string]interface{}:
|
||||
printUnstructuredContent(w, level+1, typedChild, skipExpr, skip...)
|
||||
default:
|
||||
w.Write(level+1, fmt.Sprintf("%v\n", typedChild))
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
skipExpr := fmt.Sprintf("%s.%s", skipPrefix, field)
|
||||
if slice.ContainsString(skip, skipExpr, nil) {
|
||||
continue
|
||||
}
|
||||
w.Write(level, fmt.Sprintf("%s:\t%v\n", smartLabelFor(field), typedValue))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func smartLabelFor(field string) string {
|
||||
commonAcronyms := []string{"API", "URL", "UID", "OSB", "GUID"}
|
||||
|
||||
splitted := camelcase.Split(field)
|
||||
for i := 0; i < len(splitted); i++ {
|
||||
part := splitted[i]
|
||||
|
||||
if slice.ContainsString(commonAcronyms, strings.ToUpper(part), nil) {
|
||||
part = strings.ToUpper(part)
|
||||
} else {
|
||||
part = strings.Title(part)
|
||||
}
|
||||
splitted[i] = part
|
||||
}
|
||||
|
||||
return strings.Join(splitted, " ")
|
||||
}
|
||||
|
||||
// DefaultObjectDescriber can describe the default Kubernetes objects.
|
||||
var DefaultObjectDescriber printers.ObjectDescriber
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ import (
|
|||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/kubernetes/federation/apis/federation"
|
||||
fedfake "k8s.io/kubernetes/federation/client/clientset_generated/federation_internalclientset/fake"
|
||||
|
@ -1313,3 +1314,87 @@ func TestPrintLabelsMultiline(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDescribeUnstructuredContent(t *testing.T) {
|
||||
testCases := []struct {
|
||||
expected string
|
||||
unexpected string
|
||||
}{
|
||||
{
|
||||
expected: `API Version: v1
|
||||
Dummy 2: present
|
||||
Items:
|
||||
Item Bool: true
|
||||
Item Int: 42
|
||||
Kind: Test
|
||||
Metadata:
|
||||
Creation Timestamp: 2017-04-01T00:00:00Z
|
||||
Name: MyName
|
||||
Namespace: MyNamespace
|
||||
Resource Version: 123
|
||||
UID: 00000000-0000-0000-0000-000000000001
|
||||
Status: ok
|
||||
URL: http://localhost
|
||||
`,
|
||||
},
|
||||
{
|
||||
unexpected: "\nDummy 1:\tpresent\n",
|
||||
},
|
||||
{
|
||||
unexpected: "Dummy 1",
|
||||
},
|
||||
{
|
||||
unexpected: "Dummy 3",
|
||||
},
|
||||
{
|
||||
unexpected: "Dummy3",
|
||||
},
|
||||
{
|
||||
unexpected: "dummy3",
|
||||
},
|
||||
{
|
||||
unexpected: "dummy 3",
|
||||
},
|
||||
}
|
||||
out := new(bytes.Buffer)
|
||||
w := NewPrefixWriter(out)
|
||||
obj := &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Test",
|
||||
"dummy1": "present",
|
||||
"dummy2": "present",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "MyName",
|
||||
"namespace": "MyNamespace",
|
||||
"creationTimestamp": "2017-04-01T00:00:00Z",
|
||||
"resourceVersion": 123,
|
||||
"uid": "00000000-0000-0000-0000-000000000001",
|
||||
"dummy3": "present",
|
||||
},
|
||||
"items": []interface{}{
|
||||
map[string]interface{}{
|
||||
"itemBool": true,
|
||||
"itemInt": 42,
|
||||
},
|
||||
},
|
||||
"url": "http://localhost",
|
||||
"status": "ok",
|
||||
},
|
||||
}
|
||||
printUnstructuredContent(w, LEVEL_0, obj.UnstructuredContent(), "", ".dummy1", ".metadata.dummy3")
|
||||
output := out.String()
|
||||
|
||||
for _, test := range testCases {
|
||||
if len(test.expected) > 0 {
|
||||
if !strings.Contains(output, test.expected) {
|
||||
t.Errorf("Expected to find %q in: %q", test.expected, output)
|
||||
}
|
||||
}
|
||||
if len(test.unexpected) > 0 {
|
||||
if strings.Contains(output, test.unexpected) {
|
||||
t.Errorf("Didn't expect to find %q in: %q", test.unexpected, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,6 +49,20 @@ func ShuffleStrings(s []string) []string {
|
|||
return shuffled
|
||||
}
|
||||
|
||||
// ContainsString checks if a given slice of strings contains the provided string.
|
||||
// If a modifier func is provided, it is called with the slice item before the comparation.
|
||||
func ContainsString(slice []string, s string, modifier func(s string) string) bool {
|
||||
for _, item := range slice {
|
||||
if item == s {
|
||||
return true
|
||||
}
|
||||
if modifier != nil && modifier(item) == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Int64Slice attaches the methods of Interface to []int64,
|
||||
// sorting in increasing order.
|
||||
type Int64Slice []int64
|
||||
|
|
Loading…
Reference in New Issue