From a93932e5b62590138454447c4014aa2b79e453c9 Mon Sep 17 00:00:00 2001 From: Alvaro <1841612+inercia@users.noreply.github.com> Date: Thu, 3 Dec 2020 13:34:33 +0100 Subject: [PATCH] [Feature] Options for formatted output of `cluster list` (#416, @inercia)) This adds a `--ouput` flag to `cluster list` for dumping the list of clusters as a JSON or YAML document. Signed-off-by: Alvaro Saurin --- cmd/cluster/clusterList.go | 78 ++++++++++++++++++++++++++++++++------ go.mod | 1 + pkg/types/types.go | 6 +-- vendor/modules.txt | 1 + 4 files changed, 71 insertions(+), 15 deletions(-) diff --git a/cmd/cluster/clusterList.go b/cmd/cluster/clusterList.go index 9c598c53..413bae54 100644 --- a/cmd/cluster/clusterList.go +++ b/cmd/cluster/clusterList.go @@ -23,6 +23,7 @@ package cluster import ( "context" + "encoding/json" "fmt" "os" "strings" @@ -32,6 +33,7 @@ import ( "github.com/rancher/k3d/v3/pkg/runtimes" k3d "github.com/rancher/k3d/v3/pkg/types" "github.com/spf13/cobra" + "gopkg.in/yaml.v2" log "github.com/sirupsen/logrus" @@ -42,11 +44,11 @@ import ( type clusterFlags struct { noHeader bool token bool + output string } // NewCmdClusterList returns a new cobra command func NewCmdClusterList() *cobra.Command { - clusterFlags := clusterFlags{} // create new command @@ -65,6 +67,7 @@ func NewCmdClusterList() *cobra.Command { // add flags cmd.Flags().BoolVar(&clusterFlags.noHeader, "no-headers", false, "Disable headers") cmd.Flags().BoolVar(&clusterFlags.token, "token", false, "Print k3s cluster token") + cmd.Flags().StringVarP(&clusterFlags.output, "output", "o", "", "Output format. One of: json|yaml") // add subcommands @@ -98,18 +101,33 @@ func buildClusterList(ctx context.Context, args []string) []*k3d.Cluster { // PrintPrintClusters : display list of cluster func PrintClusters(clusters []*k3d.Cluster, flags clusterFlags) { + // the output details printed when we dump JSON/YAML + type jsonOutput struct { + k3d.Cluster + ServersRunning int `yaml:"servers_running" json:"serversRunning"` + ServersCount int `yaml:"servers_count" json:"serversCount"` + AgentsRunning int `yaml:"agents_running" json:"agentsRunning"` + AgentsCount int `yaml:"agents_count" json:"agentsCount"` + LoadBalancer bool `yaml:"has_lb,omitempty" json:"hasLoadbalancer,omitempty"` + } + + jsonOutputEntries := []jsonOutput{} + + outputFormat := strings.ToLower(flags.output) tabwriter := tabwriter.NewWriter(os.Stdout, 6, 4, 3, ' ', tabwriter.RememberWidths) defer tabwriter.Flush() - if !flags.noHeader { - headers := []string{"NAME", "SERVERS", "AGENTS", "LOADBALANCER"} // TODO: getCluster: add status column - if flags.token { - headers = append(headers, "TOKEN") - } - _, err := fmt.Fprintf(tabwriter, "%s\n", strings.Join(headers, "\t")) - if err != nil { - log.Fatalln("Failed to print headers") + if outputFormat != "json" && outputFormat != "yaml" { + if !flags.noHeader { + headers := []string{"NAME", "SERVERS", "AGENTS", "LOADBALANCER"} // TODO: getCluster: add status column + if flags.token { + headers = append(headers, "TOKEN") + } + _, err := fmt.Fprintf(tabwriter, "%s\n", strings.Join(headers, "\t")) + if err != nil { + log.Fatalln("Failed to print headers") + } } } @@ -120,10 +138,46 @@ func PrintClusters(clusters []*k3d.Cluster, flags clusterFlags) { agentCount, agentsRunning := cluster.AgentCountRunning() hasLB := cluster.HasLoadBalancer() - if flags.token { - fmt.Fprintf(tabwriter, "%s\t%d/%d\t%d/%d\t%t\t%s\n", cluster.Name, serversRunning, serverCount, agentsRunning, agentCount, hasLB, cluster.Token) + if outputFormat == "json" || outputFormat == "yaml" { + entry := jsonOutput{ + Cluster: *cluster, + ServersRunning: serversRunning, + ServersCount: serverCount, + AgentsRunning: agentsRunning, + AgentsCount: agentCount, + LoadBalancer: hasLB, + } + + if !flags.token { + entry.Token = "" + } + + // clear some things + entry.ExternalDatastore = nil + + jsonOutputEntries = append(jsonOutputEntries, entry) } else { - fmt.Fprintf(tabwriter, "%s\t%d/%d\t%d/%d\t%t\n", cluster.Name, serversRunning, serverCount, agentsRunning, agentCount, hasLB) + if flags.token { + fmt.Fprintf(tabwriter, "%s\t%d/%d\t%d/%d\t%t\t%s\n", cluster.Name, serversRunning, serverCount, agentsRunning, agentCount, hasLB, cluster.Token) + } else { + fmt.Fprintf(tabwriter, "%s\t%d/%d\t%d/%d\t%t\n", cluster.Name, serversRunning, serverCount, agentsRunning, agentCount, hasLB) + } + } + } + + if outputFormat == "json" { + b, err := json.Marshal(jsonOutputEntries) + if err != nil { + fmt.Println(err) + return + } + fmt.Println(string(b)) + } else if outputFormat == "yaml" { + b, err := yaml.Marshal(jsonOutputEntries) + if err != nil { + fmt.Println(err) + return } + fmt.Println(string(b)) } } diff --git a/go.mod b/go.mod index 2e696b45..4b9918a6 100644 --- a/go.mod +++ b/go.mod @@ -39,6 +39,7 @@ require ( google.golang.org/grpc v1.33.1 // indirect google.golang.org/protobuf v1.25.0 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect + gopkg.in/yaml.v2 v2.3.0 gotest.tools/v3 v3.0.2 // indirect k8s.io/client-go v0.17.0 k8s.io/utils v0.0.0-20200109141947-94aeca20bf09 // indirect diff --git a/pkg/types/types.go b/pkg/types/types.go index bc7e3284..bf20e6df 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -182,10 +182,10 @@ type Cluster struct { Token string `yaml:"cluster_token" json:"clusterToken,omitempty"` Nodes []*Node `yaml:"nodes" json:"nodes,omitempty"` InitNode *Node // init server node - ExternalDatastore ExternalDatastore `yaml:"external_datastore" json:"externalDatastore,omitempty"` - CreateClusterOpts *ClusterCreateOpts `yaml:"options" json:"options,omitempty"` + ExternalDatastore *ExternalDatastore `yaml:"external_datastore,omitempty" json:"externalDatastore,omitempty"` + CreateClusterOpts *ClusterCreateOpts `yaml:"options,omitempty" json:"options,omitempty"` ExposeAPI ExposeAPI `yaml:"expose_api" json:"exposeAPI,omitempty"` - ServerLoadBalancer *Node `yaml:"server_loadbalancer" json:"serverLoadBalancer,omitempty"` + ServerLoadBalancer *Node `yaml:"server_loadbalancer,omitempty" json:"serverLoadBalancer,omitempty"` ImageVolume string `yaml:"image_volume" json:"imageVolume,omitempty"` } diff --git a/vendor/modules.txt b/vendor/modules.txt index 1a89fdc0..bf37e573 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -366,6 +366,7 @@ google.golang.org/protobuf/types/known/timestamppb # gopkg.in/inf.v0 v0.9.1 gopkg.in/inf.v0 # gopkg.in/yaml.v2 v2.3.0 +## explicit gopkg.in/yaml.v2 # gotest.tools/v3 v3.0.2 ## explicit