Skip to content
This repository was archived by the owner on Dec 19, 2024. It is now read-only.

retry transient upstream failures using retryablehttp #12

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

This is a simple OCI redirector service that allows for custom domains, including forwarding auth token requests to the original registry.

For example, this is used to serve `distroless.dev/*` as a redirection to `ghcr.io/distroless/*`.
For example, this is used to serve `cgr.dev/chainguard/*` as a redirection to `ghcr.io/chainguard-images/*`.

It's intended to be deployed to Google Cloud Run, which is responsible for handling HTTPS.

Expand Down
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
module github.com/chainguard-dev/registry-redirect

go 1.17
go 1.19

require (
github.com/google/go-containerregistry v0.11.0
github.com/gorilla/mux v1.8.0
github.com/hashicorp/go-retryablehttp v0.7.2
knative.dev/pkg v0.0.0-20220912140433-cc6e435120a7
)

Expand All @@ -18,6 +19,7 @@ require (
github.com/go-logr/logr v1.2.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.15.8 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
Expand Down
9 changes: 9 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQL
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
Expand Down Expand Up @@ -353,12 +354,18 @@ github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyN
github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-hclog v0.12.0 h1:d4QkX8FRTYaKaCZBoXYY8zJX2BXjWxurN/GA2tkrmZM=
github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-retryablehttp v0.7.2 h1:AcYqCvkpalPnPF2pn0KamgwamS42TqUDDYFRKq/RAd0=
github.com/hashicorp/go-retryablehttp v0.7.2/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
Expand Down Expand Up @@ -457,13 +464,15 @@ github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs=
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
Expand Down
12 changes: 6 additions & 6 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,23 @@ import (
// - take a config for registries/repos to redirect from/to.

var (
// Redirect requests for distroless.dev/static -> ghcr.io/distroless/static
// Redirect requests for $DOMAIN/[prefix/]static -> ghcr.io/chainguard-images/static
// If repo is empty, example.dev/foo/bar -> ghcr.io/foo/bar
repo = flag.String("repo", "distroless", "repo to redirect to")
repo = flag.String("repo", "chainguard-images", "repo to redirect to")

// TODO(jason): Support arbitrary registries.
gcr = flag.Bool("gcr", false, "if true, use GCR mode")

// prefix is the user-visible repo prefix.
// For example, if repo is "distroless" and prefix is "unicorns",
// For example, if repo is "chainguard-images" and prefix is "unicorns",
// users hitting example.dev/unicorns/foo/bar will be redirected to
// ghcr.io/distroless/foo/bar.
// ghcr.io/chainguard-images/foo/bar.
// If prefix is unset, hitting example.dev/unicorns/foo/bar will
// redirect to ghcr.io/unicorns/foo/bar.
// If prefix is set, and users hit a path without the prefix, it's ignored:
// - example.dev/foo/bar -> ghcr.io/distroless/foo/bar
// - example.dev/foo/bar -> ghcr.io/chainguard-images/foo/bar
// (this is for backward compatibility with prefix-less redirects)
prefix = flag.String("prefix", "", "if set, user-visible repo prefix")
prefix = flag.String("prefix", "chainguard", "if set, user-visible repo prefix")
)

func main() {
Expand Down
25 changes: 14 additions & 11 deletions pkg/redirect/redirect.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"strings"

"github.com/gorilla/mux"
"github.com/hashicorp/go-retryablehttp"
"knative.dev/pkg/logging"
)

Expand All @@ -32,13 +33,14 @@ func redact(in http.Header) http.Header {

func New(host, repo, prefix string) http.Handler {
rdr := redirect{
host: host,
repo: repo,
prefix: prefix,
host: host,
repo: repo,
prefix: prefix,
retryhttp: retryablehttp.NewClient(),
}
router := mux.NewRouter()

router.Handle("/", http.RedirectHandler("https://github.com/distroless", http.StatusTemporaryRedirect))
router.Handle("/", http.RedirectHandler("https://github.com/chainguard-images", http.StatusTemporaryRedirect))

router.HandleFunc("/v2", rdr.v2)
router.HandleFunc("/v2/", rdr.v2)
Expand All @@ -62,9 +64,10 @@ func New(host, repo, prefix string) http.Handler {
}

type redirect struct {
host string
repo string
prefix string
host string
repo string
prefix string
retryhttp *retryablehttp.Client
}

func (rdr redirect) v2(resp http.ResponseWriter, req *http.Request) {
Expand All @@ -85,7 +88,7 @@ func (rdr redirect) v2(resp http.ResponseWriter, req *http.Request) {
"header", redact(req.Header))
resp.Header().Set("X-Redirected", req.URL.String())

back, err := http.DefaultClient.Do(out)
back, err := rdr.retryhttp.Do(&retryablehttp.Request{Request: out})
if err != nil {
logger.Errorf("Error sending request: %v", err)
http.Error(resp, err.Error(), http.StatusInternalServerError)
Expand Down Expand Up @@ -152,7 +155,7 @@ func (rdr redirect) token(w http.ResponseWriter, r *http.Request) {
"header", redact(req.Header))
w.Header().Set("X-Redirected", req.URL.String())

resp, err := http.DefaultClient.Do(req)
resp, err := rdr.retryhttp.Do(&retryablehttp.Request{Request: req})
if err != nil {
logger.Errorf("Error sending request: %v", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
Expand Down Expand Up @@ -237,7 +240,7 @@ func (rdr redirect) proxy(w http.ResponseWriter, r *http.Request) {
"header", redact(req.Header))
w.Header().Set("X-Redirected", req.URL.String())

resp, err := http.DefaultTransport.RoundTrip(req) // Transport doesn't follow redirects.
resp, err := rdr.retryhttp.StandardClient().Transport.RoundTrip(req) // Transport doesn't follow redirects.
if err != nil {
logger.Errorf("Error sending request: %v", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
Expand Down Expand Up @@ -334,7 +337,7 @@ func (rdr redirect) getToken(r *http.Request) (string, *http.Response, error) {
}
req, _ := http.NewRequest(http.MethodGet, url, nil)
req.Header = r.Header.Clone()
resp, err := http.DefaultClient.Do(req) //nolint:gosec
resp, err := rdr.retryhttp.Do(&retryablehttp.Request{Request: req}) //nolint:gosec
if err != nil {
return "", nil, err
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/redirect/redirect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ import (

func TestRedirect(t *testing.T) {
for _, c := range []struct{ host, repo, prefix string }{
{"ghcr.io", "distroless", ""},
{"ghcr.io", "chainguard-images", ""},
{"gcr.io", "jason-chainguard-public", ""},
{"ghcr.io", "distroless", "unicorns"},
{"ghcr.io", "chainguard-images", "unicorns"},
{"gcr.io", "jason-chainguard-public", "unicorns"},
} {
t.Run(fmt.Sprintf("%s/%s (prefix %s)", c.host, c.repo, c.prefix), func(t *testing.T) {
Expand Down