Skip to content

[image-builder] replace reference to aliases with actual repo for cross mount blobs #10192

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
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
20 changes: 13 additions & 7 deletions components/image-builder-bob/pkg/proxy/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ type Repo struct {
Auth func() docker.Authorizer
}

func rewriteDockerAPIURL(u *url.URL, fromRepo, toRepo, host, tag string) {
func (proxy *Proxy) rewriteDockerAPIURL(u *url.URL, fromRepo, toRepo, host, tag string) {
var (
from = "/v2/" + strings.Trim(fromRepo, "/") + "/"
to = "/v2/" + strings.Trim(toRepo, "/") + "/"
Expand All @@ -75,13 +75,19 @@ func rewriteDockerAPIURL(u *url.URL, fromRepo, toRepo, host, tag string) {
u.Path = strings.Join(segs, "/")
}
}

if u.RawQuery != "" {
// As per OCI distribution spec this (from=) should be the only possible reference to a cross repo
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#push
for k, v := range proxy.Aliases {
u.RawQuery = strings.Replace(u.RawQuery, "from="+k, "from="+v.Repo, 1)
}
}
u.Host = host
}

// rewriteNonDockerAPIURL is used when a url has to be rewritten but the url
// contains a non docker api path
func rewriteNonDockerAPIURL(u *url.URL, fromPrefix, toPrefix, host string) {
func (proxy *Proxy) rewriteNonDockerAPIURL(u *url.URL, fromPrefix, toPrefix, host string) {
var (
from = "/" + strings.Trim(fromPrefix, "/") + "/"
to = "/" + strings.Trim(toPrefix, "/") + "/"
Expand Down Expand Up @@ -113,7 +119,7 @@ func (proxy *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if strings.HasPrefix(r.URL.Path, "/v2/"+k+"/") {
repo = &v
alias = k
rewriteDockerAPIURL(r.URL, alias, repo.Repo, repo.Host, repo.Tag)
proxy.rewriteDockerAPIURL(r.URL, alias, repo.Repo, repo.Host, repo.Tag)
break
}
// Non-Docker api request
Expand All @@ -122,7 +128,7 @@ func (proxy *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// repo as empty
repo = &v
alias = k
rewriteNonDockerAPIURL(r.URL, alias, "", repo.Host)
proxy.rewriteNonDockerAPIURL(r.URL, alias, "", repo.Host)
break
}
}
Expand Down Expand Up @@ -225,13 +231,13 @@ func (proxy *Proxy) reverse(alias string) *httputil.ReverseProxy {
}

if strings.HasPrefix(loc, "/v2/") {
rewriteDockerAPIURL(lurl, repo.Repo, alias, proxy.Host.Host, "")
proxy.rewriteDockerAPIURL(lurl, repo.Repo, alias, proxy.Host.Host, "")
} else {
// since this is a non docker api location we
// do not need to process the path.
// All docker api URLs always start with /v2/. See spec
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#endpoints
rewriteNonDockerAPIURL(lurl, "", alias, repo.Host)
proxy.rewriteNonDockerAPIURL(lurl, "", alias, repo.Host)
}

lurl.Host = proxy.Host.Host
Expand Down
47 changes: 45 additions & 2 deletions components/image-builder-bob/pkg/proxy/proxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

func TestRewriteNonDockerAPIURL(t *testing.T) {
type input struct {
proxy *Proxy
u url.URL
fromPrefix string
toPrefix string
Expand All @@ -24,6 +25,7 @@ func TestRewriteNonDockerAPIURL(t *testing.T) {
{
Name: "toPrefix is empty",
in: input{
proxy: &Proxy{},
fromPrefix: "base",
toPrefix: "",
host: "europe-docker.pkg.dev",
Expand All @@ -40,6 +42,7 @@ func TestRewriteNonDockerAPIURL(t *testing.T) {
{
Name: "fromPrefix is empty",
in: input{
proxy: &Proxy{},
fromPrefix: "",
toPrefix: "base",
host: "localhost.com",
Expand All @@ -56,6 +59,7 @@ func TestRewriteNonDockerAPIURL(t *testing.T) {
{
Name: "fromPrefix and toPrefix are not empty",
in: input{
proxy: &Proxy{},
fromPrefix: "from",
toPrefix: "to",
host: "localhost.com",
Expand All @@ -72,6 +76,7 @@ func TestRewriteNonDockerAPIURL(t *testing.T) {
{
Name: "fromPrefix and toPrefix are empty",
in: input{
proxy: &Proxy{},
fromPrefix: "",
toPrefix: "",
host: "localhost.com",
Expand All @@ -89,7 +94,7 @@ func TestRewriteNonDockerAPIURL(t *testing.T) {

for _, test := range tests {
t.Run(test.Name, func(t *testing.T) {
rewriteNonDockerAPIURL(&test.in.u, test.in.fromPrefix, test.in.toPrefix, test.in.host)
test.in.proxy.rewriteNonDockerAPIURL(&test.in.u, test.in.fromPrefix, test.in.toPrefix, test.in.host)
if test.in.u.Path != test.u.Path {
t.Errorf("expected path: %s but got %s", test.u.Path, test.in.u.Path)
}
Expand All @@ -106,6 +111,7 @@ func TestRewriteNonDockerAPIURL(t *testing.T) {

func TestRewriteDockerAPIURL(t *testing.T) {
type input struct {
proxy *Proxy
u url.URL
fromRepo string
toRepo string
Expand All @@ -120,6 +126,7 @@ func TestRewriteDockerAPIURL(t *testing.T) {
{
Name: "remote to localhost",
in: input{
proxy: &Proxy{},
fromRepo: "base-images",
toRepo: "base",
host: "localhost.com",
Expand All @@ -137,6 +144,7 @@ func TestRewriteDockerAPIURL(t *testing.T) {
{
Name: "localhost to remote",
in: input{
proxy: &Proxy{},
fromRepo: "base",
toRepo: "base-images",
host: "prince.azurecr.io",
Expand All @@ -154,6 +162,7 @@ func TestRewriteDockerAPIURL(t *testing.T) {
{
Name: "manifest reference update with tag",
in: input{
proxy: &Proxy{},
fromRepo: "base",
toRepo: "base-images",
host: "prince.azurecr.io",
Expand All @@ -168,14 +177,48 @@ func TestRewriteDockerAPIURL(t *testing.T) {
Path: "/v2/base-images/uploads/manifests/tag12345",
},
},
{
Name: "updates alias reference when it is cross blob mount",
in: input{
proxy: &Proxy{
Host: url.URL{Host: "localhost:8080", Scheme: "http"},
Aliases: map[string]Repo{
"base": {
Repo: "/gitpod/base-images",
},
"target": {
Repo: "/gitpod/workspace-images",
},
},
proxies: nil,
},
fromRepo: "base",
toRepo: "/gitpod/base-images",
host: "registry.gitlab.com",
tag: "tag12345",
u: url.URL{
Host: "localhost.com",
RawQuery: "mount=sha:12345&from=base",
Path: "/v2/base/mounts/uploads/",
},
},
u: url.URL{
Host: "registry.gitlab.com",
Path: "/v2/gitpod/base-images/mounts/uploads/",
RawQuery: "mount=sha:12345&from=/gitpod/base-images",
},
},
}

for _, test := range tests {
t.Run(test.Name, func(t *testing.T) {
rewriteDockerAPIURL(&test.in.u, test.in.fromRepo, test.in.toRepo, test.in.host, test.in.tag)
test.in.proxy.rewriteDockerAPIURL(&test.in.u, test.in.fromRepo, test.in.toRepo, test.in.host, test.in.tag)
if test.in.u.Path != test.u.Path {
t.Errorf("expected path: %s but got %s", test.u.Path, test.in.u.Path)
}
if test.in.u.RawQuery != test.u.RawQuery {
t.Errorf("expected raw query: %s but got %s", test.u.RawQuery, test.in.u.RawQuery)
}
if test.in.u.Host != test.u.Host {
t.Errorf("expected Host: %s but got %s", test.u.Host, test.in.u.Host)
}
Expand Down