Browse code

Remove reference package dependency from the api.

Signed-off-by: David Calavera <david.calavera@gmail.com>

David Calavera authored on 2016/04/08 06:29:18
Showing 10 changed files
... ...
@@ -4,7 +4,6 @@ import (
4 4
 	"io"
5 5
 
6 6
 	"github.com/docker/docker/api/types/backend"
7
-	"github.com/docker/docker/reference"
8 7
 	"github.com/docker/engine-api/types"
9 8
 	"github.com/docker/engine-api/types/registry"
10 9
 	"golang.org/x/net/context"
... ...
@@ -28,17 +27,17 @@ type imageBackend interface {
28 28
 	ImageHistory(imageName string) ([]*types.ImageHistory, error)
29 29
 	Images(filterArgs string, filter string, all bool) ([]*types.Image, error)
30 30
 	LookupImage(name string) (*types.ImageInspect, error)
31
-	TagImage(newTag reference.Named, imageName string) error
31
+	TagImage(imageName, repository, tag string) error
32 32
 }
33 33
 
34 34
 type importExportBackend interface {
35 35
 	LoadImage(inTar io.ReadCloser, outStream io.Writer, quiet bool) error
36
-	ImportImage(src string, newRef reference.Named, msg string, inConfig io.ReadCloser, outStream io.Writer, changes []string) error
36
+	ImportImage(src string, repository, tag string, msg string, inConfig io.ReadCloser, outStream io.Writer, changes []string) error
37 37
 	ExportImage(names []string, outStream io.Writer) error
38 38
 }
39 39
 
40 40
 type registryBackend interface {
41
-	PullImage(ctx context.Context, ref reference.Named, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
42
-	PushImage(ctx context.Context, ref reference.Named, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
41
+	PullImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
42
+	PushImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
43 43
 	SearchRegistryForImages(ctx context.Context, term string, authConfig *types.AuthConfig, metaHeaders map[string][]string) (*registry.SearchResults, error)
44 44
 }
... ...
@@ -3,20 +3,17 @@ package image
3 3
 import (
4 4
 	"encoding/base64"
5 5
 	"encoding/json"
6
-	"errors"
7 6
 	"fmt"
8 7
 	"io"
9 8
 	"net/http"
10 9
 	"net/url"
11 10
 	"strings"
12 11
 
13
-	"github.com/docker/distribution/digest"
14 12
 	"github.com/docker/distribution/registry/api/errcode"
15 13
 	"github.com/docker/docker/api/server/httputils"
16 14
 	"github.com/docker/docker/api/types/backend"
17 15
 	"github.com/docker/docker/pkg/ioutils"
18 16
 	"github.com/docker/docker/pkg/streamformatter"
19
-	"github.com/docker/docker/reference"
20 17
 	"github.com/docker/engine-api/types"
21 18
 	"github.com/docker/engine-api/types/container"
22 19
 	"golang.org/x/net/context"
... ...
@@ -89,46 +86,26 @@ func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWrite
89 89
 	w.Header().Set("Content-Type", "application/json")
90 90
 
91 91
 	if image != "" { //pull
92
-		// Special case: "pull -a" may send an image name with a
93
-		// trailing :. This is ugly, but let's not break API
94
-		// compatibility.
95
-		image = strings.TrimSuffix(image, ":")
96
-
97
-		var ref reference.Named
98
-		ref, err = reference.ParseNamed(image)
99
-		if err == nil {
100
-			if tag != "" {
101
-				// The "tag" could actually be a digest.
102
-				var dgst digest.Digest
103
-				dgst, err = digest.ParseDigest(tag)
104
-				if err == nil {
105
-					ref, err = reference.WithDigest(ref, dgst)
106
-				} else {
107
-					ref, err = reference.WithTag(ref, tag)
108
-				}
92
+		metaHeaders := map[string][]string{}
93
+		for k, v := range r.Header {
94
+			if strings.HasPrefix(k, "X-Meta-") {
95
+				metaHeaders[k] = v
109 96
 			}
110
-			if err == nil {
111
-				metaHeaders := map[string][]string{}
112
-				for k, v := range r.Header {
113
-					if strings.HasPrefix(k, "X-Meta-") {
114
-						metaHeaders[k] = v
115
-					}
116
-				}
117
-
118
-				authEncoded := r.Header.Get("X-Registry-Auth")
119
-				authConfig := &types.AuthConfig{}
120
-				if authEncoded != "" {
121
-					authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
122
-					if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil {
123
-						// for a pull it is not an error if no auth was given
124
-						// to increase compatibility with the existing api it is defaulting to be empty
125
-						authConfig = &types.AuthConfig{}
126
-					}
127
-				}
128
-
129
-				err = s.backend.PullImage(ctx, ref, metaHeaders, authConfig, output)
97
+		}
98
+
99
+		authEncoded := r.Header.Get("X-Registry-Auth")
100
+		authConfig := &types.AuthConfig{}
101
+		if authEncoded != "" {
102
+			authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
103
+			if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil {
104
+				// for a pull it is not an error if no auth was given
105
+				// to increase compatibility with the existing api it is defaulting to be empty
106
+				authConfig = &types.AuthConfig{}
130 107
 			}
131 108
 		}
109
+
110
+		err = s.backend.PullImage(ctx, image, tag, metaHeaders, authConfig, output)
111
+
132 112
 		// Check the error from pulling an image to make sure the request
133 113
 		// was authorized. Modify the status if the request was
134 114
 		// unauthorized to respond with 401 rather than 500.
... ...
@@ -136,31 +113,11 @@ func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWrite
136 136
 			err = errcode.ErrorCodeUnauthorized.WithMessage(fmt.Sprintf("Authentication is required: %s", err))
137 137
 		}
138 138
 	} else { //import
139
-		var newRef reference.Named
140
-		if repo != "" {
141
-			var err error
142
-			newRef, err = reference.ParseNamed(repo)
143
-			if err != nil {
144
-				return err
145
-			}
146
-
147
-			if _, isCanonical := newRef.(reference.Canonical); isCanonical {
148
-				return errors.New("cannot import digest reference")
149
-			}
150
-
151
-			if tag != "" {
152
-				newRef, err = reference.WithTag(newRef, tag)
153
-				if err != nil {
154
-					return err
155
-				}
156
-			}
157
-		}
158
-
159 139
 		src := r.Form.Get("fromSrc")
160 140
 		// 'err' MUST NOT be defined within this block, we need any error
161 141
 		// generated from the download to be available to the output
162 142
 		// stream processing below
163
-		err = s.backend.ImportImage(src, newRef, message, r.Body, output, r.Form["changes"])
143
+		err = s.backend.ImportImage(src, repo, tag, message, r.Body, output, r.Form["changes"])
164 144
 	}
165 145
 	if err != nil {
166 146
 		if !output.Flushed() {
... ...
@@ -200,25 +157,15 @@ func (s *imageRouter) postImagesPush(ctx context.Context, w http.ResponseWriter,
200 200
 		}
201 201
 	}
202 202
 
203
-	ref, err := reference.ParseNamed(vars["name"])
204
-	if err != nil {
205
-		return err
206
-	}
203
+	image := vars["name"]
207 204
 	tag := r.Form.Get("tag")
208
-	if tag != "" {
209
-		// Push by digest is not supported, so only tags are supported.
210
-		ref, err = reference.WithTag(ref, tag)
211
-		if err != nil {
212
-			return err
213
-		}
214
-	}
215 205
 
216 206
 	output := ioutils.NewWriteFlusher(w)
217 207
 	defer output.Close()
218 208
 
219 209
 	w.Header().Set("Content-Type", "application/json")
220 210
 
221
-	if err := s.backend.PushImage(ctx, ref, metaHeaders, authConfig, output); err != nil {
211
+	if err := s.backend.PushImage(ctx, image, tag, metaHeaders, authConfig, output); err != nil {
222 212
 		if !output.Flushed() {
223 213
 			return err
224 214
 		}
... ...
@@ -322,18 +269,7 @@ func (s *imageRouter) postImagesTag(ctx context.Context, w http.ResponseWriter,
322 322
 	if err := httputils.ParseForm(r); err != nil {
323 323
 		return err
324 324
 	}
325
-	repo := r.Form.Get("repo")
326
-	tag := r.Form.Get("tag")
327
-	newTag, err := reference.WithName(repo)
328
-	if err != nil {
329
-		return err
330
-	}
331
-	if tag != "" {
332
-		if newTag, err = reference.WithTag(newTag, tag); err != nil {
333
-			return err
334
-		}
335
-	}
336
-	if err := s.backend.TagImage(newTag, vars["name"]); err != nil {
325
+	if err := s.backend.TagImage(vars["name"], r.Form.Get("repo"), r.Form.Get("tag")); err != nil {
337 326
 		return err
338 327
 	}
339 328
 	w.WriteHeader(http.StatusCreated)
... ...
@@ -10,6 +10,7 @@ import (
10 10
 	"time"
11 11
 
12 12
 	"github.com/docker/docker/api/types/backend"
13
+	"github.com/docker/docker/image"
13 14
 	"github.com/docker/docker/reference"
14 15
 	"github.com/docker/engine-api/types"
15 16
 	"github.com/docker/engine-api/types/container"
... ...
@@ -109,7 +110,7 @@ type Backend interface {
109 109
 	// GetImageOnBuild looks up a Docker image referenced by `name`.
110 110
 	GetImageOnBuild(name string) (Image, error)
111 111
 	// TagImage tags an image with newTag
112
-	TagImage(newTag reference.Named, imageName string) error
112
+	TagImageWithReference(image.ID, reference.Named) error
113 113
 	// PullOnBuild tells Docker to pull image referenced by `name`.
114 114
 	PullOnBuild(ctx context.Context, name string, authConfigs map[string]types.AuthConfig, output io.Writer) (Image, error)
115 115
 	// ContainerAttachRaw attaches to container.
... ...
@@ -12,6 +12,7 @@ import (
12 12
 	"github.com/Sirupsen/logrus"
13 13
 	"github.com/docker/docker/builder"
14 14
 	"github.com/docker/docker/builder/dockerfile/parser"
15
+	"github.com/docker/docker/image"
15 16
 	"github.com/docker/docker/pkg/stringid"
16 17
 	"github.com/docker/docker/reference"
17 18
 	"github.com/docker/engine-api/types"
... ...
@@ -256,8 +257,9 @@ func (b *Builder) build(config *types.ImageBuildOptions, context builder.Context
256 256
 		return "", fmt.Errorf("No image was generated. Is your Dockerfile empty?")
257 257
 	}
258 258
 
259
+	imageID := image.ID(b.image)
259 260
 	for _, rt := range repoAndTags {
260
-		if err := b.docker.TagImage(rt, b.image); err != nil {
261
+		if err := b.docker.TagImageWithReference(imageID, rt); err != nil {
261 262
 			return "", err
262 263
 		}
263 264
 	}
... ...
@@ -215,7 +215,7 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str
215 215
 				return "", err
216 216
 			}
217 217
 		}
218
-		if err := daemon.TagImage(newTag, id.String()); err != nil {
218
+		if err := daemon.TagImageWithReference(id, newTag); err != nil {
219 219
 			return "", err
220 220
 		}
221 221
 	}
... ...
@@ -38,7 +38,6 @@ import (
38 38
 	_ "github.com/docker/docker/daemon/graphdriver/register"
39 39
 	"github.com/docker/docker/daemon/logger"
40 40
 	"github.com/docker/docker/daemon/network"
41
-	"github.com/docker/docker/distribution"
42 41
 	dmetadata "github.com/docker/docker/distribution/metadata"
43 42
 	"github.com/docker/docker/distribution/xfer"
44 43
 	"github.com/docker/docker/dockerversion"
... ...
@@ -951,21 +950,6 @@ func (daemon *Daemon) changes(container *container.Container) ([]archive.Change,
951 951
 	return container.RWLayer.Changes()
952 952
 }
953 953
 
954
-// TagImage creates the tag specified by newTag, pointing to the image named
955
-// imageName (alternatively, imageName can also be an image ID).
956
-func (daemon *Daemon) TagImage(newTag reference.Named, imageName string) error {
957
-	imageID, err := daemon.GetImageID(imageName)
958
-	if err != nil {
959
-		return err
960
-	}
961
-	if err := daemon.referenceStore.AddTag(newTag, imageID, true); err != nil {
962
-		return err
963
-	}
964
-
965
-	daemon.LogImageEvent(imageID.String(), newTag.String(), "tag")
966
-	return nil
967
-}
968
-
969 954
 func writeDistributionProgress(cancelFunc func(), outStream io.Writer, progressChan <-chan progress.Progress) {
970 955
 	progressOutput := streamformatter.NewJSONStreamFormatter().NewProgressOutput(outStream, false)
971 956
 	operationCancelled := false
... ...
@@ -996,69 +980,6 @@ func isBrokenPipe(e error) bool {
996 996
 	return e == syscall.EPIPE
997 997
 }
998 998
 
999
-// PullImage initiates a pull operation. image is the repository name to pull, and
1000
-// tag may be either empty, or indicate a specific tag to pull.
1001
-func (daemon *Daemon) PullImage(ctx context.Context, ref reference.Named, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error {
1002
-	// Include a buffer so that slow client connections don't affect
1003
-	// transfer performance.
1004
-	progressChan := make(chan progress.Progress, 100)
1005
-
1006
-	writesDone := make(chan struct{})
1007
-
1008
-	ctx, cancelFunc := context.WithCancel(ctx)
1009
-
1010
-	go func() {
1011
-		writeDistributionProgress(cancelFunc, outStream, progressChan)
1012
-		close(writesDone)
1013
-	}()
1014
-
1015
-	imagePullConfig := &distribution.ImagePullConfig{
1016
-		MetaHeaders:      metaHeaders,
1017
-		AuthConfig:       authConfig,
1018
-		ProgressOutput:   progress.ChanOutput(progressChan),
1019
-		RegistryService:  daemon.RegistryService,
1020
-		ImageEventLogger: daemon.LogImageEvent,
1021
-		MetadataStore:    daemon.distributionMetadataStore,
1022
-		ImageStore:       daemon.imageStore,
1023
-		ReferenceStore:   daemon.referenceStore,
1024
-		DownloadManager:  daemon.downloadManager,
1025
-	}
1026
-
1027
-	err := distribution.Pull(ctx, ref, imagePullConfig)
1028
-	close(progressChan)
1029
-	<-writesDone
1030
-	return err
1031
-}
1032
-
1033
-// PullOnBuild tells Docker to pull image referenced by `name`.
1034
-func (daemon *Daemon) PullOnBuild(ctx context.Context, name string, authConfigs map[string]types.AuthConfig, output io.Writer) (builder.Image, error) {
1035
-	ref, err := reference.ParseNamed(name)
1036
-	if err != nil {
1037
-		return nil, err
1038
-	}
1039
-	ref = reference.WithDefaultTag(ref)
1040
-
1041
-	pullRegistryAuth := &types.AuthConfig{}
1042
-	if len(authConfigs) > 0 {
1043
-		// The request came with a full auth config file, we prefer to use that
1044
-		repoInfo, err := daemon.RegistryService.ResolveRepository(ref)
1045
-		if err != nil {
1046
-			return nil, err
1047
-		}
1048
-
1049
-		resolvedConfig := registry.ResolveAuthConfig(
1050
-			authConfigs,
1051
-			repoInfo.Index,
1052
-		)
1053
-		pullRegistryAuth = &resolvedConfig
1054
-	}
1055
-
1056
-	if err := daemon.PullImage(ctx, ref, nil, pullRegistryAuth, output); err != nil {
1057
-		return nil, err
1058
-	}
1059
-	return daemon.GetImage(name)
1060
-}
1061
-
1062 999
 // ExportImage exports a list of images to the given output stream. The
1063 1000
 // exported images are archived into a tar when written to the output
1064 1001
 // stream. All images with the given tag and all versions containing
... ...
@@ -1069,41 +990,6 @@ func (daemon *Daemon) ExportImage(names []string, outStream io.Writer) error {
1069 1069
 	return imageExporter.Save(names, outStream)
1070 1070
 }
1071 1071
 
1072
-// PushImage initiates a push operation on the repository named localName.
1073
-func (daemon *Daemon) PushImage(ctx context.Context, ref reference.Named, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error {
1074
-	// Include a buffer so that slow client connections don't affect
1075
-	// transfer performance.
1076
-	progressChan := make(chan progress.Progress, 100)
1077
-
1078
-	writesDone := make(chan struct{})
1079
-
1080
-	ctx, cancelFunc := context.WithCancel(ctx)
1081
-
1082
-	go func() {
1083
-		writeDistributionProgress(cancelFunc, outStream, progressChan)
1084
-		close(writesDone)
1085
-	}()
1086
-
1087
-	imagePushConfig := &distribution.ImagePushConfig{
1088
-		MetaHeaders:      metaHeaders,
1089
-		AuthConfig:       authConfig,
1090
-		ProgressOutput:   progress.ChanOutput(progressChan),
1091
-		RegistryService:  daemon.RegistryService,
1092
-		ImageEventLogger: daemon.LogImageEvent,
1093
-		MetadataStore:    daemon.distributionMetadataStore,
1094
-		LayerStore:       daemon.layerStore,
1095
-		ImageStore:       daemon.imageStore,
1096
-		ReferenceStore:   daemon.referenceStore,
1097
-		TrustKey:         daemon.trustKey,
1098
-		UploadManager:    daemon.uploadManager,
1099
-	}
1100
-
1101
-	err := distribution.Push(ctx, ref, imagePushConfig)
1102
-	close(progressChan)
1103
-	<-writesDone
1104
-	return err
1105
-}
1106
-
1107 1072
 // LookupImage looks up an image by name and returns it as an ImageInspect
1108 1073
 // structure.
1109 1074
 func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) {
1110 1075
new file mode 100644
... ...
@@ -0,0 +1,106 @@
0
+package daemon
1
+
2
+import (
3
+	"io"
4
+	"strings"
5
+
6
+	"github.com/docker/distribution/digest"
7
+	"github.com/docker/docker/builder"
8
+	"github.com/docker/docker/distribution"
9
+	"github.com/docker/docker/pkg/progress"
10
+	"github.com/docker/docker/reference"
11
+	"github.com/docker/docker/registry"
12
+	"github.com/docker/engine-api/types"
13
+	"golang.org/x/net/context"
14
+)
15
+
16
+// PullImage initiates a pull operation. image is the repository name to pull, and
17
+// tag may be either empty, or indicate a specific tag to pull.
18
+func (daemon *Daemon) PullImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error {
19
+	// Special case: "pull -a" may send an image name with a
20
+	// trailing :. This is ugly, but let's not break API
21
+	// compatibility.
22
+	image = strings.TrimSuffix(image, ":")
23
+
24
+	ref, err := reference.ParseNamed(image)
25
+	if err != nil {
26
+		return err
27
+	}
28
+
29
+	if tag != "" {
30
+		// The "tag" could actually be a digest.
31
+		var dgst digest.Digest
32
+		dgst, err = digest.ParseDigest(tag)
33
+		if err == nil {
34
+			ref, err = reference.WithDigest(ref, dgst)
35
+		} else {
36
+			ref, err = reference.WithTag(ref, tag)
37
+		}
38
+		if err != nil {
39
+			return err
40
+		}
41
+	}
42
+
43
+	return daemon.pullImageWithReference(ctx, ref, metaHeaders, authConfig, outStream)
44
+}
45
+
46
+// PullOnBuild tells Docker to pull image referenced by `name`.
47
+func (daemon *Daemon) PullOnBuild(ctx context.Context, name string, authConfigs map[string]types.AuthConfig, output io.Writer) (builder.Image, error) {
48
+	ref, err := reference.ParseNamed(name)
49
+	if err != nil {
50
+		return nil, err
51
+	}
52
+	ref = reference.WithDefaultTag(ref)
53
+
54
+	pullRegistryAuth := &types.AuthConfig{}
55
+	if len(authConfigs) > 0 {
56
+		// The request came with a full auth config file, we prefer to use that
57
+		repoInfo, err := daemon.RegistryService.ResolveRepository(ref)
58
+		if err != nil {
59
+			return nil, err
60
+		}
61
+
62
+		resolvedConfig := registry.ResolveAuthConfig(
63
+			authConfigs,
64
+			repoInfo.Index,
65
+		)
66
+		pullRegistryAuth = &resolvedConfig
67
+	}
68
+
69
+	if err := daemon.pullImageWithReference(ctx, ref, nil, pullRegistryAuth, output); err != nil {
70
+		return nil, err
71
+	}
72
+	return daemon.GetImage(name)
73
+}
74
+
75
+func (daemon *Daemon) pullImageWithReference(ctx context.Context, ref reference.Named, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error {
76
+	// Include a buffer so that slow client connections don't affect
77
+	// transfer performance.
78
+	progressChan := make(chan progress.Progress, 100)
79
+
80
+	writesDone := make(chan struct{})
81
+
82
+	ctx, cancelFunc := context.WithCancel(ctx)
83
+
84
+	go func() {
85
+		writeDistributionProgress(cancelFunc, outStream, progressChan)
86
+		close(writesDone)
87
+	}()
88
+
89
+	imagePullConfig := &distribution.ImagePullConfig{
90
+		MetaHeaders:      metaHeaders,
91
+		AuthConfig:       authConfig,
92
+		ProgressOutput:   progress.ChanOutput(progressChan),
93
+		RegistryService:  daemon.RegistryService,
94
+		ImageEventLogger: daemon.LogImageEvent,
95
+		MetadataStore:    daemon.distributionMetadataStore,
96
+		ImageStore:       daemon.imageStore,
97
+		ReferenceStore:   daemon.referenceStore,
98
+		DownloadManager:  daemon.downloadManager,
99
+	}
100
+
101
+	err := distribution.Pull(ctx, ref, imagePullConfig)
102
+	close(progressChan)
103
+	<-writesDone
104
+	return err
105
+}
0 106
new file mode 100644
... ...
@@ -0,0 +1,58 @@
0
+package daemon
1
+
2
+import (
3
+	"io"
4
+
5
+	"github.com/docker/docker/distribution"
6
+	"github.com/docker/docker/pkg/progress"
7
+	"github.com/docker/docker/reference"
8
+	"github.com/docker/engine-api/types"
9
+	"golang.org/x/net/context"
10
+)
11
+
12
+// PushImage initiates a push operation on the repository named localName.
13
+func (daemon *Daemon) PushImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error {
14
+	ref, err := reference.ParseNamed(image)
15
+	if err != nil {
16
+		return err
17
+	}
18
+	if tag != "" {
19
+		// Push by digest is not supported, so only tags are supported.
20
+		ref, err = reference.WithTag(ref, tag)
21
+		if err != nil {
22
+			return err
23
+		}
24
+	}
25
+
26
+	// Include a buffer so that slow client connections don't affect
27
+	// transfer performance.
28
+	progressChan := make(chan progress.Progress, 100)
29
+
30
+	writesDone := make(chan struct{})
31
+
32
+	ctx, cancelFunc := context.WithCancel(ctx)
33
+
34
+	go func() {
35
+		writeDistributionProgress(cancelFunc, outStream, progressChan)
36
+		close(writesDone)
37
+	}()
38
+
39
+	imagePushConfig := &distribution.ImagePushConfig{
40
+		MetaHeaders:      metaHeaders,
41
+		AuthConfig:       authConfig,
42
+		ProgressOutput:   progress.ChanOutput(progressChan),
43
+		RegistryService:  daemon.RegistryService,
44
+		ImageEventLogger: daemon.LogImageEvent,
45
+		MetadataStore:    daemon.distributionMetadataStore,
46
+		LayerStore:       daemon.layerStore,
47
+		ImageStore:       daemon.imageStore,
48
+		ReferenceStore:   daemon.referenceStore,
49
+		TrustKey:         daemon.trustKey,
50
+		UploadManager:    daemon.uploadManager,
51
+	}
52
+
53
+	err = distribution.Push(ctx, ref, imagePushConfig)
54
+	close(progressChan)
55
+	<-writesDone
56
+	return err
57
+}
0 58
new file mode 100644
... ...
@@ -0,0 +1,37 @@
0
+package daemon
1
+
2
+import (
3
+	"github.com/docker/docker/image"
4
+	"github.com/docker/docker/reference"
5
+)
6
+
7
+// TagImage creates the tag specified by newTag, pointing to the image named
8
+// imageName (alternatively, imageName can also be an image ID).
9
+func (daemon *Daemon) TagImage(imageName, repository, tag string) error {
10
+	imageID, err := daemon.GetImageID(imageName)
11
+	if err != nil {
12
+		return err
13
+	}
14
+
15
+	newTag, err := reference.WithName(repository)
16
+	if err != nil {
17
+		return err
18
+	}
19
+	if tag != "" {
20
+		if newTag, err = reference.WithTag(newTag, tag); err != nil {
21
+			return err
22
+		}
23
+	}
24
+
25
+	return daemon.TagImageWithReference(imageID, newTag)
26
+}
27
+
28
+// TagImageWithReference adds the given reference to the image ID provided.
29
+func (daemon *Daemon) TagImageWithReference(imageID image.ID, newTag reference.Named) error {
30
+	if err := daemon.referenceStore.AddTag(newTag, imageID, true); err != nil {
31
+		return err
32
+	}
33
+
34
+	daemon.LogImageEvent(imageID.String(), newTag.String(), "tag")
35
+	return nil
36
+}
... ...
@@ -2,6 +2,7 @@ package daemon
2 2
 
3 3
 import (
4 4
 	"encoding/json"
5
+	"errors"
5 6
 	"io"
6 7
 	"net/http"
7 8
 	"net/url"
... ...
@@ -24,13 +25,33 @@ import (
24 24
 // inConfig (if src is "-"), or from a URI specified in src. Progress output is
25 25
 // written to outStream. Repository and tag names can optionally be given in
26 26
 // the repo and tag arguments, respectively.
27
-func (daemon *Daemon) ImportImage(src string, newRef reference.Named, msg string, inConfig io.ReadCloser, outStream io.Writer, changes []string) error {
27
+func (daemon *Daemon) ImportImage(src string, repository, tag string, msg string, inConfig io.ReadCloser, outStream io.Writer, changes []string) error {
28 28
 	var (
29
-		sf   = streamformatter.NewJSONStreamFormatter()
30
-		rc   io.ReadCloser
31
-		resp *http.Response
29
+		sf     = streamformatter.NewJSONStreamFormatter()
30
+		rc     io.ReadCloser
31
+		resp   *http.Response
32
+		newRef reference.Named
32 33
 	)
33 34
 
35
+	if repository != "" {
36
+		var err error
37
+		newRef, err = reference.ParseNamed(repository)
38
+		if err != nil {
39
+			return err
40
+		}
41
+
42
+		if _, isCanonical := newRef.(reference.Canonical); isCanonical {
43
+			return errors.New("cannot import digest reference")
44
+		}
45
+
46
+		if tag != "" {
47
+			newRef, err = reference.WithTag(newRef, tag)
48
+			if err != nil {
49
+				return err
50
+			}
51
+		}
52
+	}
53
+
34 54
 	config, err := dockerfile.BuildFromConfig(&container.Config{}, changes)
35 55
 	if err != nil {
36 56
 		return err
... ...
@@ -103,7 +124,7 @@ func (daemon *Daemon) ImportImage(src string, newRef reference.Named, msg string
103 103
 
104 104
 	// FIXME: connect with commit code and call refstore directly
105 105
 	if newRef != nil {
106
-		if err := daemon.TagImage(newRef, id.String()); err != nil {
106
+		if err := daemon.TagImageWithReference(id, newRef); err != nil {
107 107
 			return err
108 108
 		}
109 109
 	}