Skip to content

Commit

Permalink
parallelize gmachine status cmd
Browse files Browse the repository at this point in the history
also rename `list` to `list-config` to disambiguate it from listing
machines in the config file -vs- the status of machines in the config
file.

still might get rid of the `list-config` cmd altogether. still thinking
on it
  • Loading branch information
joemiller committed Nov 1, 2023
1 parent 9798c14 commit 02e48b9
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 51 deletions.
5 changes: 2 additions & 3 deletions cmd/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
Expand Down
110 changes: 62 additions & 48 deletions cmd/status.go
Original file line number Diff line number Diff line change
@@ -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"
)

Expand All @@ -31,78 +32,91 @@ 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
table := tabwriter.NewWriter(cmd.OutOrStdout(), 5, 0, 2, ' ', tabwriter.DiscardEmptyColumns)
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
}
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 02e48b9

Please sign in to comment.