Browse code

Update daemon and docker core to use new content addressable storage

Add distribution package for managing pulls and pushes. This is based on
the old code in the graph package, with major changes to work with the
new image/layer model.

Add v1 migration code.

Update registry, api/*, and daemon packages to use the reference
package's types where applicable.

Update daemon package to use image/layer/tag stores instead of the graph
package

Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>

Tonis Tiigi authored on 2015/11/19 07:20:54
Showing 70 changed files
... ...
@@ -18,16 +18,15 @@ import (
18 18
 	"strconv"
19 19
 	"strings"
20 20
 
21
+	"github.com/docker/distribution/reference"
21 22
 	"github.com/docker/docker/api"
22 23
 	Cli "github.com/docker/docker/cli"
23
-	"github.com/docker/docker/graph/tags"
24 24
 	"github.com/docker/docker/opts"
25 25
 	"github.com/docker/docker/pkg/archive"
26 26
 	"github.com/docker/docker/pkg/fileutils"
27 27
 	"github.com/docker/docker/pkg/httputils"
28 28
 	"github.com/docker/docker/pkg/jsonmessage"
29 29
 	flag "github.com/docker/docker/pkg/mflag"
30
-	"github.com/docker/docker/pkg/parsers"
31 30
 	"github.com/docker/docker/pkg/progressreader"
32 31
 	"github.com/docker/docker/pkg/streamformatter"
33 32
 	"github.com/docker/docker/pkg/ulimit"
... ...
@@ -35,6 +34,7 @@ import (
35 35
 	"github.com/docker/docker/pkg/urlutil"
36 36
 	"github.com/docker/docker/registry"
37 37
 	"github.com/docker/docker/runconfig"
38
+	tagpkg "github.com/docker/docker/tag"
38 39
 	"github.com/docker/docker/utils"
39 40
 )
40 41
 
... ...
@@ -323,7 +323,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
323 323
 	// Since the build was successful, now we must tag any of the resolved
324 324
 	// images from the above Dockerfile rewrite.
325 325
 	for _, resolved := range resolvedTags {
326
-		if err := cli.tagTrusted(resolved.repoInfo, resolved.digestRef, resolved.tagRef); err != nil {
326
+		if err := cli.tagTrusted(resolved.digestRef, resolved.tagRef); err != nil {
327 327
 			return err
328 328
 		}
329 329
 	}
... ...
@@ -333,16 +333,12 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
333 333
 
334 334
 // validateTag checks if the given image name can be resolved.
335 335
 func validateTag(rawRepo string) (string, error) {
336
-	repository, tag := parsers.ParseRepositoryTag(rawRepo)
337
-	if err := registry.ValidateRepositoryName(repository); err != nil {
336
+	ref, err := reference.ParseNamed(rawRepo)
337
+	if err != nil {
338 338
 		return "", err
339 339
 	}
340 340
 
341
-	if len(tag) == 0 {
342
-		return rawRepo, nil
343
-	}
344
-
345
-	if err := tags.ValidateTagName(tag); err != nil {
341
+	if err := registry.ValidateRepositoryName(ref); err != nil {
346 342
 		return "", err
347 343
 	}
348 344
 
... ...
@@ -565,15 +561,16 @@ func (td *trustedDockerfile) Close() error {
565 565
 // resolvedTag records the repository, tag, and resolved digest reference
566 566
 // from a Dockerfile rewrite.
567 567
 type resolvedTag struct {
568
-	repoInfo          *registry.RepositoryInfo
569
-	digestRef, tagRef registry.Reference
568
+	repoInfo  *registry.RepositoryInfo
569
+	digestRef reference.Canonical
570
+	tagRef    reference.NamedTagged
570 571
 }
571 572
 
572 573
 // rewriteDockerfileFrom rewrites the given Dockerfile by resolving images in
573 574
 // "FROM <image>" instructions to a digest reference. `translator` is a
574 575
 // function that takes a repository name and tag reference and returns a
575 576
 // trusted digest reference.
576
-func rewriteDockerfileFrom(dockerfileName string, translator func(string, registry.Reference) (registry.Reference, error)) (newDockerfile *trustedDockerfile, resolvedTags []*resolvedTag, err error) {
577
+func rewriteDockerfileFrom(dockerfileName string, translator func(reference.NamedTagged) (reference.Canonical, error)) (newDockerfile *trustedDockerfile, resolvedTags []*resolvedTag, err error) {
577 578
 	dockerfile, err := os.Open(dockerfileName)
578 579
 	if err != nil {
579 580
 		return nil, nil, fmt.Errorf("unable to open Dockerfile: %v", err)
... ...
@@ -607,29 +604,39 @@ func rewriteDockerfileFrom(dockerfileName string, translator func(string, regist
607 607
 		matches := dockerfileFromLinePattern.FindStringSubmatch(line)
608 608
 		if matches != nil && matches[1] != "scratch" {
609 609
 			// Replace the line with a resolved "FROM repo@digest"
610
-			repo, tag := parsers.ParseRepositoryTag(matches[1])
611
-			if tag == "" {
612
-				tag = tags.DefaultTag
610
+			ref, err := reference.ParseNamed(matches[1])
611
+			if err != nil {
612
+				return nil, nil, err
613 613
 			}
614 614
 
615
-			repoInfo, err := registry.ParseRepositoryInfo(repo)
616
-			if err != nil {
617
-				return nil, nil, fmt.Errorf("unable to parse repository info %q: %v", repo, err)
615
+			digested := false
616
+			switch ref.(type) {
617
+			case reference.Tagged:
618
+			case reference.Digested:
619
+				digested = true
620
+			default:
621
+				ref, err = reference.WithTag(ref, tagpkg.DefaultTag)
622
+				if err != nil {
623
+					return nil, nil, err
624
+				}
618 625
 			}
619 626
 
620
-			ref := registry.ParseReference(tag)
627
+			repoInfo, err := registry.ParseRepositoryInfo(ref)
628
+			if err != nil {
629
+				return nil, nil, fmt.Errorf("unable to parse repository info %q: %v", ref.String(), err)
630
+			}
621 631
 
622
-			if !ref.HasDigest() && isTrusted() {
623
-				trustedRef, err := translator(repo, ref)
632
+			if !digested && isTrusted() {
633
+				trustedRef, err := translator(ref.(reference.NamedTagged))
624 634
 				if err != nil {
625 635
 					return nil, nil, err
626 636
 				}
627 637
 
628
-				line = dockerfileFromLinePattern.ReplaceAllLiteralString(line, fmt.Sprintf("FROM %s", trustedRef.ImageName(repo)))
638
+				line = dockerfileFromLinePattern.ReplaceAllLiteralString(line, fmt.Sprintf("FROM %s", trustedRef.String()))
629 639
 				resolvedTags = append(resolvedTags, &resolvedTag{
630 640
 					repoInfo:  repoInfo,
631 641
 					digestRef: trustedRef,
632
-					tagRef:    ref,
642
+					tagRef:    ref.(reference.NamedTagged),
633 643
 				})
634 644
 			}
635 645
 		}
... ...
@@ -2,14 +2,15 @@ package client
2 2
 
3 3
 import (
4 4
 	"encoding/json"
5
+	"errors"
5 6
 	"fmt"
6 7
 	"net/url"
7 8
 
9
+	"github.com/docker/distribution/reference"
8 10
 	"github.com/docker/docker/api/types"
9 11
 	Cli "github.com/docker/docker/cli"
10 12
 	"github.com/docker/docker/opts"
11 13
 	flag "github.com/docker/docker/pkg/mflag"
12
-	"github.com/docker/docker/pkg/parsers"
13 14
 	"github.com/docker/docker/registry"
14 15
 	"github.com/docker/docker/runconfig"
15 16
 )
... ...
@@ -32,20 +33,35 @@ func (cli *DockerCli) CmdCommit(args ...string) error {
32 32
 	cmd.ParseFlags(args, true)
33 33
 
34 34
 	var (
35
-		name            = cmd.Arg(0)
36
-		repository, tag = parsers.ParseRepositoryTag(cmd.Arg(1))
35
+		name             = cmd.Arg(0)
36
+		repositoryAndTag = cmd.Arg(1)
37
+		repositoryName   string
38
+		tag              string
37 39
 	)
38 40
 
39 41
 	//Check if the given image name can be resolved
40
-	if repository != "" {
41
-		if err := registry.ValidateRepositoryName(repository); err != nil {
42
+	if repositoryAndTag != "" {
43
+		ref, err := reference.ParseNamed(repositoryAndTag)
44
+		if err != nil {
42 45
 			return err
43 46
 		}
47
+		if err := registry.ValidateRepositoryName(ref); err != nil {
48
+			return err
49
+		}
50
+
51
+		repositoryName = ref.Name()
52
+
53
+		switch x := ref.(type) {
54
+		case reference.Digested:
55
+			return errors.New("cannot commit to digest reference")
56
+		case reference.Tagged:
57
+			tag = x.Tag()
58
+		}
44 59
 	}
45 60
 
46 61
 	v := url.Values{}
47 62
 	v.Set("container", name)
48
-	v.Set("repo", repository)
63
+	v.Set("repo", repositoryName)
49 64
 	v.Set("tag", tag)
50 65
 	v.Set("comment", *flComment)
51 66
 	v.Set("author", *flAuthor)
... ...
@@ -9,12 +9,12 @@ import (
9 9
 	"os"
10 10
 	"strings"
11 11
 
12
+	"github.com/docker/distribution/reference"
12 13
 	"github.com/docker/docker/api/types"
13 14
 	Cli "github.com/docker/docker/cli"
14
-	"github.com/docker/docker/graph/tags"
15
-	"github.com/docker/docker/pkg/parsers"
16 15
 	"github.com/docker/docker/registry"
17 16
 	"github.com/docker/docker/runconfig"
17
+	tagpkg "github.com/docker/docker/tag"
18 18
 )
19 19
 
20 20
 func (cli *DockerCli) pullImage(image string) error {
... ...
@@ -23,16 +23,28 @@ func (cli *DockerCli) pullImage(image string) error {
23 23
 
24 24
 func (cli *DockerCli) pullImageCustomOut(image string, out io.Writer) error {
25 25
 	v := url.Values{}
26
-	repos, tag := parsers.ParseRepositoryTag(image)
27
-	// pull only the image tagged 'latest' if no tag was specified
28
-	if tag == "" {
29
-		tag = tags.DefaultTag
26
+
27
+	ref, err := reference.ParseNamed(image)
28
+	if err != nil {
29
+		return err
30
+	}
31
+
32
+	var tag string
33
+	switch x := ref.(type) {
34
+	case reference.Digested:
35
+		tag = x.Digest().String()
36
+	case reference.Tagged:
37
+		tag = x.Tag()
38
+	default:
39
+		// pull only the image tagged 'latest' if no tag was specified
40
+		tag = tagpkg.DefaultTag
30 41
 	}
31
-	v.Set("fromImage", repos)
42
+
43
+	v.Set("fromImage", ref.Name())
32 44
 	v.Set("tag", tag)
33 45
 
34 46
 	// Resolve the Repository name from fqn to RepositoryInfo
35
-	repoInfo, err := registry.ParseRepositoryInfo(repos)
47
+	repoInfo, err := registry.ParseRepositoryInfo(ref)
36 48
 	if err != nil {
37 49
 		return err
38 50
 	}
... ...
@@ -94,39 +106,46 @@ func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runc
94 94
 		defer containerIDFile.Close()
95 95
 	}
96 96
 
97
-	repo, tag := parsers.ParseRepositoryTag(config.Image)
98
-	if tag == "" {
99
-		tag = tags.DefaultTag
97
+	ref, err := reference.ParseNamed(config.Image)
98
+	if err != nil {
99
+		return nil, err
100 100
 	}
101 101
 
102
-	ref := registry.ParseReference(tag)
103
-	var trustedRef registry.Reference
102
+	isDigested := false
103
+	switch ref.(type) {
104
+	case reference.Tagged:
105
+	case reference.Digested:
106
+		isDigested = true
107
+	default:
108
+		ref, err = reference.WithTag(ref, tagpkg.DefaultTag)
109
+		if err != nil {
110
+			return nil, err
111
+		}
112
+	}
104 113
 
105
-	if isTrusted() && !ref.HasDigest() {
114
+	var trustedRef reference.Canonical
115
+
116
+	if isTrusted() && !isDigested {
106 117
 		var err error
107
-		trustedRef, err = cli.trustedReference(repo, ref)
118
+		trustedRef, err = cli.trustedReference(ref.(reference.NamedTagged))
108 119
 		if err != nil {
109 120
 			return nil, err
110 121
 		}
111
-		config.Image = trustedRef.ImageName(repo)
122
+		config.Image = trustedRef.String()
112 123
 	}
113 124
 
114 125
 	//create the container
115 126
 	serverResp, err := cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, nil)
116 127
 	//if image not found try to pull it
117 128
 	if serverResp.statusCode == 404 && strings.Contains(err.Error(), config.Image) {
118
-		fmt.Fprintf(cli.err, "Unable to find image '%s' locally\n", ref.ImageName(repo))
129
+		fmt.Fprintf(cli.err, "Unable to find image '%s' locally\n", ref.String())
119 130
 
120 131
 		// we don't want to write to stdout anything apart from container.ID
121 132
 		if err = cli.pullImageCustomOut(config.Image, cli.err); err != nil {
122 133
 			return nil, err
123 134
 		}
124
-		if trustedRef != nil && !ref.HasDigest() {
125
-			repoInfo, err := registry.ParseRepositoryInfo(repo)
126
-			if err != nil {
127
-				return nil, err
128
-			}
129
-			if err := cli.tagTrusted(repoInfo, trustedRef, ref); err != nil {
135
+		if trustedRef != nil && !isDigested {
136
+			if err := cli.tagTrusted(trustedRef, ref.(reference.NamedTagged)); err != nil {
130 137
 				return nil, err
131 138
 			}
132 139
 		}
... ...
@@ -4,18 +4,18 @@ import (
4 4
 	"encoding/json"
5 5
 	"fmt"
6 6
 	"net/url"
7
+	"strings"
7 8
 	"text/tabwriter"
8 9
 	"time"
9 10
 
11
+	"github.com/docker/distribution/reference"
10 12
 	"github.com/docker/docker/api/types"
11 13
 	Cli "github.com/docker/docker/cli"
12 14
 	"github.com/docker/docker/opts"
13 15
 	flag "github.com/docker/docker/pkg/mflag"
14
-	"github.com/docker/docker/pkg/parsers"
15 16
 	"github.com/docker/docker/pkg/parsers/filters"
16 17
 	"github.com/docker/docker/pkg/stringid"
17 18
 	"github.com/docker/docker/pkg/units"
18
-	"github.com/docker/docker/utils"
19 19
 )
20 20
 
21 21
 // CmdImages lists the images in a specified repository, or all top-level images if no repository is specified.
... ...
@@ -78,9 +78,9 @@ func (cli *DockerCli) CmdImages(args ...string) error {
78 78
 	w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
79 79
 	if !*quiet {
80 80
 		if *showDigests {
81
-			fmt.Fprintln(w, "REPOSITORY\tTAG\tDIGEST\tIMAGE ID\tCREATED\tVIRTUAL SIZE")
81
+			fmt.Fprintln(w, "REPOSITORY\tTAG\tDIGEST\tIMAGE ID\tCREATED\tSIZE")
82 82
 		} else {
83
-			fmt.Fprintln(w, "REPOSITORY\tTAG\tIMAGE ID\tCREATED\tVIRTUAL SIZE")
83
+			fmt.Fprintln(w, "REPOSITORY\tTAG\tIMAGE ID\tCREATED\tSIZE")
84 84
 		}
85 85
 	}
86 86
 
... ...
@@ -101,21 +101,31 @@ func (cli *DockerCli) CmdImages(args ...string) error {
101 101
 		// combine the tags and digests lists
102 102
 		tagsAndDigests := append(repoTags, repoDigests...)
103 103
 		for _, repoAndRef := range tagsAndDigests {
104
-			repo, ref := parsers.ParseRepositoryTag(repoAndRef)
105
-			// default tag and digest to none - if there's a value, it'll be set below
104
+			// default repo, tag, and digest to none - if there's a value, it'll be set below
105
+			repo := "<none>"
106 106
 			tag := "<none>"
107 107
 			digest := "<none>"
108
-			if utils.DigestReference(ref) {
109
-				digest = ref
110
-			} else {
111
-				tag = ref
108
+
109
+			if !strings.HasPrefix(repoAndRef, "<none>") {
110
+				ref, err := reference.ParseNamed(repoAndRef)
111
+				if err != nil {
112
+					return err
113
+				}
114
+				repo = ref.Name()
115
+
116
+				switch x := ref.(type) {
117
+				case reference.Digested:
118
+					digest = x.Digest().String()
119
+				case reference.Tagged:
120
+					tag = x.Tag()
121
+				}
112 122
 			}
113 123
 
114 124
 			if !*quiet {
115 125
 				if *showDigests {
116
-					fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s ago\t%s\n", repo, tag, digest, ID, units.HumanDuration(time.Now().UTC().Sub(time.Unix(int64(image.Created), 0))), units.HumanSize(float64(image.VirtualSize)))
126
+					fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s ago\t%s\n", repo, tag, digest, ID, units.HumanDuration(time.Now().UTC().Sub(time.Unix(int64(image.Created), 0))), units.HumanSize(float64(image.Size)))
117 127
 				} else {
118
-					fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\n", repo, tag, ID, units.HumanDuration(time.Now().UTC().Sub(time.Unix(int64(image.Created), 0))), units.HumanSize(float64(image.VirtualSize)))
128
+					fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\n", repo, tag, ID, units.HumanDuration(time.Now().UTC().Sub(time.Unix(int64(image.Created), 0))), units.HumanSize(float64(image.Size)))
119 129
 				}
120 130
 			} else {
121 131
 				fmt.Fprintln(w, ID)
... ...
@@ -6,10 +6,10 @@ import (
6 6
 	"net/url"
7 7
 	"os"
8 8
 
9
+	"github.com/docker/distribution/reference"
9 10
 	Cli "github.com/docker/docker/cli"
10 11
 	"github.com/docker/docker/opts"
11 12
 	flag "github.com/docker/docker/pkg/mflag"
12
-	"github.com/docker/docker/pkg/parsers"
13 13
 	"github.com/docker/docker/pkg/urlutil"
14 14
 	"github.com/docker/docker/registry"
15 15
 )
... ...
@@ -47,8 +47,11 @@ func (cli *DockerCli) CmdImport(args ...string) error {
47 47
 
48 48
 	if repository != "" {
49 49
 		//Check if the given image name can be resolved
50
-		repo, _ := parsers.ParseRepositoryTag(repository)
51
-		if err := registry.ValidateRepositoryName(repo); err != nil {
50
+		ref, err := reference.ParseNamed(repository)
51
+		if err != nil {
52
+			return err
53
+		}
54
+		if err := registry.ValidateRepositoryName(ref); err != nil {
52 55
 			return err
53 56
 		}
54 57
 	}
... ...
@@ -62,8 +62,8 @@ func (c *containerContext) Image() string {
62 62
 		return "<no image>"
63 63
 	}
64 64
 	if c.trunc {
65
-		if stringid.TruncateID(c.c.ImageID) == stringid.TruncateID(c.c.Image) {
66
-			return stringutils.Truncate(c.c.Image, 12)
65
+		if trunc := stringid.TruncateID(c.c.ImageID); trunc == stringid.TruncateID(c.c.Image) {
66
+			return trunc
67 67
 		}
68 68
 	}
69 69
 	return c.c.Image
... ...
@@ -1,16 +1,19 @@
1 1
 package client
2 2
 
3 3
 import (
4
+	"errors"
4 5
 	"fmt"
5 6
 	"net/url"
6 7
 
8
+	"github.com/docker/distribution/reference"
7 9
 	Cli "github.com/docker/docker/cli"
8
-	"github.com/docker/docker/graph/tags"
9 10
 	flag "github.com/docker/docker/pkg/mflag"
10
-	"github.com/docker/docker/pkg/parsers"
11 11
 	"github.com/docker/docker/registry"
12
+	tagpkg "github.com/docker/docker/tag"
12 13
 )
13 14
 
15
+var errTagCantBeUsed = errors.New("tag can't be used with --all-tags/-a")
16
+
14 17
 // CmdPull pulls an image or a repository from the registry.
15 18
 //
16 19
 // Usage: docker pull [OPTIONS] IMAGENAME[:TAG|@DIGEST]
... ...
@@ -23,18 +26,38 @@ func (cli *DockerCli) CmdPull(args ...string) error {
23 23
 	cmd.ParseFlags(args, true)
24 24
 	remote := cmd.Arg(0)
25 25
 
26
-	taglessRemote, tag := parsers.ParseRepositoryTag(remote)
27
-	if tag == "" && !*allTags {
28
-		tag = tags.DefaultTag
29
-		fmt.Fprintf(cli.out, "Using default tag: %s\n", tag)
30
-	} else if tag != "" && *allTags {
31
-		return fmt.Errorf("tag can't be used with --all-tags/-a")
26
+	distributionRef, err := reference.ParseNamed(remote)
27
+	if err != nil {
28
+		return err
29
+	}
30
+
31
+	var tag string
32
+	switch x := distributionRef.(type) {
33
+	case reference.Digested:
34
+		if *allTags {
35
+			return errTagCantBeUsed
36
+		}
37
+		tag = x.Digest().String()
38
+	case reference.Tagged:
39
+		if *allTags {
40
+			return errTagCantBeUsed
41
+		}
42
+		tag = x.Tag()
43
+	default:
44
+		if !*allTags {
45
+			tag = tagpkg.DefaultTag
46
+			distributionRef, err = reference.WithTag(distributionRef, tag)
47
+			if err != nil {
48
+				return err
49
+			}
50
+			fmt.Fprintf(cli.out, "Using default tag: %s\n", tag)
51
+		}
32 52
 	}
33 53
 
34 54
 	ref := registry.ParseReference(tag)
35 55
 
36 56
 	// Resolve the Repository name from fqn to RepositoryInfo
37
-	repoInfo, err := registry.ParseRepositoryInfo(taglessRemote)
57
+	repoInfo, err := registry.ParseRepositoryInfo(distributionRef)
38 58
 	if err != nil {
39 59
 		return err
40 60
 	}
... ...
@@ -46,7 +69,7 @@ func (cli *DockerCli) CmdPull(args ...string) error {
46 46
 	}
47 47
 
48 48
 	v := url.Values{}
49
-	v.Set("fromImage", ref.ImageName(taglessRemote))
49
+	v.Set("fromImage", distributionRef.String())
50 50
 
51 51
 	_, _, err = cli.clientRequestAttemptLogin("POST", "/images/create?"+v.Encode(), nil, cli.out, repoInfo.Index, "pull")
52 52
 	return err
... ...
@@ -1,12 +1,13 @@
1 1
 package client
2 2
 
3 3
 import (
4
+	"errors"
4 5
 	"fmt"
5 6
 	"net/url"
6 7
 
8
+	"github.com/docker/distribution/reference"
7 9
 	Cli "github.com/docker/docker/cli"
8 10
 	flag "github.com/docker/docker/pkg/mflag"
9
-	"github.com/docker/docker/pkg/parsers"
10 11
 	"github.com/docker/docker/registry"
11 12
 )
12 13
 
... ...
@@ -20,10 +21,21 @@ func (cli *DockerCli) CmdPush(args ...string) error {
20 20
 
21 21
 	cmd.ParseFlags(args, true)
22 22
 
23
-	remote, tag := parsers.ParseRepositoryTag(cmd.Arg(0))
23
+	ref, err := reference.ParseNamed(cmd.Arg(0))
24
+	if err != nil {
25
+		return err
26
+	}
27
+
28
+	var tag string
29
+	switch x := ref.(type) {
30
+	case reference.Digested:
31
+		return errors.New("cannot push a digest reference")
32
+	case reference.Tagged:
33
+		tag = x.Tag()
34
+	}
24 35
 
25 36
 	// Resolve the Repository name from fqn to RepositoryInfo
26
-	repoInfo, err := registry.ParseRepositoryInfo(remote)
37
+	repoInfo, err := registry.ParseRepositoryInfo(ref)
27 38
 	if err != nil {
28 39
 		return err
29 40
 	}
... ...
@@ -48,6 +60,6 @@ func (cli *DockerCli) CmdPush(args ...string) error {
48 48
 	v := url.Values{}
49 49
 	v.Set("tag", tag)
50 50
 
51
-	_, _, err = cli.clientRequestAttemptLogin("POST", "/images/"+remote+"/push?"+v.Encode(), nil, cli.out, repoInfo.Index, "push")
51
+	_, _, err = cli.clientRequestAttemptLogin("POST", "/images/"+ref.Name()+"/push?"+v.Encode(), nil, cli.out, repoInfo.Index, "push")
52 52
 	return err
53 53
 }
... ...
@@ -10,7 +10,6 @@ import (
10 10
 
11 11
 	Cli "github.com/docker/docker/cli"
12 12
 	flag "github.com/docker/docker/pkg/mflag"
13
-	"github.com/docker/docker/pkg/parsers"
14 13
 	"github.com/docker/docker/pkg/stringutils"
15 14
 	"github.com/docker/docker/registry"
16 15
 )
... ...
@@ -38,10 +37,7 @@ func (cli *DockerCli) CmdSearch(args ...string) error {
38 38
 	v := url.Values{}
39 39
 	v.Set("term", name)
40 40
 
41
-	// Resolve the Repository name from fqn to hostname + name
42
-	taglessRemote, _ := parsers.ParseRepositoryTag(name)
43
-
44
-	indexInfo, err := registry.ParseIndexInfo(taglessRemote)
41
+	indexInfo, err := registry.ParseSearchIndexInfo(name)
45 42
 	if err != nil {
46 43
 		return err
47 44
 	}
... ...
@@ -1,11 +1,12 @@
1 1
 package client
2 2
 
3 3
 import (
4
+	"errors"
4 5
 	"net/url"
5 6
 
7
+	"github.com/docker/distribution/reference"
6 8
 	Cli "github.com/docker/docker/cli"
7 9
 	flag "github.com/docker/docker/pkg/mflag"
8
-	"github.com/docker/docker/pkg/parsers"
9 10
 	"github.com/docker/docker/registry"
10 11
 )
11 12
 
... ...
@@ -19,16 +20,28 @@ func (cli *DockerCli) CmdTag(args ...string) error {
19 19
 
20 20
 	cmd.ParseFlags(args, true)
21 21
 
22
-	var (
23
-		repository, tag = parsers.ParseRepositoryTag(cmd.Arg(1))
24
-		v               = url.Values{}
25
-	)
22
+	v := url.Values{}
23
+	ref, err := reference.ParseNamed(cmd.Arg(1))
24
+	if err != nil {
25
+		return err
26
+	}
27
+
28
+	_, isDigested := ref.(reference.Digested)
29
+	if isDigested {
30
+		return errors.New("refusing to create a tag with a digest reference")
31
+	}
32
+
33
+	tag := ""
34
+	tagged, isTagged := ref.(reference.Tagged)
35
+	if isTagged {
36
+		tag = tagged.Tag()
37
+	}
26 38
 
27 39
 	//Check if the given image name can be resolved
28
-	if err := registry.ValidateRepositoryName(repository); err != nil {
40
+	if err := registry.ValidateRepositoryName(ref); err != nil {
29 41
 		return err
30 42
 	}
31
-	v.Set("repo", repository)
43
+	v.Set("repo", ref.Name())
32 44
 	v.Set("tag", tag)
33 45
 
34 46
 	if *force {
... ...
@@ -19,6 +19,7 @@ import (
19 19
 
20 20
 	"github.com/Sirupsen/logrus"
21 21
 	"github.com/docker/distribution/digest"
22
+	"github.com/docker/distribution/reference"
22 23
 	"github.com/docker/distribution/registry/client/auth"
23 24
 	"github.com/docker/distribution/registry/client/transport"
24 25
 	"github.com/docker/docker/cliconfig"
... ...
@@ -163,12 +164,12 @@ func (cli *DockerCli) getNotaryRepository(repoInfo *registry.RepositoryInfo, aut
163 163
 	}
164 164
 
165 165
 	creds := simpleCredentialStore{auth: authConfig}
166
-	tokenHandler := auth.NewTokenHandler(authTransport, creds, repoInfo.CanonicalName, "push", "pull")
166
+	tokenHandler := auth.NewTokenHandler(authTransport, creds, repoInfo.CanonicalName.Name(), "push", "pull")
167 167
 	basicHandler := auth.NewBasicHandler(creds)
168 168
 	modifiers = append(modifiers, transport.RequestModifier(auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler)))
169 169
 	tr := transport.NewTransport(base, modifiers...)
170 170
 
171
-	return client.NewNotaryRepository(cli.trustDirectory(), repoInfo.CanonicalName, server, tr, cli.getPassphraseRetriever())
171
+	return client.NewNotaryRepository(cli.trustDirectory(), repoInfo.CanonicalName.Name(), server, tr, cli.getPassphraseRetriever())
172 172
 }
173 173
 
174 174
 func convertTarget(t client.Target) (target, error) {
... ...
@@ -219,8 +220,8 @@ func (cli *DockerCli) getPassphraseRetriever() passphrase.Retriever {
219 219
 	}
220 220
 }
221 221
 
222
-func (cli *DockerCli) trustedReference(repo string, ref registry.Reference) (registry.Reference, error) {
223
-	repoInfo, err := registry.ParseRepositoryInfo(repo)
222
+func (cli *DockerCli) trustedReference(ref reference.NamedTagged) (reference.Canonical, error) {
223
+	repoInfo, err := registry.ParseRepositoryInfo(ref)
224 224
 	if err != nil {
225 225
 		return nil, err
226 226
 	}
... ...
@@ -234,7 +235,7 @@ func (cli *DockerCli) trustedReference(repo string, ref registry.Reference) (reg
234 234
 		return nil, err
235 235
 	}
236 236
 
237
-	t, err := notaryRepo.GetTargetByName(ref.String())
237
+	t, err := notaryRepo.GetTargetByName(ref.Tag())
238 238
 	if err != nil {
239 239
 		return nil, err
240 240
 	}
... ...
@@ -244,18 +245,17 @@ func (cli *DockerCli) trustedReference(repo string, ref registry.Reference) (reg
244 244
 
245 245
 	}
246 246
 
247
-	return registry.DigestReference(r.digest), nil
247
+	return reference.WithDigest(ref, r.digest)
248 248
 }
249 249
 
250
-func (cli *DockerCli) tagTrusted(repoInfo *registry.RepositoryInfo, trustedRef, ref registry.Reference) error {
251
-	fullName := trustedRef.ImageName(repoInfo.LocalName)
252
-	fmt.Fprintf(cli.out, "Tagging %s as %s\n", fullName, ref.ImageName(repoInfo.LocalName))
250
+func (cli *DockerCli) tagTrusted(trustedRef reference.Canonical, ref reference.NamedTagged) error {
251
+	fmt.Fprintf(cli.out, "Tagging %s as %s\n", trustedRef.String(), ref.String())
253 252
 	tv := url.Values{}
254
-	tv.Set("repo", repoInfo.LocalName)
255
-	tv.Set("tag", ref.String())
253
+	tv.Set("repo", trustedRef.Name())
254
+	tv.Set("tag", ref.Tag())
256 255
 	tv.Set("force", "1")
257 256
 
258
-	if _, _, err := readBody(cli.call("POST", "/images/"+fullName+"/tag?"+tv.Encode(), nil, nil)); err != nil {
257
+	if _, _, err := readBody(cli.call("POST", "/images/"+trustedRef.String()+"/tag?"+tv.Encode(), nil, nil)); err != nil {
259 258
 		return err
260 259
 	}
261 260
 
... ...
@@ -317,7 +317,7 @@ func (cli *DockerCli) trustedPull(repoInfo *registry.RepositoryInfo, ref registr
317 317
 		refs = append(refs, r)
318 318
 	}
319 319
 
320
-	v.Set("fromImage", repoInfo.LocalName)
320
+	v.Set("fromImage", repoInfo.LocalName.Name())
321 321
 	for i, r := range refs {
322 322
 		displayTag := r.reference.String()
323 323
 		if displayTag != "" {
... ...
@@ -333,7 +333,12 @@ func (cli *DockerCli) trustedPull(repoInfo *registry.RepositoryInfo, ref registr
333 333
 
334 334
 		// If reference is not trusted, tag by trusted reference
335 335
 		if !r.reference.HasDigest() {
336
-			if err := cli.tagTrusted(repoInfo, registry.DigestReference(r.digest), r.reference); err != nil {
336
+			tagged, err := reference.WithTag(repoInfo.LocalName, r.reference.String())
337
+			if err != nil {
338
+				return err
339
+			}
340
+			trustedRef, err := reference.WithDigest(repoInfo.LocalName, r.digest)
341
+			if err := cli.tagTrusted(trustedRef, tagged); err != nil {
337 342
 				return err
338 343
 
339 344
 			}
... ...
@@ -386,7 +391,7 @@ func (cli *DockerCli) trustedPush(repoInfo *registry.RepositoryInfo, tag string,
386 386
 	v := url.Values{}
387 387
 	v.Set("tag", tag)
388 388
 
389
-	_, _, err := cli.clientRequestAttemptLogin("POST", "/images/"+repoInfo.LocalName+"/push?"+v.Encode(), nil, streamOut, repoInfo.Index, "push")
389
+	_, _, err := cli.clientRequestAttemptLogin("POST", "/images/"+repoInfo.LocalName.Name()+"/push?"+v.Encode(), nil, streamOut, repoInfo.Index, "push")
390 390
 	// Close stream channel to finish target parsing
391 391
 	if err := streamOut.Close(); err != nil {
392 392
 		return err
... ...
@@ -10,6 +10,8 @@ import (
10 10
 	"strings"
11 11
 
12 12
 	"github.com/Sirupsen/logrus"
13
+	"github.com/docker/distribution/digest"
14
+	"github.com/docker/distribution/reference"
13 15
 	"github.com/docker/docker/api/server/httputils"
14 16
 	"github.com/docker/docker/api/types"
15 17
 	"github.com/docker/docker/builder"
... ...
@@ -17,17 +19,14 @@ import (
17 17
 	"github.com/docker/docker/cliconfig"
18 18
 	"github.com/docker/docker/daemon/daemonbuilder"
19 19
 	derr "github.com/docker/docker/errors"
20
-	"github.com/docker/docker/graph"
21
-	"github.com/docker/docker/graph/tags"
22 20
 	"github.com/docker/docker/pkg/archive"
23 21
 	"github.com/docker/docker/pkg/chrootarchive"
24 22
 	"github.com/docker/docker/pkg/ioutils"
25
-	"github.com/docker/docker/pkg/parsers"
26 23
 	"github.com/docker/docker/pkg/progressreader"
27 24
 	"github.com/docker/docker/pkg/streamformatter"
28 25
 	"github.com/docker/docker/pkg/ulimit"
29
-	"github.com/docker/docker/registry"
30 26
 	"github.com/docker/docker/runconfig"
27
+	tagpkg "github.com/docker/docker/tag"
31 28
 	"github.com/docker/docker/utils"
32 29
 	"golang.org/x/net/context"
33 30
 )
... ...
@@ -110,26 +109,55 @@ func (s *router) postImagesCreate(ctx context.Context, w http.ResponseWriter, r
110 110
 	w.Header().Set("Content-Type", "application/json")
111 111
 
112 112
 	if image != "" { //pull
113
-		if tag == "" {
114
-			image, tag = parsers.ParseRepositoryTag(image)
115
-		}
116
-		metaHeaders := map[string][]string{}
117
-		for k, v := range r.Header {
118
-			if strings.HasPrefix(k, "X-Meta-") {
119
-				metaHeaders[k] = v
113
+		// Special case: "pull -a" may send an image name with a
114
+		// trailing :. This is ugly, but let's not break API
115
+		// compatibility.
116
+		image = strings.TrimSuffix(image, ":")
117
+
118
+		var ref reference.Named
119
+		ref, err = reference.ParseNamed(image)
120
+		if err == nil {
121
+			if tag != "" {
122
+				// The "tag" could actually be a digest.
123
+				var dgst digest.Digest
124
+				dgst, err = digest.ParseDigest(tag)
125
+				if err == nil {
126
+					ref, err = reference.WithDigest(ref, dgst)
127
+				} else {
128
+					ref, err = reference.WithTag(ref, tag)
129
+				}
130
+			}
131
+			if err == nil {
132
+				metaHeaders := map[string][]string{}
133
+				for k, v := range r.Header {
134
+					if strings.HasPrefix(k, "X-Meta-") {
135
+						metaHeaders[k] = v
136
+					}
137
+				}
138
+
139
+				err = s.daemon.PullImage(ref, metaHeaders, authConfig, output)
120 140
 			}
121 141
 		}
142
+	} else { //import
143
+		var newRef reference.Named
144
+		if repo != "" {
145
+			var err error
146
+			newRef, err = reference.ParseNamed(repo)
147
+			if err != nil {
148
+				return err
149
+			}
122 150
 
123
-		imagePullConfig := &graph.ImagePullConfig{
124
-			MetaHeaders: metaHeaders,
125
-			AuthConfig:  authConfig,
126
-			OutStream:   output,
127
-		}
151
+			switch newRef.(type) {
152
+			case reference.Digested:
153
+				return errors.New("cannot import digest reference")
154
+			}
128 155
 
129
-		err = s.daemon.PullImage(image, tag, imagePullConfig)
130
-	} else { //import
131
-		if tag == "" {
132
-			repo, tag = parsers.ParseRepositoryTag(repo)
156
+			if tag != "" {
157
+				newRef, err = reference.WithTag(newRef, tag)
158
+				if err != nil {
159
+					return err
160
+				}
161
+			}
133 162
 		}
134 163
 
135 164
 		src := r.Form.Get("fromSrc")
... ...
@@ -143,7 +171,7 @@ func (s *router) postImagesCreate(ctx context.Context, w http.ResponseWriter, r
143 143
 			return err
144 144
 		}
145 145
 
146
-		err = s.daemon.ImportImage(src, repo, tag, message, r.Body, output, newConfig)
146
+		err = s.daemon.ImportImage(src, newRef, message, r.Body, output, newConfig)
147 147
 	}
148 148
 	if err != nil {
149 149
 		if !output.Flushed() {
... ...
@@ -183,19 +211,25 @@ func (s *router) postImagesPush(ctx context.Context, w http.ResponseWriter, r *h
183 183
 		}
184 184
 	}
185 185
 
186
-	name := vars["name"]
186
+	ref, err := reference.ParseNamed(vars["name"])
187
+	if err != nil {
188
+		return err
189
+	}
190
+	tag := r.Form.Get("tag")
191
+	if tag != "" {
192
+		// Push by digest is not supported, so only tags are supported.
193
+		ref, err = reference.WithTag(ref, tag)
194
+		if err != nil {
195
+			return err
196
+		}
197
+	}
198
+
187 199
 	output := ioutils.NewWriteFlusher(w)
188 200
 	defer output.Close()
189
-	imagePushConfig := &graph.ImagePushConfig{
190
-		MetaHeaders: metaHeaders,
191
-		AuthConfig:  authConfig,
192
-		Tag:         r.Form.Get("tag"),
193
-		OutStream:   output,
194
-	}
195 201
 
196 202
 	w.Header().Set("Content-Type", "application/json")
197 203
 
198
-	if err := s.daemon.PushImage(name, imagePushConfig); err != nil {
204
+	if err := s.daemon.PushImage(ref, metaHeaders, authConfig, output); err != nil {
199 205
 		if !output.Flushed() {
200 206
 			return err
201 207
 		}
... ...
@@ -428,7 +462,7 @@ func (s *router) postBuild(ctx context.Context, w http.ResponseWriter, r *http.R
428 428
 	}
429 429
 
430 430
 	for _, rt := range repoAndTags {
431
-		if err := s.daemon.TagImage(rt.repo, rt.tag, string(imgID), true); err != nil {
431
+		if err := s.daemon.TagImage(rt, imgID, true); err != nil {
432 432
 			return errf(err)
433 433
 		}
434 434
 	}
... ...
@@ -436,43 +470,38 @@ func (s *router) postBuild(ctx context.Context, w http.ResponseWriter, r *http.R
436 436
 	return nil
437 437
 }
438 438
 
439
-// repoAndTag is a helper struct for holding the parsed repositories and tags of
440
-// the input "t" argument.
441
-type repoAndTag struct {
442
-	repo, tag string
443
-}
444
-
445 439
 // sanitizeRepoAndTags parses the raw "t" parameter received from the client
446 440
 // to a slice of repoAndTag.
447 441
 // It also validates each repoName and tag.
448
-func sanitizeRepoAndTags(names []string) ([]repoAndTag, error) {
442
+func sanitizeRepoAndTags(names []string) ([]reference.Named, error) {
449 443
 	var (
450
-		repoAndTags []repoAndTag
444
+		repoAndTags []reference.Named
451 445
 		// This map is used for deduplicating the "-t" paramter.
452 446
 		uniqNames = make(map[string]struct{})
453 447
 	)
454 448
 	for _, repo := range names {
455
-		name, tag := parsers.ParseRepositoryTag(repo)
456
-		if name == "" {
449
+		if repo == "" {
457 450
 			continue
458 451
 		}
459 452
 
460
-		if err := registry.ValidateRepositoryName(name); err != nil {
453
+		ref, err := reference.ParseNamed(repo)
454
+		if err != nil {
461 455
 			return nil, err
462 456
 		}
463 457
 
464
-		nameWithTag := name
465
-		if len(tag) > 0 {
466
-			if err := tags.ValidateTagName(tag); err != nil {
467
-				return nil, err
468
-			}
469
-			nameWithTag += ":" + tag
470
-		} else {
471
-			nameWithTag += ":" + tags.DefaultTag
458
+		if _, isDigested := ref.(reference.Digested); isDigested {
459
+			return nil, errors.New("build tag cannot be a digest")
460
+		}
461
+
462
+		if _, isTagged := ref.(reference.Tagged); !isTagged {
463
+			ref, err = reference.WithTag(ref, tagpkg.DefaultTag)
472 464
 		}
465
+
466
+		nameWithTag := ref.String()
467
+
473 468
 		if _, exists := uniqNames[nameWithTag]; !exists {
474 469
 			uniqNames[nameWithTag] = struct{}{}
475
-			repoAndTags = append(repoAndTags, repoAndTag{repo: name, tag: tag})
470
+			repoAndTags = append(repoAndTags, ref)
476 471
 		}
477 472
 	}
478 473
 	return repoAndTags, nil
... ...
@@ -484,7 +513,7 @@ func (s *router) getImagesJSON(ctx context.Context, w http.ResponseWriter, r *ht
484 484
 	}
485 485
 
486 486
 	// FIXME: The filter parameter could just be a match filter
487
-	images, err := s.daemon.ListImages(r.Form.Get("filters"), r.Form.Get("filter"), httputils.BoolValue(r, "all"))
487
+	images, err := s.daemon.Images(r.Form.Get("filters"), r.Form.Get("filter"), httputils.BoolValue(r, "all"))
488 488
 	if err != nil {
489 489
 		return err
490 490
 	}
... ...
@@ -508,9 +537,17 @@ func (s *router) postImagesTag(ctx context.Context, w http.ResponseWriter, r *ht
508 508
 	}
509 509
 	repo := r.Form.Get("repo")
510 510
 	tag := r.Form.Get("tag")
511
-	name := vars["name"]
511
+	newTag, err := reference.WithName(repo)
512
+	if err != nil {
513
+		return err
514
+	}
515
+	if tag != "" {
516
+		if newTag, err = reference.WithTag(newTag, tag); err != nil {
517
+			return err
518
+		}
519
+	}
512 520
 	force := httputils.BoolValue(r, "force")
513
-	if err := s.daemon.TagImage(repo, tag, name, force); err != nil {
521
+	if err := s.daemon.TagImage(newTag, vars["name"], force); err != nil {
514 522
 		return err
515 523
 	}
516 524
 	w.WriteHeader(http.StatusCreated)
... ...
@@ -125,7 +125,7 @@ type Docker interface {
125 125
 	// Remove removes a container specified by `id`.
126 126
 	Remove(id string, cfg *daemon.ContainerRmConfig) error
127 127
 	// Commit creates a new Docker image from an existing Docker container.
128
-	Commit(string, *daemon.ContainerCommitConfig) (*image.Image, error)
128
+	Commit(string, *daemon.ContainerCommitConfig) (string, error)
129 129
 	// Copy copies/extracts a source FileInfo to a destination path inside a container
130 130
 	// specified by a container object.
131 131
 	// TODO: make an Extract method instead of passing `decompress`
... ...
@@ -277,9 +277,9 @@ func Commit(containerName string, d *daemon.Daemon, c *CommitConfig) (string, er
277 277
 		MergeConfigs: true,
278 278
 	}
279 279
 
280
-	img, err := d.Commit(containerName, commitCfg)
280
+	imgID, err := d.Commit(containerName, commitCfg)
281 281
 	if err != nil {
282 282
 		return "", err
283 283
 	}
284
-	return img.ID, nil
284
+	return imgID, nil
285 285
 }
... ...
@@ -83,13 +83,13 @@ func (b *Builder) commit(id string, autoCmd *stringutils.StrSlice, comment strin
83 83
 	}
84 84
 
85 85
 	// Commit the container
86
-	image, err := b.docker.Commit(id, commitCfg)
86
+	imageID, err := b.docker.Commit(id, commitCfg)
87 87
 	if err != nil {
88 88
 		return err
89 89
 	}
90
-	b.docker.Retain(b.id, image.ID)
91
-	b.activeImages = append(b.activeImages, image.ID)
92
-	b.image = image.ID
90
+	b.docker.Retain(b.id, imageID)
91
+	b.activeImages = append(b.activeImages, imageID)
92
+	b.image = imageID
93 93
 	return nil
94 94
 }
95 95
 
... ...
@@ -412,7 +412,7 @@ func containsWildcards(name string) bool {
412 412
 }
413 413
 
414 414
 func (b *Builder) processImageFrom(img *image.Image) error {
415
-	b.image = img.ID
415
+	b.image = img.ID().String()
416 416
 
417 417
 	if img.Config != nil {
418 418
 		b.runConfig = img.Config
... ...
@@ -1,10 +1,16 @@
1 1
 package daemon
2 2
 
3 3
 import (
4
+	"encoding/json"
4 5
 	"fmt"
5 6
 	"runtime"
7
+	"strings"
8
+	"time"
6 9
 
10
+	"github.com/docker/distribution/reference"
11
+	"github.com/docker/docker/dockerversion"
7 12
 	"github.com/docker/docker/image"
13
+	"github.com/docker/docker/layer"
8 14
 	"github.com/docker/docker/pkg/archive"
9 15
 	"github.com/docker/docker/pkg/ioutils"
10 16
 	"github.com/docker/docker/runconfig"
... ...
@@ -25,15 +31,15 @@ type ContainerCommitConfig struct {
25 25
 
26 26
 // Commit creates a new filesystem image from the current state of a container.
27 27
 // The image can optionally be tagged into a repository.
28
-func (daemon *Daemon) Commit(name string, c *ContainerCommitConfig) (*image.Image, error) {
28
+func (daemon *Daemon) Commit(name string, c *ContainerCommitConfig) (string, error) {
29 29
 	container, err := daemon.Get(name)
30 30
 	if err != nil {
31
-		return nil, err
31
+		return "", err
32 32
 	}
33 33
 
34 34
 	// It is not possible to commit a running container on Windows
35 35
 	if runtime.GOOS == "windows" && container.IsRunning() {
36
-		return nil, fmt.Errorf("Windows does not support commit of a running container")
36
+		return "", fmt.Errorf("Windows does not support commit of a running container")
37 37
 	}
38 38
 
39 39
 	if c.Pause && !container.isPaused() {
... ...
@@ -43,13 +49,13 @@ func (daemon *Daemon) Commit(name string, c *ContainerCommitConfig) (*image.Imag
43 43
 
44 44
 	if c.MergeConfigs {
45 45
 		if err := runconfig.Merge(c.Config, container.Config); err != nil {
46
-			return nil, err
46
+			return "", err
47 47
 		}
48 48
 	}
49 49
 
50 50
 	rwTar, err := daemon.exportContainerRw(container)
51 51
 	if err != nil {
52
-		return nil, err
52
+		return "", err
53 53
 	}
54 54
 	defer func() {
55 55
 		if rwTar != nil {
... ...
@@ -57,31 +63,99 @@ func (daemon *Daemon) Commit(name string, c *ContainerCommitConfig) (*image.Imag
57 57
 		}
58 58
 	}()
59 59
 
60
-	// Create a new image from the container's base layers + a new layer from container changes
61
-	img, err := daemon.graph.Create(rwTar, container.ID, container.ImageID, c.Comment, c.Author, container.Config, c.Config)
60
+	var history []image.History
61
+	rootFS := image.NewRootFS()
62
+
63
+	if container.ImageID != "" {
64
+		img, err := daemon.imageStore.Get(container.ImageID)
65
+		if err != nil {
66
+			return "", err
67
+		}
68
+		history = img.History
69
+		rootFS = img.RootFS
70
+	}
71
+
72
+	l, err := daemon.layerStore.Register(rwTar, rootFS.ChainID())
62 73
 	if err != nil {
63
-		return nil, err
74
+		return "", err
75
+	}
76
+	defer layer.ReleaseAndLog(daemon.layerStore, l)
77
+
78
+	h := image.History{
79
+		Author:     c.Author,
80
+		Created:    time.Now().UTC(),
81
+		CreatedBy:  strings.Join(container.Config.Cmd.Slice(), " "),
82
+		Comment:    c.Comment,
83
+		EmptyLayer: true,
84
+	}
85
+
86
+	if diffID := l.DiffID(); layer.DigestSHA256EmptyTar != diffID {
87
+		h.EmptyLayer = false
88
+		rootFS.Append(diffID)
89
+	}
90
+
91
+	history = append(history, h)
92
+
93
+	config, err := json.Marshal(&image.Image{
94
+		V1Image: image.V1Image{
95
+			DockerVersion:   dockerversion.Version,
96
+			Config:          c.Config,
97
+			Architecture:    runtime.GOARCH,
98
+			OS:              runtime.GOOS,
99
+			Container:       container.ID,
100
+			ContainerConfig: *container.Config,
101
+			Author:          c.Author,
102
+			Created:         h.Created,
103
+		},
104
+		RootFS:  rootFS,
105
+		History: history,
106
+	})
107
+
108
+	if err != nil {
109
+		return "", err
110
+	}
111
+
112
+	id, err := daemon.imageStore.Create(config)
113
+	if err != nil {
114
+		return "", err
115
+	}
116
+
117
+	if container.ImageID != "" {
118
+		if err := daemon.imageStore.SetParent(id, container.ImageID); err != nil {
119
+			return "", err
120
+		}
64 121
 	}
65 122
 
66
-	// Register the image if needed
67 123
 	if c.Repo != "" {
68
-		if err := daemon.repositories.Tag(c.Repo, c.Tag, img.ID, true); err != nil {
69
-			return img, err
124
+		newTag, err := reference.WithName(c.Repo) // todo: should move this to API layer
125
+		if err != nil {
126
+			return "", err
127
+		}
128
+		if c.Tag != "" {
129
+			if newTag, err = reference.WithTag(newTag, c.Tag); err != nil {
130
+				return "", err
131
+			}
132
+		}
133
+		if err := daemon.TagImage(newTag, id.String(), true); err != nil {
134
+			return "", err
70 135
 		}
71 136
 	}
72 137
 
73 138
 	daemon.LogContainerEvent(container, "commit")
74
-	return img, nil
139
+	return id.String(), nil
75 140
 }
76 141
 
77 142
 func (daemon *Daemon) exportContainerRw(container *Container) (archive.Archive, error) {
78
-	archive, err := daemon.diff(container)
143
+	if err := daemon.Mount(container); err != nil {
144
+		return nil, err
145
+	}
146
+
147
+	archive, err := container.rwlayer.TarStream()
79 148
 	if err != nil {
80 149
 		return nil, err
81 150
 	}
82 151
 	return ioutils.NewReadCloserWrapper(archive, func() error {
83
-			err := archive.Close()
84
-			return err
152
+			return daemon.layerStore.Unmount(container.ID)
85 153
 		}),
86 154
 		nil
87 155
 }
... ...
@@ -20,6 +20,8 @@ import (
20 20
 	"github.com/docker/docker/daemon/logger/jsonfilelog"
21 21
 	"github.com/docker/docker/daemon/network"
22 22
 	derr "github.com/docker/docker/errors"
23
+	"github.com/docker/docker/image"
24
+	"github.com/docker/docker/layer"
23 25
 	"github.com/docker/docker/pkg/nat"
24 26
 	"github.com/docker/docker/pkg/promise"
25 27
 	"github.com/docker/docker/pkg/signal"
... ...
@@ -29,6 +31,8 @@ import (
29 29
 	"github.com/docker/docker/volume"
30 30
 )
31 31
 
32
+const configFileName = "config.v2.json"
33
+
32 34
 var (
33 35
 	// ErrRootFSReadOnly is returned when a container
34 36
 	// rootfs is marked readonly.
... ...
@@ -43,12 +47,13 @@ type CommonContainer struct {
43 43
 	*State          `json:"State"` // Needed for remote api version <= 1.11
44 44
 	root            string         // Path to the "home" of the container, including metadata.
45 45
 	basefs          string         // Path to the graphdriver mountpoint
46
+	rwlayer         layer.RWLayer
46 47
 	ID              string
47 48
 	Created         time.Time
48 49
 	Path            string
49 50
 	Args            []string
50 51
 	Config          *runconfig.Config
51
-	ImageID         string `json:"Image"`
52
+	ImageID         image.ID `json:"Image"`
52 53
 	NetworkSettings *network.Settings
53 54
 	LogPath         string
54 55
 	Name            string
... ...
@@ -256,7 +261,7 @@ func (container *Container) hostConfigPath() (string, error) {
256 256
 }
257 257
 
258 258
 func (container *Container) jsonPath() (string, error) {
259
-	return container.getRootResourcePath("config.json")
259
+	return container.getRootResourcePath(configFileName)
260 260
 }
261 261
 
262 262
 // This directory is only usable when the container is running
... ...
@@ -301,7 +306,7 @@ func (container *Container) StartLogger(cfg runconfig.LogConfig) (logger.Logger,
301 301
 		ContainerName:       container.Name,
302 302
 		ContainerEntrypoint: container.Path,
303 303
 		ContainerArgs:       container.Args,
304
-		ContainerImageID:    container.ImageID,
304
+		ContainerImageID:    container.ImageID.String(),
305 305
 		ContainerImageName:  container.Config.Image,
306 306
 		ContainerCreated:    container.Created,
307 307
 		ContainerEnv:        container.Config.Env,
... ...
@@ -99,7 +99,7 @@ func TestContainerInitDNS(t *testing.T) {
99 99
 "Name":"/ubuntu","Driver":"aufs","MountLabel":"","ProcessLabel":"","AppArmorProfile":"","RestartCount":0,
100 100
 "UpdateDns":false,"Volumes":{},"VolumesRW":{},"AppliedVolumesFrom":null}`
101 101
 
102
-	if err = ioutil.WriteFile(filepath.Join(containerPath, "config.json"), []byte(config), 0644); err != nil {
102
+	if err = ioutil.WriteFile(filepath.Join(containerPath, configFileName), []byte(config), 0644); err != nil {
103 103
 		t.Fatal(err)
104 104
 	}
105 105
 
... ...
@@ -19,7 +19,6 @@ import (
19 19
 	"github.com/docker/docker/daemon/links"
20 20
 	"github.com/docker/docker/daemon/network"
21 21
 	derr "github.com/docker/docker/errors"
22
-	"github.com/docker/docker/pkg/directory"
23 22
 	"github.com/docker/docker/pkg/fileutils"
24 23
 	"github.com/docker/docker/pkg/idtools"
25 24
 	"github.com/docker/docker/pkg/mount"
... ...
@@ -388,8 +387,7 @@ func (daemon *Daemon) getSize(container *Container) (int64, int64) {
388 388
 	}
389 389
 	defer daemon.Unmount(container)
390 390
 
391
-	initID := fmt.Sprintf("%s-init", container.ID)
392
-	sizeRw, err = daemon.driver.DiffSize(container.ID, initID)
391
+	sizeRw, err = container.rwlayer.Size()
393 392
 	if err != nil {
394 393
 		logrus.Errorf("Driver %s couldn't return diff size of container %s: %s", daemon.driver, container.ID, err)
395 394
 		// FIXME: GetSize should return an error. Not changing it now in case
... ...
@@ -397,9 +395,12 @@ func (daemon *Daemon) getSize(container *Container) (int64, int64) {
397 397
 		sizeRw = -1
398 398
 	}
399 399
 
400
-	if _, err = os.Stat(container.basefs); err == nil {
401
-		if sizeRootfs, err = directory.Size(container.basefs); err != nil {
400
+	if parent := container.rwlayer.Parent(); parent != nil {
401
+		sizeRootfs, err = parent.Size()
402
+		if err != nil {
402 403
 			sizeRootfs = -1
404
+		} else if sizeRw != -1 {
405
+			sizeRootfs += sizeRw
403 406
 		}
404 407
 	}
405 408
 	return sizeRw, sizeRootfs
... ...
@@ -7,6 +7,7 @@ import (
7 7
 
8 8
 	"github.com/docker/docker/daemon/execdriver"
9 9
 	derr "github.com/docker/docker/errors"
10
+	"github.com/docker/docker/layer"
10 11
 	"github.com/docker/docker/volume"
11 12
 	"github.com/docker/libnetwork"
12 13
 )
... ...
@@ -98,22 +99,25 @@ func (daemon *Daemon) populateCommand(c *Container, env []string) error {
98 98
 	processConfig.Env = env
99 99
 
100 100
 	var layerPaths []string
101
-	img, err := daemon.graph.Get(c.ImageID)
101
+	img, err := daemon.imageStore.Get(c.ImageID)
102 102
 	if err != nil {
103 103
 		return derr.ErrorCodeGetGraph.WithArgs(c.ImageID, err)
104 104
 	}
105
-	for i := img; i != nil && err == nil; i, err = daemon.graph.GetParent(i) {
106
-		lp, err := daemon.driver.Get(i.ID, "")
107
-		if err != nil {
108
-			return derr.ErrorCodeGetLayer.WithArgs(daemon.driver.String(), i.ID, err)
109
-		}
110
-		layerPaths = append(layerPaths, lp)
111
-		err = daemon.driver.Put(i.ID)
112
-		if err != nil {
113
-			return derr.ErrorCodePutLayer.WithArgs(daemon.driver.String(), i.ID, err)
105
+
106
+	if img.RootFS != nil && img.RootFS.Type == "layers+base" {
107
+		max := len(img.RootFS.DiffIDs)
108
+		for i := 0; i <= max; i++ {
109
+			img.RootFS.DiffIDs = img.RootFS.DiffIDs[:i]
110
+			path, err := layer.GetLayerPath(daemon.layerStore, img.RootFS.ChainID())
111
+			if err != nil {
112
+				return derr.ErrorCodeGetLayer.WithArgs(err)
113
+			}
114
+			// Reverse order, expecting parent most first
115
+			layerPaths = append([]string{path}, layerPaths...)
114 116
 		}
115 117
 	}
116
-	m, err := daemon.driver.GetMetadata(c.ID)
118
+
119
+	m, err := layer.RWLayerMetadata(daemon.layerStore, c.ID)
117 120
 	if err != nil {
118 121
 		return derr.ErrorCodeGetLayerMetadata.WithArgs(err)
119 122
 	}
... ...
@@ -5,6 +5,7 @@ import (
5 5
 	"github.com/docker/docker/api/types"
6 6
 	derr "github.com/docker/docker/errors"
7 7
 	"github.com/docker/docker/image"
8
+	"github.com/docker/docker/pkg/idtools"
8 9
 	"github.com/docker/docker/pkg/stringid"
9 10
 	"github.com/docker/docker/runconfig"
10 11
 	"github.com/docker/docker/volume"
... ...
@@ -34,7 +35,7 @@ func (daemon *Daemon) ContainerCreate(params *ContainerCreateConfig) (types.Cont
34 34
 
35 35
 	container, err := daemon.create(params)
36 36
 	if err != nil {
37
-		return types.ContainerCreateResponse{ID: "", Warnings: warnings}, daemon.graphNotExistToErrcode(params.Config.Image, err)
37
+		return types.ContainerCreateResponse{ID: "", Warnings: warnings}, daemon.imageNotExistToErrcode(err)
38 38
 	}
39 39
 
40 40
 	return types.ContainerCreateResponse{ID: container.ID, Warnings: warnings}, nil
... ...
@@ -45,19 +46,16 @@ func (daemon *Daemon) create(params *ContainerCreateConfig) (retC *Container, re
45 45
 	var (
46 46
 		container *Container
47 47
 		img       *image.Image
48
-		imgID     string
48
+		imgID     image.ID
49 49
 		err       error
50 50
 	)
51 51
 
52 52
 	if params.Config.Image != "" {
53
-		img, err = daemon.repositories.LookupImage(params.Config.Image)
53
+		img, err = daemon.GetImage(params.Config.Image)
54 54
 		if err != nil {
55 55
 			return nil, err
56 56
 		}
57
-		if err = daemon.graph.CheckDepth(img); err != nil {
58
-			return nil, err
59
-		}
60
-		imgID = img.ID
57
+		imgID = img.ID()
61 58
 	}
62 59
 
63 60
 	if err := daemon.mergeAndVerifyConfig(params.Config, img); err != nil {
... ...
@@ -87,15 +85,14 @@ func (daemon *Daemon) create(params *ContainerCreateConfig) (retC *Container, re
87 87
 	if err := daemon.Register(container); err != nil {
88 88
 		return nil, err
89 89
 	}
90
-	container.Lock()
91
-	if err := parseSecurityOpt(container, params.HostConfig); err != nil {
92
-		container.Unlock()
90
+	rootUID, rootGID, err := idtools.GetRootUIDGID(daemon.uidMaps, daemon.gidMaps)
91
+	if err != nil {
93 92
 		return nil, err
94 93
 	}
95
-	container.Unlock()
96
-	if err := daemon.createRootfs(container); err != nil {
94
+	if err := idtools.MkdirAs(container.root, 0700, rootUID, rootGID); err != nil {
97 95
 		return nil, err
98 96
 	}
97
+
99 98
 	if err := daemon.setHostConfig(container, params.HostConfig); err != nil {
100 99
 		return nil, err
101 100
 	}
... ...
@@ -18,6 +18,8 @@ import (
18 18
 	"time"
19 19
 
20 20
 	"github.com/Sirupsen/logrus"
21
+	"github.com/docker/distribution/digest"
22
+	"github.com/docker/distribution/reference"
21 23
 	"github.com/docker/docker/api"
22 24
 	"github.com/docker/docker/api/types"
23 25
 	"github.com/docker/docker/cliconfig"
... ...
@@ -29,9 +31,13 @@ import (
29 29
 	_ "github.com/docker/docker/daemon/graphdriver/vfs" // register vfs
30 30
 	"github.com/docker/docker/daemon/logger"
31 31
 	"github.com/docker/docker/daemon/network"
32
+	"github.com/docker/docker/distribution"
33
+	dmetadata "github.com/docker/docker/distribution/metadata"
32 34
 	derr "github.com/docker/docker/errors"
33
-	"github.com/docker/docker/graph"
34 35
 	"github.com/docker/docker/image"
36
+	"github.com/docker/docker/image/tarexport"
37
+	"github.com/docker/docker/layer"
38
+	"github.com/docker/docker/migrate/v1"
35 39
 	"github.com/docker/docker/pkg/archive"
36 40
 	"github.com/docker/docker/pkg/discovery"
37 41
 	"github.com/docker/docker/pkg/fileutils"
... ...
@@ -50,12 +56,14 @@ import (
50 50
 	"github.com/docker/docker/pkg/truncindex"
51 51
 	"github.com/docker/docker/registry"
52 52
 	"github.com/docker/docker/runconfig"
53
+	"github.com/docker/docker/tag"
53 54
 	"github.com/docker/docker/utils"
54 55
 	volumedrivers "github.com/docker/docker/volume/drivers"
55 56
 	"github.com/docker/docker/volume/local"
56 57
 	"github.com/docker/docker/volume/store"
57 58
 	"github.com/docker/libnetwork"
58 59
 	lntypes "github.com/docker/libnetwork/types"
60
+	"github.com/docker/libtrust"
59 61
 	"github.com/opencontainers/runc/libcontainer"
60 62
 )
61 63
 
... ...
@@ -66,6 +74,15 @@ var (
66 66
 	errSystemNotSupported = errors.New("The Docker daemon is not supported on this platform.")
67 67
 )
68 68
 
69
+// ErrImageDoesNotExist is error returned when no image can be found for a reference.
70
+type ErrImageDoesNotExist struct {
71
+	RefOrID string
72
+}
73
+
74
+func (e ErrImageDoesNotExist) Error() string {
75
+	return fmt.Sprintf("no such id: %s", e.RefOrID)
76
+}
77
+
69 78
 type contStore struct {
70 79
 	s map[string]*Container
71 80
 	sync.Mutex
... ...
@@ -103,29 +120,33 @@ func (c *contStore) List() []*Container {
103 103
 
104 104
 // Daemon holds information about the Docker daemon.
105 105
 type Daemon struct {
106
-	ID               string
107
-	repository       string
108
-	sysInitPath      string
109
-	containers       *contStore
110
-	execCommands     *exec.Store
111
-	graph            *graph.Graph
112
-	repositories     *graph.TagStore
113
-	idIndex          *truncindex.TruncIndex
114
-	configStore      *Config
115
-	containerGraphDB *graphdb.Database
116
-	driver           graphdriver.Driver
117
-	execDriver       execdriver.Driver
118
-	statsCollector   *statsCollector
119
-	defaultLogConfig runconfig.LogConfig
120
-	RegistryService  *registry.Service
121
-	EventsService    *events.Events
122
-	netController    libnetwork.NetworkController
123
-	volumes          *store.VolumeStore
124
-	discoveryWatcher discovery.Watcher
125
-	root             string
126
-	shutdown         bool
127
-	uidMaps          []idtools.IDMap
128
-	gidMaps          []idtools.IDMap
106
+	ID                        string
107
+	repository                string
108
+	sysInitPath               string
109
+	containers                *contStore
110
+	execCommands              *exec.Store
111
+	tagStore                  tag.Store
112
+	distributionPool          *distribution.Pool
113
+	distributionMetadataStore dmetadata.Store
114
+	trustKey                  libtrust.PrivateKey
115
+	idIndex                   *truncindex.TruncIndex
116
+	configStore               *Config
117
+	containerGraphDB          *graphdb.Database
118
+	driver                    graphdriver.Driver
119
+	execDriver                execdriver.Driver
120
+	statsCollector            *statsCollector
121
+	defaultLogConfig          runconfig.LogConfig
122
+	RegistryService           *registry.Service
123
+	EventsService             *events.Events
124
+	netController             libnetwork.NetworkController
125
+	volumes                   *store.VolumeStore
126
+	discoveryWatcher          discovery.Watcher
127
+	root                      string
128
+	shutdown                  bool
129
+	uidMaps                   []idtools.IDMap
130
+	gidMaps                   []idtools.IDMap
131
+	layerStore                layer.Store
132
+	imageStore                image.Store
129 133
 }
130 134
 
131 135
 // Get looks for a container using the provided information, which could be
... ...
@@ -229,9 +250,7 @@ func (daemon *Daemon) Register(container *Container) error {
229 229
 
230 230
 		container.unmountIpcMounts(mount.Unmount)
231 231
 
232
-		if err := daemon.Unmount(container); err != nil {
233
-			logrus.Debugf("unmount error %s", err)
234
-		}
232
+		daemon.Unmount(container)
235 233
 		if err := container.toDiskLocking(); err != nil {
236 234
 			logrus.Errorf("Error saving stopped state to disk: %v", err)
237 235
 		}
... ...
@@ -456,7 +475,7 @@ func (daemon *Daemon) getEntrypointAndArgs(configEntrypoint *stringutils.StrSlic
456 456
 	return cmdSlice[0], cmdSlice[1:]
457 457
 }
458 458
 
459
-func (daemon *Daemon) newContainer(name string, config *runconfig.Config, imgID string) (*Container, error) {
459
+func (daemon *Daemon) newContainer(name string, config *runconfig.Config, imgID image.ID) (*Container, error) {
460 460
 	var (
461 461
 		id             string
462 462
 		err            error
... ...
@@ -542,7 +561,7 @@ func (daemon *Daemon) GetLabels(id string) map[string]string {
542 542
 		return container.Config.Labels
543 543
 	}
544 544
 
545
-	img, err := daemon.repositories.LookupImage(id)
545
+	img, err := daemon.GetImage(id)
546 546
 	if err == nil {
547 547
 		return img.ContainerConfig.Labels
548 548
 	}
... ...
@@ -702,8 +721,25 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo
702 702
 		return nil, err
703 703
 	}
704 704
 
705
-	logrus.Debug("Creating images graph")
706
-	g, err := graph.NewGraph(filepath.Join(config.Root, "graph"), d.driver, uidMaps, gidMaps)
705
+	imageRoot := filepath.Join(config.Root, "image", d.driver.String())
706
+	fms, err := layer.NewFSMetadataStore(filepath.Join(imageRoot, "layerdb"))
707
+	if err != nil {
708
+		return nil, err
709
+	}
710
+
711
+	d.layerStore, err = layer.NewStore(fms, d.driver)
712
+	if err != nil {
713
+		return nil, err
714
+	}
715
+
716
+	distributionPool := distribution.NewPool()
717
+
718
+	ifs, err := image.NewFSStoreBackend(filepath.Join(imageRoot, "imagedb"))
719
+	if err != nil {
720
+		return nil, err
721
+	}
722
+
723
+	d.imageStore, err = image.NewImageStore(ifs, d.layerStore)
707 724
 	if err != nil {
708 725
 		return nil, err
709 726
 	}
... ...
@@ -725,23 +761,24 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo
725 725
 		return nil, err
726 726
 	}
727 727
 
728
+	distributionMetadataStore, err := dmetadata.NewFSMetadataStore(filepath.Join(imageRoot, "distribution"))
729
+	if err != nil {
730
+		return nil, err
731
+	}
732
+
728 733
 	eventsService := events.New()
729
-	logrus.Debug("Creating repository list")
730
-	tagCfg := &graph.TagStoreConfig{
731
-		Graph:    g,
732
-		Key:      trustKey,
733
-		Registry: registryService,
734
-		Events:   eventsService,
735
-	}
736
-	repositories, err := graph.NewTagStore(filepath.Join(config.Root, "repositories-"+d.driver.String()), tagCfg)
734
+
735
+	tagStore, err := tag.NewTagStore(filepath.Join(imageRoot, "repositories.json"))
737 736
 	if err != nil {
738
-		return nil, fmt.Errorf("Couldn't create Tag store repositories-%s: %s", d.driver.String(), err)
737
+		return nil, fmt.Errorf("Couldn't create Tag store repositories: %s", err)
739 738
 	}
740 739
 
741
-	if restorer, ok := d.driver.(graphdriver.ImageRestorer); ok {
742
-		if _, err := restorer.RestoreCustomImages(repositories, g); err != nil {
743
-			return nil, fmt.Errorf("Couldn't restore custom images: %s", err)
744
-		}
740
+	if err := restoreCustomImage(d.driver, d.imageStore, d.layerStore, tagStore); err != nil {
741
+		return nil, fmt.Errorf("Couldn't restore custom images: %s", err)
742
+	}
743
+
744
+	if err := v1.Migrate(config.Root, d.driver.String(), d.layerStore, d.imageStore, tagStore, distributionMetadataStore); err != nil {
745
+		return nil, err
745 746
 	}
746 747
 
747 748
 	// Discovery is only enabled when the daemon is launched with an address to advertise.  When
... ...
@@ -792,8 +829,10 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo
792 792
 	d.repository = daemonRepo
793 793
 	d.containers = &contStore{s: make(map[string]*Container)}
794 794
 	d.execCommands = exec.NewStore()
795
-	d.graph = g
796
-	d.repositories = repositories
795
+	d.tagStore = tagStore
796
+	d.distributionPool = distributionPool
797
+	d.distributionMetadataStore = distributionMetadataStore
798
+	d.trustKey = trustKey
797 799
 	d.idIndex = truncindex.NewTruncIndex([]string{})
798 800
 	d.configStore = config
799 801
 	d.sysInitPath = sysInitPath
... ...
@@ -910,28 +949,44 @@ func (daemon *Daemon) Shutdown() error {
910 910
 // Mount sets container.basefs
911 911
 // (is it not set coming in? why is it unset?)
912 912
 func (daemon *Daemon) Mount(container *Container) error {
913
-	dir, err := daemon.driver.Get(container.ID, container.getMountLabel())
913
+	var layerID layer.ChainID
914
+	if container.ImageID != "" {
915
+		img, err := daemon.imageStore.Get(container.ImageID)
916
+		if err != nil {
917
+			return err
918
+		}
919
+		layerID = img.RootFS.ChainID()
920
+	}
921
+	rwlayer, err := daemon.layerStore.Mount(container.ID, layerID, container.getMountLabel(), daemon.setupInitLayer)
914 922
 	if err != nil {
915
-		return fmt.Errorf("Error getting container %s from driver %s: %s", container.ID, daemon.driver, err)
923
+		return err
924
+	}
925
+	dir, err := rwlayer.Path()
926
+	if err != nil {
927
+		return err
916 928
 	}
929
+	logrus.Debugf("container mounted via layerStore: %v", dir)
917 930
 
918 931
 	if container.basefs != dir {
919 932
 		// The mount path reported by the graph driver should always be trusted on Windows, since the
920 933
 		// volume path for a given mounted layer may change over time.  This should only be an error
921 934
 		// on non-Windows operating systems.
922 935
 		if container.basefs != "" && runtime.GOOS != "windows" {
923
-			daemon.driver.Put(container.ID)
936
+			daemon.Unmount(container)
924 937
 			return fmt.Errorf("Error: driver %s is returning inconsistent paths for container %s ('%s' then '%s')",
925 938
 				daemon.driver, container.ID, container.basefs, dir)
926 939
 		}
927 940
 	}
928
-	container.basefs = dir
941
+	container.basefs = dir // TODO: combine these fields
942
+	container.rwlayer = rwlayer
929 943
 	return nil
930 944
 }
931 945
 
932 946
 // Unmount unsets the container base filesystem
933
-func (daemon *Daemon) Unmount(container *Container) error {
934
-	return daemon.driver.Put(container.ID)
947
+func (daemon *Daemon) Unmount(container *Container) {
948
+	if err := daemon.layerStore.Unmount(container.ID); err != nil {
949
+		logrus.Errorf("Error unmounting container %s: %s", container.ID, err)
950
+	}
935 951
 }
936 952
 
937 953
 // Run uses the execution driver to run a given container
... ...
@@ -962,82 +1017,46 @@ func (daemon *Daemon) unsubscribeToContainerStats(c *Container, ch chan interfac
962 962
 }
963 963
 
964 964
 func (daemon *Daemon) changes(container *Container) ([]archive.Change, error) {
965
-	initID := fmt.Sprintf("%s-init", container.ID)
966
-	return daemon.driver.Changes(container.ID, initID)
967
-}
968
-
969
-func (daemon *Daemon) diff(container *Container) (archive.Archive, error) {
970
-	initID := fmt.Sprintf("%s-init", container.ID)
971
-	return daemon.driver.Diff(container.ID, initID)
965
+	return daemon.layerStore.Changes(container.ID)
972 966
 }
973 967
 
974
-func (daemon *Daemon) createRootfs(container *Container) error {
975
-	// Step 1: create the container directory.
976
-	// This doubles as a barrier to avoid race conditions.
977
-	rootUID, rootGID, err := idtools.GetRootUIDGID(daemon.uidMaps, daemon.gidMaps)
978
-	if err != nil {
979
-		return err
968
+// TagImage creates a tag in the repository reponame, pointing to the image named
969
+// imageName. If force is true, an existing tag with the same name may be
970
+// overwritten.
971
+func (daemon *Daemon) TagImage(newTag reference.Named, imageName string, force bool) error {
972
+	if _, isDigested := newTag.(reference.Digested); isDigested {
973
+		return errors.New("refusing to create a tag with a digest reference")
980 974
 	}
981
-	if err := idtools.MkdirAs(container.root, 0700, rootUID, rootGID); err != nil {
982
-		return err
975
+	if newTag.Name() == string(digest.Canonical) {
976
+		return errors.New("refusing to create an ambiguous tag using digest algorithm as name")
983 977
 	}
984
-	initID := fmt.Sprintf("%s-init", container.ID)
985 978
 
986
-	if err := daemon.driver.Create(initID, container.ImageID, container.getMountLabel()); err != nil {
987
-		return err
988
-	}
989
-	initPath, err := daemon.driver.Get(initID, "")
979
+	newTag = registry.NormalizeLocalReference(newTag)
980
+	imageID, err := daemon.GetImageID(imageName)
990 981
 	if err != nil {
991 982
 		return err
992 983
 	}
993
-
994
-	if err := setupInitLayer(initPath, rootUID, rootGID); err != nil {
995
-		if err := daemon.driver.Put(initID); err != nil {
996
-			logrus.Errorf("Failed to Put init layer: %v", err)
997
-		}
998
-		return err
999
-	}
1000
-
1001
-	// We want to unmount init layer before we take snapshot of it
1002
-	// for the actual container.
1003
-	if err := daemon.driver.Put(initID); err != nil {
1004
-		return err
1005
-	}
1006
-
1007
-	if err := daemon.driver.Create(container.ID, initID, ""); err != nil {
1008
-		return err
1009
-	}
1010
-	return nil
1011
-}
1012
-
1013
-// Graph returns *graph.Graph which can be using for layers graph operations.
1014
-func (daemon *Daemon) Graph() *graph.Graph {
1015
-	return daemon.graph
1016
-}
1017
-
1018
-// TagImage creates a tag in the repository reponame, pointing to the image named
1019
-// imageName. If force is true, an existing tag with the same name may be
1020
-// overwritten.
1021
-func (daemon *Daemon) TagImage(repoName, tag, imageName string, force bool) error {
1022
-	if err := daemon.repositories.Tag(repoName, tag, imageName, force); err != nil {
1023
-		return err
1024
-	}
1025
-	daemon.EventsService.Log("tag", utils.ImageReference(repoName, tag), "")
1026
-	return nil
984
+	daemon.EventsService.Log("tag", newTag.String(), "")
985
+	return daemon.tagStore.Add(newTag, imageID, force)
1027 986
 }
1028 987
 
1029 988
 // PullImage initiates a pull operation. image is the repository name to pull, and
1030 989
 // tag may be either empty, or indicate a specific tag to pull.
1031
-func (daemon *Daemon) PullImage(image string, tag string, imagePullConfig *graph.ImagePullConfig) error {
1032
-	return daemon.repositories.Pull(image, tag, imagePullConfig)
1033
-}
990
+func (daemon *Daemon) PullImage(ref reference.Named, metaHeaders map[string][]string, authConfig *cliconfig.AuthConfig, outStream io.Writer) error {
991
+	imagePullConfig := &distribution.ImagePullConfig{
992
+		MetaHeaders:     metaHeaders,
993
+		AuthConfig:      authConfig,
994
+		OutStream:       outStream,
995
+		RegistryService: daemon.RegistryService,
996
+		EventsService:   daemon.EventsService,
997
+		MetadataStore:   daemon.distributionMetadataStore,
998
+		LayerStore:      daemon.layerStore,
999
+		ImageStore:      daemon.imageStore,
1000
+		TagStore:        daemon.tagStore,
1001
+		Pool:            daemon.distributionPool,
1002
+	}
1034 1003
 
1035
-// ImportImage imports an image, getting the archived layer data either from
1036
-// inConfig (if src is "-"), or from a URI specified in src. Progress output is
1037
-// written to outStream. Repository and tag names can optionally be given in
1038
-// the repo and tag arguments, respectively.
1039
-func (daemon *Daemon) ImportImage(src, repo, tag, msg string, inConfig io.ReadCloser, outStream io.Writer, containerConfig *runconfig.Config) error {
1040
-	return daemon.repositories.Import(src, repo, tag, msg, inConfig, outStream, containerConfig)
1004
+	return distribution.Pull(ref, imagePullConfig)
1041 1005
 }
1042 1006
 
1043 1007
 // ExportImage exports a list of images to the given output stream. The
... ...
@@ -1046,47 +1065,214 @@ func (daemon *Daemon) ImportImage(src, repo, tag, msg string, inConfig io.ReadCl
1046 1046
 // the same tag are exported. names is the set of tags to export, and
1047 1047
 // outStream is the writer which the images are written to.
1048 1048
 func (daemon *Daemon) ExportImage(names []string, outStream io.Writer) error {
1049
-	return daemon.repositories.ImageExport(names, outStream)
1049
+	imageExporter := tarexport.NewTarExporter(daemon.imageStore, daemon.layerStore, daemon.tagStore)
1050
+	return imageExporter.Save(names, outStream)
1050 1051
 }
1051 1052
 
1052 1053
 // PushImage initiates a push operation on the repository named localName.
1053
-func (daemon *Daemon) PushImage(localName string, imagePushConfig *graph.ImagePushConfig) error {
1054
-	return daemon.repositories.Push(localName, imagePushConfig)
1054
+func (daemon *Daemon) PushImage(ref reference.Named, metaHeaders map[string][]string, authConfig *cliconfig.AuthConfig, outStream io.Writer) error {
1055
+	imagePushConfig := &distribution.ImagePushConfig{
1056
+		MetaHeaders:     metaHeaders,
1057
+		AuthConfig:      authConfig,
1058
+		OutStream:       outStream,
1059
+		RegistryService: daemon.RegistryService,
1060
+		EventsService:   daemon.EventsService,
1061
+		MetadataStore:   daemon.distributionMetadataStore,
1062
+		LayerStore:      daemon.layerStore,
1063
+		ImageStore:      daemon.imageStore,
1064
+		TagStore:        daemon.tagStore,
1065
+		TrustKey:        daemon.trustKey,
1066
+	}
1067
+
1068
+	return distribution.Push(ref, imagePushConfig)
1055 1069
 }
1056 1070
 
1057 1071
 // LookupImage looks up an image by name and returns it as an ImageInspect
1058 1072
 // structure.
1059 1073
 func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) {
1060
-	return daemon.repositories.Lookup(name)
1074
+	img, err := daemon.GetImage(name)
1075
+	if err != nil {
1076
+		return nil, fmt.Errorf("No such image: %s", name)
1077
+	}
1078
+
1079
+	refs := daemon.tagStore.References(img.ID())
1080
+	repoTags := []string{}
1081
+	repoDigests := []string{}
1082
+	for _, ref := range refs {
1083
+		switch ref.(type) {
1084
+		case reference.Tagged:
1085
+			repoTags = append(repoTags, ref.String())
1086
+		case reference.Digested:
1087
+			repoDigests = append(repoDigests, ref.String())
1088
+		}
1089
+	}
1090
+
1091
+	var size int64
1092
+	var layerMetadata map[string]string
1093
+	layerID := img.RootFS.ChainID()
1094
+	if layerID != "" {
1095
+		l, err := daemon.layerStore.Get(layerID)
1096
+		if err != nil {
1097
+			return nil, err
1098
+		}
1099
+		defer layer.ReleaseAndLog(daemon.layerStore, l)
1100
+		size, err = l.Size()
1101
+		if err != nil {
1102
+			return nil, err
1103
+		}
1104
+
1105
+		layerMetadata, err = l.Metadata()
1106
+		if err != nil {
1107
+			return nil, err
1108
+		}
1109
+	}
1110
+
1111
+	imageInspect := &types.ImageInspect{
1112
+		ID:              img.ID().String(),
1113
+		RepoTags:        repoTags,
1114
+		RepoDigests:     repoDigests,
1115
+		Parent:          img.Parent.String(),
1116
+		Comment:         img.Comment,
1117
+		Created:         img.Created.Format(time.RFC3339Nano),
1118
+		Container:       img.Container,
1119
+		ContainerConfig: &img.ContainerConfig,
1120
+		DockerVersion:   img.DockerVersion,
1121
+		Author:          img.Author,
1122
+		Config:          img.Config,
1123
+		Architecture:    img.Architecture,
1124
+		Os:              img.OS,
1125
+		Size:            size,
1126
+		VirtualSize:     size, // TODO: field unused, deprecate
1127
+	}
1128
+
1129
+	imageInspect.GraphDriver.Name = daemon.driver.String()
1130
+
1131
+	imageInspect.GraphDriver.Data = layerMetadata
1132
+
1133
+	return imageInspect, nil
1061 1134
 }
1062 1135
 
1063 1136
 // LoadImage uploads a set of images into the repository. This is the
1064 1137
 // complement of ImageExport.  The input stream is an uncompressed tar
1065 1138
 // ball containing images and metadata.
1066 1139
 func (daemon *Daemon) LoadImage(inTar io.ReadCloser, outStream io.Writer) error {
1067
-	return daemon.repositories.Load(inTar, outStream)
1068
-}
1069
-
1070
-// ListImages returns a filtered list of images. filterArgs is a JSON-encoded set
1071
-// of filter arguments which will be interpreted by pkg/parsers/filters.
1072
-// filter is a shell glob string applied to repository names. The argument
1073
-// named all controls whether all images in the graph are filtered, or just
1074
-// the heads.
1075
-func (daemon *Daemon) ListImages(filterArgs, filter string, all bool) ([]*types.Image, error) {
1076
-	return daemon.repositories.Images(filterArgs, filter, all)
1140
+	imageExporter := tarexport.NewTarExporter(daemon.imageStore, daemon.layerStore, daemon.tagStore)
1141
+	return imageExporter.Load(inTar, outStream)
1077 1142
 }
1078 1143
 
1079 1144
 // ImageHistory returns a slice of ImageHistory structures for the specified image
1080 1145
 // name by walking the image lineage.
1081 1146
 func (daemon *Daemon) ImageHistory(name string) ([]*types.ImageHistory, error) {
1082
-	return daemon.repositories.History(name)
1147
+	img, err := daemon.GetImage(name)
1148
+	if err != nil {
1149
+		return nil, err
1150
+	}
1151
+
1152
+	history := []*types.ImageHistory{}
1153
+
1154
+	layerCounter := 0
1155
+	rootFS := *img.RootFS
1156
+	rootFS.DiffIDs = nil
1157
+
1158
+	for _, h := range img.History {
1159
+		var layerSize int64
1160
+
1161
+		if !h.EmptyLayer {
1162
+			if len(img.RootFS.DiffIDs) <= layerCounter {
1163
+				return nil, errors.New("too many non-empty layers in History section")
1164
+			}
1165
+
1166
+			rootFS.Append(img.RootFS.DiffIDs[layerCounter])
1167
+			l, err := daemon.layerStore.Get(rootFS.ChainID())
1168
+			if err != nil {
1169
+				return nil, err
1170
+			}
1171
+			layerSize, err = l.DiffSize()
1172
+			layer.ReleaseAndLog(daemon.layerStore, l)
1173
+			if err != nil {
1174
+				return nil, err
1175
+			}
1176
+
1177
+			layerCounter++
1178
+		}
1179
+
1180
+		history = append([]*types.ImageHistory{{
1181
+			ID:        "<missing>",
1182
+			Created:   h.Created.Unix(),
1183
+			CreatedBy: h.CreatedBy,
1184
+			Comment:   h.Comment,
1185
+			Size:      layerSize,
1186
+		}}, history...)
1187
+	}
1188
+
1189
+	// Fill in image IDs and tags
1190
+	histImg := img
1191
+	id := img.ID()
1192
+	for _, h := range history {
1193
+		h.ID = id.String()
1194
+
1195
+		var tags []string
1196
+		for _, r := range daemon.tagStore.References(id) {
1197
+			if _, ok := r.(reference.NamedTagged); ok {
1198
+				tags = append(tags, r.String())
1199
+			}
1200
+		}
1201
+
1202
+		h.Tags = tags
1203
+
1204
+		id = histImg.Parent
1205
+		if id == "" {
1206
+			break
1207
+		}
1208
+		histImg, err = daemon.GetImage(id.String())
1209
+		if err != nil {
1210
+			break
1211
+		}
1212
+	}
1213
+
1214
+	return history, nil
1083 1215
 }
1084 1216
 
1085
-// GetImage returns pointer to an Image struct corresponding to the given
1086
-// name. The name can include an optional tag; otherwise the default tag will
1087
-// be used.
1088
-func (daemon *Daemon) GetImage(name string) (*image.Image, error) {
1089
-	return daemon.repositories.LookupImage(name)
1217
+// GetImageID returns an image ID corresponding to the image referred to by
1218
+// refOrID.
1219
+func (daemon *Daemon) GetImageID(refOrID string) (image.ID, error) {
1220
+	// Treat as an ID
1221
+	if id, err := digest.ParseDigest(refOrID); err == nil {
1222
+		return image.ID(id), nil
1223
+	}
1224
+
1225
+	// Treat it as a possible tag or digest reference
1226
+	if ref, err := reference.ParseNamed(refOrID); err == nil {
1227
+		ref = registry.NormalizeLocalReference(ref)
1228
+		if id, err := daemon.tagStore.Get(ref); err == nil {
1229
+			return id, nil
1230
+		}
1231
+		if tagged, ok := ref.(reference.Tagged); ok {
1232
+			if id, err := daemon.imageStore.Search(tagged.Tag()); err == nil {
1233
+				for _, namedRef := range daemon.tagStore.References(id) {
1234
+					if namedRef.Name() == ref.Name() {
1235
+						return id, nil
1236
+					}
1237
+				}
1238
+			}
1239
+		}
1240
+	}
1241
+
1242
+	// Search based on ID
1243
+	if id, err := daemon.imageStore.Search(refOrID); err == nil {
1244
+		return id, nil
1245
+	}
1246
+
1247
+	return "", ErrImageDoesNotExist{refOrID}
1248
+}
1249
+
1250
+// GetImage returns an image corresponding to the image referred to by refOrID.
1251
+func (daemon *Daemon) GetImage(refOrID string) (*image.Image, error) {
1252
+	imgID, err := daemon.GetImageID(refOrID)
1253
+	if err != nil {
1254
+		return nil, err
1255
+	}
1256
+	return daemon.imageStore.Get(imgID)
1090 1257
 }
1091 1258
 
1092 1259
 func (daemon *Daemon) config() *Config {
... ...
@@ -1132,33 +1318,23 @@ func (daemon *Daemon) GetRemappedUIDGID() (int, int) {
1132 1132
 // of the image with imgID, that had the same config when it was
1133 1133
 // created. nil is returned if a child cannot be found. An error is
1134 1134
 // returned if the parent image cannot be found.
1135
-func (daemon *Daemon) ImageGetCached(imgID string, config *runconfig.Config) (*image.Image, error) {
1136
-	// for now just exit if imgID has no children.
1137
-	// maybe parentRefs in graph could be used to store
1138
-	// the Image obj children for faster lookup below but this can
1139
-	// be quite memory hungry.
1140
-	if !daemon.Graph().HasChildren(imgID) {
1141
-		return nil, nil
1142
-	}
1143
-
1135
+func (daemon *Daemon) ImageGetCached(imgID image.ID, config *runconfig.Config) (*image.Image, error) {
1144 1136
 	// Retrieve all images
1145
-	images := daemon.Graph().Map()
1137
+	imgs := daemon.Map()
1146 1138
 
1147
-	// Store the tree in a map of map (map[parentId][childId])
1148
-	imageMap := make(map[string]map[string]struct{})
1149
-	for _, img := range images {
1150
-		if _, exists := imageMap[img.Parent]; !exists {
1151
-			imageMap[img.Parent] = make(map[string]struct{})
1139
+	var siblings []image.ID
1140
+	for id, img := range imgs {
1141
+		if img.Parent == imgID {
1142
+			siblings = append(siblings, id)
1152 1143
 		}
1153
-		imageMap[img.Parent][img.ID] = struct{}{}
1154 1144
 	}
1155 1145
 
1156 1146
 	// Loop on the children of the given image and check the config
1157 1147
 	var match *image.Image
1158
-	for elem := range imageMap[imgID] {
1159
-		img, ok := images[elem]
1148
+	for _, id := range siblings {
1149
+		img, ok := imgs[id]
1160 1150
 		if !ok {
1161
-			return nil, fmt.Errorf("unable to find image %q", elem)
1151
+			return nil, fmt.Errorf("unable to find image %q", id)
1162 1152
 		}
1163 1153
 		if runconfig.Compare(&img.ContainerConfig, config) {
1164 1154
 			if match == nil || match.Created.Before(img.Created) {
... ...
@@ -1179,6 +1355,12 @@ func tempDir(rootDir string, rootUID, rootGID int) (string, error) {
1179 1179
 }
1180 1180
 
1181 1181
 func (daemon *Daemon) setHostConfig(container *Container, hostConfig *runconfig.HostConfig) error {
1182
+	container.Lock()
1183
+	if err := parseSecurityOpt(container, hostConfig); err != nil {
1184
+		container.Unlock()
1185
+		return err
1186
+	}
1187
+	container.Unlock()
1182 1188
 
1183 1189
 	// Do not lock while creating volumes since this could be calling out to external plugins
1184 1190
 	// Don't want to block other actions, like `docker ps` because we're waiting on an external plugin
... ...
@@ -1199,6 +1381,11 @@ func (daemon *Daemon) setHostConfig(container *Container, hostConfig *runconfig.
1199 1199
 	return nil
1200 1200
 }
1201 1201
 
1202
+func (daemon *Daemon) setupInitLayer(initPath string) error {
1203
+	rootUID, rootGID := daemon.GetRemappedUIDGID()
1204
+	return setupInitLayer(initPath, rootUID, rootGID)
1205
+}
1206
+
1202 1207
 func setDefaultMtu(config *Config) {
1203 1208
 	// do nothing if the config does not have the default 0 value.
1204 1209
 	if config.Mtu != 0 {
... ...
@@ -14,12 +14,15 @@ import (
14 14
 	"github.com/Sirupsen/logrus"
15 15
 	"github.com/docker/docker/daemon/graphdriver"
16 16
 	derr "github.com/docker/docker/errors"
17
+	"github.com/docker/docker/image"
18
+	"github.com/docker/docker/layer"
17 19
 	pblkiodev "github.com/docker/docker/pkg/blkiodev"
18 20
 	"github.com/docker/docker/pkg/idtools"
19 21
 	"github.com/docker/docker/pkg/parsers"
20 22
 	"github.com/docker/docker/pkg/parsers/kernel"
21 23
 	"github.com/docker/docker/pkg/sysinfo"
22 24
 	"github.com/docker/docker/runconfig"
25
+	"github.com/docker/docker/tag"
23 26
 	"github.com/docker/libnetwork"
24 27
 	nwconfig "github.com/docker/libnetwork/config"
25 28
 	"github.com/docker/libnetwork/drivers/bridge"
... ...
@@ -601,9 +604,7 @@ func (daemon *Daemon) conditionalMountOnStart(container *Container) error {
601 601
 // conditionalUnmountOnCleanup is a platform specific helper function called
602 602
 // during the cleanup of a container to unmount.
603 603
 func (daemon *Daemon) conditionalUnmountOnCleanup(container *Container) {
604
-	if err := daemon.Unmount(container); err != nil {
605
-		logrus.Errorf("%v: Failed to umount filesystem: %v", container.ID, err)
606
-	}
604
+	daemon.Unmount(container)
607 605
 }
608 606
 
609 607
 // getDefaultRouteMtu returns the MTU for the default route's interface.
... ...
@@ -624,3 +625,8 @@ func getDefaultRouteMtu() (int, error) {
624 624
 	}
625 625
 	return 0, errNoDefaultRoute
626 626
 }
627
+
628
+func restoreCustomImage(driver graphdriver.Driver, is image.Store, ls layer.Store, ts tag.Store) error {
629
+	// Unix has no custom images to register
630
+	return nil
631
+}
... ...
@@ -1,12 +1,22 @@
1 1
 package daemon
2 2
 
3 3
 import (
4
+	"encoding/json"
5
+	"errors"
4 6
 	"fmt"
7
+	"path/filepath"
8
+	"runtime"
9
+	"strings"
5 10
 
6 11
 	"github.com/Sirupsen/logrus"
12
+	"github.com/docker/distribution/reference"
7 13
 	"github.com/docker/docker/daemon/graphdriver"
14
+	"github.com/docker/docker/dockerversion"
15
+	"github.com/docker/docker/image"
16
+	"github.com/docker/docker/layer"
17
+	"github.com/docker/docker/tag"
8 18
 	// register the windows graph driver
9
-	_ "github.com/docker/docker/daemon/graphdriver/windows"
19
+	"github.com/docker/docker/daemon/graphdriver/windows"
10 20
 	"github.com/docker/docker/pkg/system"
11 21
 	"github.com/docker/docker/runconfig"
12 22
 	"github.com/docker/libnetwork"
... ...
@@ -128,8 +138,71 @@ func (daemon *Daemon) conditionalMountOnStart(container *Container) error {
128 128
 func (daemon *Daemon) conditionalUnmountOnCleanup(container *Container) {
129 129
 	// We do not unmount if a Hyper-V container
130 130
 	if !container.hostConfig.Isolation.IsHyperV() {
131
-		if err := daemon.Unmount(container); err != nil {
132
-			logrus.Errorf("%v: Failed to umount filesystem: %v", container.ID, err)
131
+		daemon.Unmount(container)
132
+	}
133
+}
134
+
135
+func restoreCustomImage(driver graphdriver.Driver, is image.Store, ls layer.Store, ts tag.Store) error {
136
+	if wd, ok := driver.(*windows.Driver); ok {
137
+		imageInfos, err := wd.GetCustomImageInfos()
138
+		if err != nil {
139
+			return err
133 140
 		}
141
+
142
+		// Convert imageData to valid image configuration
143
+		for i := range imageInfos {
144
+			name := strings.ToLower(imageInfos[i].Name)
145
+
146
+			type registrar interface {
147
+				RegisterDiffID(graphID string, size int64) (layer.Layer, error)
148
+			}
149
+			r, ok := ls.(registrar)
150
+			if !ok {
151
+				return errors.New("Layerstore doesn't support RegisterDiffID")
152
+			}
153
+			if _, err := r.RegisterDiffID(imageInfos[i].ID, imageInfos[i].Size); err != nil {
154
+				return err
155
+			}
156
+			// layer is intentionally not released
157
+
158
+			rootFS := image.NewRootFS()
159
+			rootFS.BaseLayer = filepath.Base(imageInfos[i].Path)
160
+
161
+			// Create history for base layer
162
+			config, err := json.Marshal(&image.Image{
163
+				V1Image: image.V1Image{
164
+					DockerVersion: dockerversion.Version,
165
+					Architecture:  runtime.GOARCH,
166
+					OS:            runtime.GOOS,
167
+					Created:       imageInfos[i].CreatedTime,
168
+				},
169
+				RootFS:  rootFS,
170
+				History: []image.History{},
171
+			})
172
+
173
+			named, err := reference.ParseNamed(name)
174
+			if err != nil {
175
+				return err
176
+			}
177
+
178
+			ref, err := reference.WithTag(named, imageInfos[i].Version)
179
+			if err != nil {
180
+				return err
181
+			}
182
+
183
+			id, err := is.Create(config)
184
+			if err != nil {
185
+				return err
186
+			}
187
+
188
+			if err := ts.Add(ref, id, true); err != nil {
189
+				return err
190
+			}
191
+
192
+			logrus.Debugf("Registered base layer %s as %s", ref, id)
193
+		}
194
+
134 195
 	}
196
+
197
+	return nil
135 198
 }
... ...
@@ -9,17 +9,16 @@ import (
9 9
 	"strings"
10 10
 
11 11
 	"github.com/Sirupsen/logrus"
12
+	"github.com/docker/distribution/reference"
12 13
 	"github.com/docker/docker/api"
13 14
 	"github.com/docker/docker/builder"
14 15
 	"github.com/docker/docker/cliconfig"
15 16
 	"github.com/docker/docker/daemon"
16
-	"github.com/docker/docker/graph"
17 17
 	"github.com/docker/docker/image"
18 18
 	"github.com/docker/docker/pkg/archive"
19 19
 	"github.com/docker/docker/pkg/httputils"
20 20
 	"github.com/docker/docker/pkg/idtools"
21 21
 	"github.com/docker/docker/pkg/ioutils"
22
-	"github.com/docker/docker/pkg/parsers"
23 22
 	"github.com/docker/docker/pkg/progressreader"
24 23
 	"github.com/docker/docker/pkg/urlutil"
25 24
 	"github.com/docker/docker/registry"
... ...
@@ -44,15 +43,24 @@ func (d Docker) LookupImage(name string) (*image.Image, error) {
44 44
 
45 45
 // Pull tells Docker to pull image referenced by `name`.
46 46
 func (d Docker) Pull(name string) (*image.Image, error) {
47
-	remote, tag := parsers.ParseRepositoryTag(name)
48
-	if tag == "" {
49
-		tag = "latest"
47
+	ref, err := reference.ParseNamed(name)
48
+	if err != nil {
49
+		return nil, err
50
+	}
51
+	switch ref.(type) {
52
+	case reference.Tagged:
53
+	case reference.Digested:
54
+	default:
55
+		ref, err = reference.WithTag(ref, "latest")
56
+		if err != nil {
57
+			return nil, err
58
+		}
50 59
 	}
51 60
 
52 61
 	pullRegistryAuth := &cliconfig.AuthConfig{}
53 62
 	if len(d.AuthConfigs) > 0 {
54 63
 		// The request came with a full auth config file, we prefer to use that
55
-		repoInfo, err := d.Daemon.RegistryService.ResolveRepository(remote)
64
+		repoInfo, err := d.Daemon.RegistryService.ResolveRepository(ref)
56 65
 		if err != nil {
57 66
 			return nil, err
58 67
 		}
... ...
@@ -64,12 +72,7 @@ func (d Docker) Pull(name string) (*image.Image, error) {
64 64
 		pullRegistryAuth = &resolvedConfig
65 65
 	}
66 66
 
67
-	imagePullConfig := &graph.ImagePullConfig{
68
-		AuthConfig: pullRegistryAuth,
69
-		OutStream:  ioutils.NopWriteCloser(d.OutOld),
70
-	}
71
-
72
-	if err := d.Daemon.PullImage(remote, tag, imagePullConfig); err != nil {
67
+	if err := d.Daemon.PullImage(ref, nil, pullRegistryAuth, ioutils.NopWriteCloser(d.OutOld)); err != nil {
73 68
 		return nil, err
74 69
 	}
75 70
 
... ...
@@ -106,18 +109,20 @@ func (d Docker) Remove(id string, cfg *daemon.ContainerRmConfig) error {
106 106
 }
107 107
 
108 108
 // Commit creates a new Docker image from an existing Docker container.
109
-func (d Docker) Commit(name string, cfg *daemon.ContainerCommitConfig) (*image.Image, error) {
109
+func (d Docker) Commit(name string, cfg *daemon.ContainerCommitConfig) (string, error) {
110 110
 	return d.Daemon.Commit(name, cfg)
111 111
 }
112 112
 
113 113
 // Retain retains an image avoiding it to be removed or overwritten until a corresponding Release() call.
114 114
 func (d Docker) Retain(sessionID, imgID string) {
115
-	d.Daemon.Graph().Retain(sessionID, imgID)
115
+	// FIXME: This will be solved with tags in client-side builder
116
+	//d.Daemon.Graph().Retain(sessionID, imgID)
116 117
 }
117 118
 
118 119
 // Release releases a list of images that were retained for the time of a build.
119 120
 func (d Docker) Release(sessionID string, activeImages []string) {
120
-	d.Daemon.Graph().Release(sessionID, activeImages...)
121
+	// FIXME: This will be solved with tags in client-side builder
122
+	//d.Daemon.Graph().Release(sessionID, activeImages...)
121 123
 }
122 124
 
123 125
 // Copy copies/extracts a source FileInfo to a destination path inside a container
... ...
@@ -199,11 +204,11 @@ func (d Docker) Copy(c *daemon.Container, destPath string, src builder.FileInfo,
199 199
 // GetCachedImage returns a reference to a cached image whose parent equals `parent`
200 200
 // and runconfig equals `cfg`. A cache miss is expected to return an empty ID and a nil error.
201 201
 func (d Docker) GetCachedImage(imgID string, cfg *runconfig.Config) (string, error) {
202
-	cache, err := d.Daemon.ImageGetCached(imgID, cfg)
202
+	cache, err := d.Daemon.ImageGetCached(image.ID(imgID), cfg)
203 203
 	if cache == nil || err != nil {
204 204
 		return "", err
205 205
 	}
206
-	return cache.ID, nil
206
+	return cache.ID().String(), nil
207 207
 }
208 208
 
209 209
 // Kill stops the container execution abruptly.
... ...
@@ -218,7 +223,8 @@ func (d Docker) Mount(c *daemon.Container) error {
218 218
 
219 219
 // Unmount unmounts the root filesystem for the container.
220 220
 func (d Docker) Unmount(c *daemon.Container) error {
221
-	return d.Daemon.Unmount(c)
221
+	d.Daemon.Unmount(c)
222
+	return nil
222 223
 }
223 224
 
224 225
 // Start starts a container
... ...
@@ -1,12 +1,12 @@
1 1
 package daemon
2 2
 
3 3
 import (
4
-	"fmt"
5 4
 	"os"
6 5
 	"path"
7 6
 
8 7
 	"github.com/Sirupsen/logrus"
9 8
 	derr "github.com/docker/docker/errors"
9
+	"github.com/docker/docker/layer"
10 10
 	volumestore "github.com/docker/docker/volume/store"
11 11
 )
12 12
 
... ...
@@ -119,15 +119,12 @@ func (daemon *Daemon) rm(container *Container, forceRemove bool) (err error) {
119 119
 		logrus.Debugf("Unable to remove container from link graph: %s", err)
120 120
 	}
121 121
 
122
-	if err = daemon.driver.Remove(container.ID); err != nil {
122
+	metadata, err := daemon.layerStore.DeleteMount(container.ID)
123
+	layer.LogReleaseMetadata(metadata)
124
+	if err != nil {
123 125
 		return derr.ErrorCodeRmDriverFS.WithArgs(daemon.driver, container.ID, err)
124 126
 	}
125 127
 
126
-	initID := fmt.Sprintf("%s-init", container.ID)
127
-	if err := daemon.driver.Remove(initID); err != nil {
128
-		return derr.ErrorCodeRmInit.WithArgs(daemon.driver, initID, err)
129
-	}
130
-
131 128
 	if err = os.RemoveAll(container.root); err != nil {
132 129
 		return derr.ErrorCodeRmFS.WithArgs(container.ID, err)
133 130
 	}
... ...
@@ -3,21 +3,25 @@ package daemon
3 3
 import (
4 4
 	"strings"
5 5
 
6
+	"github.com/docker/distribution/reference"
6 7
 	derr "github.com/docker/docker/errors"
7
-	"github.com/docker/docker/graph/tags"
8
-	"github.com/docker/docker/pkg/parsers"
8
+	tagpkg "github.com/docker/docker/tag"
9 9
 )
10 10
 
11
-func (d *Daemon) graphNotExistToErrcode(imageName string, err error) error {
12
-	if d.Graph().IsNotExist(err, imageName) {
13
-		if strings.Contains(imageName, "@") {
14
-			return derr.ErrorCodeNoSuchImageHash.WithArgs(imageName)
11
+func (d *Daemon) imageNotExistToErrcode(err error) error {
12
+	if dne, isDNE := err.(ErrImageDoesNotExist); isDNE {
13
+		if strings.Contains(dne.RefOrID, "@") {
14
+			return derr.ErrorCodeNoSuchImageHash.WithArgs(dne.RefOrID)
15 15
 		}
16
-		img, tag := parsers.ParseRepositoryTag(imageName)
17
-		if tag == "" {
18
-			tag = tags.DefaultTag
16
+		tag := tagpkg.DefaultTag
17
+		ref, err := reference.ParseNamed(dne.RefOrID)
18
+		if err != nil {
19
+			return derr.ErrorCodeNoSuchImageTag.WithArgs(dne.RefOrID, tag)
19 20
 		}
20
-		return derr.ErrorCodeNoSuchImageTag.WithArgs(img, tag)
21
+		if tagged, isTagged := ref.(reference.Tagged); isTagged {
22
+			tag = tagged.Tag()
23
+		}
24
+		return derr.ErrorCodeNoSuchImageTag.WithArgs(ref.Name(), tag)
21 25
 	}
22 26
 	return err
23 27
 }
... ...
@@ -1,8 +1,8 @@
1 1
 package events
2 2
 
3 3
 import (
4
+	"github.com/docker/distribution/reference"
4 5
 	"github.com/docker/docker/pkg/jsonmessage"
5
-	"github.com/docker/docker/pkg/parsers"
6 6
 	"github.com/docker/docker/pkg/parsers/filters"
7 7
 )
8 8
 
... ...
@@ -38,8 +38,11 @@ func (ef *Filter) isLabelFieldIncluded(id string) bool {
38 38
 // against the stripped repo name without any tags.
39 39
 func (ef *Filter) isImageIncluded(eventID string, eventFrom string) bool {
40 40
 	stripTag := func(image string) string {
41
-		repo, _ := parsers.ParseRepositoryTag(image)
42
-		return repo
41
+		ref, err := reference.ParseNamed(image)
42
+		if err != nil {
43
+			return image
44
+		}
45
+		return ref.Name()
43 46
 	}
44 47
 
45 48
 	return isFieldIncluded(eventID, ef.filter["image"]) ||
... ...
@@ -1,5 +1,3 @@
1
-// +build daemon
2
-
3 1
 package graphdriver
4 2
 
5 3
 import (
... ...
@@ -13,6 +11,12 @@ import (
13 13
 	"github.com/docker/docker/pkg/ioutils"
14 14
 )
15 15
 
16
+var (
17
+	// ApplyUncompressedLayer defines the unpack method used by the graph
18
+	// driver.
19
+	ApplyUncompressedLayer = chrootarchive.ApplyUncompressedLayer
20
+)
21
+
16 22
 // NaiveDiffDriver takes a ProtoDriver and adds the
17 23
 // capability of the Diffing methods which it may or may not
18 24
 // support on its own. See the comment on the exported
... ...
@@ -129,7 +133,7 @@ func (gdw *NaiveDiffDriver) ApplyDiff(id, parent string, diff archive.Reader) (s
129 129
 		GIDMaps: gdw.gidMaps}
130 130
 	start := time.Now().UTC()
131 131
 	logrus.Debugf("Start untar layer")
132
-	if size, err = chrootarchive.ApplyUncompressedLayer(layerFs, diff, options); err != nil {
132
+	if size, err = ApplyUncompressedLayer(layerFs, diff, options); err != nil {
133 133
 		return
134 134
 	}
135 135
 	logrus.Debugf("Untar time: %vs", time.Now().UTC().Sub(start).Seconds())
136 136
deleted file mode 100644
... ...
@@ -1,31 +0,0 @@
1
-package graphdriver
2
-
3
-import (
4
-	"io"
5
-
6
-	"github.com/docker/docker/image"
7
-)
8
-
9
-// NOTE: These interfaces are used for implementing specific features of the Windows
10
-// graphdriver implementation.  The current versions are a short-term solution and
11
-// likely to change or possibly be eliminated, so avoid using them outside of the Windows
12
-// graphdriver code.
13
-
14
-// ImageRestorer interface allows the implementer to add a custom image to
15
-// the graph and tagstore.
16
-type ImageRestorer interface {
17
-	RestoreCustomImages(tagger Tagger, recorder Recorder) ([]string, error)
18
-}
19
-
20
-// Tagger is an interface that exposes the TagStore.Tag function without needing
21
-// to import graph.
22
-type Tagger interface {
23
-	Tag(repoName, tag, imageName string, force bool) error
24
-}
25
-
26
-// Recorder is an interface that exposes the Graph.Register and Graph.Exists
27
-// functions without needing to import graph.
28
-type Recorder interface {
29
-	Exists(id string) bool
30
-	Register(img image.Descriptor, layerData io.Reader) error
31
-}
... ...
@@ -1,5 +1,4 @@
1 1
 // +build experimental
2
-// +build daemon
3 2
 
4 3
 package graphdriver
5 4
 
... ...
@@ -1,5 +1,4 @@
1 1
 // +build experimental
2
-// +build daemon
3 2
 
4 3
 package graphdriver
5 4
 
... ...
@@ -1,5 +1,3 @@
1
-// +build daemon
2
-
3 1
 package vfs
4 2
 
5 3
 import (
... ...
@@ -14,6 +12,11 @@ import (
14 14
 	"github.com/opencontainers/runc/libcontainer/label"
15 15
 )
16 16
 
17
+var (
18
+	// CopyWithTar defines the copy method to use.
19
+	CopyWithTar = chrootarchive.CopyWithTar
20
+)
21
+
17 22
 func init() {
18 23
 	graphdriver.Register("vfs", Init)
19 24
 }
... ...
@@ -89,7 +92,7 @@ func (d *Driver) Create(id, parent, mountLabel string) error {
89 89
 	if err != nil {
90 90
 		return fmt.Errorf("%s: %s", parent, err)
91 91
 	}
92
-	if err := chrootarchive.CopyWithTar(parentDir, dir); err != nil {
92
+	if err := CopyWithTar(parentDir, dir); err != nil {
93 93
 		return err
94 94
 	}
95 95
 	return nil
96 96
deleted file mode 100644
... ...
@@ -1,3 +0,0 @@
1
-// +build !daemon
2
-
3
-package vfs
... ...
@@ -6,10 +6,10 @@ import (
6 6
 	"crypto/sha512"
7 7
 	"encoding/json"
8 8
 	"fmt"
9
+	"io"
9 10
 	"io/ioutil"
10 11
 	"os"
11 12
 	"path/filepath"
12
-	"runtime"
13 13
 	"strconv"
14 14
 	"strings"
15 15
 	"sync"
... ...
@@ -17,8 +17,6 @@ import (
17 17
 
18 18
 	"github.com/Sirupsen/logrus"
19 19
 	"github.com/docker/docker/daemon/graphdriver"
20
-	"github.com/docker/docker/dockerversion"
21
-	"github.com/docker/docker/image"
22 20
 	"github.com/docker/docker/pkg/archive"
23 21
 	"github.com/docker/docker/pkg/chrootarchive"
24 22
 	"github.com/docker/docker/pkg/idtools"
... ...
@@ -40,26 +38,6 @@ const (
40 40
 	filterDriver
41 41
 )
42 42
 
43
-// CustomImageDescriptor is an image descriptor for use by RestoreCustomImages
44
-type customImageDescriptor struct {
45
-	img *image.Image
46
-}
47
-
48
-// ID returns the image ID specified in the image structure.
49
-func (img customImageDescriptor) ID() string {
50
-	return img.img.ID
51
-}
52
-
53
-// Parent returns the parent ID - in this case, none
54
-func (img customImageDescriptor) Parent() string {
55
-	return ""
56
-}
57
-
58
-// MarshalConfig renders the image structure into JSON.
59
-func (img customImageDescriptor) MarshalConfig() ([]byte, error) {
60
-	return json.Marshal(img.img)
61
-}
62
-
63 43
 // Driver represents a windows graph driver.
64 44
 type Driver struct {
65 45
 	// info stores the shim driver information
... ...
@@ -195,7 +173,7 @@ func (d *Driver) Remove(id string) error {
195 195
 	if err != nil {
196 196
 		return err
197 197
 	}
198
-
198
+	os.RemoveAll(filepath.Join(d.info.HomeDir, "sysfile-backups", rID)) // ok to fail
199 199
 	return hcsshim.DestroyLayer(d.info, rID)
200 200
 }
201 201
 
... ...
@@ -402,22 +380,27 @@ func (d *Driver) DiffSize(id, parent string) (size int64, err error) {
402 402
 	return archive.ChangesSize(layerFs, changes), nil
403 403
 }
404 404
 
405
-// RestoreCustomImages adds any auto-detected OS specific images to the tag and graph store.
406
-func (d *Driver) RestoreCustomImages(tagger graphdriver.Tagger, recorder graphdriver.Recorder) (imageIDs []string, err error) {
405
+// CustomImageInfo is the object returned by the driver describing the base
406
+// image.
407
+type CustomImageInfo struct {
408
+	ID          string
409
+	Name        string
410
+	Version     string
411
+	Path        string
412
+	Size        int64
413
+	CreatedTime time.Time
414
+}
415
+
416
+// GetCustomImageInfos returns the image infos for window specific
417
+// base images which should always be present.
418
+func (d *Driver) GetCustomImageInfos() ([]CustomImageInfo, error) {
407 419
 	strData, err := hcsshim.GetSharedBaseImages()
408 420
 	if err != nil {
409 421
 		return nil, fmt.Errorf("Failed to restore base images: %s", err)
410 422
 	}
411 423
 
412
-	type customImageInfo struct {
413
-		Name        string
414
-		Version     string
415
-		Path        string
416
-		Size        int64
417
-		CreatedTime time.Time
418
-	}
419 424
 	type customImageInfoList struct {
420
-		Images []customImageInfo
425
+		Images []CustomImageInfo
421 426
 	}
422 427
 
423 428
 	var infoData customImageInfoList
... ...
@@ -428,43 +411,28 @@ func (d *Driver) RestoreCustomImages(tagger graphdriver.Tagger, recorder graphdr
428 428
 		return nil, err
429 429
 	}
430 430
 
431
+	var images []CustomImageInfo
432
+
431 433
 	for _, imageData := range infoData.Images {
432
-		_, folderName := filepath.Split(imageData.Path)
434
+		folderName := filepath.Base(imageData.Path)
433 435
 
434 436
 		// Use crypto hash of the foldername to generate a docker style id.
435 437
 		h := sha512.Sum384([]byte(folderName))
436 438
 		id := fmt.Sprintf("%x", h[:32])
437 439
 
438
-		if !recorder.Exists(id) {
439
-			// Register the image.
440
-			img := &image.Image{
441
-				ID:            id,
442
-				Created:       imageData.CreatedTime,
443
-				DockerVersion: dockerversion.Version,
444
-				Architecture:  runtime.GOARCH,
445
-				OS:            runtime.GOOS,
446
-				Size:          imageData.Size,
447
-			}
448
-
449
-			if err := recorder.Register(customImageDescriptor{img}, nil); err != nil {
450
-				return nil, err
451
-			}
452
-
453
-			// Create tags for the new image.
454
-			if err := tagger.Tag(strings.ToLower(imageData.Name), imageData.Version, img.ID, true); err != nil {
455
-				return nil, err
456
-			}
457
-
458
-			// Create the alternate ID file.
459
-			if err := d.setID(img.ID, folderName); err != nil {
460
-				return nil, err
461
-			}
462
-
463
-			imageIDs = append(imageIDs, img.ID)
440
+		if err := d.Create(id, "", ""); err != nil {
441
+			return nil, err
464 442
 		}
443
+		// Create the alternate ID file.
444
+		if err := d.setID(id, folderName); err != nil {
445
+			return nil, err
446
+		}
447
+
448
+		imageData.ID = id
449
+		images = append(images, imageData)
465 450
 	}
466 451
 
467
-	return imageIDs, nil
452
+	return images, nil
468 453
 }
469 454
 
470 455
 // GetMetadata returns custom driver information.
... ...
@@ -533,6 +501,10 @@ func (d *Driver) importLayer(id string, layerData archive.Reader, parentLayerPat
533 533
 	if size, err = chrootarchive.ApplyLayer(tempFolder, layerData); err != nil {
534 534
 		return
535 535
 	}
536
+	err = copySysFiles(tempFolder, filepath.Join(d.info.HomeDir, "sysfile-backups", id))
537
+	if err != nil {
538
+		return
539
+	}
536 540
 	logrus.Debugf("Untar time: %vs", time.Now().UTC().Sub(start).Seconds())
537 541
 
538 542
 	if err = hcsshim.ImportLayer(d.info, id, tempFolder, parentLayerPaths); err != nil {
... ...
@@ -596,3 +568,103 @@ func (d *Driver) setLayerChain(id string, chain []string) error {
596 596
 
597 597
 	return nil
598 598
 }
599
+
600
+// DiffPath returns a directory that contains files needed to construct layer diff.
601
+func (d *Driver) DiffPath(id string) (path string, release func() error, err error) {
602
+	id, err = d.resolveID(id)
603
+	if err != nil {
604
+		return
605
+	}
606
+
607
+	// Getting the layer paths must be done outside of the lock.
608
+	layerChain, err := d.getLayerChain(id)
609
+	if err != nil {
610
+		return
611
+	}
612
+
613
+	layerFolder := d.dir(id)
614
+	tempFolder := layerFolder + "-" + strconv.FormatUint(uint64(random.Rand.Uint32()), 10)
615
+	if err = os.MkdirAll(tempFolder, 0755); err != nil {
616
+		logrus.Errorf("Could not create %s %s", tempFolder, err)
617
+		return
618
+	}
619
+
620
+	defer func() {
621
+		if err != nil {
622
+			_, folderName := filepath.Split(tempFolder)
623
+			if err2 := hcsshim.DestroyLayer(d.info, folderName); err2 != nil {
624
+				logrus.Warnf("Couldn't clean-up tempFolder: %s %s", tempFolder, err2)
625
+			}
626
+		}
627
+	}()
628
+
629
+	if err = hcsshim.ExportLayer(d.info, id, tempFolder, layerChain); err != nil {
630
+		return
631
+	}
632
+
633
+	err = copySysFiles(filepath.Join(d.info.HomeDir, "sysfile-backups", id), tempFolder)
634
+	if err != nil {
635
+		return
636
+	}
637
+
638
+	return tempFolder, func() error {
639
+		// TODO: activate layers and release here?
640
+		_, folderName := filepath.Split(tempFolder)
641
+		return hcsshim.DestroyLayer(d.info, folderName)
642
+	}, nil
643
+}
644
+
645
+var sysFileWhiteList = []string{
646
+	"Hives\\*",
647
+	"Files\\BOOTNXT",
648
+	"tombstones.txt",
649
+}
650
+
651
+// note this only handles files
652
+func copySysFiles(src string, dest string) error {
653
+	if err := os.MkdirAll(dest, 0700); err != nil {
654
+		return err
655
+	}
656
+	return filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
657
+		rel, err := filepath.Rel(src, path)
658
+		if err != nil {
659
+			return err
660
+		}
661
+		for _, sysfile := range sysFileWhiteList {
662
+			if matches, err := filepath.Match(sysfile, rel); err != nil || !matches {
663
+				continue
664
+			}
665
+
666
+			fi, err := os.Lstat(path)
667
+			if err != nil {
668
+				return err
669
+			}
670
+
671
+			if !fi.Mode().IsRegular() {
672
+				continue
673
+			}
674
+
675
+			targetPath := filepath.Join(dest, rel)
676
+			if err = os.MkdirAll(filepath.Dir(targetPath), 0700); err != nil {
677
+				return err
678
+			}
679
+
680
+			in, err := os.Open(path)
681
+			if err != nil {
682
+				return err
683
+			}
684
+			out, err := os.Create(targetPath)
685
+			if err != nil {
686
+				in.Close()
687
+				return err
688
+			}
689
+			_, err = io.Copy(out, in)
690
+			in.Close()
691
+			out.Close()
692
+			if err != nil {
693
+				return err
694
+			}
695
+		}
696
+		return nil
697
+	})
698
+}
... ...
@@ -4,13 +4,12 @@ import (
4 4
 	"fmt"
5 5
 	"strings"
6 6
 
7
+	"github.com/docker/distribution/reference"
7 8
 	"github.com/docker/docker/api/types"
8 9
 	derr "github.com/docker/docker/errors"
9
-	"github.com/docker/docker/graph/tags"
10 10
 	"github.com/docker/docker/image"
11
-	"github.com/docker/docker/pkg/parsers"
12 11
 	"github.com/docker/docker/pkg/stringid"
13
-	"github.com/docker/docker/utils"
12
+	tagpkg "github.com/docker/docker/tag"
14 13
 )
15 14
 
16 15
 // ImageDelete deletes the image referenced by the given imageRef from this
... ...
@@ -53,39 +52,46 @@ import (
53 53
 func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.ImageDelete, error) {
54 54
 	records := []types.ImageDelete{}
55 55
 
56
-	img, err := daemon.repositories.LookupImage(imageRef)
56
+	imgID, err := daemon.GetImageID(imageRef)
57 57
 	if err != nil {
58
-		return nil, daemon.graphNotExistToErrcode(imageRef, err)
58
+		return nil, daemon.imageNotExistToErrcode(err)
59 59
 	}
60 60
 
61
+	repoRefs := daemon.tagStore.References(imgID)
62
+
61 63
 	var removedRepositoryRef bool
62
-	if !isImageIDPrefix(img.ID, imageRef) {
64
+	if !isImageIDPrefix(imgID.String(), imageRef) {
63 65
 		// A repository reference was given and should be removed
64 66
 		// first. We can only remove this reference if either force is
65 67
 		// true, there are multiple repository references to this
66 68
 		// image, or there are no containers using the given reference.
67
-		if !(force || daemon.imageHasMultipleRepositoryReferences(img.ID)) {
68
-			if container := daemon.getContainerUsingImage(img.ID); container != nil {
69
+		if !(force || len(repoRefs) > 1) {
70
+			if container := daemon.getContainerUsingImage(imgID); container != nil {
69 71
 				// If we removed the repository reference then
70 72
 				// this image would remain "dangling" and since
71 73
 				// we really want to avoid that the client must
72 74
 				// explicitly force its removal.
73
-				return nil, derr.ErrorCodeImgDelUsed.WithArgs(imageRef, stringid.TruncateID(container.ID), stringid.TruncateID(img.ID))
75
+				return nil, derr.ErrorCodeImgDelUsed.WithArgs(imageRef, stringid.TruncateID(container.ID), stringid.TruncateID(imgID.String()))
74 76
 			}
75 77
 		}
76 78
 
77
-		parsedRef, err := daemon.removeImageRef(imageRef)
79
+		parsedRef, err := reference.ParseNamed(imageRef)
78 80
 		if err != nil {
79 81
 			return nil, err
80 82
 		}
81 83
 
82
-		untaggedRecord := types.ImageDelete{Untagged: parsedRef}
84
+		parsedRef, err = daemon.removeImageRef(parsedRef)
85
+		if err != nil {
86
+			return nil, err
87
+		}
88
+
89
+		untaggedRecord := types.ImageDelete{Untagged: parsedRef.String()}
83 90
 
84
-		daemon.EventsService.Log("untag", img.ID, "")
91
+		daemon.EventsService.Log("untag", imgID.String(), "")
85 92
 		records = append(records, untaggedRecord)
86 93
 
87 94
 		// If has remaining references then untag finishes the remove
88
-		if daemon.repositories.HasReferences(img) {
95
+		if len(repoRefs) > 1 {
89 96
 			return records, nil
90 97
 		}
91 98
 
... ...
@@ -95,38 +101,39 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
95 95
 		// repository reference to the image then we will want to
96 96
 		// remove that reference.
97 97
 		// FIXME: Is this the behavior we want?
98
-		repoRefs := daemon.repositories.ByID()[img.ID]
99 98
 		if len(repoRefs) == 1 {
100 99
 			parsedRef, err := daemon.removeImageRef(repoRefs[0])
101 100
 			if err != nil {
102 101
 				return nil, err
103 102
 			}
104 103
 
105
-			untaggedRecord := types.ImageDelete{Untagged: parsedRef}
104
+			untaggedRecord := types.ImageDelete{Untagged: parsedRef.String()}
106 105
 
107
-			daemon.EventsService.Log("untag", img.ID, "")
106
+			daemon.EventsService.Log("untag", imgID.String(), "")
108 107
 			records = append(records, untaggedRecord)
109 108
 		}
110 109
 	}
111 110
 
112
-	return records, daemon.imageDeleteHelper(img, &records, force, prune, removedRepositoryRef)
111
+	return records, daemon.imageDeleteHelper(imgID, &records, force, prune, removedRepositoryRef)
113 112
 }
114 113
 
115 114
 // isImageIDPrefix returns whether the given possiblePrefix is a prefix of the
116 115
 // given imageID.
117 116
 func isImageIDPrefix(imageID, possiblePrefix string) bool {
118
-	return strings.HasPrefix(imageID, possiblePrefix)
119
-}
117
+	if strings.HasPrefix(imageID, possiblePrefix) {
118
+		return true
119
+	}
120
+
121
+	if i := strings.IndexRune(imageID, ':'); i >= 0 {
122
+		return strings.HasPrefix(imageID[i+1:], possiblePrefix)
123
+	}
120 124
 
121
-// imageHasMultipleRepositoryReferences returns whether there are multiple
122
-// repository references to the given imageID.
123
-func (daemon *Daemon) imageHasMultipleRepositoryReferences(imageID string) bool {
124
-	return len(daemon.repositories.ByID()[imageID]) > 1
125
+	return false
125 126
 }
126 127
 
127 128
 // getContainerUsingImage returns a container that was created using the given
128 129
 // imageID. Returns nil if there is no such container.
129
-func (daemon *Daemon) getContainerUsingImage(imageID string) *Container {
130
+func (daemon *Daemon) getContainerUsingImage(imageID image.ID) *Container {
130 131
 	for _, container := range daemon.List() {
131 132
 		if container.ImageID == imageID {
132 133
 			return container
... ...
@@ -141,18 +148,24 @@ func (daemon *Daemon) getContainerUsingImage(imageID string) *Container {
141 141
 // repositoryRef must not be an image ID but a repository name followed by an
142 142
 // optional tag or digest reference. If tag or digest is omitted, the default
143 143
 // tag is used. Returns the resolved image reference and an error.
144
-func (daemon *Daemon) removeImageRef(repositoryRef string) (string, error) {
145
-	repository, ref := parsers.ParseRepositoryTag(repositoryRef)
146
-	if ref == "" {
147
-		ref = tags.DefaultTag
144
+func (daemon *Daemon) removeImageRef(ref reference.Named) (reference.Named, error) {
145
+	switch ref.(type) {
146
+	case reference.Tagged:
147
+	case reference.Digested:
148
+	default:
149
+		var err error
150
+		ref, err = reference.WithTag(ref, tagpkg.DefaultTag)
151
+		if err != nil {
152
+			return nil, err
153
+		}
148 154
 	}
149 155
 
150 156
 	// Ignore the boolean value returned, as far as we're concerned, this
151 157
 	// is an idempotent operation and it's okay if the reference didn't
152 158
 	// exist in the first place.
153
-	_, err := daemon.repositories.Delete(repository, ref)
159
+	_, err := daemon.tagStore.Delete(ref)
154 160
 
155
-	return utils.ImageReference(repository, ref), err
161
+	return ref, err
156 162
 }
157 163
 
158 164
 // removeAllReferencesToImageID attempts to remove every reference to the given
... ...
@@ -160,8 +173,8 @@ func (daemon *Daemon) removeImageRef(repositoryRef string) (string, error) {
160 160
 // on the first encountered error. Removed references are logged to this
161 161
 // daemon's event service. An "Untagged" types.ImageDelete is added to the
162 162
 // given list of records.
163
-func (daemon *Daemon) removeAllReferencesToImageID(imgID string, records *[]types.ImageDelete) error {
164
-	imageRefs := daemon.repositories.ByID()[imgID]
163
+func (daemon *Daemon) removeAllReferencesToImageID(imgID image.ID, records *[]types.ImageDelete) error {
164
+	imageRefs := daemon.tagStore.References(imgID)
165 165
 
166 166
 	for _, imageRef := range imageRefs {
167 167
 		parsedRef, err := daemon.removeImageRef(imageRef)
... ...
@@ -169,9 +182,9 @@ func (daemon *Daemon) removeAllReferencesToImageID(imgID string, records *[]type
169 169
 			return err
170 170
 		}
171 171
 
172
-		untaggedRecord := types.ImageDelete{Untagged: parsedRef}
172
+		untaggedRecord := types.ImageDelete{Untagged: parsedRef.String()}
173 173
 
174
-		daemon.EventsService.Log("untag", imgID, "")
174
+		daemon.EventsService.Log("untag", imgID.String(), "")
175 175
 		*records = append(*records, untaggedRecord)
176 176
 	}
177 177
 
... ...
@@ -182,7 +195,7 @@ func (daemon *Daemon) removeAllReferencesToImageID(imgID string, records *[]type
182 182
 // Implements the error interface.
183 183
 type imageDeleteConflict struct {
184 184
 	hard    bool
185
-	imgID   string
185
+	imgID   image.ID
186 186
 	message string
187 187
 }
188 188
 
... ...
@@ -194,7 +207,7 @@ func (idc *imageDeleteConflict) Error() string {
194 194
 		forceMsg = "must be forced"
195 195
 	}
196 196
 
197
-	return fmt.Sprintf("conflict: unable to delete %s (%s) - %s", stringid.TruncateID(idc.imgID), forceMsg, idc.message)
197
+	return fmt.Sprintf("conflict: unable to delete %s (%s) - %s", stringid.TruncateID(idc.imgID.String()), forceMsg, idc.message)
198 198
 }
199 199
 
200 200
 // imageDeleteHelper attempts to delete the given image from this daemon. If
... ...
@@ -208,11 +221,11 @@ func (idc *imageDeleteConflict) Error() string {
208 208
 // conflict is encountered, it will be returned immediately without deleting
209 209
 // the image. If quiet is true, any encountered conflicts will be ignored and
210 210
 // the function will return nil immediately without deleting the image.
211
-func (daemon *Daemon) imageDeleteHelper(img *image.Image, records *[]types.ImageDelete, force, prune, quiet bool) error {
211
+func (daemon *Daemon) imageDeleteHelper(imgID image.ID, records *[]types.ImageDelete, force, prune, quiet bool) error {
212 212
 	// First, determine if this image has any conflicts. Ignore soft conflicts
213 213
 	// if force is true.
214
-	if conflict := daemon.checkImageDeleteConflict(img, force); conflict != nil {
215
-		if quiet && !daemon.imageIsDangling(img) {
214
+	if conflict := daemon.checkImageDeleteConflict(imgID, force); conflict != nil {
215
+		if quiet && !daemon.imageIsDangling(imgID) {
216 216
 			// Ignore conflicts UNLESS the image is "dangling" in
217 217
 			// which case we want the user to know.
218 218
 			return nil
... ...
@@ -223,33 +236,38 @@ func (daemon *Daemon) imageDeleteHelper(img *image.Image, records *[]types.Image
223 223
 		return conflict
224 224
 	}
225 225
 
226
+	parent, err := daemon.imageStore.GetParent(imgID)
227
+	if err != nil {
228
+		// There may be no parent
229
+		parent = ""
230
+	}
231
+
226 232
 	// Delete all repository tag/digest references to this image.
227
-	if err := daemon.removeAllReferencesToImageID(img.ID, records); err != nil {
233
+	if err := daemon.removeAllReferencesToImageID(imgID, records); err != nil {
228 234
 		return err
229 235
 	}
230 236
 
231
-	if err := daemon.Graph().Delete(img.ID); err != nil {
237
+	removedLayers, err := daemon.imageStore.Delete(imgID)
238
+	if err != nil {
232 239
 		return err
233 240
 	}
234 241
 
235
-	daemon.EventsService.Log("delete", img.ID, "")
236
-	*records = append(*records, types.ImageDelete{Deleted: img.ID})
242
+	daemon.EventsService.Log("delete", imgID.String(), "")
243
+	*records = append(*records, types.ImageDelete{Deleted: imgID.String()})
244
+	for _, removedLayer := range removedLayers {
245
+		*records = append(*records, types.ImageDelete{Deleted: removedLayer.ChainID.String()})
246
+	}
237 247
 
238
-	if !prune || img.Parent == "" {
248
+	if !prune || parent == "" {
239 249
 		return nil
240 250
 	}
241 251
 
242 252
 	// We need to prune the parent image. This means delete it if there are
243 253
 	// no tags/digests referencing it and there are no containers using it (
244 254
 	// either running or stopped).
245
-	parentImg, err := daemon.Graph().Get(img.Parent)
246
-	if err != nil {
247
-		return derr.ErrorCodeImgNoParent.WithArgs(err)
248
-	}
249
-
250 255
 	// Do not force prunings, but do so quietly (stopping on any encountered
251 256
 	// conflicts).
252
-	return daemon.imageDeleteHelper(parentImg, records, false, true, true)
257
+	return daemon.imageDeleteHelper(parent, records, false, true, true)
253 258
 }
254 259
 
255 260
 // checkImageDeleteConflict determines whether there are any conflicts
... ...
@@ -258,9 +276,9 @@ func (daemon *Daemon) imageDeleteHelper(img *image.Image, records *[]types.Image
258 258
 // using the image. A soft conflict is any tags/digest referencing the given
259 259
 // image or any stopped container using the image. If ignoreSoftConflicts is
260 260
 // true, this function will not check for soft conflict conditions.
261
-func (daemon *Daemon) checkImageDeleteConflict(img *image.Image, ignoreSoftConflicts bool) *imageDeleteConflict {
261
+func (daemon *Daemon) checkImageDeleteConflict(imgID image.ID, ignoreSoftConflicts bool) *imageDeleteConflict {
262 262
 	// Check for hard conflicts first.
263
-	if conflict := daemon.checkImageDeleteHardConflict(img); conflict != nil {
263
+	if conflict := daemon.checkImageDeleteHardConflict(imgID); conflict != nil {
264 264
 		return conflict
265 265
 	}
266 266
 
... ...
@@ -270,24 +288,15 @@ func (daemon *Daemon) checkImageDeleteConflict(img *image.Image, ignoreSoftConfl
270 270
 		return nil
271 271
 	}
272 272
 
273
-	return daemon.checkImageDeleteSoftConflict(img)
273
+	return daemon.checkImageDeleteSoftConflict(imgID)
274 274
 }
275 275
 
276
-func (daemon *Daemon) checkImageDeleteHardConflict(img *image.Image) *imageDeleteConflict {
277
-	// Check if the image ID is being used by a pull or build.
278
-	if daemon.Graph().IsHeld(img.ID) {
279
-		return &imageDeleteConflict{
280
-			hard:    true,
281
-			imgID:   img.ID,
282
-			message: "image is held by an ongoing pull or build",
283
-		}
284
-	}
285
-
276
+func (daemon *Daemon) checkImageDeleteHardConflict(imgID image.ID) *imageDeleteConflict {
286 277
 	// Check if the image has any descendent images.
287
-	if daemon.Graph().HasChildren(img.ID) {
278
+	if len(daemon.imageStore.Children(imgID)) > 0 {
288 279
 		return &imageDeleteConflict{
289 280
 			hard:    true,
290
-			imgID:   img.ID,
281
+			imgID:   imgID,
291 282
 			message: "image has dependent child images",
292 283
 		}
293 284
 	}
... ...
@@ -299,9 +308,9 @@ func (daemon *Daemon) checkImageDeleteHardConflict(img *image.Image) *imageDelet
299 299
 			continue
300 300
 		}
301 301
 
302
-		if container.ImageID == img.ID {
302
+		if container.ImageID == imgID {
303 303
 			return &imageDeleteConflict{
304
-				imgID:   img.ID,
304
+				imgID:   imgID,
305 305
 				hard:    true,
306 306
 				message: fmt.Sprintf("image is being used by running container %s", stringid.TruncateID(container.ID)),
307 307
 			}
... ...
@@ -311,11 +320,11 @@ func (daemon *Daemon) checkImageDeleteHardConflict(img *image.Image) *imageDelet
311 311
 	return nil
312 312
 }
313 313
 
314
-func (daemon *Daemon) checkImageDeleteSoftConflict(img *image.Image) *imageDeleteConflict {
314
+func (daemon *Daemon) checkImageDeleteSoftConflict(imgID image.ID) *imageDeleteConflict {
315 315
 	// Check if any repository tags/digest reference this image.
316
-	if daemon.repositories.HasReferences(img) {
316
+	if len(daemon.tagStore.References(imgID)) > 0 {
317 317
 		return &imageDeleteConflict{
318
-			imgID:   img.ID,
318
+			imgID:   imgID,
319 319
 			message: "image is referenced in one or more repositories",
320 320
 		}
321 321
 	}
... ...
@@ -327,9 +336,9 @@ func (daemon *Daemon) checkImageDeleteSoftConflict(img *image.Image) *imageDelet
327 327
 			continue
328 328
 		}
329 329
 
330
-		if container.ImageID == img.ID {
330
+		if container.ImageID == imgID {
331 331
 			return &imageDeleteConflict{
332
-				imgID:   img.ID,
332
+				imgID:   imgID,
333 333
 				message: fmt.Sprintf("image is being used by stopped container %s", stringid.TruncateID(container.ID)),
334 334
 			}
335 335
 		}
... ...
@@ -341,6 +350,6 @@ func (daemon *Daemon) checkImageDeleteSoftConflict(img *image.Image) *imageDelet
341 341
 // imageIsDangling returns whether the given image is "dangling" which means
342 342
 // that there are no repository references to the given image and it has no
343 343
 // child images.
344
-func (daemon *Daemon) imageIsDangling(img *image.Image) bool {
345
-	return !(daemon.repositories.HasReferences(img) || daemon.Graph().HasChildren(img.ID))
344
+func (daemon *Daemon) imageIsDangling(imgID image.ID) bool {
345
+	return !(len(daemon.tagStore.References(imgID)) > 0 || len(daemon.imageStore.Children(imgID)) > 0)
346 346
 }
347 347
new file mode 100644
... ...
@@ -0,0 +1,163 @@
0
+package daemon
1
+
2
+import (
3
+	"fmt"
4
+	"path"
5
+	"sort"
6
+	"strings"
7
+
8
+	"github.com/docker/distribution/reference"
9
+	"github.com/docker/docker/api/types"
10
+	"github.com/docker/docker/image"
11
+	"github.com/docker/docker/layer"
12
+	"github.com/docker/docker/pkg/parsers/filters"
13
+)
14
+
15
+var acceptedImageFilterTags = map[string]struct{}{
16
+	"dangling": {},
17
+	"label":    {},
18
+}
19
+
20
+// byCreated is a temporary type used to sort a list of images by creation
21
+// time.
22
+type byCreated []*types.Image
23
+
24
+func (r byCreated) Len() int           { return len(r) }
25
+func (r byCreated) Swap(i, j int)      { r[i], r[j] = r[j], r[i] }
26
+func (r byCreated) Less(i, j int) bool { return r[i].Created < r[j].Created }
27
+
28
+// Map returns a map of all images in the ImageStore
29
+func (daemon *Daemon) Map() map[image.ID]*image.Image {
30
+	return daemon.imageStore.Map()
31
+}
32
+
33
+// Images returns a filtered list of images. filterArgs is a JSON-encoded set
34
+// of filter arguments which will be interpreted by pkg/parsers/filters.
35
+// filter is a shell glob string applied to repository names. The argument
36
+// named all controls whether all images in the graph are filtered, or just
37
+// the heads.
38
+func (daemon *Daemon) Images(filterArgs, filter string, all bool) ([]*types.Image, error) {
39
+	var (
40
+		allImages    map[image.ID]*image.Image
41
+		err          error
42
+		danglingOnly = false
43
+	)
44
+
45
+	imageFilters, err := filters.FromParam(filterArgs)
46
+	if err != nil {
47
+		return nil, err
48
+	}
49
+	for name := range imageFilters {
50
+		if _, ok := acceptedImageFilterTags[name]; !ok {
51
+			return nil, fmt.Errorf("Invalid filter '%s'", name)
52
+		}
53
+	}
54
+
55
+	if i, ok := imageFilters["dangling"]; ok {
56
+		for _, value := range i {
57
+			if v := strings.ToLower(value); v == "true" {
58
+				danglingOnly = true
59
+			} else if v != "false" {
60
+				return nil, fmt.Errorf("Invalid filter 'dangling=%s'", v)
61
+			}
62
+		}
63
+	}
64
+
65
+	if danglingOnly {
66
+		allImages = daemon.imageStore.Heads()
67
+	} else {
68
+		allImages = daemon.imageStore.Map()
69
+	}
70
+
71
+	images := []*types.Image{}
72
+
73
+	var filterTagged bool
74
+	if filter != "" {
75
+		filterRef, err := reference.Parse(filter)
76
+		if err == nil { // parse error means wildcard repo
77
+			if _, ok := filterRef.(reference.Tagged); ok {
78
+				filterTagged = true
79
+			}
80
+		}
81
+	}
82
+
83
+	for id, img := range allImages {
84
+		if _, ok := imageFilters["label"]; ok {
85
+			if img.Config == nil {
86
+				// Very old image that do not have image.Config (or even labels)
87
+				continue
88
+			}
89
+			// We are now sure image.Config is not nil
90
+			if !imageFilters.MatchKVList("label", img.Config.Labels) {
91
+				continue
92
+			}
93
+		}
94
+
95
+		layerID := img.RootFS.ChainID()
96
+		var size int64
97
+		if layerID != "" {
98
+			l, err := daemon.layerStore.Get(layerID)
99
+			if err != nil {
100
+				return nil, err
101
+			}
102
+
103
+			size, err = l.Size()
104
+			layer.ReleaseAndLog(daemon.layerStore, l)
105
+			if err != nil {
106
+				return nil, err
107
+			}
108
+		}
109
+
110
+		newImage := newImage(img, size)
111
+
112
+		for _, ref := range daemon.tagStore.References(id) {
113
+			if filter != "" { // filter by tag/repo name
114
+				if filterTagged { // filter by tag, require full ref match
115
+					if ref.String() != filter {
116
+						continue
117
+					}
118
+				} else if matched, err := path.Match(filter, ref.Name()); !matched || err != nil { // name only match, FIXME: docs say exact
119
+					continue
120
+				}
121
+			}
122
+			if _, ok := ref.(reference.Digested); ok {
123
+				newImage.RepoDigests = append(newImage.RepoDigests, ref.String())
124
+			}
125
+			if _, ok := ref.(reference.Tagged); ok {
126
+				newImage.RepoTags = append(newImage.RepoTags, ref.String())
127
+			}
128
+		}
129
+		if newImage.RepoDigests == nil && newImage.RepoTags == nil {
130
+			if all || len(daemon.imageStore.Children(id)) == 0 {
131
+				if filter != "" { // skip images with no references if filtering by tag
132
+					continue
133
+				}
134
+				newImage.RepoDigests = []string{"<none>@<none>"}
135
+				newImage.RepoTags = []string{"<none>:<none>"}
136
+			} else {
137
+				continue
138
+			}
139
+		} else if danglingOnly {
140
+			continue
141
+		}
142
+
143
+		images = append(images, newImage)
144
+	}
145
+
146
+	sort.Sort(sort.Reverse(byCreated(images)))
147
+
148
+	return images, nil
149
+}
150
+
151
+func newImage(image *image.Image, size int64) *types.Image {
152
+	newImage := new(types.Image)
153
+	newImage.ParentID = image.Parent.String()
154
+	newImage.ID = image.ID().String()
155
+	newImage.Created = image.Created.Unix()
156
+	newImage.Size = size
157
+	newImage.VirtualSize = size
158
+	if image.Config != nil {
159
+		newImage.Labels = image.Config.Labels
160
+	}
161
+	return newImage
162
+}
0 163
new file mode 100644
... ...
@@ -0,0 +1,111 @@
0
+package daemon
1
+
2
+import (
3
+	"encoding/json"
4
+	"io"
5
+	"net/http"
6
+	"net/url"
7
+	"runtime"
8
+	"time"
9
+
10
+	"github.com/docker/distribution/reference"
11
+	"github.com/docker/docker/dockerversion"
12
+	"github.com/docker/docker/image"
13
+	"github.com/docker/docker/layer"
14
+	"github.com/docker/docker/pkg/httputils"
15
+	"github.com/docker/docker/pkg/progressreader"
16
+	"github.com/docker/docker/pkg/streamformatter"
17
+	"github.com/docker/docker/runconfig"
18
+)
19
+
20
+// ImportImage imports an image, getting the archived layer data either from
21
+// inConfig (if src is "-"), or from a URI specified in src. Progress output is
22
+// written to outStream. Repository and tag names can optionally be given in
23
+// the repo and tag arguments, respectively.
24
+func (daemon *Daemon) ImportImage(src string, newRef reference.Named, msg string, inConfig io.ReadCloser, outStream io.Writer, config *runconfig.Config) error {
25
+	var (
26
+		sf      = streamformatter.NewJSONStreamFormatter()
27
+		archive io.ReadCloser
28
+		resp    *http.Response
29
+	)
30
+
31
+	if src == "-" {
32
+		archive = inConfig
33
+	} else {
34
+		inConfig.Close()
35
+		u, err := url.Parse(src)
36
+		if err != nil {
37
+			return err
38
+		}
39
+		if u.Scheme == "" {
40
+			u.Scheme = "http"
41
+			u.Host = src
42
+			u.Path = ""
43
+		}
44
+		outStream.Write(sf.FormatStatus("", "Downloading from %s", u))
45
+		resp, err = httputils.Download(u.String())
46
+		if err != nil {
47
+			return err
48
+		}
49
+		progressReader := progressreader.New(progressreader.Config{
50
+			In:        resp.Body,
51
+			Out:       outStream,
52
+			Formatter: sf,
53
+			Size:      resp.ContentLength,
54
+			NewLines:  true,
55
+			ID:        "",
56
+			Action:    "Importing",
57
+		})
58
+		archive = progressReader
59
+	}
60
+
61
+	defer archive.Close()
62
+	if len(msg) == 0 {
63
+		msg = "Imported from " + src
64
+	}
65
+	// TODO: support windows baselayer?
66
+	l, err := daemon.layerStore.Register(archive, "")
67
+	if err != nil {
68
+		return err
69
+	}
70
+	defer layer.ReleaseAndLog(daemon.layerStore, l)
71
+
72
+	created := time.Now().UTC()
73
+	imgConfig, err := json.Marshal(&image.Image{
74
+		V1Image: image.V1Image{
75
+			DockerVersion: dockerversion.Version,
76
+			Config:        config,
77
+			Architecture:  runtime.GOARCH,
78
+			OS:            runtime.GOOS,
79
+			Created:       created,
80
+			Comment:       msg,
81
+		},
82
+		RootFS: &image.RootFS{
83
+			Type:    "layers",
84
+			DiffIDs: []layer.DiffID{l.DiffID()},
85
+		},
86
+		History: []image.History{{
87
+			Created: created,
88
+			Comment: msg,
89
+		}},
90
+	})
91
+	if err != nil {
92
+		return err
93
+	}
94
+
95
+	id, err := daemon.imageStore.Create(imgConfig)
96
+	if err != nil {
97
+		return err
98
+	}
99
+
100
+	// FIXME: connect with commit code and call tagstore directly
101
+	if newRef != nil {
102
+		if err := daemon.TagImage(newRef, id.String(), true); err != nil {
103
+			return err
104
+		}
105
+	}
106
+
107
+	outStream.Write(sf.FormatStatus("", id.String()))
108
+	daemon.EventsService.Log("import", id.String(), "")
109
+	return nil
110
+}
... ...
@@ -62,7 +62,7 @@ func (daemon *Daemon) SystemInfo() (*types.Info, error) {
62 62
 	v := &types.Info{
63 63
 		ID:                 daemon.ID,
64 64
 		Containers:         len(daemon.List()),
65
-		Images:             len(daemon.Graph().Map()),
65
+		Images:             len(daemon.imageStore.Map()),
66 66
 		Driver:             daemon.GraphDriver().String(),
67 67
 		DriverStatus:       daemon.GraphDriver().Status(),
68 68
 		Plugins:            daemon.showPluginsInfo(),
... ...
@@ -8,6 +8,7 @@ import (
8 8
 	"github.com/docker/docker/api/types/versions/v1p20"
9 9
 	"github.com/docker/docker/daemon/exec"
10 10
 	"github.com/docker/docker/daemon/network"
11
+	"github.com/docker/docker/layer"
11 12
 )
12 13
 
13 14
 // ContainerInspect returns low-level information about a
... ...
@@ -124,7 +125,7 @@ func (daemon *Daemon) getInspectData(container *Container, size bool) (*types.Co
124 124
 		Path:         container.Path,
125 125
 		Args:         container.Args,
126 126
 		State:        containerState,
127
-		Image:        container.ImageID,
127
+		Image:        container.ImageID.String(),
128 128
 		LogPath:      container.LogPath,
129 129
 		Name:         container.Name,
130 130
 		RestartCount: container.RestartCount,
... ...
@@ -149,7 +150,18 @@ func (daemon *Daemon) getInspectData(container *Container, size bool) (*types.Co
149 149
 	contJSONBase = setPlatformSpecificContainerFields(container, contJSONBase)
150 150
 
151 151
 	contJSONBase.GraphDriver.Name = container.Driver
152
-	graphDriverData, err := daemon.driver.GetMetadata(container.ID)
152
+
153
+	image, err := daemon.imageStore.Get(container.ImageID)
154
+	if err != nil {
155
+		return nil, err
156
+	}
157
+	l, err := daemon.layerStore.Get(image.RootFS.ChainID())
158
+	if err != nil {
159
+		return nil, err
160
+	}
161
+	defer layer.ReleaseAndLog(daemon.layerStore, l)
162
+
163
+	graphDriverData, err := l.Metadata()
153 164
 	if err != nil {
154 165
 		return nil, err
155 166
 	}
... ...
@@ -9,7 +9,6 @@ import (
9 9
 	"github.com/Sirupsen/logrus"
10 10
 	"github.com/docker/docker/api/types"
11 11
 	derr "github.com/docker/docker/errors"
12
-	"github.com/docker/docker/graph"
13 12
 	"github.com/docker/docker/image"
14 13
 	"github.com/docker/docker/pkg/graphdb"
15 14
 	"github.com/docker/docker/pkg/nat"
... ...
@@ -66,7 +65,7 @@ type listContext struct {
66 66
 	// names is a list of container names to filter with
67 67
 	names map[string][]string
68 68
 	// images is a list of images to filter with
69
-	images map[string]bool
69
+	images map[image.ID]bool
70 70
 	// filters is a collection of arguments to filter with, specified by the user
71 71
 	filters filters.Args
72 72
 	// exitAllowed is a list of exit codes allowed to filter with
... ...
@@ -176,25 +175,24 @@ func (daemon *Daemon) foldFilter(config *ContainersConfig) (*listContext, error)
176 176
 		}
177 177
 	}
178 178
 
179
-	imagesFilter := map[string]bool{}
179
+	imagesFilter := map[image.ID]bool{}
180 180
 	var ancestorFilter bool
181 181
 	if ancestors, ok := psFilters["ancestor"]; ok {
182 182
 		ancestorFilter = true
183
-		byParents := daemon.Graph().ByParent()
184 183
 		// The idea is to walk the graph down the most "efficient" way.
185 184
 		for _, ancestor := range ancestors {
186 185
 			// First, get the imageId of the ancestor filter (yay)
187
-			image, err := daemon.repositories.LookupImage(ancestor)
186
+			id, err := daemon.GetImageID(ancestor)
188 187
 			if err != nil {
189 188
 				logrus.Warnf("Error while looking up for image %v", ancestor)
190 189
 				continue
191 190
 			}
192
-			if imagesFilter[ancestor] {
191
+			if imagesFilter[id] {
193 192
 				// Already seen this ancestor, skip it
194 193
 				continue
195 194
 			}
196 195
 			// Then walk down the graph and put the imageIds in imagesFilter
197
-			populateImageFilterByParents(imagesFilter, image.ID, byParents)
196
+			populateImageFilterByParents(imagesFilter, id, daemon.imageStore.Children)
198 197
 		}
199 198
 	}
200 199
 
... ...
@@ -310,41 +308,29 @@ func includeContainerInList(container *Container, ctx *listContext) iterationAct
310 310
 	return includeContainer
311 311
 }
312 312
 
313
-func getImage(s *graph.TagStore, img, imgID string) (string, error) {
314
-	// both Image and ImageID is actually ids, nothing to guess
315
-	if strings.HasPrefix(imgID, img) {
316
-		return img, nil
317
-	}
318
-	id, err := s.GetID(img)
319
-	if err != nil {
320
-		if err == graph.ErrNameIsNotExist {
321
-			return imgID, nil
322
-		}
323
-		return "", err
324
-	}
325
-	if id != imgID {
326
-		return imgID, nil
327
-	}
328
-	return img, nil
329
-}
330
-
331 313
 // transformContainer generates the container type expected by the docker ps command.
332 314
 func (daemon *Daemon) transformContainer(container *Container, ctx *listContext) (*types.Container, error) {
333 315
 	newC := &types.Container{
334 316
 		ID:      container.ID,
335 317
 		Names:   ctx.names[container.ID],
336
-		ImageID: container.ImageID,
318
+		ImageID: container.ImageID.String(),
337 319
 	}
338 320
 	if newC.Names == nil {
339 321
 		// Dead containers will often have no name, so make sure the response isn't  null
340 322
 		newC.Names = []string{}
341 323
 	}
342 324
 
343
-	showImg, err := getImage(daemon.repositories, container.Config.Image, container.ImageID)
344
-	if err != nil {
345
-		return nil, err
325
+	image := container.Config.Image // if possible keep the original ref
326
+	if image != container.ImageID.String() {
327
+		id, err := daemon.GetImageID(image)
328
+		if _, isDNE := err.(ErrImageDoesNotExist); err != nil && !isDNE {
329
+			return nil, err
330
+		}
331
+		if err != nil || id != container.ImageID {
332
+			image = container.ImageID.String()
333
+		}
346 334
 	}
347
-	newC.Image = showImg
335
+	newC.Image = image
348 336
 
349 337
 	if len(container.Args) > 0 {
350 338
 		args := []string{}
... ...
@@ -433,12 +419,10 @@ func (daemon *Daemon) Volumes(filter string) ([]*types.Volume, error) {
433 433
 	return volumesOut, nil
434 434
 }
435 435
 
436
-func populateImageFilterByParents(ancestorMap map[string]bool, imageID string, byParents map[string][]*image.Image) {
436
+func populateImageFilterByParents(ancestorMap map[image.ID]bool, imageID image.ID, getChildren func(image.ID) []image.ID) {
437 437
 	if !ancestorMap[imageID] {
438
-		if images, ok := byParents[imageID]; ok {
439
-			for _, image := range images {
440
-				populateImageFilterByParents(ancestorMap, image.ID, byParents)
441
-			}
438
+		for _, id := range getChildren(imageID) {
439
+			populateImageFilterByParents(ancestorMap, id, getChildren)
442 440
 		}
443 441
 		ancestorMap[imageID] = true
444 442
 	}
... ...
@@ -832,15 +832,6 @@ var (
832 832
 		HTTPStatusCode: http.StatusInternalServerError,
833 833
 	})
834 834
 
835
-	// ErrorCodeRmInit is generated when we try to delete a container
836
-	// but failed deleting its init filesystem.
837
-	ErrorCodeRmInit = errcode.Register(errGroup, errcode.ErrorDescriptor{
838
-		Value:          "RMINIT",
839
-		Message:        "Driver %s failed to remove init filesystem %s: %s",
840
-		Description:    "While trying to delete a container, the driver failed to remove the init filesystem",
841
-		HTTPStatusCode: http.StatusInternalServerError,
842
-	})
843
-
844 835
 	// ErrorCodeRmFS is generated when we try to delete a container
845 836
 	// but failed deleting its filesystem.
846 837
 	ErrorCodeRmFS = errcode.Register(errGroup, errcode.ErrorDescriptor{
... ...
@@ -553,7 +553,7 @@ func (s *DockerSuite) TestContainerApiCommit(c *check.C) {
553 553
 	cName := "testapicommit"
554 554
 	dockerCmd(c, "run", "--name="+cName, "busybox", "/bin/sh", "-c", "touch /test")
555 555
 
556
-	name := "TestContainerApiCommit"
556
+	name := "testcontainerapicommit"
557 557
 	status, b, err := sockRequest("POST", "/commit?repo="+name+"&testtag=tag&container="+cName, nil)
558 558
 	c.Assert(err, check.IsNil)
559 559
 	c.Assert(status, check.Equals, http.StatusCreated)
... ...
@@ -586,7 +586,7 @@ func (s *DockerSuite) TestContainerApiCommitWithLabelInConfig(c *check.C) {
586 586
 		"Labels": map[string]string{"key1": "value1", "key2": "value2"},
587 587
 	}
588 588
 
589
-	name := "TestContainerApiCommitWithConfig"
589
+	name := "testcontainerapicommitwithconfig"
590 590
 	status, b, err := sockRequest("POST", "/commit?repo="+name+"&container="+cName, config)
591 591
 	c.Assert(err, check.IsNil)
592 592
 	c.Assert(status, check.Equals, http.StatusCreated)
... ...
@@ -4543,7 +4543,7 @@ func (s *DockerSuite) TestBuildInvalidTag(c *check.C) {
4543 4543
 	_, out, err := buildImageWithOut(name, "FROM scratch\nMAINTAINER quux\n", true)
4544 4544
 	// if the error doesnt check for illegal tag name, or the image is built
4545 4545
 	// then this should fail
4546
-	if !strings.Contains(out, "Illegal tag name") || strings.Contains(out, "Sending build context to Docker daemon") {
4546
+	if !strings.Contains(out, "invalid reference format") || strings.Contains(out, "Sending build context to Docker daemon") {
4547 4547
 		c.Fatalf("failed to stop before building. Error: %s, Output: %s", err, out)
4548 4548
 	}
4549 4549
 }
... ...
@@ -6377,7 +6377,7 @@ func (s *DockerSuite) TestBuildTagEvent(c *check.C) {
6377 6377
 	select {
6378 6378
 	case ev := <-ch:
6379 6379
 		c.Assert(ev.Status, check.Equals, "tag")
6380
-		c.Assert(ev.ID, check.Equals, "test:")
6380
+		c.Assert(ev.ID, check.Equals, "test:latest")
6381 6381
 	case <-time.After(time.Second):
6382 6382
 		c.Fatal("The 'tag' event not heard from the server")
6383 6383
 	}
... ...
@@ -3,6 +3,8 @@ package main
3 3
 import (
4 4
 	"encoding/json"
5 5
 	"fmt"
6
+	"os"
7
+	"path/filepath"
6 8
 	"regexp"
7 9
 	"strings"
8 10
 
... ...
@@ -11,7 +13,6 @@ import (
11 11
 	"github.com/docker/docker/api/types"
12 12
 	"github.com/docker/docker/pkg/integration/checker"
13 13
 	"github.com/docker/docker/pkg/stringutils"
14
-	"github.com/docker/docker/utils"
15 14
 	"github.com/go-check/check"
16 15
 )
17 16
 
... ...
@@ -32,7 +33,7 @@ func setupImageWithTag(c *check.C, tag string) (digest.Digest, error) {
32 32
 	dockerCmd(c, "run", "-d", "-e", "digest=1", "--name", containerName, "busybox")
33 33
 
34 34
 	// tag the image to upload it to the private registry
35
-	repoAndTag := utils.ImageReference(repoName, tag)
35
+	repoAndTag := repoName + ":" + tag
36 36
 	out, _, err := dockerCmdWithError("commit", containerName, repoAndTag)
37 37
 	c.Assert(err, checker.IsNil, check.Commentf("image tagging failed: %s", out))
38 38
 
... ...
@@ -438,6 +439,11 @@ func (s *DockerRegistrySuite) TestPullFailsWithAlteredLayer(c *check.C) {
438 438
 	// Now try pulling that image by digest. We should get an error about
439 439
 	// digest verification for the target layer digest.
440 440
 
441
+	// Remove distribution cache to force a re-pull of the blobs
442
+	if err := os.RemoveAll(filepath.Join(dockerBasePath, "image", s.d.storageDriver, "distribution")); err != nil {
443
+		c.Fatalf("error clearing distribution cache: %v", err)
444
+	}
445
+
441 446
 	// Pull from the registry using the <name>@<digest> reference.
442 447
 	imageReference := fmt.Sprintf("%s@%s", repoName, manifestDigest)
443 448
 	out, exitStatus, _ := dockerCmdWithError("pull", imageReference)
... ...
@@ -14,6 +14,7 @@ import (
14 14
 
15 15
 	"github.com/docker/docker/pkg/integration/checker"
16 16
 	"github.com/docker/docker/pkg/nat"
17
+	"github.com/docker/docker/pkg/stringid"
17 18
 	"github.com/go-check/check"
18 19
 )
19 20
 
... ...
@@ -243,6 +244,41 @@ func (s *DockerSuite) TestCreateModeIpcContainer(c *check.C) {
243 243
 	dockerCmd(c, "create", fmt.Sprintf("--ipc=container:%s", id), "busybox")
244 244
 }
245 245
 
246
+func (s *DockerSuite) TestCreateByImageID(c *check.C) {
247
+	imageName := "testcreatebyimageid"
248
+	imageID, err := buildImage(imageName,
249
+		`FROM busybox
250
+		MAINTAINER dockerio`,
251
+		true)
252
+	if err != nil {
253
+		c.Fatal(err)
254
+	}
255
+	truncatedImageID := stringid.TruncateID(imageID)
256
+
257
+	dockerCmd(c, "create", imageID)
258
+	dockerCmd(c, "create", truncatedImageID)
259
+	dockerCmd(c, "create", fmt.Sprintf("%s:%s", imageName, truncatedImageID))
260
+
261
+	// Ensure this fails
262
+	out, exit, _ := dockerCmdWithError("create", fmt.Sprintf("%s:%s", imageName, imageID))
263
+	if exit == 0 {
264
+		c.Fatalf("expected non-zero exit code; received %d", exit)
265
+	}
266
+
267
+	if expected := "invalid reference format"; !strings.Contains(out, expected) {
268
+		c.Fatalf(`Expected %q in output; got: %s`, expected, out)
269
+	}
270
+
271
+	out, exit, _ = dockerCmdWithError("create", fmt.Sprintf("%s:%s", "wrongimage", truncatedImageID))
272
+	if exit == 0 {
273
+		c.Fatalf("expected non-zero exit code; received %d", exit)
274
+	}
275
+
276
+	if expected := "Unable to find image"; !strings.Contains(out, expected) {
277
+		c.Fatalf(`Expected %q in output; got: %s`, expected, out)
278
+	}
279
+}
280
+
246 281
 func (s *DockerTrustSuite) TestTrustedCreate(c *check.C) {
247 282
 	repoName := s.setupTrustedImage(c, "trusted-create")
248 283
 
... ...
@@ -325,6 +325,8 @@ func (s *DockerExternalGraphdriverSuite) TestExternalGraphDriver(c *check.C) {
325 325
 	err = s.d.Stop()
326 326
 	c.Assert(err, check.IsNil)
327 327
 
328
+	// Don't check s.ec.exists, because the daemon no longer calls the
329
+	// Exists function.
328 330
 	c.Assert(s.ec.activations, check.Equals, 2)
329 331
 	c.Assert(s.ec.init, check.Equals, 2)
330 332
 	c.Assert(s.ec.creations >= 1, check.Equals, true)
... ...
@@ -333,7 +335,6 @@ func (s *DockerExternalGraphdriverSuite) TestExternalGraphDriver(c *check.C) {
333 333
 	c.Assert(s.ec.puts >= 1, check.Equals, true)
334 334
 	c.Assert(s.ec.stats, check.Equals, 3)
335 335
 	c.Assert(s.ec.cleanups, check.Equals, 2)
336
-	c.Assert(s.ec.exists >= 1, check.Equals, true)
337 336
 	c.Assert(s.ec.applydiff >= 1, check.Equals, true)
338 337
 	c.Assert(s.ec.changes, check.Equals, 1)
339 338
 	c.Assert(s.ec.diffsize, check.Equals, 0)
... ...
@@ -98,9 +98,9 @@ func (s *DockerSuite) TestImagesFilterLabel(c *check.C) {
98 98
 
99 99
 	out, _ := dockerCmd(c, "images", "--no-trunc", "-q", "-f", "label=match")
100 100
 	out = strings.TrimSpace(out)
101
-	c.Assert(out, check.Matches, fmt.Sprintf("[\\s\\w]*%s[\\s\\w]*", image1ID))
102
-	c.Assert(out, check.Matches, fmt.Sprintf("[\\s\\w]*%s[\\s\\w]*", image2ID))
103
-	c.Assert(out, check.Not(check.Matches), fmt.Sprintf("[\\s\\w]*%s[\\s\\w]*", image3ID))
101
+	c.Assert(out, check.Matches, fmt.Sprintf("[\\s\\w:]*%s[\\s\\w:]*", image1ID))
102
+	c.Assert(out, check.Matches, fmt.Sprintf("[\\s\\w:]*%s[\\s\\w:]*", image2ID))
103
+	c.Assert(out, check.Not(check.Matches), fmt.Sprintf("[\\s\\w:]*%s[\\s\\w:]*", image3ID))
104 104
 
105 105
 	out, _ = dockerCmd(c, "images", "--no-trunc", "-q", "-f", "label=match=me too")
106 106
 	out = strings.TrimSpace(out)
... ...
@@ -204,7 +204,7 @@ func (s *DockerSuite) TestImagesEnsureOnlyHeadsImagesShown(c *check.C) {
204 204
 	// images shouldn't show non-heads images
205 205
 	c.Assert(out, checker.Not(checker.Contains), intermediate)
206 206
 	// images should contain final built images
207
-	c.Assert(out, checker.Contains, head[:12])
207
+	c.Assert(out, checker.Contains, stringid.TruncateID(head))
208 208
 }
209 209
 
210 210
 func (s *DockerSuite) TestImagesEnsureImagesFromScratchShown(c *check.C) {
... ...
@@ -219,5 +219,5 @@ func (s *DockerSuite) TestImagesEnsureImagesFromScratchShown(c *check.C) {
219 219
 
220 220
 	out, _ := dockerCmd(c, "images")
221 221
 	// images should contain images built from scratch
222
-	c.Assert(out, checker.Contains, id[:12])
222
+	c.Assert(out, checker.Contains, stringid.TruncateID(id))
223 223
 }
... ...
@@ -23,7 +23,12 @@ func checkValidGraphDriver(c *check.C, name string) {
23 23
 func (s *DockerSuite) TestInspectImage(c *check.C) {
24 24
 	testRequires(c, DaemonIsLinux)
25 25
 	imageTest := "emptyfs"
26
-	imageTestID := "511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158"
26
+	// It is important that this ID remain stable. If a code change causes
27
+	// it to be different, this is equivalent to a cache bust when pulling
28
+	// a legacy-format manifest. If the check at the end of this function
29
+	// fails, fix the difference in the image serialization instead of
30
+	// updating this hash.
31
+	imageTestID := "sha256:11f64303f0f7ffdc71f001788132bca5346831939a956e3e975c93267d89a16d"
27 32
 	id, err := inspectField(imageTest, "Id")
28 33
 	c.Assert(err, checker.IsNil)
29 34
 
... ...
@@ -159,3 +159,71 @@ func (s *DockerRegistrySuite) TestConcurrentPullMultipleTags(c *check.C) {
159 159
 		c.Assert(strings.TrimSpace(out), check.Equals, "/bin/sh -c echo "+repo, check.Commentf("CMD did not contain /bin/sh -c echo %s; %s", repo, out))
160 160
 	}
161 161
 }
162
+
163
+// TestPullIDStability verifies that pushing an image and pulling it back
164
+// preserves the image ID.
165
+func (s *DockerRegistrySuite) TestPullIDStability(c *check.C) {
166
+	derivedImage := privateRegistryURL + "/dockercli/id-stability"
167
+	baseImage := "busybox"
168
+
169
+	_, err := buildImage(derivedImage, fmt.Sprintf(`
170
+	    FROM %s
171
+	    ENV derived true
172
+	    ENV asdf true
173
+	    RUN dd if=/dev/zero of=/file bs=1024 count=1024
174
+	    CMD echo %s
175
+	`, baseImage, derivedImage), true)
176
+	if err != nil {
177
+		c.Fatal(err)
178
+	}
179
+
180
+	originalID, err := getIDByName(derivedImage)
181
+	if err != nil {
182
+		c.Fatalf("error inspecting: %v", err)
183
+	}
184
+	dockerCmd(c, "push", derivedImage)
185
+
186
+	// Pull
187
+	out, _ := dockerCmd(c, "pull", derivedImage)
188
+	if strings.Contains(out, "Pull complete") {
189
+		c.Fatalf("repull redownloaded a layer: %s", out)
190
+	}
191
+
192
+	derivedIDAfterPull, err := getIDByName(derivedImage)
193
+	if err != nil {
194
+		c.Fatalf("error inspecting: %v", err)
195
+	}
196
+
197
+	if derivedIDAfterPull != originalID {
198
+		c.Fatal("image's ID unexpectedly changed after a repush/repull")
199
+	}
200
+
201
+	// Make sure the image runs correctly
202
+	out, _ = dockerCmd(c, "run", "--rm", derivedImage)
203
+	if strings.TrimSpace(out) != derivedImage {
204
+		c.Fatalf("expected %s; got %s", derivedImage, out)
205
+	}
206
+
207
+	// Confirm that repushing and repulling does not change the computed ID
208
+	dockerCmd(c, "push", derivedImage)
209
+	dockerCmd(c, "rmi", derivedImage)
210
+	dockerCmd(c, "pull", derivedImage)
211
+
212
+	derivedIDAfterPull, err = getIDByName(derivedImage)
213
+	if err != nil {
214
+		c.Fatalf("error inspecting: %v", err)
215
+	}
216
+
217
+	if derivedIDAfterPull != originalID {
218
+		c.Fatal("image's ID unexpectedly changed after a repush/repull")
219
+	}
220
+	if err != nil {
221
+		c.Fatalf("error inspecting: %v", err)
222
+	}
223
+
224
+	// Make sure the image still runs
225
+	out, _ = dockerCmd(c, "run", "--rm", derivedImage)
226
+	if strings.TrimSpace(out) != derivedImage {
227
+		c.Fatalf("expected %s; got %s", derivedImage, out)
228
+	}
229
+}
... ...
@@ -1,11 +1,7 @@
1 1
 package main
2 2
 
3 3
 import (
4
-	"encoding/json"
5 4
 	"fmt"
6
-	"io/ioutil"
7
-	"os"
8
-	"path/filepath"
9 5
 	"regexp"
10 6
 	"strings"
11 7
 	"time"
... ...
@@ -46,19 +42,19 @@ func (s *DockerHubPullSuite) TestPullFromCentralRegistry(c *check.C) {
46 46
 func (s *DockerHubPullSuite) TestPullNonExistingImage(c *check.C) {
47 47
 	testRequires(c, DaemonIsLinux)
48 48
 	for _, e := range []struct {
49
-		Image string
49
+		Repo  string
50 50
 		Alias string
51 51
 	}{
52
-		{"library/asdfasdf:foobar", "asdfasdf:foobar"},
53
-		{"library/asdfasdf:foobar", "library/asdfasdf:foobar"},
54
-		{"library/asdfasdf:latest", "asdfasdf"},
55
-		{"library/asdfasdf:latest", "asdfasdf:latest"},
56
-		{"library/asdfasdf:latest", "library/asdfasdf"},
57
-		{"library/asdfasdf:latest", "library/asdfasdf:latest"},
52
+		{"library/asdfasdf", "asdfasdf:foobar"},
53
+		{"library/asdfasdf", "library/asdfasdf:foobar"},
54
+		{"library/asdfasdf", "asdfasdf"},
55
+		{"library/asdfasdf", "asdfasdf:latest"},
56
+		{"library/asdfasdf", "library/asdfasdf"},
57
+		{"library/asdfasdf", "library/asdfasdf:latest"},
58 58
 	} {
59 59
 		out, err := s.CmdWithError("pull", e.Alias)
60 60
 		c.Assert(err, checker.NotNil, check.Commentf("expected non-zero exit status when pulling non-existing image: %s", out))
61
-		c.Assert(out, checker.Contains, fmt.Sprintf("Error: image %s not found", e.Image), check.Commentf("expected image not found error messages"))
61
+		c.Assert(out, checker.Contains, fmt.Sprintf("Error: image %s not found", e.Repo), check.Commentf("expected image not found error messages"))
62 62
 	}
63 63
 }
64 64
 
... ...
@@ -163,254 +159,3 @@ func (s *DockerHubPullSuite) TestPullClientDisconnect(c *check.C) {
163 163
 		time.Sleep(500 * time.Millisecond)
164 164
 	}
165 165
 }
166
-
167
-type idAndParent struct {
168
-	ID     string
169
-	Parent string
170
-}
171
-
172
-func inspectImage(c *check.C, imageRef string) idAndParent {
173
-	out, _ := dockerCmd(c, "inspect", imageRef)
174
-	var inspectOutput []idAndParent
175
-	err := json.Unmarshal([]byte(out), &inspectOutput)
176
-	if err != nil {
177
-		c.Fatal(err)
178
-	}
179
-
180
-	return inspectOutput[0]
181
-}
182
-
183
-func imageID(c *check.C, imageRef string) string {
184
-	return inspectImage(c, imageRef).ID
185
-}
186
-
187
-func imageParent(c *check.C, imageRef string) string {
188
-	return inspectImage(c, imageRef).Parent
189
-}
190
-
191
-// TestPullMigration verifies that pulling an image based on layers
192
-// that already exists locally will reuse those existing layers.
193
-func (s *DockerRegistrySuite) TestPullMigration(c *check.C) {
194
-	repoName := privateRegistryURL + "/dockercli/migration"
195
-
196
-	baseImage := repoName + ":base"
197
-	_, err := buildImage(baseImage, fmt.Sprintf(`
198
-	    FROM scratch
199
-	    ENV IMAGE base
200
-	    CMD echo %s
201
-	`, baseImage), true)
202
-	if err != nil {
203
-		c.Fatal(err)
204
-	}
205
-
206
-	baseIDBeforePush := imageID(c, baseImage)
207
-	baseParentBeforePush := imageParent(c, baseImage)
208
-
209
-	derivedImage := repoName + ":derived"
210
-	_, err = buildImage(derivedImage, fmt.Sprintf(`
211
-	    FROM %s
212
-	    CMD echo %s
213
-	`, baseImage, derivedImage), true)
214
-	if err != nil {
215
-		c.Fatal(err)
216
-	}
217
-
218
-	derivedIDBeforePush := imageID(c, derivedImage)
219
-
220
-	dockerCmd(c, "push", derivedImage)
221
-
222
-	// Remove derived image from the local store
223
-	dockerCmd(c, "rmi", derivedImage)
224
-
225
-	// Repull
226
-	dockerCmd(c, "pull", derivedImage)
227
-
228
-	// Check that the parent of this pulled image is the original base
229
-	// image
230
-	derivedIDAfterPull1 := imageID(c, derivedImage)
231
-	derivedParentAfterPull1 := imageParent(c, derivedImage)
232
-
233
-	if derivedIDAfterPull1 == derivedIDBeforePush {
234
-		c.Fatal("image's ID should have changed on after deleting and pulling")
235
-	}
236
-
237
-	if derivedParentAfterPull1 != baseIDBeforePush {
238
-		c.Fatalf("pulled image's parent ID (%s) does not match base image's ID (%s)", derivedParentAfterPull1, baseIDBeforePush)
239
-	}
240
-
241
-	// Confirm that repushing and repulling does not change the computed ID
242
-	dockerCmd(c, "push", derivedImage)
243
-	dockerCmd(c, "rmi", derivedImage)
244
-	dockerCmd(c, "pull", derivedImage)
245
-
246
-	derivedIDAfterPull2 := imageID(c, derivedImage)
247
-	derivedParentAfterPull2 := imageParent(c, derivedImage)
248
-
249
-	if derivedIDAfterPull2 != derivedIDAfterPull1 {
250
-		c.Fatal("image's ID unexpectedly changed after a repush/repull")
251
-	}
252
-
253
-	if derivedParentAfterPull2 != baseIDBeforePush {
254
-		c.Fatalf("pulled image's parent ID (%s) does not match base image's ID (%s)", derivedParentAfterPull2, baseIDBeforePush)
255
-	}
256
-
257
-	// Remove everything, repull, and make sure everything uses computed IDs
258
-	dockerCmd(c, "rmi", baseImage, derivedImage)
259
-	dockerCmd(c, "pull", derivedImage)
260
-
261
-	derivedIDAfterPull3 := imageID(c, derivedImage)
262
-	derivedParentAfterPull3 := imageParent(c, derivedImage)
263
-	derivedGrandparentAfterPull3 := imageParent(c, derivedParentAfterPull3)
264
-
265
-	if derivedIDAfterPull3 != derivedIDAfterPull1 {
266
-		c.Fatal("image's ID unexpectedly changed after a second repull")
267
-	}
268
-
269
-	if derivedParentAfterPull3 == baseIDBeforePush {
270
-		c.Fatalf("pulled image's parent ID (%s) should not match base image's original ID (%s)", derivedParentAfterPull3, derivedIDBeforePush)
271
-	}
272
-
273
-	if derivedGrandparentAfterPull3 == baseParentBeforePush {
274
-		c.Fatal("base image's parent ID should have been rewritten on pull")
275
-	}
276
-}
277
-
278
-// TestPullMigrationRun verifies that pulling an image based on layers
279
-// that already exists locally will result in an image that runs properly.
280
-func (s *DockerRegistrySuite) TestPullMigrationRun(c *check.C) {
281
-	type idAndParent struct {
282
-		ID     string
283
-		Parent string
284
-	}
285
-
286
-	derivedImage := privateRegistryURL + "/dockercli/migration-run"
287
-	baseImage := "busybox"
288
-
289
-	_, err := buildImage(derivedImage, fmt.Sprintf(`
290
-	    FROM %s
291
-	    RUN dd if=/dev/zero of=/file bs=1024 count=1024
292
-	    CMD echo %s
293
-	`, baseImage, derivedImage), true)
294
-	if err != nil {
295
-		c.Fatal(err)
296
-	}
297
-
298
-	baseIDBeforePush := imageID(c, baseImage)
299
-	derivedIDBeforePush := imageID(c, derivedImage)
300
-
301
-	dockerCmd(c, "push", derivedImage)
302
-
303
-	// Remove derived image from the local store
304
-	dockerCmd(c, "rmi", derivedImage)
305
-
306
-	// Repull
307
-	dockerCmd(c, "pull", derivedImage)
308
-
309
-	// Check that this pulled image is based on the original base image
310
-	derivedIDAfterPull1 := imageID(c, derivedImage)
311
-	derivedParentAfterPull1 := imageParent(c, imageParent(c, derivedImage))
312
-
313
-	if derivedIDAfterPull1 == derivedIDBeforePush {
314
-		c.Fatal("image's ID should have changed on after deleting and pulling")
315
-	}
316
-
317
-	if derivedParentAfterPull1 != baseIDBeforePush {
318
-		c.Fatalf("pulled image's parent ID (%s) does not match base image's ID (%s)", derivedParentAfterPull1, baseIDBeforePush)
319
-	}
320
-
321
-	// Make sure the image runs correctly
322
-	out, _ := dockerCmd(c, "run", "--rm", derivedImage)
323
-	if strings.TrimSpace(out) != derivedImage {
324
-		c.Fatalf("expected %s; got %s", derivedImage, out)
325
-	}
326
-
327
-	// Confirm that repushing and repulling does not change the computed ID
328
-	dockerCmd(c, "push", derivedImage)
329
-	dockerCmd(c, "rmi", derivedImage)
330
-	dockerCmd(c, "pull", derivedImage)
331
-
332
-	derivedIDAfterPull2 := imageID(c, derivedImage)
333
-	derivedParentAfterPull2 := imageParent(c, imageParent(c, derivedImage))
334
-
335
-	if derivedIDAfterPull2 != derivedIDAfterPull1 {
336
-		c.Fatal("image's ID unexpectedly changed after a repush/repull")
337
-	}
338
-
339
-	if derivedParentAfterPull2 != baseIDBeforePush {
340
-		c.Fatalf("pulled image's parent ID (%s) does not match base image's ID (%s)", derivedParentAfterPull2, baseIDBeforePush)
341
-	}
342
-
343
-	// Make sure the image still runs
344
-	out, _ = dockerCmd(c, "run", "--rm", derivedImage)
345
-	if strings.TrimSpace(out) != derivedImage {
346
-		c.Fatalf("expected %s; got %s", derivedImage, out)
347
-	}
348
-}
349
-
350
-// TestPullConflict provides coverage of the situation where a computed
351
-// strongID conflicts with some unverifiable data in the graph.
352
-func (s *DockerRegistrySuite) TestPullConflict(c *check.C) {
353
-	repoName := privateRegistryURL + "/dockercli/conflict"
354
-
355
-	_, err := buildImage(repoName, `
356
-	    FROM scratch
357
-	    ENV IMAGE conflict
358
-	    CMD echo conflict
359
-	`, true)
360
-	if err != nil {
361
-		c.Fatal(err)
362
-	}
363
-
364
-	dockerCmd(c, "push", repoName)
365
-
366
-	// Pull to make it content-addressable
367
-	dockerCmd(c, "rmi", repoName)
368
-	dockerCmd(c, "pull", repoName)
369
-
370
-	IDBeforeLoad := imageID(c, repoName)
371
-
372
-	// Load/save to turn this into an unverified image with the same ID
373
-	tmpDir, err := ioutil.TempDir("", "conflict-save-output")
374
-	if err != nil {
375
-		c.Errorf("failed to create temporary directory: %s", err)
376
-	}
377
-	defer os.RemoveAll(tmpDir)
378
-
379
-	tarFile := filepath.Join(tmpDir, "repo.tar")
380
-
381
-	dockerCmd(c, "save", "-o", tarFile, repoName)
382
-	dockerCmd(c, "rmi", repoName)
383
-	dockerCmd(c, "load", "-i", tarFile)
384
-
385
-	// Check that the the ID is the same after save/load.
386
-	IDAfterLoad := imageID(c, repoName)
387
-
388
-	if IDAfterLoad != IDBeforeLoad {
389
-		c.Fatal("image's ID should be the same after save/load")
390
-	}
391
-
392
-	// Repull
393
-	dockerCmd(c, "pull", repoName)
394
-
395
-	// Check that the ID is now different because of the conflict.
396
-	IDAfterPull1 := imageID(c, repoName)
397
-
398
-	// Expect the new ID to be SHA256(oldID)
399
-	expectedIDDigest, err := digest.FromBytes([]byte(IDBeforeLoad))
400
-	if err != nil {
401
-		c.Fatalf("digest error: %v", err)
402
-	}
403
-	expectedID := expectedIDDigest.Hex()
404
-	if IDAfterPull1 != expectedID {
405
-		c.Fatalf("image's ID should have changed on pull to %s (got %s)", expectedID, IDAfterPull1)
406
-	}
407
-
408
-	// A second pull should use the new ID again.
409
-	dockerCmd(c, "pull", repoName)
410
-
411
-	IDAfterPull2 := imageID(c, repoName)
412
-
413
-	if IDAfterPull2 != IDAfterPull1 {
414
-		c.Fatal("image's ID unexpectedly changed after a repull")
415
-	}
416
-}
... ...
@@ -2,16 +2,13 @@ package main
2 2
 
3 3
 import (
4 4
 	"archive/tar"
5
-	"encoding/json"
6 5
 	"fmt"
7 6
 	"io/ioutil"
8 7
 	"os"
9 8
 	"os/exec"
10
-	"path/filepath"
11 9
 	"strings"
12 10
 	"time"
13 11
 
14
-	"github.com/docker/docker/image"
15 12
 	"github.com/docker/docker/pkg/integration/checker"
16 13
 	"github.com/go-check/check"
17 14
 )
... ...
@@ -86,46 +83,6 @@ func (s *DockerRegistrySuite) TestPushMultipleTags(c *check.C) {
86 86
 	}
87 87
 }
88 88
 
89
-// TestPushBadParentChain tries to push an image with a corrupted parent chain
90
-// in the v1compatibility files, and makes sure the push process fixes it.
91
-func (s *DockerRegistrySuite) TestPushBadParentChain(c *check.C) {
92
-	repoName := fmt.Sprintf("%v/dockercli/badparent", privateRegistryURL)
93
-
94
-	id, err := buildImage(repoName, `
95
-	    FROM busybox
96
-	    CMD echo "adding another layer"
97
-	    `, true)
98
-	if err != nil {
99
-		c.Fatal(err)
100
-	}
101
-
102
-	// Push to create v1compatibility file
103
-	dockerCmd(c, "push", repoName)
104
-
105
-	// Corrupt the parent in the v1compatibility file from the top layer
106
-	filename := filepath.Join(dockerBasePath, "graph", id, "v1Compatibility")
107
-
108
-	jsonBytes, err := ioutil.ReadFile(filename)
109
-	c.Assert(err, check.IsNil, check.Commentf("Could not read v1Compatibility file: %s", err))
110
-
111
-	var img image.Image
112
-	err = json.Unmarshal(jsonBytes, &img)
113
-	c.Assert(err, check.IsNil, check.Commentf("Could not unmarshal json: %s", err))
114
-
115
-	img.Parent = "1234123412341234123412341234123412341234123412341234123412341234"
116
-
117
-	jsonBytes, err = json.Marshal(&img)
118
-	c.Assert(err, check.IsNil, check.Commentf("Could not marshal json: %s", err))
119
-
120
-	err = ioutil.WriteFile(filename, jsonBytes, 0600)
121
-	c.Assert(err, check.IsNil, check.Commentf("Could not write v1Compatibility file: %s", err))
122
-
123
-	dockerCmd(c, "push", repoName)
124
-
125
-	// pull should succeed
126
-	dockerCmd(c, "pull", repoName)
127
-}
128
-
129 89
 func (s *DockerRegistrySuite) TestPushEmptyLayer(c *check.C) {
130 90
 	repoName := fmt.Sprintf("%v/dockercli/emptylayer", privateRegistryURL)
131 91
 	emptyTarball, err := ioutil.TempFile("", "empty_tarball")
... ...
@@ -6,6 +6,7 @@ import (
6 6
 	"strings"
7 7
 
8 8
 	"github.com/docker/docker/pkg/integration/checker"
9
+	"github.com/docker/docker/pkg/stringid"
9 10
 	"github.com/go-check/check"
10 11
 )
11 12
 
... ...
@@ -85,7 +86,7 @@ func (s *DockerSuite) TestRmiImgIDMultipleTag(c *check.C) {
85 85
 
86 86
 	// first checkout without force it fails
87 87
 	out, _, err = dockerCmdWithError("rmi", imgID)
88
-	expected := fmt.Sprintf("conflict: unable to delete %s (cannot be forced) - image is being used by running container %s", imgID[:12], containerID[:12])
88
+	expected := fmt.Sprintf("conflict: unable to delete %s (cannot be forced) - image is being used by running container %s", stringid.TruncateID(imgID), stringid.TruncateID(containerID))
89 89
 	// rmi tagged in multiple repos should have failed without force
90 90
 	c.Assert(err, checker.NotNil)
91 91
 	c.Assert(out, checker.Contains, expected)
... ...
@@ -3749,3 +3749,15 @@ func (s *DockerSuite) TestDockerFails(c *check.C) {
3749 3749
 		c.Fatalf("Docker run with flag not defined should exit with 125, but we got out: %s, exit: %d, err: %s", out, exit, err)
3750 3750
 	}
3751 3751
 }
3752
+
3753
+// TestRunInvalidReference invokes docker run with a bad reference.
3754
+func (s *DockerSuite) TestRunInvalidReference(c *check.C) {
3755
+	out, exit, _ := dockerCmdWithError("run", "busybox@foo")
3756
+	if exit == 0 {
3757
+		c.Fatalf("expected non-zero exist code; received %d", exit)
3758
+	}
3759
+
3760
+	if !strings.Contains(out, "invalid reference format") {
3761
+		c.Fatalf(`Expected "invalid reference format" in output; got: %s`, out)
3762
+	}
3763
+}
... ...
@@ -8,10 +8,12 @@ import (
8 8
 	"os/exec"
9 9
 	"path/filepath"
10 10
 	"reflect"
11
+	"regexp"
11 12
 	"sort"
12 13
 	"strings"
13 14
 	"time"
14 15
 
16
+	"github.com/docker/distribution/digest"
15 17
 	"github.com/docker/docker/pkg/integration/checker"
16 18
 	"github.com/go-check/check"
17 19
 )
... ...
@@ -100,7 +102,7 @@ func (s *DockerSuite) TestSaveCheckTimes(c *check.C) {
100 100
 	out, _, err = runCommandPipelineWithOutput(
101 101
 		exec.Command(dockerBinary, "save", repoName),
102 102
 		exec.Command("tar", "tv"),
103
-		exec.Command("grep", "-E", fmt.Sprintf("%s %s", data[0].Created.Format(tarTvTimeFormat), data[0].ID)))
103
+		exec.Command("grep", "-E", fmt.Sprintf("%s %s", data[0].Created.Format(tarTvTimeFormat), digest.Digest(data[0].ID).Hex())))
104 104
 	c.Assert(err, checker.IsNil, check.Commentf("failed to save repo with image ID and 'repositories' file: %s, %v", out, err))
105 105
 }
106 106
 
... ...
@@ -110,7 +112,7 @@ func (s *DockerSuite) TestSaveImageId(c *check.C) {
110 110
 	dockerCmd(c, "tag", "emptyfs:latest", fmt.Sprintf("%v:latest", repoName))
111 111
 
112 112
 	out, _ := dockerCmd(c, "images", "-q", "--no-trunc", repoName)
113
-	cleanedLongImageID := strings.TrimSpace(out)
113
+	cleanedLongImageID := strings.TrimPrefix(strings.TrimSpace(out), "sha256:")
114 114
 
115 115
 	out, _ = dockerCmd(c, "images", "-q", repoName)
116 116
 	cleanedShortImageID := strings.TrimSpace(out)
... ...
@@ -207,20 +209,30 @@ func (s *DockerSuite) TestSaveRepoWithMultipleImages(c *check.C) {
207 207
 
208 208
 	// create the archive
209 209
 	out, _, err := runCommandPipelineWithOutput(
210
-		exec.Command(dockerBinary, "save", repoName),
211
-		exec.Command("tar", "t"),
212
-		exec.Command("grep", "VERSION"),
213
-		exec.Command("cut", "-d", "/", "-f1"))
210
+		exec.Command(dockerBinary, "save", repoName, "busybox:latest"),
211
+		exec.Command("tar", "t"))
214 212
 	c.Assert(err, checker.IsNil, check.Commentf("failed to save multiple images: %s, %v", out, err))
215
-	actual := strings.Split(strings.TrimSpace(out), "\n")
213
+
214
+	lines := strings.Split(strings.TrimSpace(out), "\n")
215
+	var actual []string
216
+	for _, l := range lines {
217
+		if regexp.MustCompile("^[a-f0-9]{64}\\.json$").Match([]byte(l)) {
218
+			actual = append(actual, strings.TrimSuffix(l, ".json"))
219
+		}
220
+	}
216 221
 
217 222
 	// make the list of expected layers
218
-	out, _ = dockerCmd(c, "history", "-q", "--no-trunc", "busybox:latest")
219
-	expected := append(strings.Split(strings.TrimSpace(out), "\n"), idFoo, idBar)
223
+	out, _ = dockerCmd(c, "inspect", "-f", "{{.Id}}", "busybox:latest")
224
+	expected := []string{strings.TrimSpace(out), idFoo, idBar}
225
+
226
+	// prefixes are not in tar
227
+	for i := range expected {
228
+		expected[i] = digest.Digest(expected[i]).Hex()
229
+	}
220 230
 
221 231
 	sort.Strings(actual)
222 232
 	sort.Strings(expected)
223
-	c.Assert(actual, checker.DeepEquals, expected, check.Commentf("archive does not contains the right layers: got %v, expected %v", actual, expected))
233
+	c.Assert(actual, checker.DeepEquals, expected, check.Commentf("archive does not contains the right layers: got %v, expected %v, output: %q", actual, expected, out))
224 234
 }
225 235
 
226 236
 // Issue #6722 #5892 ensure directories are included in changes
... ...
@@ -18,9 +18,7 @@ func (s *DockerSuite) TestSaveAndLoadRepoStdout(c *check.C) {
18 18
 	dockerCmd(c, "run", "--name", name, "busybox", "true")
19 19
 
20 20
 	repoName := "foobar-save-load-test"
21
-	out, _ := dockerCmd(c, "commit", name, repoName)
22
-
23
-	before, _ := dockerCmd(c, "inspect", repoName)
21
+	before, _ := dockerCmd(c, "commit", name, repoName)
24 22
 
25 23
 	tmpFile, err := ioutil.TempFile("", "foobar-save-load-test.tar")
26 24
 	c.Assert(err, check.IsNil)
... ...
@@ -40,10 +38,10 @@ func (s *DockerSuite) TestSaveAndLoadRepoStdout(c *check.C) {
40 40
 	loadCmd := exec.Command(dockerBinary, "load")
41 41
 	loadCmd.Stdin = tmpFile
42 42
 
43
-	out, _, err = runCommandWithOutput(loadCmd)
43
+	out, _, err := runCommandWithOutput(loadCmd)
44 44
 	c.Assert(err, check.IsNil, check.Commentf(out))
45 45
 
46
-	after, _ := dockerCmd(c, "inspect", repoName)
46
+	after, _ := dockerCmd(c, "inspect", "-f", "{{.Id}}", repoName)
47 47
 
48 48
 	c.Assert(before, check.Equals, after) //inspect is not the same after a save / load
49 49
 
... ...
@@ -1,9 +1,11 @@
1 1
 package main
2 2
 
3 3
 import (
4
+	"fmt"
4 5
 	"strings"
5 6
 
6 7
 	"github.com/docker/docker/pkg/integration/checker"
8
+	"github.com/docker/docker/pkg/stringid"
7 9
 	"github.com/docker/docker/pkg/stringutils"
8 10
 	"github.com/go-check/check"
9 11
 )
... ...
@@ -111,7 +113,7 @@ func (s *DockerSuite) TestTagWithPrefixHyphen(c *check.C) {
111 111
 	// test index name begin with '-'
112 112
 	out, _, err = dockerCmdWithError("tag", "busybox:latest", "-index:5000/busybox:test")
113 113
 	c.Assert(err, checker.NotNil, check.Commentf(out))
114
-	c.Assert(out, checker.Contains, "Invalid index name (-index:5000). Cannot begin or end with a hyphen", check.Commentf("tag a name begin with '-' should failed"))
114
+	c.Assert(out, checker.Contains, "invalid reference format", check.Commentf("tag a name begin with '-' should failed"))
115 115
 }
116 116
 
117 117
 // ensure tagging using official names works
... ...
@@ -171,3 +173,57 @@ func (s *DockerSuite) TestTagMatchesDigest(c *check.C) {
171 171
 		c.Fatal("inspecting by digest should have failed")
172 172
 	}
173 173
 }
174
+
175
+func (s *DockerSuite) TestTagInvalidRepoName(c *check.C) {
176
+	testRequires(c, DaemonIsLinux)
177
+	if err := pullImageIfNotExist("busybox:latest"); err != nil {
178
+		c.Fatal("couldn't find the busybox:latest image locally and failed to pull it")
179
+	}
180
+
181
+	// test setting tag fails
182
+	_, _, err := dockerCmdWithError("tag", "-f", "busybox:latest", "sha256:sometag")
183
+	if err == nil {
184
+		c.Fatal("tagging with image named \"sha256\" should have failed")
185
+	}
186
+}
187
+
188
+// ensure tags cannot create ambiguity with image ids
189
+func (s *DockerSuite) TestTagTruncationAmbiguity(c *check.C) {
190
+	testRequires(c, DaemonIsLinux)
191
+	if err := pullImageIfNotExist("busybox:latest"); err != nil {
192
+		c.Fatal("couldn't find the busybox:latest image locally and failed to pull it")
193
+	}
194
+
195
+	imageID, err := buildImage("notbusybox:latest",
196
+		`FROM busybox
197
+		MAINTAINER dockerio`,
198
+		true)
199
+	if err != nil {
200
+		c.Fatal(err)
201
+	}
202
+	truncatedImageID := stringid.TruncateID(imageID)
203
+	truncatedTag := fmt.Sprintf("notbusybox:%s", truncatedImageID)
204
+
205
+	id, err := inspectField(truncatedTag, "Id")
206
+	if err != nil {
207
+		c.Fatalf("Error inspecting by image id: %s", err)
208
+	}
209
+
210
+	// Ensure inspect by image id returns image for image id
211
+	c.Assert(id, checker.Equals, imageID)
212
+	c.Logf("Built image: %s", imageID)
213
+
214
+	// test setting tag fails
215
+	_, _, err = dockerCmdWithError("tag", "-f", "busybox:latest", truncatedTag)
216
+	if err != nil {
217
+		c.Fatalf("Error tagging with an image id: %s", err)
218
+	}
219
+
220
+	id, err = inspectField(truncatedTag, "Id")
221
+	if err != nil {
222
+		c.Fatalf("Error inspecting by image id: %s", err)
223
+	}
224
+
225
+	// Ensure id is imageID and not busybox:latest
226
+	c.Assert(id, checker.Not(checker.Equals), imageID)
227
+}
... ...
@@ -110,26 +110,6 @@ func ParseTCPAddr(tryAddr string, defaultAddr string) (string, error) {
110 110
 	return fmt.Sprintf("tcp://%s%s", net.JoinHostPort(host, port), u.Path), nil
111 111
 }
112 112
 
113
-// ParseRepositoryTag gets a repos name and returns the right reposName + tag|digest
114
-// The tag can be confusing because of a port in a repository name.
115
-//     Ex: localhost.localdomain:5000/samalba/hipache:latest
116
-//     Digest ex: localhost:5000/foo/bar@sha256:bc8813ea7b3603864987522f02a76101c17ad122e1c46d790efc0fca78ca7bfb
117
-func ParseRepositoryTag(repos string) (string, string) {
118
-	n := strings.Index(repos, "@")
119
-	if n >= 0 {
120
-		parts := strings.Split(repos, "@")
121
-		return parts[0], parts[1]
122
-	}
123
-	n = strings.LastIndex(repos, ":")
124
-	if n < 0 {
125
-		return repos, ""
126
-	}
127
-	if tag := repos[n+1:]; !strings.Contains(tag, "/") {
128
-		return repos[:n], tag
129
-	}
130
-	return repos, ""
131
-}
132
-
133 113
 // PartParser parses and validates the specified string (data) using the specified template
134 114
 // e.g. ip:public:private -> 192.168.0.1:80:8000
135 115
 func PartParser(template, data string) (map[string]string, error) {
... ...
@@ -120,36 +120,6 @@ func TestParseInvalidUnixAddrInvalid(t *testing.T) {
120 120
 	}
121 121
 }
122 122
 
123
-func TestParseRepositoryTag(t *testing.T) {
124
-	if repo, tag := ParseRepositoryTag("root"); repo != "root" || tag != "" {
125
-		t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "root", "", repo, tag)
126
-	}
127
-	if repo, tag := ParseRepositoryTag("root:tag"); repo != "root" || tag != "tag" {
128
-		t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "root", "tag", repo, tag)
129
-	}
130
-	if repo, digest := ParseRepositoryTag("root@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); repo != "root" || digest != "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" {
131
-		t.Errorf("Expected repo: '%s' and digest: '%s', got '%s' and '%s'", "root", "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", repo, digest)
132
-	}
133
-	if repo, tag := ParseRepositoryTag("user/repo"); repo != "user/repo" || tag != "" {
134
-		t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "user/repo", "", repo, tag)
135
-	}
136
-	if repo, tag := ParseRepositoryTag("user/repo:tag"); repo != "user/repo" || tag != "tag" {
137
-		t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "user/repo", "tag", repo, tag)
138
-	}
139
-	if repo, digest := ParseRepositoryTag("user/repo@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); repo != "user/repo" || digest != "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" {
140
-		t.Errorf("Expected repo: '%s' and digest: '%s', got '%s' and '%s'", "user/repo", "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", repo, digest)
141
-	}
142
-	if repo, tag := ParseRepositoryTag("url:5000/repo"); repo != "url:5000/repo" || tag != "" {
143
-		t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "url:5000/repo", "", repo, tag)
144
-	}
145
-	if repo, tag := ParseRepositoryTag("url:5000/repo:tag"); repo != "url:5000/repo" || tag != "tag" {
146
-		t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "url:5000/repo", "tag", repo, tag)
147
-	}
148
-	if repo, digest := ParseRepositoryTag("url:5000/repo@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); repo != "url:5000/repo" || digest != "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" {
149
-		t.Errorf("Expected repo: '%s' and digest: '%s', got '%s' and '%s'", "url:5000/repo", "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", repo, digest)
150
-	}
151
-}
152
-
153 123
 func TestParseKeyValueOpt(t *testing.T) {
154 124
 	invalids := map[string]string{
155 125
 		"":    "Unable to parse key/value option: ",
... ...
@@ -7,6 +7,7 @@ import (
7 7
 	"io"
8 8
 	"regexp"
9 9
 	"strconv"
10
+	"strings"
10 11
 
11 12
 	"github.com/docker/docker/pkg/random"
12 13
 )
... ...
@@ -25,6 +26,9 @@ func IsShortID(id string) bool {
25 25
 // In case of a collision a lookup with TruncIndex.Get() will fail, and the caller
26 26
 // will need to use a langer prefix, or the full-length Id.
27 27
 func TruncateID(id string) string {
28
+	if i := strings.IndexRune(id, ':'); i >= 0 {
29
+		id = id[i+1:]
30
+	}
28 31
 	trimTo := shortLen
29 32
 	if len(id) < shortLen {
30 33
 		trimTo = len(id)
... ...
@@ -9,7 +9,7 @@ import (
9 9
 	"strings"
10 10
 
11 11
 	"github.com/docker/distribution/reference"
12
-	"github.com/docker/docker/image"
12
+	"github.com/docker/docker/image/v1"
13 13
 	"github.com/docker/docker/opts"
14 14
 	flag "github.com/docker/docker/pkg/mflag"
15 15
 )
... ...
@@ -216,18 +216,15 @@ func ValidateIndexName(val string) (string, error) {
216 216
 	return val, nil
217 217
 }
218 218
 
219
-func validateRemoteName(remoteName string) error {
220
-
221
-	if !strings.Contains(remoteName, "/") {
222
-
219
+func validateRemoteName(remoteName reference.Named) error {
220
+	remoteNameStr := remoteName.Name()
221
+	if !strings.Contains(remoteNameStr, "/") {
223 222
 		// the repository name must not be a valid image ID
224
-		if err := image.ValidateID(remoteName); err == nil {
223
+		if err := v1.ValidateID(remoteNameStr); err == nil {
225 224
 			return fmt.Errorf("Invalid repository name (%s), cannot specify 64-byte hexadecimal strings", remoteName)
226 225
 		}
227 226
 	}
228
-
229
-	_, err := reference.WithName(remoteName)
230
-	return err
227
+	return nil
231 228
 }
232 229
 
233 230
 func validateNoSchema(reposName string) error {
... ...
@@ -239,27 +236,24 @@ func validateNoSchema(reposName string) error {
239 239
 }
240 240
 
241 241
 // ValidateRepositoryName validates a repository name
242
-func ValidateRepositoryName(reposName string) error {
243
-	_, _, err := loadRepositoryName(reposName, true)
242
+func ValidateRepositoryName(reposName reference.Named) error {
243
+	_, _, err := loadRepositoryName(reposName)
244 244
 	return err
245 245
 }
246 246
 
247 247
 // loadRepositoryName returns the repo name splitted into index name
248 248
 // and remote repo name. It returns an error if the name is not valid.
249
-func loadRepositoryName(reposName string, checkRemoteName bool) (string, string, error) {
250
-	if err := validateNoSchema(reposName); err != nil {
251
-		return "", "", err
249
+func loadRepositoryName(reposName reference.Named) (string, reference.Named, error) {
250
+	if err := validateNoSchema(reposName.Name()); err != nil {
251
+		return "", nil, err
252 252
 	}
253
-	indexName, remoteName := splitReposName(reposName)
253
+	indexName, remoteName, err := splitReposName(reposName)
254 254
 
255
-	var err error
256 255
 	if indexName, err = ValidateIndexName(indexName); err != nil {
257
-		return "", "", err
256
+		return "", nil, err
258 257
 	}
259
-	if checkRemoteName {
260
-		if err = validateRemoteName(remoteName); err != nil {
261
-			return "", "", err
262
-		}
258
+	if err = validateRemoteName(remoteName); err != nil {
259
+		return "", nil, err
263 260
 	}
264 261
 	return indexName, remoteName, nil
265 262
 }
... ...
@@ -297,31 +291,36 @@ func (index *IndexInfo) GetAuthConfigKey() string {
297 297
 }
298 298
 
299 299
 // splitReposName breaks a reposName into an index name and remote name
300
-func splitReposName(reposName string) (string, string) {
301
-	nameParts := strings.SplitN(reposName, "/", 2)
302
-	var indexName, remoteName string
303
-	if len(nameParts) == 1 || (!strings.Contains(nameParts[0], ".") &&
304
-		!strings.Contains(nameParts[0], ":") && nameParts[0] != "localhost") {
300
+func splitReposName(reposName reference.Named) (indexName string, remoteName reference.Named, err error) {
301
+	var remoteNameStr string
302
+	indexName, remoteNameStr = reference.SplitHostname(reposName)
303
+	if indexName == "" || (!strings.Contains(indexName, ".") &&
304
+		!strings.Contains(indexName, ":") && indexName != "localhost") {
305 305
 		// This is a Docker Index repos (ex: samalba/hipache or ubuntu)
306 306
 		// 'docker.io'
307 307
 		indexName = IndexName
308 308
 		remoteName = reposName
309 309
 	} else {
310
-		indexName = nameParts[0]
311
-		remoteName = nameParts[1]
310
+		remoteName, err = reference.WithName(remoteNameStr)
312 311
 	}
313
-	return indexName, remoteName
312
+	return
314 313
 }
315 314
 
316 315
 // NewRepositoryInfo validates and breaks down a repository name into a RepositoryInfo
317
-func (config *ServiceConfig) NewRepositoryInfo(reposName string, bySearch bool) (*RepositoryInfo, error) {
318
-	indexName, remoteName, err := loadRepositoryName(reposName, !bySearch)
319
-	if err != nil {
316
+func (config *ServiceConfig) NewRepositoryInfo(reposName reference.Named) (*RepositoryInfo, error) {
317
+	if err := validateNoSchema(reposName.Name()); err != nil {
320 318
 		return nil, err
321 319
 	}
322 320
 
323
-	repoInfo := &RepositoryInfo{
324
-		RemoteName: remoteName,
321
+	repoInfo := &RepositoryInfo{}
322
+	var (
323
+		indexName string
324
+		err       error
325
+	)
326
+
327
+	indexName, repoInfo.RemoteName, err = loadRepositoryName(reposName)
328
+	if err != nil {
329
+		return nil, err
325 330
 	}
326 331
 
327 332
 	repoInfo.Index, err = config.NewIndexInfo(indexName)
... ...
@@ -330,46 +329,47 @@ func (config *ServiceConfig) NewRepositoryInfo(reposName string, bySearch bool)
330 330
 	}
331 331
 
332 332
 	if repoInfo.Index.Official {
333
-		normalizedName := normalizeLibraryRepoName(repoInfo.RemoteName)
333
+		repoInfo.LocalName, err = normalizeLibraryRepoName(repoInfo.RemoteName)
334
+		if err != nil {
335
+			return nil, err
336
+		}
337
+		repoInfo.RemoteName = repoInfo.LocalName
334 338
 
335
-		repoInfo.LocalName = normalizedName
336
-		repoInfo.RemoteName = normalizedName
337 339
 		// If the normalized name does not contain a '/' (e.g. "foo")
338 340
 		// then it is an official repo.
339
-		if strings.IndexRune(normalizedName, '/') == -1 {
341
+		if strings.IndexRune(repoInfo.RemoteName.Name(), '/') == -1 {
340 342
 			repoInfo.Official = true
341 343
 			// Fix up remote name for official repos.
342
-			repoInfo.RemoteName = "library/" + normalizedName
344
+			repoInfo.RemoteName, err = reference.WithName("library/" + repoInfo.RemoteName.Name())
345
+			if err != nil {
346
+				return nil, err
347
+			}
343 348
 		}
344 349
 
345
-		repoInfo.CanonicalName = "docker.io/" + repoInfo.RemoteName
350
+		repoInfo.CanonicalName, err = reference.WithName("docker.io/" + repoInfo.RemoteName.Name())
351
+		if err != nil {
352
+			return nil, err
353
+		}
346 354
 	} else {
347
-		repoInfo.LocalName = localNameFromRemote(repoInfo.Index.Name, repoInfo.RemoteName)
355
+		repoInfo.LocalName, err = localNameFromRemote(repoInfo.Index.Name, repoInfo.RemoteName)
356
+		if err != nil {
357
+			return nil, err
358
+		}
348 359
 		repoInfo.CanonicalName = repoInfo.LocalName
349
-
350 360
 	}
351 361
 
352 362
 	return repoInfo, nil
353 363
 }
354 364
 
355
-// GetSearchTerm special-cases using local name for official index, and
356
-// remote name for private indexes.
357
-func (repoInfo *RepositoryInfo) GetSearchTerm() string {
358
-	if repoInfo.Index.Official {
359
-		return repoInfo.LocalName
360
-	}
361
-	return repoInfo.RemoteName
362
-}
363
-
364 365
 // ParseRepositoryInfo performs the breakdown of a repository name into a RepositoryInfo, but
365 366
 // lacks registry configuration.
366
-func ParseRepositoryInfo(reposName string) (*RepositoryInfo, error) {
367
-	return emptyServiceConfig.NewRepositoryInfo(reposName, false)
367
+func ParseRepositoryInfo(reposName reference.Named) (*RepositoryInfo, error) {
368
+	return emptyServiceConfig.NewRepositoryInfo(reposName)
368 369
 }
369 370
 
370
-// ParseIndexInfo will use repository name to get back an indexInfo.
371
-func ParseIndexInfo(reposName string) (*IndexInfo, error) {
372
-	indexName, _ := splitReposName(reposName)
371
+// ParseSearchIndexInfo will use repository name to get back an indexInfo.
372
+func ParseSearchIndexInfo(reposName string) (*IndexInfo, error) {
373
+	indexName, _ := splitReposSearchTerm(reposName)
373 374
 
374 375
 	indexInfo, err := emptyServiceConfig.NewIndexInfo(indexName)
375 376
 	if err != nil {
... ...
@@ -378,12 +378,12 @@ func ParseIndexInfo(reposName string) (*IndexInfo, error) {
378 378
 	return indexInfo, nil
379 379
 }
380 380
 
381
-// NormalizeLocalName transforms a repository name into a normalize LocalName
381
+// NormalizeLocalName transforms a repository name into a normalized LocalName
382 382
 // Passes through the name without transformation on error (image id, etc)
383 383
 // It does not use the repository info because we don't want to load
384 384
 // the repository index and do request over the network.
385
-func NormalizeLocalName(name string) string {
386
-	indexName, remoteName, err := loadRepositoryName(name, true)
385
+func NormalizeLocalName(name reference.Named) reference.Named {
386
+	indexName, remoteName, err := loadRepositoryName(name)
387 387
 	if err != nil {
388 388
 		return name
389 389
 	}
... ...
@@ -395,23 +395,52 @@ func NormalizeLocalName(name string) string {
395 395
 	}
396 396
 
397 397
 	if officialIndex {
398
-		return normalizeLibraryRepoName(remoteName)
398
+		localName, err := normalizeLibraryRepoName(remoteName)
399
+		if err != nil {
400
+			return name
401
+		}
402
+		return localName
399 403
 	}
400
-	return localNameFromRemote(indexName, remoteName)
404
+	localName, err := localNameFromRemote(indexName, remoteName)
405
+	if err != nil {
406
+		return name
407
+	}
408
+	return localName
401 409
 }
402 410
 
403 411
 // normalizeLibraryRepoName removes the library prefix from
404 412
 // the repository name for official repos.
405
-func normalizeLibraryRepoName(name string) string {
406
-	if strings.HasPrefix(name, "library/") {
413
+func normalizeLibraryRepoName(name reference.Named) (reference.Named, error) {
414
+	if strings.HasPrefix(name.Name(), "library/") {
407 415
 		// If pull "library/foo", it's stored locally under "foo"
408
-		name = strings.SplitN(name, "/", 2)[1]
416
+		return reference.WithName(strings.SplitN(name.Name(), "/", 2)[1])
409 417
 	}
410
-	return name
418
+	return name, nil
411 419
 }
412 420
 
413 421
 // localNameFromRemote combines the index name and the repo remote name
414 422
 // to generate a repo local name.
415
-func localNameFromRemote(indexName, remoteName string) string {
416
-	return indexName + "/" + remoteName
423
+func localNameFromRemote(indexName string, remoteName reference.Named) (reference.Named, error) {
424
+	return reference.WithName(indexName + "/" + remoteName.Name())
425
+}
426
+
427
+// NormalizeLocalReference transforms a reference to use a normalized LocalName
428
+// for the name poriton. Passes through the reference without transformation on
429
+// error.
430
+func NormalizeLocalReference(ref reference.Named) reference.Named {
431
+	localName := NormalizeLocalName(ref)
432
+	if tagged, isTagged := ref.(reference.Tagged); isTagged {
433
+		newRef, err := reference.WithTag(localName, tagged.Tag())
434
+		if err != nil {
435
+			return ref
436
+		}
437
+		return newRef
438
+	} else if digested, isDigested := ref.(reference.Digested); isDigested {
439
+		newRef, err := reference.WithDigest(localName, digested.Digest())
440
+		if err != nil {
441
+			return ref
442
+		}
443
+		return newRef
444
+	}
445
+	return localName
417 446
 }
... ...
@@ -15,6 +15,7 @@ import (
15 15
 	"testing"
16 16
 	"time"
17 17
 
18
+	"github.com/docker/distribution/reference"
18 19
 	"github.com/docker/docker/opts"
19 20
 	"github.com/gorilla/mux"
20 21
 
... ...
@@ -349,15 +350,19 @@ func handlerGetDeleteTags(w http.ResponseWriter, r *http.Request) {
349 349
 	if !requiresAuth(w, r) {
350 350
 		return
351 351
 	}
352
-	repositoryName := mux.Vars(r)["repository"]
352
+	repositoryName, err := reference.WithName(mux.Vars(r)["repository"])
353
+	if err != nil {
354
+		apiError(w, "Could not parse repository", 400)
355
+		return
356
+	}
353 357
 	repositoryName = NormalizeLocalName(repositoryName)
354
-	tags, exists := testRepositories[repositoryName]
358
+	tags, exists := testRepositories[repositoryName.String()]
355 359
 	if !exists {
356 360
 		apiError(w, "Repository not found", 404)
357 361
 		return
358 362
 	}
359 363
 	if r.Method == "DELETE" {
360
-		delete(testRepositories, repositoryName)
364
+		delete(testRepositories, repositoryName.String())
361 365
 		writeResponse(w, true, 200)
362 366
 		return
363 367
 	}
... ...
@@ -369,10 +374,14 @@ func handlerGetTag(w http.ResponseWriter, r *http.Request) {
369 369
 		return
370 370
 	}
371 371
 	vars := mux.Vars(r)
372
-	repositoryName := vars["repository"]
372
+	repositoryName, err := reference.WithName(vars["repository"])
373
+	if err != nil {
374
+		apiError(w, "Could not parse repository", 400)
375
+		return
376
+	}
373 377
 	repositoryName = NormalizeLocalName(repositoryName)
374 378
 	tagName := vars["tag"]
375
-	tags, exists := testRepositories[repositoryName]
379
+	tags, exists := testRepositories[repositoryName.String()]
376 380
 	if !exists {
377 381
 		apiError(w, "Repository not found", 404)
378 382
 		return
... ...
@@ -390,13 +399,17 @@ func handlerPutTag(w http.ResponseWriter, r *http.Request) {
390 390
 		return
391 391
 	}
392 392
 	vars := mux.Vars(r)
393
-	repositoryName := vars["repository"]
393
+	repositoryName, err := reference.WithName(vars["repository"])
394
+	if err != nil {
395
+		apiError(w, "Could not parse repository", 400)
396
+		return
397
+	}
394 398
 	repositoryName = NormalizeLocalName(repositoryName)
395 399
 	tagName := vars["tag"]
396
-	tags, exists := testRepositories[repositoryName]
400
+	tags, exists := testRepositories[repositoryName.String()]
397 401
 	if !exists {
398
-		tags := make(map[string]string)
399
-		testRepositories[repositoryName] = tags
402
+		tags = make(map[string]string)
403
+		testRepositories[repositoryName.String()] = tags
400 404
 	}
401 405
 	tagValue := ""
402 406
 	readJSON(r, tagValue)
... ...
@@ -8,6 +8,7 @@ import (
8 8
 	"strings"
9 9
 	"testing"
10 10
 
11
+	"github.com/docker/distribution/reference"
11 12
 	"github.com/docker/distribution/registry/client/transport"
12 13
 	"github.com/docker/docker/cliconfig"
13 14
 )
... ...
@@ -214,13 +215,21 @@ func TestGetRemoteImageLayer(t *testing.T) {
214 214
 
215 215
 func TestGetRemoteTag(t *testing.T) {
216 216
 	r := spawnTestRegistrySession(t)
217
-	tag, err := r.GetRemoteTag([]string{makeURL("/v1/")}, REPO, "test")
217
+	repoRef, err := reference.ParseNamed(REPO)
218
+	if err != nil {
219
+		t.Fatal(err)
220
+	}
221
+	tag, err := r.GetRemoteTag([]string{makeURL("/v1/")}, repoRef, "test")
218 222
 	if err != nil {
219 223
 		t.Fatal(err)
220 224
 	}
221 225
 	assertEqual(t, tag, imageID, "Expected tag test to map to "+imageID)
222 226
 
223
-	_, err = r.GetRemoteTag([]string{makeURL("/v1/")}, "foo42/baz", "foo")
227
+	bazRef, err := reference.ParseNamed("foo42/baz")
228
+	if err != nil {
229
+		t.Fatal(err)
230
+	}
231
+	_, err = r.GetRemoteTag([]string{makeURL("/v1/")}, bazRef, "foo")
224 232
 	if err != ErrRepoNotFound {
225 233
 		t.Fatal("Expected ErrRepoNotFound error when fetching tag for bogus repo")
226 234
 	}
... ...
@@ -228,7 +237,11 @@ func TestGetRemoteTag(t *testing.T) {
228 228
 
229 229
 func TestGetRemoteTags(t *testing.T) {
230 230
 	r := spawnTestRegistrySession(t)
231
-	tags, err := r.GetRemoteTags([]string{makeURL("/v1/")}, REPO)
231
+	repoRef, err := reference.ParseNamed(REPO)
232
+	if err != nil {
233
+		t.Fatal(err)
234
+	}
235
+	tags, err := r.GetRemoteTags([]string{makeURL("/v1/")}, repoRef)
232 236
 	if err != nil {
233 237
 		t.Fatal(err)
234 238
 	}
... ...
@@ -236,7 +249,11 @@ func TestGetRemoteTags(t *testing.T) {
236 236
 	assertEqual(t, tags["latest"], imageID, "Expected tag latest to map to "+imageID)
237 237
 	assertEqual(t, tags["test"], imageID, "Expected tag test to map to "+imageID)
238 238
 
239
-	_, err = r.GetRemoteTags([]string{makeURL("/v1/")}, "foo42/baz")
239
+	bazRef, err := reference.ParseNamed("foo42/baz")
240
+	if err != nil {
241
+		t.Fatal(err)
242
+	}
243
+	_, err = r.GetRemoteTags([]string{makeURL("/v1/")}, bazRef)
240 244
 	if err != ErrRepoNotFound {
241 245
 		t.Fatal("Expected ErrRepoNotFound error when fetching tags for bogus repo")
242 246
 	}
... ...
@@ -249,7 +266,11 @@ func TestGetRepositoryData(t *testing.T) {
249 249
 		t.Fatal(err)
250 250
 	}
251 251
 	host := "http://" + parsedURL.Host + "/v1/"
252
-	data, err := r.GetRepositoryData("foo42/bar")
252
+	repoRef, err := reference.ParseNamed(REPO)
253
+	if err != nil {
254
+		t.Fatal(err)
255
+	}
256
+	data, err := r.GetRepositoryData(repoRef)
253 257
 	if err != nil {
254 258
 		t.Fatal(err)
255 259
 	}
... ...
@@ -315,29 +336,41 @@ func TestValidateRepositoryName(t *testing.T) {
315 315
 	}
316 316
 
317 317
 	for _, name := range invalidRepoNames {
318
-		err := ValidateRepositoryName(name)
319
-		assertNotEqual(t, err, nil, "Expected invalid repo name: "+name)
318
+		named, err := reference.WithName(name)
319
+		if err == nil {
320
+			err := ValidateRepositoryName(named)
321
+			assertNotEqual(t, err, nil, "Expected invalid repo name: "+name)
322
+		}
320 323
 	}
321 324
 
322 325
 	for _, name := range validRepoNames {
323
-		err := ValidateRepositoryName(name)
326
+		named, err := reference.WithName(name)
327
+		if err != nil {
328
+			t.Fatalf("could not parse valid name: %s", name)
329
+		}
330
+		err = ValidateRepositoryName(named)
324 331
 		assertEqual(t, err, nil, "Expected valid repo name: "+name)
325 332
 	}
326
-
327
-	err := ValidateRepositoryName(invalidRepoNames[0])
328
-	assertEqual(t, err, ErrInvalidRepositoryName, "Expected ErrInvalidRepositoryName: "+invalidRepoNames[0])
329 333
 }
330 334
 
331 335
 func TestParseRepositoryInfo(t *testing.T) {
336
+	withName := func(name string) reference.Named {
337
+		named, err := reference.WithName(name)
338
+		if err != nil {
339
+			t.Fatalf("could not parse reference %s", name)
340
+		}
341
+		return named
342
+	}
343
+
332 344
 	expectedRepoInfos := map[string]RepositoryInfo{
333 345
 		"fooo/bar": {
334 346
 			Index: &IndexInfo{
335 347
 				Name:     IndexName,
336 348
 				Official: true,
337 349
 			},
338
-			RemoteName:    "fooo/bar",
339
-			LocalName:     "fooo/bar",
340
-			CanonicalName: "docker.io/fooo/bar",
350
+			RemoteName:    withName("fooo/bar"),
351
+			LocalName:     withName("fooo/bar"),
352
+			CanonicalName: withName("docker.io/fooo/bar"),
341 353
 			Official:      false,
342 354
 		},
343 355
 		"library/ubuntu": {
... ...
@@ -345,9 +378,9 @@ func TestParseRepositoryInfo(t *testing.T) {
345 345
 				Name:     IndexName,
346 346
 				Official: true,
347 347
 			},
348
-			RemoteName:    "library/ubuntu",
349
-			LocalName:     "ubuntu",
350
-			CanonicalName: "docker.io/library/ubuntu",
348
+			RemoteName:    withName("library/ubuntu"),
349
+			LocalName:     withName("ubuntu"),
350
+			CanonicalName: withName("docker.io/library/ubuntu"),
351 351
 			Official:      true,
352 352
 		},
353 353
 		"nonlibrary/ubuntu": {
... ...
@@ -355,9 +388,9 @@ func TestParseRepositoryInfo(t *testing.T) {
355 355
 				Name:     IndexName,
356 356
 				Official: true,
357 357
 			},
358
-			RemoteName:    "nonlibrary/ubuntu",
359
-			LocalName:     "nonlibrary/ubuntu",
360
-			CanonicalName: "docker.io/nonlibrary/ubuntu",
358
+			RemoteName:    withName("nonlibrary/ubuntu"),
359
+			LocalName:     withName("nonlibrary/ubuntu"),
360
+			CanonicalName: withName("docker.io/nonlibrary/ubuntu"),
361 361
 			Official:      false,
362 362
 		},
363 363
 		"ubuntu": {
... ...
@@ -365,9 +398,9 @@ func TestParseRepositoryInfo(t *testing.T) {
365 365
 				Name:     IndexName,
366 366
 				Official: true,
367 367
 			},
368
-			RemoteName:    "library/ubuntu",
369
-			LocalName:     "ubuntu",
370
-			CanonicalName: "docker.io/library/ubuntu",
368
+			RemoteName:    withName("library/ubuntu"),
369
+			LocalName:     withName("ubuntu"),
370
+			CanonicalName: withName("docker.io/library/ubuntu"),
371 371
 			Official:      true,
372 372
 		},
373 373
 		"other/library": {
... ...
@@ -375,9 +408,9 @@ func TestParseRepositoryInfo(t *testing.T) {
375 375
 				Name:     IndexName,
376 376
 				Official: true,
377 377
 			},
378
-			RemoteName:    "other/library",
379
-			LocalName:     "other/library",
380
-			CanonicalName: "docker.io/other/library",
378
+			RemoteName:    withName("other/library"),
379
+			LocalName:     withName("other/library"),
380
+			CanonicalName: withName("docker.io/other/library"),
381 381
 			Official:      false,
382 382
 		},
383 383
 		"127.0.0.1:8000/private/moonbase": {
... ...
@@ -385,9 +418,9 @@ func TestParseRepositoryInfo(t *testing.T) {
385 385
 				Name:     "127.0.0.1:8000",
386 386
 				Official: false,
387 387
 			},
388
-			RemoteName:    "private/moonbase",
389
-			LocalName:     "127.0.0.1:8000/private/moonbase",
390
-			CanonicalName: "127.0.0.1:8000/private/moonbase",
388
+			RemoteName:    withName("private/moonbase"),
389
+			LocalName:     withName("127.0.0.1:8000/private/moonbase"),
390
+			CanonicalName: withName("127.0.0.1:8000/private/moonbase"),
391 391
 			Official:      false,
392 392
 		},
393 393
 		"127.0.0.1:8000/privatebase": {
... ...
@@ -395,9 +428,9 @@ func TestParseRepositoryInfo(t *testing.T) {
395 395
 				Name:     "127.0.0.1:8000",
396 396
 				Official: false,
397 397
 			},
398
-			RemoteName:    "privatebase",
399
-			LocalName:     "127.0.0.1:8000/privatebase",
400
-			CanonicalName: "127.0.0.1:8000/privatebase",
398
+			RemoteName:    withName("privatebase"),
399
+			LocalName:     withName("127.0.0.1:8000/privatebase"),
400
+			CanonicalName: withName("127.0.0.1:8000/privatebase"),
401 401
 			Official:      false,
402 402
 		},
403 403
 		"localhost:8000/private/moonbase": {
... ...
@@ -405,9 +438,9 @@ func TestParseRepositoryInfo(t *testing.T) {
405 405
 				Name:     "localhost:8000",
406 406
 				Official: false,
407 407
 			},
408
-			RemoteName:    "private/moonbase",
409
-			LocalName:     "localhost:8000/private/moonbase",
410
-			CanonicalName: "localhost:8000/private/moonbase",
408
+			RemoteName:    withName("private/moonbase"),
409
+			LocalName:     withName("localhost:8000/private/moonbase"),
410
+			CanonicalName: withName("localhost:8000/private/moonbase"),
411 411
 			Official:      false,
412 412
 		},
413 413
 		"localhost:8000/privatebase": {
... ...
@@ -415,9 +448,9 @@ func TestParseRepositoryInfo(t *testing.T) {
415 415
 				Name:     "localhost:8000",
416 416
 				Official: false,
417 417
 			},
418
-			RemoteName:    "privatebase",
419
-			LocalName:     "localhost:8000/privatebase",
420
-			CanonicalName: "localhost:8000/privatebase",
418
+			RemoteName:    withName("privatebase"),
419
+			LocalName:     withName("localhost:8000/privatebase"),
420
+			CanonicalName: withName("localhost:8000/privatebase"),
421 421
 			Official:      false,
422 422
 		},
423 423
 		"example.com/private/moonbase": {
... ...
@@ -425,9 +458,9 @@ func TestParseRepositoryInfo(t *testing.T) {
425 425
 				Name:     "example.com",
426 426
 				Official: false,
427 427
 			},
428
-			RemoteName:    "private/moonbase",
429
-			LocalName:     "example.com/private/moonbase",
430
-			CanonicalName: "example.com/private/moonbase",
428
+			RemoteName:    withName("private/moonbase"),
429
+			LocalName:     withName("example.com/private/moonbase"),
430
+			CanonicalName: withName("example.com/private/moonbase"),
431 431
 			Official:      false,
432 432
 		},
433 433
 		"example.com/privatebase": {
... ...
@@ -435,9 +468,9 @@ func TestParseRepositoryInfo(t *testing.T) {
435 435
 				Name:     "example.com",
436 436
 				Official: false,
437 437
 			},
438
-			RemoteName:    "privatebase",
439
-			LocalName:     "example.com/privatebase",
440
-			CanonicalName: "example.com/privatebase",
438
+			RemoteName:    withName("privatebase"),
439
+			LocalName:     withName("example.com/privatebase"),
440
+			CanonicalName: withName("example.com/privatebase"),
441 441
 			Official:      false,
442 442
 		},
443 443
 		"example.com:8000/private/moonbase": {
... ...
@@ -445,9 +478,9 @@ func TestParseRepositoryInfo(t *testing.T) {
445 445
 				Name:     "example.com:8000",
446 446
 				Official: false,
447 447
 			},
448
-			RemoteName:    "private/moonbase",
449
-			LocalName:     "example.com:8000/private/moonbase",
450
-			CanonicalName: "example.com:8000/private/moonbase",
448
+			RemoteName:    withName("private/moonbase"),
449
+			LocalName:     withName("example.com:8000/private/moonbase"),
450
+			CanonicalName: withName("example.com:8000/private/moonbase"),
451 451
 			Official:      false,
452 452
 		},
453 453
 		"example.com:8000/privatebase": {
... ...
@@ -455,9 +488,9 @@ func TestParseRepositoryInfo(t *testing.T) {
455 455
 				Name:     "example.com:8000",
456 456
 				Official: false,
457 457
 			},
458
-			RemoteName:    "privatebase",
459
-			LocalName:     "example.com:8000/privatebase",
460
-			CanonicalName: "example.com:8000/privatebase",
458
+			RemoteName:    withName("privatebase"),
459
+			LocalName:     withName("example.com:8000/privatebase"),
460
+			CanonicalName: withName("example.com:8000/privatebase"),
461 461
 			Official:      false,
462 462
 		},
463 463
 		"localhost/private/moonbase": {
... ...
@@ -465,9 +498,9 @@ func TestParseRepositoryInfo(t *testing.T) {
465 465
 				Name:     "localhost",
466 466
 				Official: false,
467 467
 			},
468
-			RemoteName:    "private/moonbase",
469
-			LocalName:     "localhost/private/moonbase",
470
-			CanonicalName: "localhost/private/moonbase",
468
+			RemoteName:    withName("private/moonbase"),
469
+			LocalName:     withName("localhost/private/moonbase"),
470
+			CanonicalName: withName("localhost/private/moonbase"),
471 471
 			Official:      false,
472 472
 		},
473 473
 		"localhost/privatebase": {
... ...
@@ -475,9 +508,9 @@ func TestParseRepositoryInfo(t *testing.T) {
475 475
 				Name:     "localhost",
476 476
 				Official: false,
477 477
 			},
478
-			RemoteName:    "privatebase",
479
-			LocalName:     "localhost/privatebase",
480
-			CanonicalName: "localhost/privatebase",
478
+			RemoteName:    withName("privatebase"),
479
+			LocalName:     withName("localhost/privatebase"),
480
+			CanonicalName: withName("localhost/privatebase"),
481 481
 			Official:      false,
482 482
 		},
483 483
 		IndexName + "/public/moonbase": {
... ...
@@ -485,9 +518,9 @@ func TestParseRepositoryInfo(t *testing.T) {
485 485
 				Name:     IndexName,
486 486
 				Official: true,
487 487
 			},
488
-			RemoteName:    "public/moonbase",
489
-			LocalName:     "public/moonbase",
490
-			CanonicalName: "docker.io/public/moonbase",
488
+			RemoteName:    withName("public/moonbase"),
489
+			LocalName:     withName("public/moonbase"),
490
+			CanonicalName: withName("docker.io/public/moonbase"),
491 491
 			Official:      false,
492 492
 		},
493 493
 		"index." + IndexName + "/public/moonbase": {
... ...
@@ -495,9 +528,9 @@ func TestParseRepositoryInfo(t *testing.T) {
495 495
 				Name:     IndexName,
496 496
 				Official: true,
497 497
 			},
498
-			RemoteName:    "public/moonbase",
499
-			LocalName:     "public/moonbase",
500
-			CanonicalName: "docker.io/public/moonbase",
498
+			RemoteName:    withName("public/moonbase"),
499
+			LocalName:     withName("public/moonbase"),
500
+			CanonicalName: withName("docker.io/public/moonbase"),
501 501
 			Official:      false,
502 502
 		},
503 503
 		"ubuntu-12.04-base": {
... ...
@@ -505,9 +538,9 @@ func TestParseRepositoryInfo(t *testing.T) {
505 505
 				Name:     IndexName,
506 506
 				Official: true,
507 507
 			},
508
-			RemoteName:    "library/ubuntu-12.04-base",
509
-			LocalName:     "ubuntu-12.04-base",
510
-			CanonicalName: "docker.io/library/ubuntu-12.04-base",
508
+			RemoteName:    withName("library/ubuntu-12.04-base"),
509
+			LocalName:     withName("ubuntu-12.04-base"),
510
+			CanonicalName: withName("docker.io/library/ubuntu-12.04-base"),
511 511
 			Official:      true,
512 512
 		},
513 513
 		IndexName + "/ubuntu-12.04-base": {
... ...
@@ -515,9 +548,9 @@ func TestParseRepositoryInfo(t *testing.T) {
515 515
 				Name:     IndexName,
516 516
 				Official: true,
517 517
 			},
518
-			RemoteName:    "library/ubuntu-12.04-base",
519
-			LocalName:     "ubuntu-12.04-base",
520
-			CanonicalName: "docker.io/library/ubuntu-12.04-base",
518
+			RemoteName:    withName("library/ubuntu-12.04-base"),
519
+			LocalName:     withName("ubuntu-12.04-base"),
520
+			CanonicalName: withName("docker.io/library/ubuntu-12.04-base"),
521 521
 			Official:      true,
522 522
 		},
523 523
 		"index." + IndexName + "/ubuntu-12.04-base": {
... ...
@@ -525,22 +558,27 @@ func TestParseRepositoryInfo(t *testing.T) {
525 525
 				Name:     IndexName,
526 526
 				Official: true,
527 527
 			},
528
-			RemoteName:    "library/ubuntu-12.04-base",
529
-			LocalName:     "ubuntu-12.04-base",
530
-			CanonicalName: "docker.io/library/ubuntu-12.04-base",
528
+			RemoteName:    withName("library/ubuntu-12.04-base"),
529
+			LocalName:     withName("ubuntu-12.04-base"),
530
+			CanonicalName: withName("docker.io/library/ubuntu-12.04-base"),
531 531
 			Official:      true,
532 532
 		},
533 533
 	}
534 534
 
535 535
 	for reposName, expectedRepoInfo := range expectedRepoInfos {
536
-		repoInfo, err := ParseRepositoryInfo(reposName)
536
+		named, err := reference.WithName(reposName)
537
+		if err != nil {
538
+			t.Error(err)
539
+		}
540
+
541
+		repoInfo, err := ParseRepositoryInfo(named)
537 542
 		if err != nil {
538 543
 			t.Error(err)
539 544
 		} else {
540 545
 			checkEqual(t, repoInfo.Index.Name, expectedRepoInfo.Index.Name, reposName)
541
-			checkEqual(t, repoInfo.RemoteName, expectedRepoInfo.RemoteName, reposName)
542
-			checkEqual(t, repoInfo.LocalName, expectedRepoInfo.LocalName, reposName)
543
-			checkEqual(t, repoInfo.CanonicalName, expectedRepoInfo.CanonicalName, reposName)
546
+			checkEqual(t, repoInfo.RemoteName.String(), expectedRepoInfo.RemoteName.String(), reposName)
547
+			checkEqual(t, repoInfo.LocalName.String(), expectedRepoInfo.LocalName.String(), reposName)
548
+			checkEqual(t, repoInfo.CanonicalName.String(), expectedRepoInfo.CanonicalName.String(), reposName)
544 549
 			checkEqual(t, repoInfo.Index.Official, expectedRepoInfo.Index.Official, reposName)
545 550
 			checkEqual(t, repoInfo.Official, expectedRepoInfo.Official, reposName)
546 551
 		}
... ...
@@ -687,8 +725,11 @@ func TestMirrorEndpointLookup(t *testing.T) {
687 687
 		return false
688 688
 	}
689 689
 	s := Service{Config: makeServiceConfig([]string{"my.mirror"}, nil)}
690
-	imageName := IndexName + "/test/image"
691 690
 
691
+	imageName, err := reference.WithName(IndexName + "/test/image")
692
+	if err != nil {
693
+		t.Error(err)
694
+	}
692 695
 	pushAPIEndpoints, err := s.LookupPushEndpoints(imageName)
693 696
 	if err != nil {
694 697
 		t.Fatal(err)
... ...
@@ -708,7 +749,11 @@ func TestMirrorEndpointLookup(t *testing.T) {
708 708
 
709 709
 func TestPushRegistryTag(t *testing.T) {
710 710
 	r := spawnTestRegistrySession(t)
711
-	err := r.PushRegistryTag("foo42/bar", imageID, "stable", makeURL("/v1/"))
711
+	repoRef, err := reference.ParseNamed(REPO)
712
+	if err != nil {
713
+		t.Fatal(err)
714
+	}
715
+	err = r.PushRegistryTag(repoRef, imageID, "stable", makeURL("/v1/"))
712 716
 	if err != nil {
713 717
 		t.Fatal(err)
714 718
 	}
... ...
@@ -726,14 +771,18 @@ func TestPushImageJSONIndex(t *testing.T) {
726 726
 			Checksum: "sha256:bea7bf2e4bacd479344b737328db47b18880d09096e6674165533aa994f5e9f2",
727 727
 		},
728 728
 	}
729
-	repoData, err := r.PushImageJSONIndex("foo42/bar", imgData, false, nil)
729
+	repoRef, err := reference.ParseNamed(REPO)
730
+	if err != nil {
731
+		t.Fatal(err)
732
+	}
733
+	repoData, err := r.PushImageJSONIndex(repoRef, imgData, false, nil)
730 734
 	if err != nil {
731 735
 		t.Fatal(err)
732 736
 	}
733 737
 	if repoData == nil {
734 738
 		t.Fatal("Expected RepositoryData object")
735 739
 	}
736
-	repoData, err = r.PushImageJSONIndex("foo42/bar", imgData, true, []string{r.indexEndpoint.String()})
740
+	repoData, err = r.PushImageJSONIndex(repoRef, imgData, true, []string{r.indexEndpoint.String()})
737 741
 	if err != nil {
738 742
 		t.Fatal(err)
739 743
 	}
... ...
@@ -781,7 +830,11 @@ func TestValidRemoteName(t *testing.T) {
781 781
 		"dock__er/docker",
782 782
 	}
783 783
 	for _, repositoryName := range validRepositoryNames {
784
-		if err := validateRemoteName(repositoryName); err != nil {
784
+		repositoryRef, err := reference.WithName(repositoryName)
785
+		if err != nil {
786
+			t.Errorf("Repository name should be valid: %v. Error: %v", repositoryName, err)
787
+		}
788
+		if err := validateRemoteName(repositoryRef); err != nil {
785 789
 			t.Errorf("Repository name should be valid: %v. Error: %v", repositoryName, err)
786 790
 		}
787 791
 	}
... ...
@@ -818,7 +871,11 @@ func TestValidRemoteName(t *testing.T) {
818 818
 		"this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255_this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255_this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255_this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255/docker",
819 819
 	}
820 820
 	for _, repositoryName := range invalidRepositoryNames {
821
-		if err := validateRemoteName(repositoryName); err == nil {
821
+		repositoryRef, err := reference.ParseNamed(repositoryName)
822
+		if err != nil {
823
+			continue
824
+		}
825
+		if err := validateRemoteName(repositoryRef); err == nil {
822 826
 			t.Errorf("Repository name should be invalid: %v", repositoryName)
823 827
 		}
824 828
 	}
... ...
@@ -4,7 +4,9 @@ import (
4 4
 	"crypto/tls"
5 5
 	"net/http"
6 6
 	"net/url"
7
+	"strings"
7 8
 
9
+	"github.com/docker/distribution/reference"
8 10
 	"github.com/docker/distribution/registry/client/auth"
9 11
 	"github.com/docker/docker/cliconfig"
10 12
 )
... ...
@@ -51,17 +53,39 @@ func (s *Service) Auth(authConfig *cliconfig.AuthConfig) (string, error) {
51 51
 	return Login(authConfig, endpoint)
52 52
 }
53 53
 
54
+// splitReposSearchTerm breaks a search term into an index name and remote name
55
+func splitReposSearchTerm(reposName string) (string, string) {
56
+	nameParts := strings.SplitN(reposName, "/", 2)
57
+	var indexName, remoteName string
58
+	if len(nameParts) == 1 || (!strings.Contains(nameParts[0], ".") &&
59
+		!strings.Contains(nameParts[0], ":") && nameParts[0] != "localhost") {
60
+		// This is a Docker Index repos (ex: samalba/hipache or ubuntu)
61
+		// 'docker.io'
62
+		indexName = IndexName
63
+		remoteName = reposName
64
+	} else {
65
+		indexName = nameParts[0]
66
+		remoteName = nameParts[1]
67
+	}
68
+	return indexName, remoteName
69
+}
70
+
54 71
 // Search queries the public registry for images matching the specified
55 72
 // search terms, and returns the results.
56 73
 func (s *Service) Search(term string, authConfig *cliconfig.AuthConfig, headers map[string][]string) (*SearchResults, error) {
74
+	if err := validateNoSchema(term); err != nil {
75
+		return nil, err
76
+	}
77
+
78
+	indexName, remoteName := splitReposSearchTerm(term)
57 79
 
58
-	repoInfo, err := s.ResolveRepositoryBySearch(term)
80
+	index, err := s.Config.NewIndexInfo(indexName)
59 81
 	if err != nil {
60 82
 		return nil, err
61 83
 	}
62 84
 
63 85
 	// *TODO: Search multiple indexes.
64
-	endpoint, err := NewEndpoint(repoInfo.Index, http.Header(headers), APIVersionUnknown)
86
+	endpoint, err := NewEndpoint(index, http.Header(headers), APIVersionUnknown)
65 87
 	if err != nil {
66 88
 		return nil, err
67 89
 	}
... ...
@@ -70,19 +94,23 @@ func (s *Service) Search(term string, authConfig *cliconfig.AuthConfig, headers
70 70
 	if err != nil {
71 71
 		return nil, err
72 72
 	}
73
-	return r.SearchRepositories(repoInfo.GetSearchTerm())
74
-}
75 73
 
76
-// ResolveRepository splits a repository name into its components
77
-// and configuration of the associated registry.
78
-func (s *Service) ResolveRepository(name string) (*RepositoryInfo, error) {
79
-	return s.Config.NewRepositoryInfo(name, false)
74
+	if index.Official {
75
+		localName := remoteName
76
+		if strings.HasPrefix(localName, "library/") {
77
+			// If pull "library/foo", it's stored locally under "foo"
78
+			localName = strings.SplitN(localName, "/", 2)[1]
79
+		}
80
+
81
+		return r.SearchRepositories(localName)
82
+	}
83
+	return r.SearchRepositories(remoteName)
80 84
 }
81 85
 
82
-// ResolveRepositoryBySearch splits a repository name into its components
86
+// ResolveRepository splits a repository name into its components
83 87
 // and configuration of the associated registry.
84
-func (s *Service) ResolveRepositoryBySearch(name string) (*RepositoryInfo, error) {
85
-	return s.Config.NewRepositoryInfo(name, true)
88
+func (s *Service) ResolveRepository(name reference.Named) (*RepositoryInfo, error) {
89
+	return s.Config.NewRepositoryInfo(name)
86 90
 }
87 91
 
88 92
 // ResolveIndex takes indexName and returns index info
... ...
@@ -123,14 +151,14 @@ func (s *Service) tlsConfigForMirror(mirror string) (*tls.Config, error) {
123 123
 // LookupPullEndpoints creates an list of endpoints to try to pull from, in order of preference.
124 124
 // It gives preference to v2 endpoints over v1, mirrors over the actual
125 125
 // registry, and HTTPS over plain HTTP.
126
-func (s *Service) LookupPullEndpoints(repoName string) (endpoints []APIEndpoint, err error) {
126
+func (s *Service) LookupPullEndpoints(repoName reference.Named) (endpoints []APIEndpoint, err error) {
127 127
 	return s.lookupEndpoints(repoName)
128 128
 }
129 129
 
130 130
 // LookupPushEndpoints creates an list of endpoints to try to push to, in order of preference.
131 131
 // It gives preference to v2 endpoints over v1, and HTTPS over plain HTTP.
132 132
 // Mirrors are not included.
133
-func (s *Service) LookupPushEndpoints(repoName string) (endpoints []APIEndpoint, err error) {
133
+func (s *Service) LookupPushEndpoints(repoName reference.Named) (endpoints []APIEndpoint, err error) {
134 134
 	allEndpoints, err := s.lookupEndpoints(repoName)
135 135
 	if err == nil {
136 136
 		for _, endpoint := range allEndpoints {
... ...
@@ -142,7 +170,7 @@ func (s *Service) LookupPushEndpoints(repoName string) (endpoints []APIEndpoint,
142 142
 	return endpoints, err
143 143
 }
144 144
 
145
-func (s *Service) lookupEndpoints(repoName string) (endpoints []APIEndpoint, err error) {
145
+func (s *Service) lookupEndpoints(repoName reference.Named) (endpoints []APIEndpoint, err error) {
146 146
 	endpoints, err = s.lookupV2Endpoints(repoName)
147 147
 	if err != nil {
148 148
 		return nil, err
... ...
@@ -4,13 +4,15 @@ import (
4 4
 	"fmt"
5 5
 	"strings"
6 6
 
7
+	"github.com/docker/distribution/reference"
7 8
 	"github.com/docker/docker/pkg/tlsconfig"
8 9
 )
9 10
 
10
-func (s *Service) lookupV1Endpoints(repoName string) (endpoints []APIEndpoint, err error) {
11
+func (s *Service) lookupV1Endpoints(repoName reference.Named) (endpoints []APIEndpoint, err error) {
11 12
 	var cfg = tlsconfig.ServerDefault
12 13
 	tlsConfig := &cfg
13
-	if strings.HasPrefix(repoName, DefaultNamespace+"/") {
14
+	nameString := repoName.Name()
15
+	if strings.HasPrefix(nameString, DefaultNamespace+"/") {
14 16
 		endpoints = append(endpoints, APIEndpoint{
15 17
 			URL:          DefaultV1Registry,
16 18
 			Version:      APIVersion1,
... ...
@@ -21,11 +23,11 @@ func (s *Service) lookupV1Endpoints(repoName string) (endpoints []APIEndpoint, e
21 21
 		return endpoints, nil
22 22
 	}
23 23
 
24
-	slashIndex := strings.IndexRune(repoName, '/')
24
+	slashIndex := strings.IndexRune(nameString, '/')
25 25
 	if slashIndex <= 0 {
26
-		return nil, fmt.Errorf("invalid repo name: missing '/':  %s", repoName)
26
+		return nil, fmt.Errorf("invalid repo name: missing '/':  %s", nameString)
27 27
 	}
28
-	hostname := repoName[:slashIndex]
28
+	hostname := nameString[:slashIndex]
29 29
 
30 30
 	tlsConfig, err = s.TLSConfig(hostname)
31 31
 	if err != nil {
... ...
@@ -4,14 +4,16 @@ import (
4 4
 	"fmt"
5 5
 	"strings"
6 6
 
7
+	"github.com/docker/distribution/reference"
7 8
 	"github.com/docker/distribution/registry/client/auth"
8 9
 	"github.com/docker/docker/pkg/tlsconfig"
9 10
 )
10 11
 
11
-func (s *Service) lookupV2Endpoints(repoName string) (endpoints []APIEndpoint, err error) {
12
+func (s *Service) lookupV2Endpoints(repoName reference.Named) (endpoints []APIEndpoint, err error) {
12 13
 	var cfg = tlsconfig.ServerDefault
13 14
 	tlsConfig := &cfg
14
-	if strings.HasPrefix(repoName, DefaultNamespace+"/") {
15
+	nameString := repoName.Name()
16
+	if strings.HasPrefix(nameString, DefaultNamespace+"/") {
15 17
 		// v2 mirrors
16 18
 		for _, mirror := range s.Config.Mirrors {
17 19
 			mirrorTLSConfig, err := s.tlsConfigForMirror(mirror)
... ...
@@ -39,11 +41,11 @@ func (s *Service) lookupV2Endpoints(repoName string) (endpoints []APIEndpoint, e
39 39
 		return endpoints, nil
40 40
 	}
41 41
 
42
-	slashIndex := strings.IndexRune(repoName, '/')
42
+	slashIndex := strings.IndexRune(nameString, '/')
43 43
 	if slashIndex <= 0 {
44
-		return nil, fmt.Errorf("invalid repo name: missing '/':  %s", repoName)
44
+		return nil, fmt.Errorf("invalid repo name: missing '/':  %s", nameString)
45 45
 	}
46
-	hostname := repoName[:slashIndex]
46
+	hostname := nameString[:slashIndex]
47 47
 
48 48
 	tlsConfig, err = s.TLSConfig(hostname)
49 49
 	if err != nil {
... ...
@@ -20,6 +20,7 @@ import (
20 20
 	"time"
21 21
 
22 22
 	"github.com/Sirupsen/logrus"
23
+	"github.com/docker/distribution/reference"
23 24
 	"github.com/docker/docker/cliconfig"
24 25
 	"github.com/docker/docker/pkg/httputils"
25 26
 	"github.com/docker/docker/pkg/ioutils"
... ...
@@ -320,7 +321,9 @@ func (r *Session) GetRemoteImageLayer(imgID, registry string, imgSize int64) (io
320 320
 // repository. It queries each of the registries supplied in the registries
321 321
 // argument, and returns data from the first one that answers the query
322 322
 // successfully.
323
-func (r *Session) GetRemoteTag(registries []string, repository string, askedTag string) (string, error) {
323
+func (r *Session) GetRemoteTag(registries []string, repositoryRef reference.Named, askedTag string) (string, error) {
324
+	repository := repositoryRef.Name()
325
+
324 326
 	if strings.Count(repository, "/") == 0 {
325 327
 		// This will be removed once the registry supports auto-resolution on
326 328
 		// the "library" namespace
... ...
@@ -356,7 +359,9 @@ func (r *Session) GetRemoteTag(registries []string, repository string, askedTag
356 356
 // of the registries supplied in the registries argument, and returns data from
357 357
 // the first one that answers the query successfully. It returns a map with
358 358
 // tag names as the keys and image IDs as the values.
359
-func (r *Session) GetRemoteTags(registries []string, repository string) (map[string]string, error) {
359
+func (r *Session) GetRemoteTags(registries []string, repositoryRef reference.Named) (map[string]string, error) {
360
+	repository := repositoryRef.Name()
361
+
360 362
 	if strings.Count(repository, "/") == 0 {
361 363
 		// This will be removed once the registry supports auto-resolution on
362 364
 		// the "library" namespace
... ...
@@ -408,8 +413,8 @@ func buildEndpointsList(headers []string, indexEp string) ([]string, error) {
408 408
 }
409 409
 
410 410
 // GetRepositoryData returns lists of images and endpoints for the repository
411
-func (r *Session) GetRepositoryData(remote string) (*RepositoryData, error) {
412
-	repositoryTarget := fmt.Sprintf("%srepositories/%s/images", r.indexEndpoint.VersionString(1), remote)
411
+func (r *Session) GetRepositoryData(remote reference.Named) (*RepositoryData, error) {
412
+	repositoryTarget := fmt.Sprintf("%srepositories/%s/images", r.indexEndpoint.VersionString(1), remote.Name())
413 413
 
414 414
 	logrus.Debugf("[registry] Calling GET %s", repositoryTarget)
415 415
 
... ...
@@ -443,7 +448,7 @@ func (r *Session) GetRepositoryData(remote string) (*RepositoryData, error) {
443 443
 		if err != nil {
444 444
 			logrus.Debugf("Error reading response body: %s", err)
445 445
 		}
446
-		return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to pull repository %s: %q", res.StatusCode, remote, errBody), res)
446
+		return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to pull repository %s: %q", res.StatusCode, remote.Name(), errBody), res)
447 447
 	}
448 448
 
449 449
 	var endpoints []string
... ...
@@ -595,10 +600,10 @@ func (r *Session) PushImageLayerRegistry(imgID string, layer io.Reader, registry
595 595
 
596 596
 // PushRegistryTag pushes a tag on the registry.
597 597
 // Remote has the format '<user>/<repo>
598
-func (r *Session) PushRegistryTag(remote, revision, tag, registry string) error {
598
+func (r *Session) PushRegistryTag(remote reference.Named, revision, tag, registry string) error {
599 599
 	// "jsonify" the string
600 600
 	revision = "\"" + revision + "\""
601
-	path := fmt.Sprintf("repositories/%s/tags/%s", remote, tag)
601
+	path := fmt.Sprintf("repositories/%s/tags/%s", remote.Name(), tag)
602 602
 
603 603
 	req, err := http.NewRequest("PUT", registry+path, strings.NewReader(revision))
604 604
 	if err != nil {
... ...
@@ -612,13 +617,13 @@ func (r *Session) PushRegistryTag(remote, revision, tag, registry string) error
612 612
 	}
613 613
 	res.Body.Close()
614 614
 	if res.StatusCode != 200 && res.StatusCode != 201 {
615
-		return httputils.NewHTTPRequestError(fmt.Sprintf("Internal server error: %d trying to push tag %s on %s", res.StatusCode, tag, remote), res)
615
+		return httputils.NewHTTPRequestError(fmt.Sprintf("Internal server error: %d trying to push tag %s on %s", res.StatusCode, tag, remote.Name()), res)
616 616
 	}
617 617
 	return nil
618 618
 }
619 619
 
620 620
 // PushImageJSONIndex uploads an image list to the repository
621
-func (r *Session) PushImageJSONIndex(remote string, imgList []*ImgData, validate bool, regs []string) (*RepositoryData, error) {
621
+func (r *Session) PushImageJSONIndex(remote reference.Named, imgList []*ImgData, validate bool, regs []string) (*RepositoryData, error) {
622 622
 	cleanImgList := []*ImgData{}
623 623
 	if validate {
624 624
 		for _, elem := range imgList {
... ...
@@ -638,7 +643,7 @@ func (r *Session) PushImageJSONIndex(remote string, imgList []*ImgData, validate
638 638
 	if validate {
639 639
 		suffix = "images"
640 640
 	}
641
-	u := fmt.Sprintf("%srepositories/%s/%s", r.indexEndpoint.VersionString(1), remote, suffix)
641
+	u := fmt.Sprintf("%srepositories/%s/%s", r.indexEndpoint.VersionString(1), remote.Name(), suffix)
642 642
 	logrus.Debugf("[registry] PUT %s", u)
643 643
 	logrus.Debugf("Image list pushed to index:\n%s", imgListJSON)
644 644
 	headers := map[string][]string{
... ...
@@ -676,7 +681,7 @@ func (r *Session) PushImageJSONIndex(remote string, imgList []*ImgData, validate
676 676
 			if err != nil {
677 677
 				logrus.Debugf("Error reading response body: %s", err)
678 678
 			}
679
-			return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push repository %s: %q", res.StatusCode, remote, errBody), res)
679
+			return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push repository %s: %q", res.StatusCode, remote.Name(), errBody), res)
680 680
 		}
681 681
 		tokens = res.Header["X-Docker-Token"]
682 682
 		logrus.Debugf("Auth token: %v", tokens)
... ...
@@ -694,7 +699,7 @@ func (r *Session) PushImageJSONIndex(remote string, imgList []*ImgData, validate
694 694
 			if err != nil {
695 695
 				logrus.Debugf("Error reading response body: %s", err)
696 696
 			}
697
-			return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push checksums %s: %q", res.StatusCode, remote, errBody), res)
697
+			return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push checksums %s: %q", res.StatusCode, remote.Name(), errBody), res)
698 698
 		}
699 699
 	}
700 700
 
... ...
@@ -1,5 +1,9 @@
1 1
 package registry
2 2
 
3
+import (
4
+	"github.com/docker/distribution/reference"
5
+)
6
+
3 7
 // SearchResult describes a search result returned from a registry
4 8
 type SearchResult struct {
5 9
 	// StarCount indicates the number of stars this repository has
... ...
@@ -126,13 +130,13 @@ type RepositoryInfo struct {
126 126
 	Index *IndexInfo
127 127
 	// RemoteName is the remote name of the repository, such as
128 128
 	// "library/ubuntu-12.04-base"
129
-	RemoteName string
129
+	RemoteName reference.Named
130 130
 	// LocalName is the local name of the repository, such as
131 131
 	// "ubuntu-12.04-base"
132
-	LocalName string
132
+	LocalName reference.Named
133 133
 	// CanonicalName is the canonical name of the repository, such as
134 134
 	// "docker.io/library/ubuntu-12.04-base"
135
-	CanonicalName string
135
+	CanonicalName reference.Named
136 136
 	// Official indicates whether the repository is considered official.
137 137
 	// If the registry is official, and the normalized name does not
138 138
 	// contain a '/' (e.g. "foo"), then it is considered an official repo.
... ...
@@ -269,23 +269,6 @@ func ReadDockerIgnore(reader io.ReadCloser) ([]string, error) {
269 269
 	return excludes, nil
270 270
 }
271 271
 
272
-// ImageReference combines `repo` and `ref` and returns a string representing
273
-// the combination. If `ref` is a digest (meaning it's of the form
274
-// <algorithm>:<digest>, the returned string is <repo>@<ref>. Otherwise,
275
-// ref is assumed to be a tag, and the returned string is <repo>:<tag>.
276
-func ImageReference(repo, ref string) string {
277
-	if DigestReference(ref) {
278
-		return repo + "@" + ref
279
-	}
280
-	return repo + ":" + ref
281
-}
282
-
283
-// DigestReference returns true if ref is a digest reference; i.e. if it
284
-// is of the form <algorithm>:<digest>.
285
-func DigestReference(ref string) bool {
286
-	return strings.Contains(ref, ":")
287
-}
288
-
289 272
 // GetErrorMessage returns the human readable message associated with
290 273
 // the passed-in error. In some cases the default Error() func returns
291 274
 // something that is less than useful so based on its types this func
... ...
@@ -26,36 +26,6 @@ func TestReplaceAndAppendEnvVars(t *testing.T) {
26 26
 	}
27 27
 }
28 28
 
29
-func TestImageReference(t *testing.T) {
30
-	tests := []struct {
31
-		repo     string
32
-		ref      string
33
-		expected string
34
-	}{
35
-		{"repo", "tag", "repo:tag"},
36
-		{"repo", "sha256:c100b11b25d0cacd52c14e0e7bf525e1a4c0e6aec8827ae007055545909d1a64", "repo@sha256:c100b11b25d0cacd52c14e0e7bf525e1a4c0e6aec8827ae007055545909d1a64"},
37
-	}
38
-
39
-	for i, test := range tests {
40
-		actual := ImageReference(test.repo, test.ref)
41
-		if test.expected != actual {
42
-			t.Errorf("%d: expected %q, got %q", i, test.expected, actual)
43
-		}
44
-	}
45
-}
46
-
47
-func TestDigestReference(t *testing.T) {
48
-	input := "sha256:c100b11b25d0cacd52c14e0e7bf525e1a4c0e6aec8827ae007055545909d1a64"
49
-	if !DigestReference(input) {
50
-		t.Errorf("Expected DigestReference=true for input %q", input)
51
-	}
52
-
53
-	input = "latest"
54
-	if DigestReference(input) {
55
-		t.Errorf("Unexpected DigestReference=true for input %q", input)
56
-	}
57
-}
58
-
59 29
 func TestReadDockerIgnore(t *testing.T) {
60 30
 	tmpDir, err := ioutil.TempDir("", "dockerignore-test")
61 31
 	if err != nil {