From c678c3948a9183fef6610ef1742c067035d08cf1 Mon Sep 17 00:00:00 2001 From: jerryzhuang Date: Wed, 22 May 2019 21:55:27 +0800 Subject: [PATCH] feature: support registry mirrors registryMirrors is a list of registry URLs that act as a mirror for the default registry Signed-off-by: zhuangqh --- ctrd/image.go | 14 +++++++ ctrd/interface.go | 3 ++ daemon/config/config.go | 4 +- daemon/config/config_test.go | 27 ------------ daemon/mgr/image.go | 79 ++++++++++++++++++++++++++++++++---- daemon/mgr/system.go | 5 ++- main.go | 1 + 7 files changed, 96 insertions(+), 37 deletions(-) diff --git a/ctrd/image.go b/ctrd/image.go index a3a7dc40a..7709098d5 100644 --- a/ctrd/image.go +++ b/ctrd/image.go @@ -251,6 +251,20 @@ func (c *Client) PushImage(ctx context.Context, ref string, authConfig *types.Au return nil } +// ResolveImage attempts to resolve the image reference into a name and descriptor. +func (c *Client) ResolveImage(ctx context.Context, ref string, authConfig *types.AuthConfig) (name string, desc ocispec.Descriptor, err error) { + resolver, err := c.getResolver(authConfig, ref, docker.ResolverOptions{}) + if err != nil { + return "", ocispec.Descriptor{}, err + } + + name, desc, err = resolver.Resolve(ctx, ref) + if err != nil { + err = errors.Wrapf(err, "failed to resolve reference %q", ref) + } + return +} + // FetchImage fetches image content from the remote repository. func (c *Client) FetchImage(ctx context.Context, ref string, authConfig *types.AuthConfig, stream *jsonstream.JSONStream) (containerd.Image, error) { wrapperCli, err := c.Get(ctx) diff --git a/ctrd/interface.go b/ctrd/interface.go index 113f4f6df..167a0bc91 100644 --- a/ctrd/interface.go +++ b/ctrd/interface.go @@ -15,6 +15,7 @@ import ( "github.com/containerd/containerd/mount" "github.com/containerd/containerd/snapshots" digest "github.com/opencontainers/go-digest" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) // APIClient defines common methods of containerd api client @@ -79,6 +80,8 @@ type ImageAPIClient interface { ListImages(ctx context.Context, filter ...string) ([]containerd.Image, error) // FetchImage fetchs image content by the given reference. FetchImage(ctx context.Context, ref string, authConfig *types.AuthConfig, stream *jsonstream.JSONStream) (containerd.Image, error) + // ResolveImage attempts to resolve the image reference into a name and descriptor. + ResolveImage(ctx context.Context, ref string, authConfig *types.AuthConfig) (name string, desc ocispec.Descriptor, err error) // RemoveImage removes the image by the given reference. RemoveImage(ctx context.Context, ref string) error // ImportImage creates a set of images by tarstream. diff --git a/daemon/config/config.go b/daemon/config/config.go index 81f73dec4..cc9037a25 100644 --- a/daemon/config/config.go +++ b/daemon/config/config.go @@ -107,8 +107,8 @@ type Config struct { // Default log configuration DefaultLogConfig types.LogConfig `json:"default-log-config,omitempty"` - // RegistryService - RegistryService types.RegistryServiceConfig `json:"registry-service,omitempty" ` + // RegistryMirrors is a list of registry URLs that act as a mirror for the default registry. + RegistryMirrors []string `json:"registry-mirrors,omitempty"` // oom_score_adj for the daemon OOMScoreAdjust int `json:"oom-score-adjust,omitempty"` diff --git a/daemon/config/config_test.go b/daemon/config/config_test.go index ba4ed814b..b6dcfded8 100644 --- a/daemon/config/config_test.go +++ b/daemon/config/config_test.go @@ -118,33 +118,6 @@ func TestConfigValidate(t *testing.T) { cfg = &Config{ DefaultRegistry: "registry.hub.docker.com", DefaultRegistryNS: "library", - RegistryService: types.RegistryServiceConfig{ - AllowNondistributableArtifactsCIDRs: []string{ - "::1/128", - "127.0.0.0/8", - }, - AllowNondistributableArtifactsHostnames: []string{ - "registry.internal.corp.example.com:3000", - "[2001:db8:a0b:12f0::1]:443", - }, - InsecureRegistryCIDRs: []string{ - "insecure.hub.docker.com", - }, - IndexConfigs: map[string]types.IndexInfo{ - "127.0.0.1:5000": { - Name: "127.0.0.1:5000", - Mirrors: []string{ - "https://hub-mirror.corp.example.com:5000/", - }, - Secure: false, - Official: false, - }, - }, - Mirrors: []string{ - "https://[2001:db8:a0b:12f0::1]/", - "https://hub-mirror.corp.example.com:5000/", - }, - }, } assert.Equal(nil, cfg.Validate()) diff --git a/daemon/mgr/image.go b/daemon/mgr/image.go index a12d270d4..cd076a02a 100644 --- a/daemon/mgr/image.go +++ b/daemon/mgr/image.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "net/http" + "path" "strings" "time" @@ -45,6 +46,9 @@ var acceptedImageFilterTags = map[string]bool{ // ImageMgr as an interface defines all operations against images. type ImageMgr interface { + // LookupImageReferences find possible image reference list. + LookupImageReferences(ref string) []string + // PullImage pulls images from specified registry. PullImage(ctx context.Context, ref string, authConfig *types.AuthConfig, out io.Writer) error @@ -98,6 +102,9 @@ type ImageManager struct { // DefaultNamespace is the default namespace used in DefaultRegistry. DefaultNamespace string + // RegistryMirrors is a list of registry URLs that act as a mirror for the default registry. + RegistryMirrors []string + // client is a interface to the containerd client. // It is used to interact with containerd. client ctrd.APIClient @@ -122,6 +129,7 @@ func NewImageManager(cfg *config.Config, client ctrd.APIClient, eventsService *e mgr := &ImageManager{ DefaultRegistry: cfg.DefaultRegistry, DefaultNamespace: cfg.DefaultRegistryNS, + RegistryMirrors: cfg.RegistryMirrors, client: client, localStore: store, @@ -135,14 +143,45 @@ func NewImageManager(cfg *config.Config, client ctrd.APIClient, eventsService *e return mgr, nil } -// PullImage pulls images from specified registry. -func (mgr *ImageManager) PullImage(ctx context.Context, ref string, authConfig *types.AuthConfig, out io.Writer) error { - newRef := addDefaultRegistryIfMissing(ref, mgr.DefaultRegistry, mgr.DefaultNamespace) - namedRef, err := reference.Parse(newRef) - if err != nil { - return err +// LookupImageReferences find possible image reference list. +func (mgr *ImageManager) LookupImageReferences(ref string) []string { + var ( + registry string + remainder string + ) + + // extract the domain field + idx := strings.IndexRune(ref, '/') + if idx != -1 && strings.ContainsAny(ref[:idx], ".:") { + registry, remainder = ref[:idx], ref[idx+1:] + } else { + remainder = ref + } + + // create a list of reference name in order of RegistryMirrors, DefaultRegistry + // for partial reference like 'ns/ubuntu', 'ubuntu' + var fullRefs []string + + // if the domain field is empty, concat the ref with registry mirror urls. + if registry == "" { + for _, reg := range mgr.RegistryMirrors { + fullRefs = append(fullRefs, path.Join(reg, ref)) + } + registry = mgr.DefaultRegistry } + // attach the default namespace if the registry match the default registry. + if registry == mgr.DefaultRegistry && !strings.ContainsAny(remainder, "/") { + remainder = mgr.DefaultNamespace + "/" + remainder + } + + fullRefs = append(fullRefs, registry+"/"+remainder) + + return fullRefs +} + +// PullImage pulls images from specified registry. +func (mgr *ImageManager) PullImage(ctx context.Context, ref string, authConfig *types.AuthConfig, out io.Writer) error { pctx, cancel := context.WithCancel(ctx) stream := jsonstream.New(out, nil) @@ -166,7 +205,33 @@ func (mgr *ImageManager) PullImage(ctx context.Context, ref string, authConfig * closeStream() } - namedRef = reference.TrimTagForDigest(reference.WithDefaultTagIfMissing(namedRef)) + var ( + namedRef reference.Named + err error + ) + + // find an available image which could be resolved. + fullRefs := mgr.LookupImageReferences(ref) + for _, newRef := range fullRefs { + namedRef, err = reference.Parse(newRef) + if err != nil { + logrus.Warnf("failed to parse image reference when trying to pull image %s, raw reference is %s: %v", newRef, ref, err) + continue + } + namedRef = reference.TrimTagForDigest(reference.WithDefaultTagIfMissing(namedRef)) + _, _, err = mgr.client.ResolveImage(pctx, namedRef.String(), authConfig) + + // got available one + if err == nil { + break + } + } + + if err != nil { + writeStream(err) + return err + } + img, err := mgr.client.FetchImage(pctx, namedRef.String(), authConfig, stream) if err != nil { writeStream(err) diff --git a/daemon/mgr/system.go b/daemon/mgr/system.go index 807e32399..fa3c24e77 100644 --- a/daemon/mgr/system.go +++ b/daemon/mgr/system.go @@ -167,7 +167,10 @@ func (mgr *SystemManager) Info() (types.SystemInfo, error) { OperatingSystem: OSName, OSType: runtime.GOOS, PouchRootDir: mgr.config.HomeDir, - RegistryConfig: &mgr.config.RegistryService, + RegistryConfig: &types.RegistryServiceConfig{ + InsecureRegistryCIDRs: mgr.config.InsecureRegistries, + Mirrors: mgr.config.RegistryMirrors, + }, // RuncCommit: , Runtimes: mgr.config.Runtimes, SecurityOptions: securityOpts, diff --git a/main.go b/main.go index 00b101269..5be38bfa0 100644 --- a/main.go +++ b/main.go @@ -140,6 +140,7 @@ func setupFlags(cmd *cobra.Command) { // registry flagSet.StringArrayVar(&cfg.InsecureRegistries, "insecure-registries", []string{}, "enable insecure registry") + flagSet.StringArrayVar(&cfg.RegistryMirrors, "registry-mirrors", []string{}, "preferred mirror registry list") // buildkit flagSet.BoolVar(&cfg.EnableBuilder, "enable-builder", false, "Enable buildkit functionality")