diff --git a/go.mod b/go.mod index 553cab85..71b568ed 100644 --- a/go.mod +++ b/go.mod @@ -34,6 +34,7 @@ require ( github.com/gliderlabs/ssh v0.3.3 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/gorilla/mux v1.8.0 // indirect + github.com/hashicorp/go-retryablehttp v0.7.0 github.com/kevinburke/ssh_config v1.1.0 github.com/libdns/gandi v1.0.2 github.com/libdns/libdns v0.2.1 diff --git a/go.sum b/go.sum index f9e19ad7..03654e7f 100644 --- a/go.sum +++ b/go.sum @@ -444,8 +444,14 @@ github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= +github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-retryablehttp v0.7.0 h1:eu1EI/mbirUgP5C8hVsTNaGZreBDlYiwC1FZWkvQPQ4= +github.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= diff --git a/pkg/catalogue/catalogue.go b/pkg/catalogue/catalogue.go index 68ff5b98..b3a85700 100644 --- a/pkg/catalogue/catalogue.go +++ b/pkg/catalogue/catalogue.go @@ -7,7 +7,6 @@ import ( "encoding/json" "fmt" "io/ioutil" - "net/http" "os" "path" "strings" @@ -122,7 +121,7 @@ func (r ByRecipeName) Less(i, j int) bool { // recipeCatalogueFSIsLatest checks whether the recipe catalogue stored locally // is up to date. func recipeCatalogueFSIsLatest() (bool, error) { - httpClient := &http.Client{Timeout: web.Timeout} + httpClient := web.NewHTTPRetryClient() res, err := httpClient.Head(RecipeCatalogueURL) if err != nil { return false, err diff --git a/pkg/client/registry.go b/pkg/client/registry.go index f374266e..eac7ab11 100644 --- a/pkg/client/registry.go +++ b/pkg/client/registry.go @@ -9,6 +9,7 @@ import ( "coopcloud.tech/abra/pkg/web" "github.com/docker/distribution/reference" + "github.com/hashicorp/go-retryablehttp" ) type RawTag struct { @@ -35,12 +36,12 @@ func GetRegistryTags(image string) (RawTags, error) { func getRegv2Token(image reference.Named) (string, error) { img := reference.Path(image) authTokenURL := fmt.Sprintf("https://auth.docker.io/token?service=registry.docker.io&scope=repository:%s:pull", img) - req, err := http.NewRequest("GET", authTokenURL, nil) + req, err := retryablehttp.NewRequest("GET", authTokenURL, nil) if err != nil { return "", err } - client := &http.Client{Timeout: web.Timeout} + client := web.NewHTTPRetryClient() res, err := client.Do(req) if err != nil { return "", err @@ -78,7 +79,7 @@ func GetTagDigest(image reference.Named) (string, error) { tag := image.(reference.NamedTagged).Tag() manifestURL := fmt.Sprintf("https://index.docker.io/v2/%s/manifests/%s", img, tag) - req, err := http.NewRequest("GET", manifestURL, nil) + req, err := retryablehttp.NewRequest("GET", manifestURL, nil) if err != nil { return "", err } @@ -96,7 +97,7 @@ func GetTagDigest(image reference.Named) (string, error) { "Authorization": []string{fmt.Sprintf("Bearer %s", token)}, } - client := &http.Client{Timeout: web.Timeout} + client := web.NewHTTPRetryClient() res, err := client.Do(req) if err != nil { return "", err diff --git a/pkg/web/client.go b/pkg/web/client.go new file mode 100644 index 00000000..db889fc1 --- /dev/null +++ b/pkg/web/client.go @@ -0,0 +1,23 @@ +package web + +import ( + "fmt" + + "github.com/hashicorp/go-retryablehttp" + "github.com/sirupsen/logrus" +) + +type customLeveledLogger struct { + retryablehttp.Logger +} + +func (l customLeveledLogger) Printf(msg string, args ...interface{}) { + logrus.Debugf(fmt.Sprintf(msg, args...)) +} + +// NewHTTPRetryClient instantiates a new http client with retries baked in +func NewHTTPRetryClient() *retryablehttp.Client { + retryClient := retryablehttp.NewClient() + retryClient.Logger = customLeveledLogger{} + return retryClient +} diff --git a/pkg/web/web.go b/pkg/web/web.go index 3102118c..0fe567a7 100644 --- a/pkg/web/web.go +++ b/pkg/web/web.go @@ -3,7 +3,6 @@ package web import ( "encoding/json" - "net/http" "time" ) @@ -13,7 +12,7 @@ const Timeout = 10 * time.Second // ReadJSON reads JSON and parses it into your chosen interface pointer func ReadJSON(url string, target interface{}) error { - httpClient := &http.Client{Timeout: Timeout} + httpClient := NewHTTPRetryClient() res, err := httpClient.Get(url) if err != nil { return err