diff --git a/pkg/cri/config/config.go b/pkg/cri/config/config.go index e6f13f90ea38..7cca3d73ba12 100644 --- a/pkg/cri/config/config.go +++ b/pkg/cri/config/config.go @@ -120,6 +120,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 8a6a8bf0fdbb..33eb9c1aff10 100644 --- a/pkg/cri/server/image_pull.go +++ b/pkg/cri/server/image_pull.go @@ -356,6 +356,10 @@ func (c *criService) registryHosts(ctx context.Context, auth *runtime.AuthConfig 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 { @@ -398,6 +402,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 @@ -463,6 +468,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 1e77d4c86cd3..ebb45b86181d 100644 --- a/remotes/docker/registry.go +++ b/remotes/docker/registry.go @@ -75,6 +75,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 1be9e1d05c67..96ace66b824b 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" @@ -462,11 +463,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], "/") {