diff --git a/cmd/node/nodeList.go b/cmd/node/nodeList.go index a85dd6d4..42c091c3 100644 --- a/cmd/node/nodeList.go +++ b/cmd/node/nodeList.go @@ -23,8 +23,6 @@ package node import ( "fmt" - "os" - "sort" "strings" "github.com/liggitt/tabwriter" @@ -37,8 +35,14 @@ import ( log "github.com/sirupsen/logrus" ) +type nodeListFlags struct { + noHeader bool + output string +} + // NewCmdNodeList returns a new cobra command func NewCmdNodeList() *cobra.Command { + nodeListFlags := nodeListFlags{} // create new command cmd := &cobra.Command{ @@ -49,7 +53,13 @@ func NewCmdNodeList() *cobra.Command { Args: cobra.MinimumNArgs(0), // 0 or more; 0 = all ValidArgsFunction: util.ValidArgsAvailableNodes, Run: func(cmd *cobra.Command, args []string) { - nodes, headersOff := parseGetNodeCmd(cmd, args) + nodes := []*k3d.Node{} + for _, name := range args { + nodes = append(nodes, &k3d.Node{ + Name: name, + }) + } + var existingNodes []*k3d.Node if len(nodes) == 0 { // Option a) no name specified -> get all nodes found, err := client.NodeList(cmd.Context(), runtimes.SelectedRuntime) @@ -66,58 +76,29 @@ func NewCmdNodeList() *cobra.Command { existingNodes = append(existingNodes, found) } } - // print existing clusters - printNodes(existingNodes, headersOff) + + // print existing nodes + headers := &[]string{} + if !nodeListFlags.noHeader { + headers = &[]string{"NAME", "ROLE", "CLUSTER", "STATUS"} + } + + util.PrintNodes(existingNodes, nodeListFlags.output, + headers, util.NodePrinterFunc(func(tabwriter *tabwriter.Writer, node *k3d.Node) { + fmt.Fprintf(tabwriter, "%s\t%s\t%s\t%s\n", + strings.TrimPrefix(node.Name, "/"), + string(node.Role), + node.Labels[k3d.LabelClusterName], + node.State.Status) + })) }, } - // add flags - cmd.Flags().Bool("no-headers", false, "Disable headers") + cmd.Flags().BoolVar(&nodeListFlags.noHeader, "no-headers", false, "Disable headers") + cmd.Flags().StringVarP(&nodeListFlags.output, "output", "o", "", "Output format. One of: json|yaml") // add subcommands // done return cmd } - -func parseGetNodeCmd(cmd *cobra.Command, args []string) ([]*k3d.Node, bool) { - // --no-headers - headersOff, err := cmd.Flags().GetBool("no-headers") - if err != nil { - log.Fatalln(err) - } - - // Args = node name - if len(args) == 0 { - return nil, headersOff - } - - nodes := []*k3d.Node{} - for _, name := range args { - nodes = append(nodes, &k3d.Node{Name: name}) - } - - return nodes, headersOff -} - -func printNodes(nodes []*k3d.Node, headersOff bool) { - - tabwriter := tabwriter.NewWriter(os.Stdout, 6, 4, 3, ' ', tabwriter.RememberWidths) - defer tabwriter.Flush() - - if !headersOff { - headers := []string{"NAME", "ROLE", "CLUSTER", "STATUS"} - _, err := fmt.Fprintf(tabwriter, "%s\n", strings.Join(headers, "\t")) - if err != nil { - log.Fatalln("Failed to print headers") - } - } - - sort.Slice(nodes, func(i, j int) bool { - return nodes[i].Name < nodes[j].Name - }) - - for _, node := range nodes { - fmt.Fprintf(tabwriter, "%s\t%s\t%s\t%s\n", strings.TrimPrefix(node.Name, "/"), string(node.Role), node.Labels[k3d.LabelClusterName], node.State.Status) - } -} diff --git a/cmd/registry/registryList.go b/cmd/registry/registryList.go index 83f824e4..97146a50 100644 --- a/cmd/registry/registryList.go +++ b/cmd/registry/registryList.go @@ -23,8 +23,6 @@ package registry import ( "fmt" - "os" - "sort" "strings" "github.com/liggitt/tabwriter" @@ -36,8 +34,15 @@ import ( "github.com/spf13/cobra" ) +type registryListFlags struct { + noHeader bool + output string +} + // NewCmdRegistryList creates a new cobra command func NewCmdRegistryList() *cobra.Command { + registryListFlags := registryListFlags{} + // create new command cmd := &cobra.Command{ Use: "list [NAME [NAME...]]", @@ -47,8 +52,15 @@ func NewCmdRegistryList() *cobra.Command { Args: cobra.MinimumNArgs(0), // 0 or more; 0 = all ValidArgsFunction: util.ValidArgsAvailableRegistries, Run: func(cmd *cobra.Command, args []string) { - nodes, headersOff := parseRegistryListCmd(cmd, args) var existingNodes []*k3d.Node + + nodes := []*k3d.Node{} + for _, name := range args { + nodes = append(nodes, &k3d.Node{ + Name: name, + }) + } + if len(nodes) == 0 { // Option a) no name specified -> get all registries found, err := client.NodeList(cmd.Context(), runtimes.SelectedRuntime) if err != nil { @@ -66,62 +78,35 @@ func NewCmdRegistryList() *cobra.Command { } } existingNodes = client.NodeFilterByRoles(existingNodes, []k3d.Role{k3d.RegistryRole}, []k3d.Role{}) + // print existing registries - if len(existingNodes) > 0 { - printNodes(existingNodes, headersOff) + headers := &[]string{} + if !registryListFlags.noHeader { + headers = &[]string{"NAME", "ROLE", "CLUSTER"} // TODO: add status } + + util.PrintNodes(existingNodes, registryListFlags.output, + headers, util.NodePrinterFunc(func(tabwriter *tabwriter.Writer, node *k3d.Node) { + cluster := "*" + if _, ok := node.Labels[k3d.LabelClusterName]; ok { + cluster = node.Labels[k3d.LabelClusterName] + } + fmt.Fprintf(tabwriter, "%s\t%s\t%s\n", + strings.TrimPrefix(node.Name, "/"), + string(node.Role), + cluster, + ) + }), + ) }, } // add flags - cmd.Flags().Bool("no-headers", false, "Disable headers") + cmd.Flags().BoolVar(®istryListFlags.noHeader, "no-headers", false, "Disable headers") + cmd.Flags().StringVarP(®istryListFlags.output, "output", "o", "", "Output format. One of: json|yaml") // add subcommands // done return cmd } - -func parseRegistryListCmd(cmd *cobra.Command, args []string) ([]*k3d.Node, bool) { - // --no-headers - headersOff, err := cmd.Flags().GetBool("no-headers") - if err != nil { - log.Fatalln(err) - } - - // Args = node name - if len(args) == 0 { - return nil, headersOff - } - - nodes := []*k3d.Node{} - for _, name := range args { - nodes = append(nodes, &k3d.Node{ - Name: name, - }) - } - - return nodes, headersOff -} - -func printNodes(nodes []*k3d.Node, headersOff bool) { - - tabwriter := tabwriter.NewWriter(os.Stdout, 6, 4, 3, ' ', tabwriter.RememberWidths) - defer tabwriter.Flush() - - if !headersOff { - headers := []string{"NAME", "ROLE", "CLUSTER"} // TODO: add status - _, err := fmt.Fprintf(tabwriter, "%s\n", strings.Join(headers, "\t")) - if err != nil { - log.Fatalln("Failed to print headers") - } - } - - sort.Slice(nodes, func(i, j int) bool { - return nodes[i].Name < nodes[j].Name - }) - - for _, node := range nodes { - fmt.Fprintf(tabwriter, "%s\t%s\t%s\n", strings.TrimPrefix(node.Name, "/"), string(node.Role), node.Labels[k3d.LabelClusterName]) - } -} diff --git a/cmd/util/listings.go b/cmd/util/listings.go new file mode 100644 index 00000000..13365052 --- /dev/null +++ b/cmd/util/listings.go @@ -0,0 +1,89 @@ +/* +Copyright © 2020 The k3d Author(s) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +package util + +import ( + "encoding/json" + "fmt" + "os" + "sort" + "strings" + + "github.com/liggitt/tabwriter" + k3d "github.com/rancher/k3d/v4/pkg/types" + log "github.com/sirupsen/logrus" + "gopkg.in/yaml.v2" +) + +type NodePrinter interface { + Print(*tabwriter.Writer, *k3d.Node) +} + +type NodePrinterFunc func(*tabwriter.Writer, *k3d.Node) + +func (npf NodePrinterFunc) Print(writter *tabwriter.Writer, node *k3d.Node) { + npf(writter, node) +} + +// PrintNodes prints a list of nodes, either as a table or as a JSON/YAML listing +func PrintNodes(nodes []*k3d.Node, outputFormat string, headers *[]string, nodePrinter NodePrinter) { + outputFormat = strings.ToLower(outputFormat) + + tabwriter := tabwriter.NewWriter(os.Stdout, 6, 4, 3, ' ', tabwriter.RememberWidths) + defer tabwriter.Flush() + + if outputFormat != "json" && outputFormat != "yaml" { + if headers != nil { + _, err := fmt.Fprintf(tabwriter, "%s\n", strings.Join(*headers, "\t")) + if err != nil { + log.Fatalln("Failed to print headers") + } + } + } + + sort.Slice(nodes, func(i, j int) bool { + return nodes[i].Name < nodes[j].Name + }) + + if outputFormat == "json" || outputFormat == "yaml" { + var b []byte + var err error + + switch outputFormat { + case "json": + b, err = json.Marshal(nodes) + case "yaml": + b, err = yaml.Marshal(nodes) + } + if err != nil { + fmt.Println(err) + return + } + fmt.Println(string(b)) + } else { + for _, node := range nodes { + if !(outputFormat == "json" || outputFormat == "yaml") { + nodePrinter.Print(tabwriter, node) + } + } + } +}