From 7c55fdcf024e83d78b47102dd27315728400c860 Mon Sep 17 00:00:00 2001 From: Elad Iwanir Date: Tue, 8 Jan 2019 20:00:49 +0200 Subject: [PATCH] Sort resources output by 'helm status' Signed-off-by: Elad Iwanir --- pkg/kube/client.go | 81 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 62 insertions(+), 19 deletions(-) diff --git a/pkg/kube/client.go b/pkg/kube/client.go index 4a387d524..513f7400d 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -24,6 +24,7 @@ import ( "fmt" "io" "log" + "sort" "strings" "time" @@ -151,13 +152,41 @@ func (c *Client) Build(namespace string, reader io.Reader) (Result, error) { return result, scrubValidationError(err) } +// Return the resource info as internal +func resourceInfoToObject(info *resource.Info) runtime.Object { + internalObj, err := asInternal(info) + if err != nil { + // If the problem is just that the resource is not registered, don't print any + // error. This is normal for custom resources. + if !runtime.IsNotRegisteredError(err) { + c.Log("Warning: conversion to internal type failed: %v", err) + } + // Add the unstructured object in this situation. It will still get listed, just + // with less information. + return info.Object + } + + return internalObj +} + +func sortByKey(objs map[string](map[string]runtime.Object)) []string { + var keys []string + // Create a simple slice, so we can sort it + for key := range objs { + keys = append(keys, key) + } + // Sort alphabetically by version/kind keys + sort.Strings(keys) + return keys +} + // Get gets Kubernetes resources as pretty-printed string. // // Namespace will set the namespace. func (c *Client) Get(namespace string, reader io.Reader) (string, error) { - // Since we don't know what order the objects come in, let's group them by the types, so + // Since we don't know what order the objects come in, let's group them by the types and then sort them, so // that when we print them, they come out looking good (headers apply to subgroups, etc.). - objs := make(map[string][]runtime.Object) + objs := make(map[string](map[string]runtime.Object)) infos, err := c.BuildUnstructured(namespace, reader) if err != nil { return "", err @@ -178,19 +207,15 @@ func (c *Client) Get(namespace string, reader io.Reader) (string, error) { // versions per cluster, but this certainly won't hurt anything, so let's be safe. gvk := info.ResourceMapping().GroupVersionKind vk := gvk.Version + "/" + gvk.Kind - internalObj, err := asInternal(info) - if err != nil { - // If the problem is just that the resource is not registered, don't print any - // error. This is normal for custom resources. - if !runtime.IsNotRegisteredError(err) { - c.Log("Warning: conversion to internal type failed: %v", err) - } - // Add the unstructured object in this situation. It will still get listed, just - // with less information. - objs[vk] = append(objs[vk], info.Object) - } else { - objs[vk] = append(objs[vk], internalObj) + + // Initialize map. The main map groups resources based on version/kind + // The second level is a simple 'Name' to 'Object', that will help sort + // the individual resource later + if objs[vk] == nil { + objs[vk] = make(map[string]runtime.Object) } + // Map between the resource name to the underlying info object + objs[vk][info.Name] = resourceInfoToObject(info) //Get the relation pods objPods, err = c.getSelectRelationPod(info, objPods) @@ -208,8 +233,12 @@ func (c *Client) Get(namespace string, reader io.Reader) (string, error) { for key, podItems := range objPods { for i := range podItems { pod := &core.Pod{} + legacyscheme.Scheme.Convert(&podItems[i], pod, nil) - objs[key+"(related)"] = append(objs[key+"(related)"], pod) + if objs[key+"(related)"] == nil { + objs[key+"(related)"] = make(map[string]runtime.Object) + } + objs[key+"(related)"][pod.ObjectMeta.Name] = runtime.Object(pod) } } @@ -219,14 +248,28 @@ func (c *Client) Get(namespace string, reader io.Reader) (string, error) { // track of tab widths. buf := new(bytes.Buffer) printFlags := get.NewHumanPrintFlags() - for t, ot := range objs { + + // Sort alphabetically by version/kind keys + vkKeys := sortByKey(objs) + // Iterate on sorted version/kind types + for _, t := range vkKeys { if _, err = fmt.Fprintf(buf, "==> %s\n", t); err != nil { return "", err } typePrinter, _ := printFlags.ToPrinter("") - for _, o := range ot { - if err := typePrinter.PrintObj(o, buf); err != nil { - c.Log("failed to print object type %s, object: %q :\n %v", t, o, err) + + var sortedResources []string + for resource := range objs[t] { + sortedResources = append(sortedResources, resource) + } + sort.Strings(sortedResources) + + // Now that each individual resource within the specific version/kind + // is sorted, we print each resource using the k8s printer + for _, resourceName := range sortedResources { + vk := objs[t] + if err := typePrinter.PrintObj(vk[resourceName], buf); err != nil { + c.Log("failed to print object type %s, object: %q :\n %v", t, resourceName, err) return "", err } }