From 4fe82f3d86184b4fd418de6d4e77a8499159f957 Mon Sep 17 00:00:00 2001 From: fabriziopandini Date: Thu, 17 Aug 2023 23:28:54 +0200 Subject: [PATCH] use http get to download file from GitHub --- .../client/repository/repository_github.go | 87 +++++++++++++++++-- 1 file changed, 82 insertions(+), 5 deletions(-) diff --git a/cmd/clusterctl/client/repository/repository_github.go b/cmd/clusterctl/client/repository/repository_github.go index ab51f41fb3ab..0c1be75f6186 100644 --- a/cmd/clusterctl/client/repository/repository_github.go +++ b/cmd/clusterctl/client/repository/repository_github.go @@ -156,6 +156,22 @@ func (g *gitHubRepository) ComponentsPath() string { // GetFile returns a file for a given provider version. func (g *gitHubRepository) GetFile(version, path string) ([]byte, error) { + cacheID := fmt.Sprintf("%s/%s:%s:%s", g.owner, g.repository, version, path) + if content, ok := cacheFiles[cacheID]; ok { + return content, nil + } + + // Try to get the file using http get. + // NOTE: this can be disabled by setting GORPOXY to `direct` or `off` (same knobs used for skipping goproxy requests). + if goProxyClient, _ := g.getGoproxyClient(); goProxyClient != nil { + if files, err := g.httpGetFilesFromRelease(version, path); err == nil { + cacheFiles[cacheID] = files + return files, nil + } + } + + // If the http get request failed (or it is disabled) falls back on using the GITHUB api to download the file + release, err := g.getReleaseByTag(version) if err != nil { if errors.Is(err, errNotFound) { @@ -172,6 +188,7 @@ func (g *gitHubRepository) GetFile(version, path string) ([]byte, error) { return nil, errors.Wrapf(err, "failed to download files from GitHub release %s", version) } + cacheFiles[cacheID] = files return files, nil } @@ -284,6 +301,8 @@ func (g *gitHubRepository) setClientToken(token string) { // getVersions returns all the release versions for a github repository. func (g *gitHubRepository) getVersions() ([]string, error) { + fmt.Println("GITHUB: getVersions") + client := g.getClient() // Get all the releases. @@ -349,6 +368,8 @@ func (g *gitHubRepository) getReleaseByTag(tag string) (*github.RepositoryReleas return release, nil } + fmt.Println("GITHUB: getReleaseByTag", tag) + client := g.getClient() var release *github.RepositoryRelease @@ -379,14 +400,32 @@ func (g *gitHubRepository) getReleaseByTag(tag string) (*github.RepositoryReleas return release, nil } +// httpGetFilesFromRelease gets a file from github using http get. +func (g *gitHubRepository) httpGetFilesFromRelease(version, fileName string) ([]byte, error) { + ctx := context.TODO() + + fmt.Println("HTTP: downloadFilesFromRelease", g.owner, g.repository, version, fileName) + + downloadURL := fmt.Sprintf("https://github.com/%s/%s/releases/download/%s/%s", g.owner, g.repository, version, fileName) + var retryError error + var content []byte + _ = wait.PollUntilContextTimeout(ctx, retryableOperationInterval, retryableOperationTimeout, true, func(ctx context.Context) (bool, error) { + if content, retryError = httpGetFile(downloadURL); retryError != nil { + return false, retryError + } + return true, nil + }) + if retryError != nil { + return content, retryError + } + return content, nil +} + // downloadFilesFromRelease download a file from release. func (g *gitHubRepository) downloadFilesFromRelease(release *github.RepositoryRelease, fileName string) ([]byte, error) { ctx := context.TODO() - cacheID := fmt.Sprintf("%s/%s:%s:%s", g.owner, g.repository, *release.TagName, fileName) - if content, ok := cacheFiles[cacheID]; ok { - return content, nil - } + fmt.Println("GITHUB: downloadFilesFromRelease", g.owner, g.repository, *release.TagName, fileName) client := g.getClient() absoluteFileName := filepath.Join(g.rootPath, fileName) @@ -441,7 +480,6 @@ func (g *gitHubRepository) downloadFilesFromRelease(release *github.RepositoryRe return nil, retryError } - cacheFiles[cacheID] = content return content, nil } @@ -457,3 +495,42 @@ func (g *gitHubRepository) handleGithubErr(err error, message string, args ...in } return errors.Wrapf(err, message, args...) } + +func httpGetFile(url string) ([]byte, error) { + // create a new HTTP client + client := &http.Client{} + + // create a new GET request + req, err := http.NewRequestWithContext(context.TODO(), http.MethodGet, url, http.NoBody) + if err != nil { + return nil, errors.Wrap(err, "error creating request") + } + + // send the request and get the response + resp, err := client.Do(req) + if err != nil { + return nil, errors.Wrap(err, "error sending request") + } + defer resp.Body.Close() + + // check if the response was a redirect + if resp.StatusCode >= 300 && resp.StatusCode <= 399 { + redirectURL, err := resp.Location() + if err != nil { + return nil, errors.Wrap(err, "error getting redirect location") + } + + // Follow the redirect + return httpGetFile(redirectURL.String()) + } + + if resp.StatusCode != http.StatusOK { + return nil, errors.Errorf("error getting file, status code: %d", resp.StatusCode) + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "error reading response body") + } + return body, nil +}