From 81ca274c6facf46d52b9ebf26ab21db0adb371b7 Mon Sep 17 00:00:00 2001 From: Lantao Liu Date: Tue, 13 Aug 2019 01:17:48 -0700 Subject: [PATCH] Add wildcard mirror support. Signed-off-by: Lantao Liu --- docs/registry.md | 13 +++- pkg/server/image_pull.go | 20 ++++--- pkg/server/image_pull_test.go | 109 +++++++++++++++++++++++++++------- 3 files changed, 114 insertions(+), 28 deletions(-) diff --git a/docs/registry.md b/docs/registry.md index 9146bbcc1..9a80248a1 100644 --- a/docs/registry.md +++ b/docs/registry.md @@ -13,12 +13,23 @@ To configure image registries create/modify the `/etc/containerd/config.toml` as endpoint = ["https://HostIP1:Port1"] [plugins.cri.registry.mirrors."test.insecure-registry.io"] endpoint = ["http://HostIP2:Port2"] + # wildcard matching is supported but not required. + [plugins.cri.registry.mirrors."*"] + endpoint = ["http://HostIP3:Port3"] ``` The default configuration can be generated by `containerd config default > /etc/containerd/config.toml`. The endpoint is a list that can contain multiple image registry URLs split by commas. When pulling an image -from a registry, containerd will try these endpoint URLs one by one, and use the first working one. +from a registry, containerd will try these endpoint URLs one by one, and use the first working one. Please note +that if the default registry endpoint is not already specified in the endpoint list, it will be automatically +tried at the end with scheme `https` and path `v2`, e.g. `https://gcr.io/v2` for `gcr.io`. + +As an example, for the image `gcr.io/library/busybox:latest`, the endpoints are: +* `gcr.io` is configured: endpoints for `gcr.io` + default endpoint `https://gcr.io/v2`. +* `*` is configured, and `gcr.io` is not: endpoints for `*` + default + endpoint `https://gcr.io/v2`. +* None of above is configured: default endpoint `https:/gcr.io/v2`. After modify this config, you need restart the `containerd` service. diff --git a/pkg/server/image_pull.go b/pkg/server/image_pull.go index 2033927dd..daa388481 100644 --- a/pkg/server/image_pull.go +++ b/pkg/server/image_pull.go @@ -293,11 +293,9 @@ func (c *criService) registryHosts(auth *runtime.AuthConfig) docker.RegistryHost return func(host string) ([]docker.RegistryHost, error) { var registries []docker.RegistryHost - // Try mirrors in order, and then try the default registry if not tried. - endpoints, err := addDefaultEndpoint( - c.config.Registry.Mirrors[host].Endpoints, host) + endpoints, err := c.registryEndpoints(host) if err != nil { - return nil, errors.Wrapf(err, "add default endpoint") + return nil, errors.Wrap(err, "get registry endpoints") } for _, e := range endpoints { u, err := url.Parse(e) @@ -347,9 +345,17 @@ func (c *criService) registryHosts(auth *runtime.AuthConfig) docker.RegistryHost } } -// addDefaultEndpoint add default registry endpoint if it does not -// exist in the passed-in endpoint list. -func addDefaultEndpoint(endpoints []string, host string) ([]string, error) { +// registryEndpoints returns endpoints for a given host. +// It adds default registry endpoint if it does not exist in the passed-in endpoint list. +// It also supports wildcard host matching with `*`. +func (c *criService) registryEndpoints(host string) ([]string, error) { + var endpoints []string + _, ok := c.config.Registry.Mirrors[host] + if ok { + endpoints = c.config.Registry.Mirrors[host].Endpoints + } else { + endpoints = c.config.Registry.Mirrors["*"].Endpoints + } defaultHost, err := docker.DefaultHost(host) if err != nil { return nil, errors.Wrap(err, "get default host") diff --git a/pkg/server/image_pull_test.go b/pkg/server/image_pull_test.go index 5ad1bbf39..91908c731 100644 --- a/pkg/server/image_pull_test.go +++ b/pkg/server/image_pull_test.go @@ -22,6 +22,8 @@ import ( "github.com/stretchr/testify/assert" runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2" + + criconfig "github.com/containerd/cri/pkg/config" ) func TestParseAuth(t *testing.T) { @@ -103,16 +105,34 @@ func TestParseAuth(t *testing.T) { } } -func TestAddDefaultEndpoint(t *testing.T) { +func TestRegistryEndpoints(t *testing.T) { for desc, test := range map[string]struct { - endpoints []string - host string - expected []string + mirrors map[string]criconfig.Mirror + host string + expected []string }{ - "default endpoint not in list": { - endpoints: []string{ - "https://registry-1.io", - "https://registry-2.io", + "no mirror configured": { + mirrors: map[string]criconfig.Mirror{ + "registry-1.io": { + Endpoints: []string{ + "https://registry-1.io", + "https://registry-2.io", + }, + }, + }, + host: "registry-3.io", + expected: []string{ + "https://registry-3.io", + }, + }, + "mirror configured": { + mirrors: map[string]criconfig.Mirror{ + "registry-3.io": { + Endpoints: []string{ + "https://registry-1.io", + "https://registry-2.io", + }, + }, }, host: "registry-3.io", expected: []string{ @@ -121,11 +141,50 @@ func TestAddDefaultEndpoint(t *testing.T) { "https://registry-3.io", }, }, - "default endpoint in list with http": { - endpoints: []string{ + "wildcard mirror configured": { + mirrors: map[string]criconfig.Mirror{ + "*": { + Endpoints: []string{ + "https://registry-1.io", + "https://registry-2.io", + }, + }, + }, + host: "registry-3.io", + expected: []string{ "https://registry-1.io", "https://registry-2.io", - "http://registry-3.io", + "https://registry-3.io", + }, + }, + "host should take precedence if both host and wildcard mirrors are configured": { + mirrors: map[string]criconfig.Mirror{ + "*": { + Endpoints: []string{ + "https://registry-1.io", + }, + }, + "registry-3.io": { + Endpoints: []string{ + "https://registry-2.io", + }, + }, + }, + host: "registry-3.io", + expected: []string{ + "https://registry-2.io", + "https://registry-3.io", + }, + }, + "default endpoint in list with http": { + mirrors: map[string]criconfig.Mirror{ + "registry-3.io": { + Endpoints: []string{ + "https://registry-1.io", + "https://registry-2.io", + "http://registry-3.io", + }, + }, }, host: "registry-3.io", expected: []string{ @@ -135,10 +194,14 @@ func TestAddDefaultEndpoint(t *testing.T) { }, }, "default endpoint in list with https": { - endpoints: []string{ - "https://registry-1.io", - "https://registry-2.io", - "https://registry-3.io", + mirrors: map[string]criconfig.Mirror{ + "registry-3.io": { + Endpoints: []string{ + "https://registry-1.io", + "https://registry-2.io", + "https://registry-3.io", + }, + }, }, host: "registry-3.io", expected: []string{ @@ -148,10 +211,14 @@ func TestAddDefaultEndpoint(t *testing.T) { }, }, "default endpoint in list with path": { - endpoints: []string{ - "https://registry-1.io", - "https://registry-2.io", - "https://registry-3.io/path", + mirrors: map[string]criconfig.Mirror{ + "registry-3.io": { + Endpoints: []string{ + "https://registry-1.io", + "https://registry-2.io", + "https://registry-3.io/path", + }, + }, }, host: "registry-3.io", expected: []string{ @@ -162,7 +229,9 @@ func TestAddDefaultEndpoint(t *testing.T) { }, } { t.Logf("TestCase %q", desc) - got, err := addDefaultEndpoint(test.endpoints, test.host) + c := newTestCRIService() + c.config.Registry.Mirrors = test.mirrors + got, err := c.registryEndpoints(test.host) assert.NoError(t, err) assert.Equal(t, test.expected, got) }