diff --git a/cmd/list.go b/cmd/list.go index e462ee2..652d643 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -8,12 +8,12 @@ import ( // listCmd represents the list command var listCmd = &cobra.Command{ - Use: "list", + Use: "list-config", Short: "List all cloud machines in the config file", Long: "List all cloud machines in the config file", Example: indentor.Indent(" ", ` # list -gmachine list +gmachine list-config `), SilenceUsage: true, RunE: list, @@ -34,7 +34,6 @@ func list(cmd *cobra.Command, _ []string) error { if m.CSEK != nil && len(m.CSEK) > 0 { encrypted = true } - // TODO print out some kind of indication next to the default machine cmd.Printf("%s (%s, %s, %s, encrypted: %t)\n", m.Name, m.Account, m.Project, m.Zone, encrypted) } return nil diff --git a/cmd/status.go b/cmd/status.go index fea3f31..b9557b1 100644 --- a/cmd/status.go +++ b/cmd/status.go @@ -1,16 +1,17 @@ package cmd import ( - "errors" "fmt" "path" "strings" + "sync" "text/tabwriter" "github.com/joemiller/gmachine/internal/config" "github.com/joemiller/gmachine/internal/gcp" "github.com/joemiller/gmachine/internal/indentor" "github.com/spf13/cobra" + "golang.org/x/sync/errgroup" "google.golang.org/api/compute/v1" ) @@ -31,36 +32,18 @@ gmachine status machine2 } func init() { - statusCmd.Flags().BoolP("all", "a", false, "Print status of all instances") - rootCmd.AddCommand(statusCmd) } func status(cmd *cobra.Command, args []string) error { - all, err := cmd.Flags().GetBool("all") - if err != nil { - return err - } - cfg, err := config.LoadFile(cfgFile) if err != nil { return err } names := []string{} - if !all { - name := cfg.GetDefault() - if len(args) > 0 { - name = args[0] - } - if name == "" { - return errors.New("must specify machine or set a default machine with 'set-default'") - } - names = append(names, name) - } else { - for _, m := range cfg.Machines { - names = append(names, m.Name) - } + for _, m := range cfg.Machines { + names = append(names, m.Name) } // initialize output table writer @@ -68,41 +51,72 @@ func status(cmd *cobra.Command, args []string) error { print := func(values ...string) { fmt.Fprintln(table, strings.Join(values, "\t")) } - print("NAME", "ACCOUNT", "PROJECT", "ZONE", "MACHINE_TYPE", "PREEMPTIBLE", "INTERNAL_IP", "EXTERNAL_IP", "STATUS", "DEFAULT") + print("NAME", "ACCOUNT", "PROJECT", "ZONE", "MACHINE_TYPE", "PREEMPTIBLE", "ENCRYPTION", "INTERNAL_IP", "EXTERNAL_IP", "STATUS", "DEFAULT") - // status row for each machine - for _, name := range names { - machine, err := cfg.Get(name) - if err != nil { - return err + eg := errgroup.Group{} + eg.SetLimit(8) + + printerWg := sync.WaitGroup{} + outputCh := make(chan []string) + + printerWg.Add(1) + go func() { + defer printerWg.Done() + for row := range outputCh { + print(row...) } - meta, err := gcp.DescribeInstance(name, machine.Account, machine.Project, machine.Zone) - if err != nil { + + if err := table.Flush(); err != nil { cmd.PrintErr(err) - continue } + }() - print( - name, - machine.Account, - machine.Project, - path.Base(meta.Zone), - path.Base(meta.MachineType), - fmt.Sprintf("%t", meta.Scheduling.Preemptible), - internalIP(meta.NetworkInterfaces), - externalIP(meta.NetworkInterfaces), - meta.Status, - defaultStr(cfg.GetDefault(), name), - ) - + // status row for each machine + for _, name := range names { + name := name + + eg.Go(func() error { + machine, err := cfg.Get(name) + if err != nil { + return err + } + meta, err := gcp.DescribeInstance(name, machine.Account, machine.Project, machine.Zone) + if err != nil { + cmd.PrintErr(err) + return nil + } + + encryptStatus := "false" + if len(machine.CSEK) > 0 { + encryptStatus = "CSEK" + } + // TODO: also handle CMEK encryption some day + + outputCh <- []string{ + name, + machine.Account, + machine.Project, + path.Base(meta.Zone), + path.Base(meta.MachineType), + fmt.Sprintf("%t", meta.Scheduling.Preemptible), + encryptStatus, + internalIP(meta.NetworkInterfaces), + externalIP(meta.NetworkInterfaces), + meta.Status, + defaultStr(cfg.GetDefault(), name), + } + return nil + }) } - err = table.Flush() - if err != nil { - return err + + // wait for the `gcloud` describers to finish + if err := eg.Wait(); err != nil { + return fmt.Errorf("gcloud error: %v", err) } + close(outputCh) - // TODO: fanout 'describe' calls to a limited size worker pool - // fanin results to table printer.. errgroup + // wait for the table printer go routine to finish: + printerWg.Wait() return nil } diff --git a/go.mod b/go.mod index f0f3e8c..d506797 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/spf13/cobra v1.7.0 github.com/spf13/viper v1.17.0 github.com/stretchr/testify v1.8.4 + golang.org/x/sync v0.4.0 golang.org/x/sys v0.13.0 google.golang.org/api v0.149.0 gopkg.in/yaml.v2 v2.4.0