From 86ff460968752016ffd49504d7d60dff52e2b78e Mon Sep 17 00:00:00 2001 From: Dawid Pogorzelski Date: Fri, 30 Jul 2021 22:51:20 +0200 Subject: [PATCH] Tweaks (#53) * Fix loglevel parsing * Bump dependencies * Change --filter to --target * Update documentation * Import log handler as "loghandler" * Change command color to purple * Change flag name to target * Update formatting --- README.md | 8 ++++---- cmd/root.go | 8 +++++++- cmd/run.go | 13 ++++++------- colors/colors.go | 5 +++++ go.mod | 10 +++++----- go.sum | 25 +++++++++++++++++++------ main.go | 4 ++-- marathon/marathon.go | 10 +++++++--- 8 files changed, 55 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 574a830..aee0b28 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ speedrun run systemctl stop nginx No hassles with setting up and maintaining a server with agents as speedrun has none. No runtime to install/maintain as it's a single self-contained binary. Speedrun leverages SSH as transport with tight cloud vendor integration to take the burden of mundane things like key generation and propagation/revocation away from the user. -Server selection is made as intuitive as possible, currently based on [gcloud topic filters](https://cloud.google.com/sdk/gcloud/reference/topic/filters) but in the future will be replaced by a generic selection mechanism to provide a seamless experience across different cloud vendors. +Server targeting (`--target`) is made as intuitive as possible, currently based on [gcloud topic filters](https://cloud.google.com/sdk/gcloud/reference/topic/filters) but in the future will be replaced by a generic selection mechanism to provide a seamless experience across different cloud vendors. Features: @@ -56,16 +56,16 @@ Use 1000 concurrent SSH workers speedrun run "uname -r" --concurrency 1000 ``` -Stop Nginx on VMs matching the filter +Stop Nginx on VMs matching the target selector ```bash -speedrun run sudo systemctl stop nginx --filter "labels.env=staging AND labels.app=foobar" +speedrun run sudo systemctl stop nginx --target "labels.env=staging AND labels.app=foobar" ``` Ignore SSH fingerprint mismatch and connect via private IP addresses ```bash -speedrun run "ls -la" --filter "labels.env != prod" --ignore-fingerprint --concurrency 1000 --use-private-ip +speedrun run "ls -la" --target "labels.env != prod" --ignore-fingerprint --concurrency 1000 --use-private-ip ``` Use a different config file diff --git a/cmd/root.go b/cmd/root.go index 65b6645..60cc9d2 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -62,5 +62,11 @@ func initConfig() { log.Fatal(err.Error()) } } - log.SetLevelFromString(viper.GetString("loglevel")) + lvl, err := log.ParseLevel(viper.GetString("loglevel")) + if err != nil { + log.Fatalf("Couldn't parse log level: %s", err) + return + } + log.SetLevel(lvl) + } diff --git a/cmd/run.go b/cmd/run.go index 0f80254..feb7017 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -15,7 +15,7 @@ import ( var runCmd = &cobra.Command{ Use: "run ", Short: "Run command on remote servers", - Example: " speedrun run whoami\n speedrun run whoami --only-failures --filter \"labels.foo = bar AND labels.environment = staging\"", + Example: " speedrun run whoami\n speedrun run whoami --only-failures --target \"labels.foo = bar AND labels.environment = staging\"", Args: cobra.MinimumNArgs(1), PreRun: func(cmd *cobra.Command, args []string) { initConfig() @@ -24,7 +24,7 @@ var runCmd = &cobra.Command{ } func init() { - runCmd.Flags().String("filter", "", "Fetch instances that match the filter") + runCmd.Flags().StringP("target", "t", "", "Fetch instances that match the target selection criteria") runCmd.Flags().String("projectid", "", "Override GCP project id") runCmd.Flags().Bool("only-failures", false, "Print only failures and errors") runCmd.Flags().Bool("ignore-fingerprint", false, "Ignore host's fingerprint mismatch") @@ -39,7 +39,6 @@ func init() { viper.BindPFlag("ssh.only-failures", runCmd.Flags().Lookup("only-failures")) viper.BindPFlag("ssh.concurrency", runCmd.Flags().Lookup("concurrency")) viper.BindPFlag("ssh.use-private-ip", runCmd.Flags().Lookup("use-private-ip")) - } func run(cmd *cobra.Command, args []string) error { @@ -52,7 +51,7 @@ func run(cmd *cobra.Command, args []string) error { usePrivateIP := viper.GetBool("ssh.use-private-ip") useOSlogin := viper.GetBool("gcp.use-oslogin") - filter, err := cmd.Flags().GetString("filter") + target, err := cmd.Flags().GetString("target") if err != nil { return err } @@ -72,8 +71,8 @@ func run(cmd *cobra.Command, args []string) error { return err } - log.Info("Fetching list of GCE instances") - instances, err := gcpClient.GetInstances(filter, usePrivateIP) + log.Info("Fetching instance list") + instances, err := gcpClient.GetInstances(target, usePrivateIP) if err != nil { return err } @@ -94,7 +93,7 @@ func run(cmd *cobra.Command, args []string) error { m := marathon.New(command, timeout, concurrency) err = m.Run(instances, k, ignoreFingerprint) if err != nil { - log.Fatal(err.Error()) + return err } m.PrintResult(onlyFailures) diff --git a/colors/colors.go b/colors/colors.go index f9eb03d..62a4cf5 100644 --- a/colors/colors.go +++ b/colors/colors.go @@ -28,3 +28,8 @@ func Blue(s string) string { func White(s string) string { return gchalk.White(s) } + +// Purple returns a purple string +func Purple(s string) string { + return gchalk.RGB(196, 16, 224)(s) +} diff --git a/go.mod b/go.mod index 4302f54..2c7c804 100644 --- a/go.mod +++ b/go.mod @@ -15,11 +15,11 @@ require ( github.com/spf13/viper v1.8.1 golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914 - google.golang.org/api v0.50.0 + google.golang.org/api v0.52.0 ) require ( - cloud.google.com/go v0.87.0 // indirect + cloud.google.com/go v0.89.0 // indirect github.com/VividCortex/ewma v1.2.0 // indirect github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect github.com/fatih/color v1.12.0 // indirect @@ -43,17 +43,17 @@ require ( github.com/pkg/sftp v1.13.2 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/spf13/afero v1.6.0 // indirect - github.com/spf13/cast v1.3.1 // indirect + github.com/spf13/cast v1.4.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.2.0 // indirect go.opencensus.io v0.23.0 // indirect golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420 // indirect - golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 // indirect + golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect golang.org/x/text v0.3.6 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20210714021259-044028024a4f // indirect + google.golang.org/genproto v0.0.0-20210729151513-df9385d47c1b // indirect google.golang.org/grpc v1.39.0 // indirect google.golang.org/protobuf v1.27.1 // indirect gopkg.in/ini.v1 v1.62.0 // indirect diff --git a/go.sum b/go.sum index c10a278..ee18421 100644 --- a/go.sum +++ b/go.sum @@ -21,8 +21,10 @@ cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0 h1:8ZtzmY4a2JIO2sljMbpqkDYxA8aJQveYr3AMa+X40oc= cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.88.0/go.mod h1:dnKwfYbP9hQhefiUvpbcAyoGSHUrOxR20JVElLiUvEY= +cloud.google.com/go v0.89.0 h1:ZT4GU+y59fC95Mfdn2RtxuzN2gc69dzlVevQK8Ykyqs= +cloud.google.com/go v0.89.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -171,6 +173,8 @@ github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210715191844-86eeefc3e471/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -310,8 +314,9 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9 github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs= github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.4.0 h1:WhlbjwB9EGCc8W5Rxdkus+wmH2ASRwwTJk6tgHKwdqQ= +github.com/spf13/cast v1.4.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= @@ -524,8 +529,9 @@ golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -597,6 +603,7 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -627,8 +634,10 @@ google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0 h1:LX7NFCFYOHzr7WHaYiRUpeipZe9o5L8T+2F4Z798VDw= google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.52.0 h1:m5FLEd6dp5CU1F0tMWyqDi2XjchviIz8ntzOSz7w8As= +google.golang.org/api v0.52.0/go.mod h1:Him/adpjt0sxtkWViy0b6xyKW/SD71CwdJ7HqJo7SrU= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -683,8 +692,12 @@ google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxH google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210714021259-044028024a4f h1:UUQZ0BNnLgSI3v/uqq0hjdxwctexOPwnvKA5AA/Aze4= -google.golang.org/genproto v0.0.0-20210714021259-044028024a4f/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210721163202-f1cecdd8b78a/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210729151513-df9385d47c1b h1:4xoALQmXxqVdDdLimpPyPeDdsJzo+nFTJw9euAMpqgM= +google.golang.org/genproto v0.0.0-20210729151513-df9385d47c1b/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= diff --git a/main.go b/main.go index af8c692..4048fb1 100644 --- a/main.go +++ b/main.go @@ -5,11 +5,11 @@ import ( "speedrun/cmd" "github.com/apex/log" - "github.com/apex/log/handlers/cli" + loghandler "github.com/apex/log/handlers/cli" ) func main() { - h := cli.New(os.Stdout) + h := loghandler.New(os.Stdout) h.Padding = 0 log.SetHandler(h) cmd.Execute() diff --git a/marathon/marathon.go b/marathon/marathon.go index e4d2f76..a8e6ca5 100644 --- a/marathon/marathon.go +++ b/marathon/marathon.go @@ -65,9 +65,14 @@ func (m *Marathon) Run(instances []cloud.Instance, key *key.Key, ignoreFingerpri pool := pond.New(m.Concurrency, 10000) bar := pb.New(len(instances)) - if log.MustParseLevel(viper.GetString("loglevel")) > 0 { + lvl, err := log.ParseLevel(viper.GetString("loglevel")) + if err != nil { + return fmt.Errorf("couldn't parse log level: %s", err) + } + + if lvl > 0 { bar.SetMaxWidth(1) - bar.SetTemplateString(fmt.Sprintf("%s Running [%s]: {{counters . }}", colors.Blue("•"), colors.Blue(m.Command))) + bar.SetTemplateString(fmt.Sprintf("%s Running [%s]: {{counters . }}", colors.Blue("•"), colors.Purple(m.Command))) bar.Start() } @@ -168,7 +173,6 @@ func checkHostsFile() error { // PrintResult prints the results of the ssh command run func (m *Marathon) PrintResult(failures bool) { - if !failures { for host, msg := range m.successes { fmt.Printf(" %s:\n%s\n", colors.Green(host), colors.White(msg))