diff --git a/pkg/cri/config/config.go b/pkg/cri/config/config.go index 2eededa232fb..5f049be3914d 100644 --- a/pkg/cri/config/config.go +++ b/pkg/cri/config/config.go @@ -119,6 +119,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 2165b3438a8c..4e15942a8495 100644 --- a/pkg/cri/server/image_pull.go +++ b/pkg/cri/server/image_pull.go @@ -320,6 +320,10 @@ func (c *criService) registryHosts(auth *runtime.AuthConfig) docker.RegistryHost if err != nil { return nil, errors.Wrap(err, "get registry endpoints") } + rewrites, err := c.registryRewrites(host) + if err != nil { + return nil, errors.Wrap(err, "get registry rewrites") + } for _, e := range endpoints { u, err := url.Parse(e) if err != nil { @@ -358,6 +362,7 @@ func (c *criService) registryHosts(auth *runtime.AuthConfig) docker.RegistryHost Scheme: u.Scheme, Path: u.Path, Capabilities: docker.HostCapabilityResolve | docker.HostCapabilityPull, + Rewrites: rewrites, }) } return registries, nil @@ -423,6 +428,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 7c231d928465..d41dcf98565c 100644 --- a/remotes/docker/registry.go +++ b/remotes/docker/registry.go @@ -71,6 +71,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 3481c5132c87..91d5aaa9fde7 100644 --- a/remotes/docker/resolver.go +++ b/remotes/docker/resolver.go @@ -24,6 +24,7 @@ import ( "net/http" "net/url" "path" + "regexp" "strings" "github.com/containerd/containerd/errdefs" @@ -449,11 +450,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 { + logrus.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], "/") {