diff --git a/pkg/cri/config/config.go b/pkg/cri/config/config.go index 2200e2778fae..48430cfa3e76 100644 --- a/pkg/cri/config/config.go +++ b/pkg/cri/config/config.go @@ -199,6 +199,23 @@ type Mirror struct { // with host specified. // The scheme, host and path from the endpoint URL will be used. Endpoints []string `toml:"endpoint" json:"endpoint"` + + // Rewrites are repository rewrite rules for a namespace. When fetching image resources + // from an endpoint and a key matches the repository via regular expression matching + // it will be replaced with the corresponding value from the map in the resource request. + // + // This example configures CRI to pull docker.io/library/* images from docker.io/my-org/*: + // + // [plugins] + // [plugins."io.containerd.grpc.v1.cri"] + // [plugins."io.containerd.grpc.v1.cri".registry] + // [plugins."io.containerd.grpc.v1.cri".registry.mirrors] + // [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"] + // endpoint = ["https://registry-1.docker.io/v2"] + // [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io".rewrite] + // "^library/(.*)" = "my-org/$1" + // + Rewrites map[string]string `toml:"rewrite" json:"rewrite"` } // AuthConfig contains the config related to authentication to a specific registry diff --git a/pkg/cri/server/image_pull.go b/pkg/cri/server/image_pull.go index 679f03abb65d..674ab298942b 100644 --- a/pkg/cri/server/image_pull.go +++ b/pkg/cri/server/image_pull.go @@ -448,6 +448,10 @@ func (c *criService) registryHosts(ctx context.Context, auth *runtime.AuthConfig if err != nil { return nil, fmt.Errorf("get registry endpoints: %w", err) } + rewrites, err := c.registryRewrites(host) + if err != nil { + return nil, fmt.Errorf("get registry rewrites: %w", err) + } for _, e := range endpoints { u, err := url.Parse(e) if err != nil { @@ -502,6 +506,7 @@ func (c *criService) registryHosts(ctx context.Context, auth *runtime.AuthConfig Scheme: u.Scheme, Path: u.Path, Capabilities: docker.HostCapabilityResolve | docker.HostCapabilityPull, + Rewrites: rewrites, }) } return registries, nil @@ -564,6 +569,20 @@ func (c *criService) registryEndpoints(host string) ([]string, error) { return append(endpoints, defaultScheme(defaultHost)+"://"+defaultHost), nil } +func (c *criService) registryRewrites(host string) (map[string]string, error) { + var rewrites map[string]string + _, ok := c.config.Registry.Mirrors[host] + if ok { + rewrites = c.config.Registry.Mirrors[host].Rewrites + } else { + rewrites = c.config.Registry.Mirrors["*"].Rewrites + } + if rewrites == nil { + rewrites = map[string]string{} + } + return rewrites, nil +} + // newTransport returns a new HTTP transport used to pull image. // TODO(random-liu): Create a library and share this code with `ctr`. func newTransport() *http.Transport { diff --git a/remotes/docker/registry.go b/remotes/docker/registry.go index 98cafcd069e6..5c01a4b27974 100644 --- a/remotes/docker/registry.go +++ b/remotes/docker/registry.go @@ -74,6 +74,7 @@ type RegistryHost struct { Path string Capabilities HostCapabilities Header http.Header + Rewrites map[string]string } func (h RegistryHost) isProxy(refhost string) bool { diff --git a/remotes/docker/resolver.go b/remotes/docker/resolver.go index 89939d78c9d1..4f91349bfbbc 100644 --- a/remotes/docker/resolver.go +++ b/remotes/docker/resolver.go @@ -26,6 +26,7 @@ import ( "net/http" "net/url" "path" + "regexp" "strings" "github.com/containerd/containerd/images" @@ -480,11 +481,22 @@ func (r *dockerBase) request(host RegistryHost, method string, ps ...string) *re if header == nil { header = http.Header{} } - for key, value := range host.Header { header[key] = append(header[key], value...) } - parts := append([]string{"/", host.Path, r.repository}, ps...) + repository := r.repository + for pattern, replace := range host.Rewrites { + exp, err := regexp.Compile(pattern) + if err != nil { + log.L.WithError(err).Warnf("Failed to compile rewrite, `%s`, for %s", pattern, host.Host) + continue + } + if rr := exp.ReplaceAllString(repository, replace); rr != repository { + repository = rr + break + } + } + parts := append([]string{"/", host.Path, repository}, ps...) p := path.Join(parts...) // Join strips trailing slash, re-add ending "/" if included if len(parts) > 0 && strings.HasSuffix(parts[len(parts)-1], "/") {