Add daemon flag to specify public registry mirrors

Adds support for a --registry-mirror=scheme://<host>[:port]
daemon flag. The flag may be present multiple times. If
provided, mirrors are prepended to the list of endpoints used
for image pull. Note that only mirrors of the public
index.docker.io registry are supported, and image/tag resolution
is still performed via the official index.

Docker-DCO-1.1-Signed-off-by: Tim Smith <timbot@google.com> (github: timbot)
This commit is contained in:
Tim Smith
2014-09-09 19:03:38 +00:00
parent 2a832f617b
commit 69a75c673c
10 changed files with 148 additions and 15 deletions
+30 -12
View File
@@ -25,6 +25,7 @@ func (s *TagStore) CmdPull(job *engine.Job) engine.Status {
sf = utils.NewStreamFormatter(job.GetenvBool("json"))
authConfig = &registry.AuthConfig{}
metaHeaders map[string][]string
mirrors []string
)
if len(job.Args) > 1 {
tag = job.Args[1]
@@ -64,16 +65,19 @@ func (s *TagStore) CmdPull(job *engine.Job) engine.Status {
if endpoint == registry.IndexServerAddress() {
// If pull "index.docker.io/foo/bar", it's stored locally under "foo/bar"
localName = remoteName
// Use provided mirrors, if any
mirrors = s.mirrors
}
if err = s.pullRepository(r, job.Stdout, localName, remoteName, tag, sf, job.GetenvBool("parallel")); err != nil {
if err = s.pullRepository(r, job.Stdout, localName, remoteName, tag, sf, job.GetenvBool("parallel"), mirrors); err != nil {
return job.Error(err)
}
return engine.StatusOK
}
func (s *TagStore) pullRepository(r *registry.Session, out io.Writer, localName, remoteName, askedTag string, sf *utils.StreamFormatter, parallel bool) error {
func (s *TagStore) pullRepository(r *registry.Session, out io.Writer, localName, remoteName, askedTag string, sf *utils.StreamFormatter, parallel bool, mirrors []string) error {
out.Write(sf.FormatStatus("", "Pulling repository %s", localName))
repoData, err := r.GetRepositoryData(remoteName)
@@ -153,17 +157,31 @@ func (s *TagStore) pullRepository(r *registry.Session, out io.Writer, localName,
out.Write(sf.FormatProgress(utils.TruncateID(img.ID), fmt.Sprintf("Pulling image (%s) from %s", img.Tag, localName), nil))
success := false
var lastErr error
for _, ep := range repoData.Endpoints {
out.Write(sf.FormatProgress(utils.TruncateID(img.ID), fmt.Sprintf("Pulling image (%s) from %s, endpoint: %s", img.Tag, localName, ep), nil))
if err := s.pullImage(r, out, img.ID, ep, repoData.Tokens, sf); err != nil {
// It's not ideal that only the last error is returned, it would be better to concatenate the errors.
// As the error is also given to the output stream the user will see the error.
lastErr = err
out.Write(sf.FormatProgress(utils.TruncateID(img.ID), fmt.Sprintf("Error pulling image (%s) from %s, endpoint: %s, %s", img.Tag, localName, ep, err), nil))
continue
if mirrors != nil {
for _, ep := range mirrors {
out.Write(sf.FormatProgress(utils.TruncateID(img.ID), fmt.Sprintf("Pulling image (%s) from %s, mirror: %s", img.Tag, localName, ep), nil))
if err := s.pullImage(r, out, img.ID, ep, repoData.Tokens, sf); err != nil {
// Don't report errors when pulling from mirrors.
log.Debugf("Error pulling image (%s) from %s, mirror: %s, %s", img.Tag, localName, ep, err)
continue
}
success = true
break
}
}
if !success {
for _, ep := range repoData.Endpoints {
out.Write(sf.FormatProgress(utils.TruncateID(img.ID), fmt.Sprintf("Pulling image (%s) from %s, endpoint: %s", img.Tag, localName, ep), nil))
if err := s.pullImage(r, out, img.ID, ep, repoData.Tokens, sf); err != nil {
// It's not ideal that only the last error is returned, it would be better to concatenate the errors.
// As the error is also given to the output stream the user will see the error.
lastErr = err
out.Write(sf.FormatProgress(utils.TruncateID(img.ID), fmt.Sprintf("Error pulling image (%s) from %s, endpoint: %s, %s", img.Tag, localName, ep, err), nil))
continue
}
success = true
break
}
success = true
break
}
if !success {
err := fmt.Errorf("Error pulling image (%s) from %s, %v", img.Tag, localName, lastErr)
+3 -1
View File
@@ -20,6 +20,7 @@ const DEFAULTTAG = "latest"
type TagStore struct {
path string
graph *Graph
mirrors []string
Repositories map[string]Repository
sync.Mutex
// FIXME: move push/pull-related fields
@@ -48,7 +49,7 @@ func (r Repository) Contains(u Repository) bool {
return true
}
func NewTagStore(path string, graph *Graph) (*TagStore, error) {
func NewTagStore(path string, graph *Graph, mirrors []string) (*TagStore, error) {
abspath, err := filepath.Abs(path)
if err != nil {
return nil, err
@@ -56,6 +57,7 @@ func NewTagStore(path string, graph *Graph) (*TagStore, error) {
store := &TagStore{
path: abspath,
graph: graph,
mirrors: mirrors,
Repositories: make(map[string]Repository),
pullingPool: make(map[string]chan struct{}),
pushingPool: make(map[string]chan struct{}),
+1 -1
View File
@@ -52,7 +52,7 @@ func mkTestTagStore(root string, t *testing.T) *TagStore {
if err != nil {
t.Fatal(err)
}
store, err := NewTagStore(path.Join(root, "tags"), graph)
store, err := NewTagStore(path.Join(root, "tags"), graph, nil)
if err != nil {
t.Fatal(err)
}