From 13deed380127d4a0e7982bb0d4f6529d0962116b Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 15 May 2015 17:48:20 -0700 Subject: [PATCH 1/4] Registry v2 mirror support. The v2 registry will act as a pull-through cache, and needs to be handled differently by the client to the v1 registry mirror. See docker/distribution#459 for details Configuration Only one v2 registry can be configured as a mirror. Acceptable configurations in this chanage are: 0...n v1 mirrors or 1 v2 mirror. A mixture of v1 and v2 mirrors is considered an error. Pull If a v2 mirror is configured, all pulls are redirected to that mirror. The mirror will serve the content locally or attempt a pull from the upstream mirror, cache it locally, and then serve to the client. Push If an image is tagged to a mirror, it will be pushed to the mirror and be stored locally there. Otherwise, images are pushed to the hub. This is unchanged behavior. Signed-off-by: Richard Scothern --- graph/pull.go | 109 ++++++++++++++++++++++++++++++++++++++++++--- registry/config.go | 4 +- 2 files changed, 107 insertions(+), 6 deletions(-) diff --git a/graph/pull.go b/graph/pull.go index 0c33e313a..f18221d1b 100644 --- a/graph/pull.go +++ b/graph/pull.go @@ -55,6 +55,22 @@ func (s *TagStore) Pull(image string, tag string, imagePullConfig *ImagePullConf } defer s.poolRemove("pull", utils.ImageReference(repoInfo.LocalName, tag)) + logName := repoInfo.LocalName + if tag != "" { + logName = utils.ImageReference(logName, tag) + } + + v2mirrorEndpoint, v2mirrorRepoInfo, err := configureV2Mirror(repoInfo.Index.Mirrors, repoInfo, s.registryService) + if err != nil { + logrus.Errorf("Error configuring mirrors: %s", err) + return err + } + + if v2mirrorEndpoint != nil { + logrus.Debugf("Attempting pull from v2 mirror: %s", v2mirrorEndpoint.URL) + return s.pullFromV2Mirror(v2mirrorEndpoint, v2mirrorRepoInfo, imagePullConfig, tag, sf, logName) + } + logrus.Debugf("pulling image from host %q with remote name %q", repoInfo.Index.Name, repoInfo.RemoteName) endpoint, err := repoInfo.GetEndpoint(imagePullConfig.MetaHeaders) @@ -73,11 +89,6 @@ func (s *TagStore) Pull(image string, tag string, imagePullConfig *ImagePullConf return err } - logName := repoInfo.LocalName - if tag != "" { - logName = utils.ImageReference(logName, tag) - } - if len(repoInfo.Index.Mirrors) == 0 && (repoInfo.Index.Official || endpoint.Version == registry.APIVersion2) { if repoInfo.Official { s.trustService.UpdateBase() @@ -102,6 +113,94 @@ func (s *TagStore) Pull(image string, tag string, imagePullConfig *ImagePullConf s.eventsService.Log("pull", logName, "") return nil + +} + +func makeMirrorRepoInfo(repoInfo *registry.RepositoryInfo, mirror string) *registry.RepositoryInfo { + mirrorRepo := ®istry.RepositoryInfo{ + RemoteName: repoInfo.RemoteName, + LocalName: repoInfo.LocalName, + CanonicalName: repoInfo.CanonicalName, + Official: false, + + Index: ®istry.IndexInfo{ + Official: false, + Secure: repoInfo.Index.Secure, + Name: mirror, + Mirrors: []string{}, + }, + } + return mirrorRepo +} + +func configureV2Mirror(mirrors []string, repoInfo *registry.RepositoryInfo, s *registry.Service) (*registry.Endpoint, *registry.RepositoryInfo, error) { + if len(mirrors) > 0 { + // repoInfo + } else { + officialIndex, err := s.ResolveIndex(registry.IndexServerName()) + if err != nil { + return nil, nil, err + } + mirrors = officialIndex.Mirrors + } + + v1MirrorCount := 0 + var v2MirrorEndpoint *registry.Endpoint + var v2MirrorRepoInfo *registry.RepositoryInfo + var lastErr error + for _, mirror := range mirrors { + mirrorRepoInfo := makeMirrorRepoInfo(repoInfo, mirror) + endpoint, err := registry.NewEndpoint(mirrorRepoInfo.Index, nil) + if err != nil { + logrus.Errorf("Unable to create endpoint for %s: %s", mirror, err) + lastErr = err + continue + } + if endpoint.Version == 2 { + if v2MirrorEndpoint == nil { + v2MirrorEndpoint = endpoint + v2MirrorRepoInfo = mirrorRepoInfo + } else { + // > 1 v2 mirrors given + return nil, nil, fmt.Errorf("multiple v2 mirrors configured") + } + } else { + v1MirrorCount++ + } + } + + if v1MirrorCount == len(mirrors) { + // OK, but mirrors are v1 + return nil, nil, nil + } + if v2MirrorEndpoint != nil && v1MirrorCount == 0 { + // OK, 1 v2 mirror specified + return v2MirrorEndpoint, v2MirrorRepoInfo, nil + } + if v2MirrorEndpoint != nil && v1MirrorCount > 0 { + lastErr = fmt.Errorf("v1 and v2 mirrors configured") + } + return nil, nil, lastErr +} + +func (s *TagStore) pullFromV2Mirror(mirrorEndpoint *registry.Endpoint, repoInfo *registry.RepositoryInfo, + imagePullConfig *ImagePullConfig, tag string, sf *streamformatter.StreamFormatter, logName string) error { + + tr := transport.NewTransport( + registry.NewTransport(registry.ReceiveTimeout, mirrorEndpoint.IsSecure), + registry.DockerHeaders(imagePullConfig.MetaHeaders)..., + ) + client := registry.HTTPClient(tr) + mirrorSession, err := registry.NewSession(client, imagePullConfig.AuthConfig, mirrorEndpoint) + if err != nil { + return err + } + logrus.Debugf("Pulling v2 repository with local name %q from %s", repoInfo.LocalName, mirrorEndpoint.URL) + if err := s.pullV2Repository(mirrorSession, imagePullConfig.OutStream, repoInfo, tag, sf); err != nil { + return err + } + s.eventsService.Log("pull", logName, "") + return nil } func (s *TagStore) pullRepository(r *registry.Session, out io.Writer, repoInfo *registry.RepositoryInfo, askedTag string, sf *streamformatter.StreamFormatter) error { diff --git a/registry/config.go b/registry/config.go index 568756f4e..92ef4d997 100644 --- a/registry/config.go +++ b/registry/config.go @@ -189,7 +189,7 @@ func ValidateMirror(val string) (string, error) { return "", fmt.Errorf("Unsupported path/query/fragment at end of the URI") } - return fmt.Sprintf("%s://%s/v1/", uri.Scheme, uri.Host), nil + return fmt.Sprintf("%s://%s/", uri.Scheme, uri.Host), nil } // ValidateIndexName validates an index name. @@ -358,7 +358,9 @@ func (config *ServiceConfig) NewRepositoryInfo(reposName string) (*RepositoryInf // *TODO: Decouple index name from hostname (via registry configuration?) repoInfo.LocalName = repoInfo.Index.Name + "/" + repoInfo.RemoteName repoInfo.CanonicalName = repoInfo.LocalName + } + return repoInfo, nil } From c19962ade10619e5edd8057249566c494d4719bb Mon Sep 17 00:00:00 2001 From: Derek McGowan Date: Fri, 22 May 2015 18:20:54 -0700 Subject: [PATCH 2/4] Allow mirroring only for the official index Strip authconfig from session to keep credentials from being sent to the mirror. Signed-off-by: Derek McGowan (github: dmcgowan) --- graph/pull.go | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/graph/pull.go b/graph/pull.go index f18221d1b..9d6b75d7a 100644 --- a/graph/pull.go +++ b/graph/pull.go @@ -60,15 +60,18 @@ func (s *TagStore) Pull(image string, tag string, imagePullConfig *ImagePullConf logName = utils.ImageReference(logName, tag) } - v2mirrorEndpoint, v2mirrorRepoInfo, err := configureV2Mirror(repoInfo.Index.Mirrors, repoInfo, s.registryService) - if err != nil { - logrus.Errorf("Error configuring mirrors: %s", err) - return err - } + // Attempt pulling official content from a provided v2 mirror + if repoInfo.Index.Official { + v2mirrorEndpoint, v2mirrorRepoInfo, err := configureV2Mirror(repoInfo, s.registryService) + if err != nil { + logrus.Errorf("Error configuring mirrors: %s", err) + return err + } - if v2mirrorEndpoint != nil { - logrus.Debugf("Attempting pull from v2 mirror: %s", v2mirrorEndpoint.URL) - return s.pullFromV2Mirror(v2mirrorEndpoint, v2mirrorRepoInfo, imagePullConfig, tag, sf, logName) + if v2mirrorEndpoint != nil { + logrus.Debugf("Attempting pull from v2 mirror: %s", v2mirrorEndpoint.URL) + return s.pullFromV2Mirror(v2mirrorEndpoint, v2mirrorRepoInfo, imagePullConfig, tag, sf, logName) + } } logrus.Debugf("pulling image from host %q with remote name %q", repoInfo.Index.Name, repoInfo.RemoteName) @@ -133,10 +136,9 @@ func makeMirrorRepoInfo(repoInfo *registry.RepositoryInfo, mirror string) *regis return mirrorRepo } -func configureV2Mirror(mirrors []string, repoInfo *registry.RepositoryInfo, s *registry.Service) (*registry.Endpoint, *registry.RepositoryInfo, error) { - if len(mirrors) > 0 { - // repoInfo - } else { +func configureV2Mirror(repoInfo *registry.RepositoryInfo, s *registry.Service) (*registry.Endpoint, *registry.RepositoryInfo, error) { + mirrors := repoInfo.Index.Mirrors + if len(mirrors) == 0 && !repoInfo.Index.Official { officialIndex, err := s.ResolveIndex(registry.IndexServerName()) if err != nil { return nil, nil, err @@ -191,7 +193,7 @@ func (s *TagStore) pullFromV2Mirror(mirrorEndpoint *registry.Endpoint, repoInfo registry.DockerHeaders(imagePullConfig.MetaHeaders)..., ) client := registry.HTTPClient(tr) - mirrorSession, err := registry.NewSession(client, imagePullConfig.AuthConfig, mirrorEndpoint) + mirrorSession, err := registry.NewSession(client, &cliconfig.AuthConfig{}, mirrorEndpoint) if err != nil { return err } From f6f7d3524862563f8388e1382bb28ecbde8cf265 Mon Sep 17 00:00:00 2001 From: Richard Date: Wed, 27 May 2015 14:59:57 -0700 Subject: [PATCH 3/4] Restore the stripped registry version number Signed-off-by: Richard Scothern --- graph/pull.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/graph/pull.go b/graph/pull.go index 9d6b75d7a..98831eba8 100644 --- a/graph/pull.go +++ b/graph/pull.go @@ -282,6 +282,8 @@ func (s *TagStore) pullRepository(r *registry.Session, out io.Writer, repoInfo * var lastErr, err error var isDownloaded bool for _, ep := range repoInfo.Index.Mirrors { + // Ensure endpoint is v1 + ep = ep + "v1/" out.Write(sf.FormatProgress(stringid.TruncateID(img.ID), fmt.Sprintf("Pulling image (%s) from %s, mirror: %s", img.Tag, repoInfo.CanonicalName, ep), nil)) if isDownloaded, err = s.pullImage(r, out, img.ID, ep, repoData.Tokens, sf); err != nil { // Don't report errors when pulling from mirrors. From e817e08481b91638037e55b8d4855f56814c81f5 Mon Sep 17 00:00:00 2001 From: Richard Date: Wed, 27 May 2015 15:52:51 -0700 Subject: [PATCH 4/4] Review feedback: - Match verbiage with other output - Remove dead code and clearer flow Signed-off-by: Richard Scothern --- graph/pull.go | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/graph/pull.go b/graph/pull.go index 98831eba8..208cadd2f 100644 --- a/graph/pull.go +++ b/graph/pull.go @@ -69,7 +69,7 @@ func (s *TagStore) Pull(image string, tag string, imagePullConfig *ImagePullConf } if v2mirrorEndpoint != nil { - logrus.Debugf("Attempting pull from v2 mirror: %s", v2mirrorEndpoint.URL) + logrus.Debugf("Attempting to pull from v2 mirror: %s", v2mirrorEndpoint.URL) return s.pullFromV2Mirror(v2mirrorEndpoint, v2mirrorRepoInfo, imagePullConfig, tag, sf, logName) } } @@ -138,12 +138,10 @@ func makeMirrorRepoInfo(repoInfo *registry.RepositoryInfo, mirror string) *regis func configureV2Mirror(repoInfo *registry.RepositoryInfo, s *registry.Service) (*registry.Endpoint, *registry.RepositoryInfo, error) { mirrors := repoInfo.Index.Mirrors - if len(mirrors) == 0 && !repoInfo.Index.Official { - officialIndex, err := s.ResolveIndex(registry.IndexServerName()) - if err != nil { - return nil, nil, err - } - mirrors = officialIndex.Mirrors + + if len(mirrors) == 0 { + // no mirrors configured + return nil, nil, nil } v1MirrorCount := 0