Skip to content

Commit

Permalink
mirror repository rewrites
Browse files Browse the repository at this point in the history
Support CRI configuration to allow for request-time rewrite rules
applicable only to the repository portion of resource paths when pulling
images. Because the rewrites are applied at request time, images
themselves will not be "rewritten" -- images as stored by CRI (and the
underlying containerd facility) will continue to present as normal.

As an example, if you use the following config for your containerd:
```toml
[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"
```

And then subsequently invoke `crictl pull alpine:3.13` it will pull
content from `docker.io/my-org/alpine:3.13` but still show up as
`docker.io/library/alpine:3.13` in the `crictl images` listing.

Signed-off-by: Jacob Blain Christen <[email protected]>
(cherry picked from commit dee7dfe)
Signed-off-by: Brad Davidson <[email protected]>
(cherry picked from commit 6d0872d)
  • Loading branch information
dweomer authored and brandond committed Mar 10, 2023
1 parent 1e1ea6e commit 67b5134
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 2 deletions.
17 changes: 17 additions & 0 deletions pkg/cri/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,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
Expand Down
19 changes: 19 additions & 0 deletions pkg/cri/server/image_pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,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 {
Expand Down Expand Up @@ -405,6 +409,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
Expand Down Expand Up @@ -467,6 +472,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 {
Expand Down
1 change: 1 addition & 0 deletions remotes/docker/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
16 changes: 14 additions & 2 deletions remotes/docker/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"net/http"
"net/url"
"path"
"regexp"
"strings"

"github.com/containerd/containerd/errdefs"
Expand Down Expand Up @@ -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], "/") {
Expand Down

0 comments on commit 67b5134

Please sign in to comment.