From 7f7defbc59e8f77543a2bd5bce118d11d122b3f0 Mon Sep 17 00:00:00 2001 From: Kavinraja Ganesan <62742678+Kavinraja-G@users.noreply.github.com> Date: Fri, 13 Oct 2023 12:16:04 +0530 Subject: [PATCH] feat: Added sorting feature for node & nodepool commands (#20) --- go.mod | 2 +- pkg/cmd/nodepool/nodepool.go | 29 +++++++---------------------- pkg/cmd/nodes/capacity.go | 3 ++- pkg/cmd/nodes/exec.go | 3 ++- pkg/cmd/nodes/nodes.go | 8 +++++++- pkg/outputs/sort.go | 27 +++++++++++++++++++++++++++ pkg/outputs/table.go | 11 +++++++++-- 7 files changed, 55 insertions(+), 28 deletions(-) create mode 100644 pkg/outputs/sort.go diff --git a/go.mod b/go.mod index 1cc505a..0ecbae2 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( k8s.io/api v0.28.0 k8s.io/apimachinery v0.28.0 k8s.io/client-go v0.28.0 + k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 ) require ( @@ -47,7 +48,6 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/klog/v2 v2.100.1 // indirect k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect - k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect sigs.k8s.io/yaml v1.3.0 // indirect diff --git a/pkg/cmd/nodepool/nodepool.go b/pkg/cmd/nodepool/nodepool.go index b983728..db0a353 100644 --- a/pkg/cmd/nodepool/nodepool.go +++ b/pkg/cmd/nodepool/nodepool.go @@ -2,17 +2,18 @@ package nodepool import ( "context" - "github.com/Kavinraja-G/node-gizmo/utils" "strings" "github.com/Kavinraja-G/node-gizmo/pkg/outputs" + "github.com/Kavinraja-G/node-gizmo/utils" "github.com/Kavinraja-G/node-gizmo/pkg" "github.com/spf13/cobra" - corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +var sortByHeader string + func NewCmdNodepoolInfo() *cobra.Command { cmd := &cobra.Command{ Use: "nodepool", @@ -23,6 +24,9 @@ func NewCmdNodepoolInfo() *cobra.Command { }, } + // additional local flags + cmd.Flags().StringVarP(&sortByHeader, "sort-by", "", "nodepool", "Sorts output using a valid Column name. Defaults to 'nodepool' if the column name is not valid") + return cmd } @@ -56,31 +60,12 @@ func showNodePoolInfo(cmd *cobra.Command, args []string) error { } outputHeaders, outputData := generateNodepoolInfoData(genericNodepoolInfos) + outputs.SortOutputBasedOnHeader(outputHeaders, outputData, sortByHeader) outputs.TableOutput(outputHeaders, outputData) return nil } -// getNodeAddresses returns the respective nodeAddress values for 'Hostname', 'InternalIP', 'ExternalIP', 'ExternalDNS' -// TODO: Add nodeAddress fields for detailed node info using flags -func getNodeAddresses(addresses []corev1.NodeAddress, addressType string) string { - for _, address := range addresses { - if (address.Type == corev1.NodeHostName) && (addressType == string(corev1.NodeHostName)) { - return address.Address - } - if (address.Type == corev1.NodeInternalIP) && (addressType == string(corev1.NodeInternalIP)) { - return address.Address - } - if (address.Type == corev1.NodeExternalIP) && (addressType == string(corev1.NodeExternalIP)) { - return address.Address - } - if (address.Type == corev1.NodeExternalDNS) && (addressType == string(corev1.NodeExternalDNS)) { - return address.Address - } - } - return "Unknown" -} - // getNodepoolIDAndProvider returns the cloud provider type for the nodepool (EKS, GKE, AKS, can be Unknown) func getNodepoolIDAndProvider(labels map[string]string) (string, string) { if id, ok := labels[pkg.AwsNodepoolLabel]; ok { diff --git a/pkg/cmd/nodes/capacity.go b/pkg/cmd/nodes/capacity.go index fc872c3..08b6ab7 100644 --- a/pkg/cmd/nodes/capacity.go +++ b/pkg/cmd/nodes/capacity.go @@ -2,10 +2,10 @@ package nodes import ( "context" - "github.com/Kavinraja-G/node-gizmo/utils" "github.com/Kavinraja-G/node-gizmo/pkg" "github.com/Kavinraja-G/node-gizmo/pkg/outputs" + "github.com/Kavinraja-G/node-gizmo/utils" "github.com/spf13/cobra" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -47,6 +47,7 @@ func showNodeCapacities(cmd *cobra.Command, args []string) error { } outputHeaders, outputData := generateNodeCapacityOutputData(nodeCapacityInfo) + outputs.SortOutputBasedOnHeader(outputHeaders, outputData, sortByHeader) outputs.TableOutput(outputHeaders, outputData) return nil diff --git a/pkg/cmd/nodes/exec.go b/pkg/cmd/nodes/exec.go index dc641b5..54ce107 100644 --- a/pkg/cmd/nodes/exec.go +++ b/pkg/cmd/nodes/exec.go @@ -4,11 +4,12 @@ import ( "context" "errors" "fmt" - "github.com/Kavinraja-G/node-gizmo/utils" "log" "os" "time" + "github.com/Kavinraja-G/node-gizmo/utils" + k8errors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/util/wait" diff --git a/pkg/cmd/nodes/nodes.go b/pkg/cmd/nodes/nodes.go index 3d74d73..2232041 100644 --- a/pkg/cmd/nodes/nodes.go +++ b/pkg/cmd/nodes/nodes.go @@ -3,9 +3,10 @@ package nodes import ( "context" "fmt" - "github.com/Kavinraja-G/node-gizmo/utils" "strings" + "github.com/Kavinraja-G/node-gizmo/utils" + "github.com/Kavinraja-G/node-gizmo/pkg/outputs" "github.com/Kavinraja-G/node-gizmo/pkg" @@ -18,6 +19,7 @@ var ( showTaints bool showNodeProviderInfo bool showNodeTopologyInfo bool + sortByHeader string ) // NewCmdNodeInfo initializes the 'node' command @@ -29,12 +31,14 @@ func NewCmdNodeInfo() *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return showNodeInfo(cmd, args) }, + TraverseChildren: true, } // additional local flags cmd.Flags().BoolVarP(&showTaints, "show-taints", "t", false, "Shows taints added on a node") cmd.Flags().BoolVarP(&showNodeProviderInfo, "show-providers", "p", false, "Shows cloud provider name for a node") cmd.Flags().BoolVarP(&showNodeTopologyInfo, "show-topology", "T", false, "Shows node topology info like region & zones for a node") + cmd.PersistentFlags().StringVarP(&sortByHeader, "sort-by", "", "name", "Sorts output using a valid Column name. Defaults to 'name' if the column name is not valid") // additional sub-commands cmd.AddCommand(NewCmdNodeCapacityInfo()) @@ -81,6 +85,7 @@ func showNodeInfo(cmd *cobra.Command, args []string) error { } outputHeaders, outputData := generateNodeInfoOutputData(nodeInfos, outputOpts) + outputs.SortOutputBasedOnHeader(outputHeaders, outputData, sortByHeader) outputs.TableOutput(outputHeaders, outputData) return nil @@ -151,5 +156,6 @@ func generateNodeInfoOutputData(genericNodeInfos []pkg.GenericNodeInfo, outputOp } outputData = append(outputData, lineItems) } + return headers, outputData } diff --git a/pkg/outputs/sort.go b/pkg/outputs/sort.go new file mode 100644 index 0000000..0015e11 --- /dev/null +++ b/pkg/outputs/sort.go @@ -0,0 +1,27 @@ +package outputs + +import ( + "k8s.io/utils/strings/slices" + "sort" + "strings" +) + +// getSortKeyIdxFromHeader retrieves the index of the sortKey in the header slice +func getSortKeyIdxFromHeader(headers []string, sortKey string) int { + // defaults to first column always (usually node/nodepool name) + idx := 0 + if slices.Contains(headers, strings.ToUpper(sortKey)) { + idx = slices.Index(headers, strings.ToUpper(sortKey)) + } + + return idx +} + +// SortOutputBasedOnHeader sorts output based on the Header key provided in the flags +func SortOutputBasedOnHeader(headers []string, sortSlices [][]string, sortKey string) { + sortByHeaderIndex := getSortKeyIdxFromHeader(headers, sortKey) + + sort.SliceStable(sortSlices, func(i, j int) bool { + return sortSlices[i][sortByHeaderIndex] < sortSlices[j][sortByHeaderIndex] + }) +} diff --git a/pkg/outputs/table.go b/pkg/outputs/table.go index 86e985a..4b2ce0d 100644 --- a/pkg/outputs/table.go +++ b/pkg/outputs/table.go @@ -13,10 +13,17 @@ func TableOutput(headers []string, outputData [][]string) { // misc configs for the table-writer table.SetRowLine(false) table.SetBorder(false) + table.SetAutoWrapText(false) + table.SetAutoFormatHeaders(true) + table.SetHeaderLine(false) + table.SetHeaderAlignment(tablewriter.ALIGN_LEFT) table.SetAlignment(tablewriter.ALIGN_LEFT) - table.SetAutoWrapText(true) + table.SetAutoWrapText(false) + table.SetCenterSeparator("") table.SetColumnSeparator("") - table.SetHeaderLine(false) + table.SetRowSeparator("") + table.SetTablePadding("\t") + table.SetNoWhiteSpace(true) // set headers and add the outputData table.SetHeader(headers)