diff --git a/internal/fetch/download.go b/internal/fetch/download.go index 5bff4b5..eb825a3 100644 --- a/internal/fetch/download.go +++ b/internal/fetch/download.go @@ -17,7 +17,7 @@ import ( // Download handles direct binary releases type Download struct { - url string + urls []string headers map[string]string } @@ -25,39 +25,62 @@ type Download struct { func (d Download) Fetch(ctx context.Context, dist, v string, mapper mapping.Mapper) (string, error) { logger := zerolog.Ctx(ctx).With().Str("func", "Download.Fetch").Logger() - args := tpl.New(v, mapper) - - url, err := args.Render(d.url) - if err != nil { - return "", err - } - - logger.Debug().Msgf("fetching version %q for arch %q and OS %q at %s", v, runtime.GOARCH, runtime.GOOS, url) - - req, err := http.NewRequest("GET", url, nil) - if err != nil { - return "", err - } - - for k, v := range d.headers { - req.Header.Add(k, v) - } - - resp, err := http.DefaultClient.Do(req) - if err != nil { - return "", err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return "", fmt.Errorf("unable to download binary at %s: %s", url, resp.Status) + var resp *http.Response + + for i, u := range d.urls { + + args := tpl.New(v, mapper) + + url, err := args.Render(u) + if err != nil { + if len(d.urls)-1 > i { + continue + } else { + return "", err + } + } + + logger.Debug().Msgf("fetching version %q for arch %q and OS %q at %s", v, runtime.GOARCH, runtime.GOOS, url) + + req, err := http.NewRequest("GET", url, nil) + if err != nil { + if len(d.urls)-1 > i { + continue + } else { + return "", err + } + } + + for k, v := range d.headers { + req.Header.Add(k, v) + } + + resp, err = http.DefaultClient.Do(req) + if err != nil { + if len(d.urls)-1 > i { + continue + } else { + return "", err + } + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + if len(d.urls)-1 > i { + logger.Debug().Msgf("unable to download binary at %s: %s, %d urls left to try...", url, resp.Status, len(d.urls)-1-i) + continue + } else { + return "", fmt.Errorf("unable to download binary at %s: %s", url, resp.Status) + } + } + // if we reach this point, download was successful, let's move on + break } tmpfile, err := os.CreateTemp("", v) if err != nil { logger.Fatal().Err(err) } - defer tmpfile.Close() bar := progressbar.DefaultBytes( diff --git a/internal/fetch/fetch.go b/internal/fetch/fetch.go index 2c8a11f..ca41cea 100644 --- a/internal/fetch/fetch.go +++ b/internal/fetch/fetch.go @@ -16,9 +16,10 @@ type Fetcher interface { // Fetch contains fetch configuration type Fetch struct { - Type string `yaml:"type"` - URL string `yaml:"url"` - TokenEnv string `yaml:"token_env"` + Type string `yaml:"type"` + URL string `yaml:"url"` + URLs []string `yaml:"urls"` + TokenEnv string `yaml:"token_env"` } // Factory returns instances that comply to Fetcher interface @@ -37,10 +38,13 @@ func (r Fetch) Factory() (Fetcher, error) { } headers["PRIVATE-TOKEN"] = token } - return Download{ - url: r.URL, + urls: []string{r.URL}, headers: headers, }, nil + case "download_list": + return Download{ + urls: r.URLs, + }, nil } }