Skip to content

Commit

Permalink
move goproxy getVersions to a separate package and reuse at releaselink
Browse files Browse the repository at this point in the history
  • Loading branch information
chrischdi committed Dec 9, 2022
1 parent fb060da commit 2e5e0c9
Show file tree
Hide file tree
Showing 8 changed files with 276 additions and 149 deletions.
100 changes: 0 additions & 100 deletions cmd/clusterctl/client/repository/goproxy_test.go

This file was deleted.

24 changes: 18 additions & 6 deletions cmd/clusterctl/client/repository/repository_github.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ import (
"net/http"
"net/url"
"os"
"path"
"path/filepath"
"strings"
"time"

"github.com/blang/semver"
"github.com/google/go-github/v45/github"
"github.com/pkg/errors"
"golang.org/x/oauth2"
Expand All @@ -36,6 +38,7 @@ import (
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/config"
logf "sigs.k8s.io/cluster-api/cmd/clusterctl/log"
"sigs.k8s.io/cluster-api/internal/goproxy"
)

const (
Expand Down Expand Up @@ -70,7 +73,7 @@ type gitHubRepository struct {
rootPath string
componentsPath string
injectClient *github.Client
injectGoproxyClient *goproxyClient
injectGoproxyClient *goproxy.Client
}

var _ Repository = &gitHubRepository{}
Expand All @@ -83,7 +86,7 @@ func injectGithubClient(c *github.Client) githubRepositoryOption {
}
}

func injectGoproxyClient(c *goproxyClient) githubRepositoryOption {
func injectGoproxyClient(c *goproxy.Client) githubRepositoryOption {
return func(g *gitHubRepository) {
g.injectGoproxyClient = c
}
Expand All @@ -110,11 +113,20 @@ func (g *gitHubRepository) GetVersions() ([]string, error) {

var versions []string
if goProxyClient != nil {
versions, err = goProxyClient.getVersions(context.TODO(), githubDomain, g.owner, g.repository)
// A goproxy is also able to handle the github repository path instead of the actual go module name.
gomodulePath := path.Join(githubDomain, g.owner, g.repository)

var parsedVersions semver.Versions
parsedVersions, err = goProxyClient.GetVersions(context.TODO(), gomodulePath)

// Log the error before fallback to github repository client happens.
if err != nil {
log.V(5).Info("error using Goproxy client to list versions for repository, falling back to github client", "owner", g.owner, "repository", g.repository, "error", err)
}

for _, v := range parsedVersions {
versions = append(versions, "v"+v.String())
}
}

// Fallback to github repository client if goProxyClient is nil or an error occurred.
Expand Down Expand Up @@ -239,19 +251,19 @@ func (g *gitHubRepository) getClient() *github.Client {
// getGoproxyClient returns a go proxy client.
// It returns nil, nil if the environment variable is set to `direct` or `off`
// to skip goproxy requests.
func (g *gitHubRepository) getGoproxyClient() (*goproxyClient, error) {
func (g *gitHubRepository) getGoproxyClient() (*goproxy.Client, error) {
if g.injectGoproxyClient != nil {
return g.injectGoproxyClient, nil
}
scheme, host, err := getGoproxyHost(os.Getenv("GOPROXY"))
scheme, host, err := goproxy.GetSchemeAndHost(os.Getenv("GOPROXY"))
if err != nil {
return nil, err
}
// Don't return a client if scheme and host is set to empty string.
if scheme == "" && host == "" {
return nil, nil
}
return newGoproxyClient(scheme, host), nil
return goproxy.NewGoproxyClient(scheme, host), nil
}

// setClientToken sets authenticatingHTTPClient field of gitHubRepository struct.
Expand Down
5 changes: 3 additions & 2 deletions cmd/clusterctl/client/repository/repository_github_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/config"
"sigs.k8s.io/cluster-api/cmd/clusterctl/internal/test"
"sigs.k8s.io/cluster-api/internal/goproxy"
)

func Test_gitHubRepository_GetVersions(t *testing.T) {
Expand Down Expand Up @@ -833,7 +834,7 @@ func resetCaches() {
// newFakeGoproxy sets up a test HTTP server along with a github.Client that is
// configured to talk to that test server. Tests should register handlers on
// mux which provide mock responses for the API method being tested.
func newFakeGoproxy() (client *goproxyClient, mux *http.ServeMux, teardown func()) {
func newFakeGoproxy() (client *goproxy.Client, mux *http.ServeMux, teardown func()) {
// mux is the HTTP request multiplexer used with the test server.
mux = http.NewServeMux()

Expand All @@ -845,5 +846,5 @@ func newFakeGoproxy() (client *goproxyClient, mux *http.ServeMux, teardown func(

// client is the GitHub client being tested and is configured to use test server.
url, _ := url.Parse(server.URL + "/")
return &goproxyClient{scheme: url.Scheme, host: url.Host}, mux, server.Close
return goproxy.NewGoproxyClient(url.Scheme, url.Host), mux, server.Close
}
6 changes: 3 additions & 3 deletions docs/book/src/user/quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ Download the latest binary of `clusterawsadm` from the [AWS provider releases].
Download the latest release; on Linux, type:
```
curl -L {{#releaselink gomodule:"sigs.k8s.io/cluster-api-provider-aws" asset:"clusterawsadm-linux-amd64" version:"1.x"}} -o clusterawsadm
curl -L {{#releaselink gomodule:"sigs.k8s.io/cluster-api-provider-aws" asset:"clusterawsadm-linux-amd64" version:">=1.0.0"}} -o clusterawsadm
```
Make it executable
Expand All @@ -284,12 +284,12 @@ clusterawsadm version
Download the latest release; on macOs, type:
```
curl -L {{#releaselink gomodule:"sigs.k8s.io/cluster-api-provider-aws" asset:"clusterawsadm-darwin-amd64" version:"1.x"}} -o clusterawsadm
curl -L {{#releaselink gomodule:"sigs.k8s.io/cluster-api-provider-aws" asset:"clusterawsadm-darwin-amd64" version:">=1.0.0"}} -o clusterawsadm
```
Or if your Mac has an M1 CPU (”Apple Silicon”):
```
curl -L {{#releaselink gomodule:"sigs.k8s.io/cluster-api-provider-aws" asset:"clusterawsadm-darwin-arm64" version:"1.x"}} -o clusterawsadm
curl -L {{#releaselink gomodule:"sigs.k8s.io/cluster-api-provider-aws" asset:"clusterawsadm-darwin-arm64" version:">=1.0.0"}} -o clusterawsadm
```
Make it executable
Expand Down
30 changes: 7 additions & 23 deletions hack/tools/mdbook/releaselink/releaselink.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,18 @@ limitations under the License.
package main

import (
"context"
"fmt"
"io"
"log"
"net/http"
"net/url"
"os"
"path"
"reflect"
"sort"
"strings"

"github.com/blang/semver"
"golang.org/x/tools/go/vcs"
"sigs.k8s.io/kubebuilder/docs/book/utils/plugin"

"sigs.k8s.io/cluster-api/internal/goproxy"
)

// ReleaseLink responds to {{#releaselink <args>}} input. It asks for a `gomodule` parameter
Expand All @@ -58,36 +56,22 @@ func (l ReleaseLink) Process(input *plugin.Input) error {
versionRange := semver.MustParseRange(tags.Get("version"))
includePrereleases := tags.Get("prereleases") == "true"

repo, err := vcs.RepoRootForImportPath(gomodule, false)
scheme, host, err := goproxy.GetSchemeAndHost(os.Getenv("GOPROXY"))
if err != nil {
return "", err
}
goproxyClient := goproxy.NewGoproxyClient(scheme, host)

rawURL := url.URL{
Scheme: "https",
Host: "proxy.golang.org",
Path: path.Join(gomodule, "@v", "/list"),
}

resp, err := http.Get(rawURL.String()) //nolint:noctx // NB: as we're just implementing an external interface we won't be able to get a context here.
repo, err := vcs.RepoRootForImportPath(gomodule, false)
if err != nil {
return "", err
}
defer resp.Body.Close()

out, err := io.ReadAll(resp.Body)
parsedTags, err := goproxyClient.GetVersions(context.Background(), gomodule)
if err != nil {
return "", err
}

parsedTags := semver.Versions{}
for _, line := range strings.Split(string(out), "\n") {
if strings.HasPrefix(line, "v") {
parsedTags = append(parsedTags, semver.MustParse(strings.TrimPrefix(line, "v")))
}
}
sort.Sort(parsedTags)

var picked semver.Version
for i, tag := range parsedTags {
if !includePrereleases && len(tag.Pre) > 0 {
Expand Down
18 changes: 18 additions & 0 deletions internal/goproxy/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
Copyright 2022 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Package goproxy implements a goproxy client.
package goproxy
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

package repository
package goproxy

import (
"context"
Expand All @@ -26,6 +26,7 @@ import (
"path/filepath"
"sort"
"strings"
"time"

"github.com/blang/semver"
"github.com/pkg/errors"
Expand All @@ -36,21 +37,27 @@ const (
defaultGoProxyHost = "proxy.golang.org"
)

type goproxyClient struct {
var (
retryableOperationInterval = 10 * time.Second
retryableOperationTimeout = 1 * time.Minute
)

// Client is a client to query versions from a goproxy instance.
type Client struct {
scheme string
host string
}

func newGoproxyClient(scheme, host string) *goproxyClient {
return &goproxyClient{
// NewGoproxyClient returns a new goproxyClient instance.
func NewGoproxyClient(scheme, host string) *Client {
return &Client{
scheme: scheme,
host: host,
}
}

func (g *goproxyClient) getVersions(ctx context.Context, base, owner, repository string) ([]string, error) {
// A goproxy is also able to handle the github repository path instead of the actual go module name.
gomodulePath := path.Join(base, owner, repository)
// GetVersions returns the a sorted list of semantical versions which exist for a go module.
func (g *Client) GetVersions(ctx context.Context, gomodulePath string) (semver.Versions, error) {
parsedVersions := semver.Versions{}

majorVersionNumber := 1
Expand Down Expand Up @@ -132,17 +139,12 @@ func (g *goproxyClient) getVersions(ctx context.Context, base, owner, repository

sort.Sort(parsedVersions)

versions := []string{}
for _, v := range parsedVersions {
versions = append(versions, "v"+v.String())
}

return versions, nil
return parsedVersions, nil
}

// getGoproxyHost detects and returns the scheme and host for goproxy requests.
// GetSchemeAndHost detects and returns the scheme and host for goproxy requests.
// It returns empty strings if goproxy is disabled via `off` or `direct` values.
func getGoproxyHost(goproxy string) (string, string, error) {
func GetSchemeAndHost(goproxy string) (string, string, error) {
// Fallback to default
if goproxy == "" {
return "https", defaultGoProxyHost, nil
Expand Down
Loading

0 comments on commit 2e5e0c9

Please sign in to comment.