Skip to content

Commit cd23003

Browse files
author
Axel von Bertoldi
committed
Add retryCommand type
This is a simplified version of the packagecloud::push target implementation. The main difference is that there isn't a separate retryable error type. This implementation also doesn't handle ignorable errors because there aren't any so far, but that can be added easily.
1 parent d94d4f8 commit cd23003

1 file changed

Lines changed: 58 additions & 0 deletions

File tree

magefiles/pulp/push.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ import (
1212
"path/filepath"
1313
"regexp"
1414
"strings"
15+
"time"
1516

17+
"github.com/jpillora/backoff"
1618
"github.com/magefile/mage/sh"
1719
"github.com/samber/lo"
1820
"github.com/sourcegraph/conc/pool"
@@ -325,3 +327,59 @@ func parseRPMInfo(out io.Reader) (rpmInfo, error) {
325327

326328
return info, nil
327329
}
330+
331+
type retryCommand struct {
332+
cmd string
333+
args []string
334+
backoff backoff.Backoff
335+
out io.Writer
336+
retryableErrs []*regexp.Regexp
337+
exec shExec
338+
}
339+
340+
func newRetryCommand(cmd string, args []string, retryableErrs []*regexp.Regexp, out io.Writer, exec shExec) *retryCommand {
341+
return &retryCommand{
342+
cmd: cmd,
343+
args: args,
344+
backoff: backoff.Backoff{
345+
Min: time.Second,
346+
Max: 5 * time.Second,
347+
},
348+
out: out,
349+
retryableErrs: retryableErrs,
350+
exec: exec,
351+
}
352+
}
353+
354+
func (c *retryCommand) run() error {
355+
for i := range 5 {
356+
slog.Info("attempting to run command", "attempt", i+1, "command", c.cmd, "args", c.args)
357+
358+
outBuf, errBuf := bytes.Buffer{}, bytes.Buffer{}
359+
stdout := io.MultiWriter(&outBuf, os.Stdout)
360+
stderr := io.MultiWriter(&errBuf, os.Stderr)
361+
362+
_, err := c.exec(nil, stdout, stderr, c.cmd, c.args...)
363+
364+
if err == nil {
365+
_, _ = io.Copy(c.out, &outBuf)
366+
return nil
367+
}
368+
if c.isRetryable(errBuf.String()) {
369+
time.Sleep(c.backoff.Duration())
370+
continue
371+
}
372+
return fmt.Errorf("execution of command (%s %s) failed: %s", c.cmd, c.args, errBuf.String())
373+
}
374+
375+
return fmt.Errorf("execution of command (%s %s) failed after 5 retries ", c.cmd, c.args)
376+
}
377+
378+
func (c *retryCommand) isRetryable(stderr string) bool {
379+
for _, re := range c.retryableErrs {
380+
if re.MatchString(stderr) {
381+
return true
382+
}
383+
}
384+
return false
385+
}

0 commit comments

Comments
 (0)