diff --git a/upctl/cfg/config.go b/upctl/cfg/config.go index b16ded1..4808b2e 100644 --- a/upctl/cfg/config.go +++ b/upctl/cfg/config.go @@ -28,6 +28,7 @@ var VERSION string // maintained by -x type Config struct { Endpoint string Debug bool + Retries int } func Getversion() string { diff --git a/upctl/cmd/root.go b/upctl/cmd/root.go index 3147369..7b6b8cc 100644 --- a/upctl/cmd/root.go +++ b/upctl/cmd/root.go @@ -68,9 +68,9 @@ func Execute() { return completion(cmd, ShowCompletion) } - // conf.ApplyDefaults() + // errors at this stage do not cause the usage to be shown + cmd.SilenceUsage = true - // actual execution starts here return lib.Runclient(&conf, args) }, PersistentPreRunE: func(cmd *cobra.Command, args []string) error { @@ -82,6 +82,7 @@ func Execute() { rootCmd.PersistentFlags().BoolVarP(&ShowVersion, "version", "v", false, "Print program version") rootCmd.PersistentFlags().BoolVarP(&conf.Debug, "debug", "d", false, "Enable debugging") rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "custom config file") + rootCmd.PersistentFlags().IntVarP(&conf.Retries, "retries", "r", 3, "How often shall we retry to access our endpoint") rootCmd.PersistentFlags().StringVarP(&conf.Endpoint, "endpoint", "e", "http://localhost:8080/api", "upload api endpoint url") err := rootCmd.Execute() diff --git a/upctl/lib/client.go b/upctl/lib/client.go index 2aa1cc3..ec3c7e7 100644 --- a/upctl/lib/client.go +++ b/upctl/lib/client.go @@ -33,23 +33,40 @@ type Response struct { Message string `json:"message"` } -func Runclient(cfg *cfg.Config, args []string) error { +func Runclient(c *cfg.Config, args []string) error { if len(args) == 0 { return errors.New("No files specified to upload.") } client := req.C() - if cfg.Debug { + if c.Debug { client.DevMode() } - url := cfg.Endpoint + "/putfile" + client.SetUserAgent("upctl-" + cfg.VERSION) + + url := c.Endpoint + "/putfile" rq := client.R() for _, file := range args { rq.SetFile("upload[]", file) } + if c.Retries > 0 { + // Enable retry and set the maximum retry count. + rq.SetRetryCount(c.Retries). + // Set the retry sleep interval with a commonly used + // algorithm: capped exponential backoff with jitter + // (https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/). + SetRetryBackoffInterval(1*time.Second, 5*time.Second). + AddRetryHook(func(resp *req.Response, err error) { + req := resp.Request.RawRequest + if c.Debug { + fmt.Println("Retrying endpoint request:", req.Method, req.URL) + } + }) + } + resp, err := rq. SetFormData(map[string]string{ "expire": "1d", @@ -71,7 +88,7 @@ func Runclient(cfg *cfg.Config, args []string) error { fmt.Println(r) - if cfg.Debug { + if c.Debug { trace := resp.TraceInfo() // Use `resp.Request.TraceInfo()` to avoid unnecessary struct copy in production. fmt.Println(trace.Blame()) // Print out exactly where the http request is slowing down. fmt.Println("----------")