Skip to content

Commit 8a7233c

Browse files
Add OCI distribution-spec registry proxying support
This commit implements support for the OCI distribution-spec registry proxying feature as defined in: https://github.com/opencontainers/distribution-spec/blob/main/spec.md#registry-proxying When a registry acts as a proxy for multiple upstream registries, it needs to know which upstream registry to route requests to. The distribution-spec defines the "ns" query parameter for this purpose. For example, a request to a proxy for docker.io images would include "?ns=docker.io". Signed-off-by: Sohan Kunkerkar <sohank2602@gmail.com>
1 parent b0f86df commit 8a7233c

4 files changed

Lines changed: 69 additions & 0 deletions

File tree

image/docker/docker_client.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@ type dockerClient struct {
120120
signatureBase lookasideStorageBase
121121
useSigstoreAttachments bool
122122
scope authScope
123+
// namespaceProxy enables OCI distribution-spec registry proxying.
124+
// When set, an "ns" query parameter is appended to all requests.
125+
namespaceProxy string
123126

124127
// The following members are detected registry properties:
125128
// They are set after a successful detectProperties(), and never change afterwards.
@@ -491,6 +494,11 @@ func (c *dockerClient) resolveRequestURL(path string) (*url.URL, error) {
491494
if err != nil {
492495
return nil, err
493496
}
497+
if c.namespaceProxy != "" {
498+
q := res.Query()
499+
q.Set("ns", c.namespaceProxy)
500+
res.RawQuery = q.Encode()
501+
}
494502
return res, nil
495503
}
496504

image/docker/docker_client_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,3 +445,58 @@ func TestIsManifestUnknownError(t *testing.T) {
445445
assert.True(t, res, "%s: %#v", c.name, err)
446446
}
447447
}
448+
449+
func TestResolveRequestURLWithNamespaceProxy(t *testing.T) {
450+
for _, c := range []struct {
451+
name string
452+
registry string
453+
scheme string
454+
path string
455+
namespaceProxy string
456+
expected string
457+
}{
458+
{
459+
name: "no namespace proxy",
460+
registry: "registry.example.com",
461+
scheme: "https",
462+
path: "/v2/library/nginx/manifests/latest",
463+
namespaceProxy: "",
464+
expected: "https://registry.example.com/v2/library/nginx/manifests/latest",
465+
},
466+
{
467+
name: "with namespace proxy for docker.io",
468+
registry: "proxy.example.com",
469+
scheme: "https",
470+
path: "/v2/library/nginx/manifests/latest",
471+
namespaceProxy: "docker.io",
472+
expected: "https://proxy.example.com/v2/library/nginx/manifests/latest?ns=docker.io",
473+
},
474+
{
475+
name: "with namespace proxy for quay.io",
476+
registry: "proxy.example.com",
477+
scheme: "https",
478+
path: "/v2/coreos/etcd/blobs/sha256:abc123",
479+
namespaceProxy: "quay.io",
480+
expected: "https://proxy.example.com/v2/coreos/etcd/blobs/sha256:abc123?ns=quay.io",
481+
},
482+
{
483+
name: "with namespace proxy and port",
484+
registry: "proxy.example.com:5000",
485+
scheme: "http",
486+
path: "/v2/myimage/manifests/v1.0",
487+
namespaceProxy: "gcr.io",
488+
expected: "http://proxy.example.com:5000/v2/myimage/manifests/v1.0?ns=gcr.io",
489+
},
490+
} {
491+
t.Run(c.name, func(t *testing.T) {
492+
client := &dockerClient{
493+
registry: c.registry,
494+
scheme: c.scheme,
495+
namespaceProxy: c.namespaceProxy,
496+
}
497+
result, err := client.resolveRequestURL(c.path)
498+
require.NoError(t, err)
499+
assert.Equal(t, c.expected, result.String())
500+
})
501+
}
502+
}

image/docker/docker_image_src.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ func newImageSourceAttempt(ctx context.Context, sys *types.SystemContext, logica
150150
return nil, err
151151
}
152152
client.tlsClientConfig.InsecureSkipVerify = pullSource.Endpoint.Insecure
153+
client.namespaceProxy = pullSource.Endpoint.NamespaceProxy
153154

154155
s := &dockerImageSource{
155156
PropertyMethodsInitialize: impl.PropertyMethods(impl.Properties{

image/pkg/sysregistriesv2/system_registries_v2.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ type Endpoint struct {
7171
// This can only be set in a registry's Mirror field, not in the registry's primary Endpoint.
7272
// This per-mirror setting is allowed only when mirror-by-digest-only is not configured for the primary registry.
7373
PullFromMirror string `toml:"pull-from-mirror,omitempty"`
74+
// NamespaceProxy enables OCI distribution-spec registry proxying.
75+
// When set, an "ns" query parameter with this value is appended to all requests,
76+
// allowing a single proxy to route to multiple upstream registries.
77+
// See https://github.com/opencontainers/distribution-spec/blob/main/spec.md#registry-proxying
78+
NamespaceProxy string `toml:"namespace-proxy,omitempty"`
7479
}
7580

7681
// userRegistriesFile is the path to the per user registry configuration file.

0 commit comments

Comments
 (0)