Supports multiple tag push with daemon signature
Fixes #10444
Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
| ... | ... |
@@ -46,7 +46,6 @@ import ( |
| 46 | 46 |
"github.com/docker/docker/registry" |
| 47 | 47 |
"github.com/docker/docker/runconfig" |
| 48 | 48 |
"github.com/docker/docker/utils" |
| 49 |
- "github.com/docker/libtrust" |
|
| 50 | 49 |
) |
| 51 | 50 |
|
| 52 | 51 |
const ( |
| ... | ... |
@@ -1191,10 +1190,6 @@ func (cli *DockerCli) CmdPush(args ...string) error {
|
| 1191 | 1191 |
name := cmd.Arg(0) |
| 1192 | 1192 |
|
| 1193 | 1193 |
cli.LoadConfigFile() |
| 1194 |
- trustKey, err := api.LoadOrCreateTrustKey(cli.keyFile) |
|
| 1195 |
- if err != nil {
|
|
| 1196 |
- log.Fatal(err) |
|
| 1197 |
- } |
|
| 1198 | 1194 |
|
| 1199 | 1195 |
remote, tag := parsers.ParseRepositoryTag(name) |
| 1200 | 1196 |
|
| ... | ... |
@@ -1220,25 +1215,6 @@ func (cli *DockerCli) CmdPush(args ...string) error {
|
| 1220 | 1220 |
v := url.Values{}
|
| 1221 | 1221 |
v.Set("tag", tag)
|
| 1222 | 1222 |
|
| 1223 |
- body, _, err := readBody(cli.call("GET", "/images/"+remote+"/manifest?"+v.Encode(), nil, false))
|
|
| 1224 |
- if err != nil {
|
|
| 1225 |
- return err |
|
| 1226 |
- } |
|
| 1227 |
- |
|
| 1228 |
- js, err := libtrust.NewJSONSignature(body) |
|
| 1229 |
- if err != nil {
|
|
| 1230 |
- return err |
|
| 1231 |
- } |
|
| 1232 |
- err = js.Sign(trustKey) |
|
| 1233 |
- if err != nil {
|
|
| 1234 |
- return err |
|
| 1235 |
- } |
|
| 1236 |
- |
|
| 1237 |
- signedBody, err := js.PrettySignature("signatures")
|
|
| 1238 |
- if err != nil {
|
|
| 1239 |
- return err |
|
| 1240 |
- } |
|
| 1241 |
- |
|
| 1242 | 1223 |
push := func(authConfig registry.AuthConfig) error {
|
| 1243 | 1224 |
buf, err := json.Marshal(authConfig) |
| 1244 | 1225 |
if err != nil {
|
| ... | ... |
@@ -1248,7 +1224,7 @@ func (cli *DockerCli) CmdPush(args ...string) error {
|
| 1248 | 1248 |
base64.URLEncoding.EncodeToString(buf), |
| 1249 | 1249 |
} |
| 1250 | 1250 |
|
| 1251 |
- return cli.stream("POST", "/images/"+remote+"/push?"+v.Encode(), bytes.NewReader(signedBody), cli.out, map[string][]string{
|
|
| 1251 |
+ return cli.stream("POST", "/images/"+remote+"/push?"+v.Encode(), nil, cli.out, map[string][]string{
|
|
| 1252 | 1252 |
"X-Registry-Auth": registryAuthHeader, |
| 1253 | 1253 |
}) |
| 1254 | 1254 |
} |
| ... | ... |
@@ -621,18 +621,6 @@ func getImagesSearch(eng *engine.Engine, version version.Version, w http.Respons |
| 621 | 621 |
return job.Run() |
| 622 | 622 |
} |
| 623 | 623 |
|
| 624 |
-func getImageManifest(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 625 |
- if err := parseForm(r); err != nil {
|
|
| 626 |
- return err |
|
| 627 |
- } |
|
| 628 |
- |
|
| 629 |
- job := eng.Job("image_manifest", vars["name"])
|
|
| 630 |
- job.Setenv("tag", r.Form.Get("tag"))
|
|
| 631 |
- job.Stdout.Add(utils.NewWriteFlusher(w)) |
|
| 632 |
- |
|
| 633 |
- return job.Run() |
|
| 634 |
-} |
|
| 635 |
- |
|
| 636 | 624 |
func postImagesPush(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
| 637 | 625 |
if vars == nil {
|
| 638 | 626 |
return fmt.Errorf("Missing parameter")
|
| ... | ... |
@@ -664,15 +652,9 @@ func postImagesPush(eng *engine.Engine, version version.Version, w http.Response |
| 664 | 664 |
} |
| 665 | 665 |
} |
| 666 | 666 |
|
| 667 |
- manifest, err := ioutil.ReadAll(r.Body) |
|
| 668 |
- if err != nil {
|
|
| 669 |
- return err |
|
| 670 |
- } |
|
| 671 |
- |
|
| 672 | 667 |
job := eng.Job("push", vars["name"])
|
| 673 | 668 |
job.SetenvJson("metaHeaders", metaHeaders)
|
| 674 | 669 |
job.SetenvJson("authConfig", authConfig)
|
| 675 |
- job.Setenv("manifest", string(manifest))
|
|
| 676 | 670 |
job.Setenv("tag", r.Form.Get("tag"))
|
| 677 | 671 |
if version.GreaterThan("1.0") {
|
| 678 | 672 |
job.SetenvBool("json", true)
|
| ... | ... |
@@ -1325,7 +1307,6 @@ func createRouter(eng *engine.Engine, logging, enableCors bool, dockerVersion st |
| 1325 | 1325 |
"/images/viz": getImagesViz, |
| 1326 | 1326 |
"/images/search": getImagesSearch, |
| 1327 | 1327 |
"/images/get": getImagesGet, |
| 1328 |
- "/images/{name:.*}/manifest": getImageManifest,
|
|
| 1329 | 1328 |
"/images/{name:.*}/get": getImagesGet,
|
| 1330 | 1329 |
"/images/{name:.*}/history": getImagesHistory,
|
| 1331 | 1330 |
"/images/{name:.*}/json": getImagesByName,
|
| ... | ... |
@@ -15,35 +15,6 @@ import ( |
| 15 | 15 |
"github.com/docker/libtrust" |
| 16 | 16 |
) |
| 17 | 17 |
|
| 18 |
-func (s *TagStore) CmdManifest(job *engine.Job) engine.Status {
|
|
| 19 |
- if len(job.Args) != 1 {
|
|
| 20 |
- return job.Errorf("usage: %s NAME", job.Name)
|
|
| 21 |
- } |
|
| 22 |
- name := job.Args[0] |
|
| 23 |
- tag := job.Getenv("tag")
|
|
| 24 |
- if tag == "" {
|
|
| 25 |
- tag = "latest" |
|
| 26 |
- } |
|
| 27 |
- |
|
| 28 |
- // Resolve the Repository name from fqn to endpoint + name |
|
| 29 |
- repoInfo, err := registry.ParseRepositoryInfo(name) |
|
| 30 |
- if err != nil {
|
|
| 31 |
- return job.Error(err) |
|
| 32 |
- } |
|
| 33 |
- |
|
| 34 |
- manifestBytes, err := s.newManifest(name, repoInfo.RemoteName, tag) |
|
| 35 |
- if err != nil {
|
|
| 36 |
- return job.Error(err) |
|
| 37 |
- } |
|
| 38 |
- |
|
| 39 |
- _, err = job.Stdout.Write(manifestBytes) |
|
| 40 |
- if err != nil {
|
|
| 41 |
- return job.Error(err) |
|
| 42 |
- } |
|
| 43 |
- |
|
| 44 |
- return engine.StatusOK |
|
| 45 |
-} |
|
| 46 |
- |
|
| 47 | 18 |
func (s *TagStore) newManifest(localName, remoteName, tag string) ([]byte, error) {
|
| 48 | 19 |
manifest := ®istry.ManifestData{
|
| 49 | 20 |
Name: remoteName, |
| ... | ... |
@@ -130,7 +101,12 @@ func (s *TagStore) newManifest(localName, remoteName, tag string) ([]byte, error |
| 130 | 130 |
return manifestBytes, nil |
| 131 | 131 |
} |
| 132 | 132 |
|
| 133 |
-func (s *TagStore) verifyManifest(eng *engine.Engine, manifestBytes []byte) (*registry.ManifestData, bool, error) {
|
|
| 133 |
+// loadManifest loads a manifest from a byte array and verifies its content. |
|
| 134 |
+// The signature must be verified or an error is returned. If the manifest |
|
| 135 |
+// contains no signatures by a trusted key for the name in the manifest, the |
|
| 136 |
+// image is not considered verified. The parsed manifest object and a boolean |
|
| 137 |
+// for whether the manifest is verified is returned. |
|
| 138 |
+func (s *TagStore) loadManifest(eng *engine.Engine, manifestBytes []byte) (*registry.ManifestData, bool, error) {
|
|
| 134 | 139 |
sig, err := libtrust.ParsePrettySignature(manifestBytes, "signatures") |
| 135 | 140 |
if err != nil {
|
| 136 | 141 |
return nil, false, fmt.Errorf("error parsing payload: %s", err)
|
| ... | ... |
@@ -417,7 +417,7 @@ func (s *TagStore) pullV2Tag(eng *engine.Engine, r *registry.Session, out io.Wri |
| 417 | 417 |
return false, err |
| 418 | 418 |
} |
| 419 | 419 |
|
| 420 |
- manifest, verified, err := s.verifyManifest(eng, manifestBytes) |
|
| 420 |
+ manifest, verified, err := s.loadManifest(eng, manifestBytes) |
|
| 421 | 421 |
if err != nil {
|
| 422 | 422 |
return false, fmt.Errorf("error verifying manifest: %s", err)
|
| 423 | 423 |
} |
| ... | ... |
@@ -65,6 +65,25 @@ func (s *TagStore) getImageList(localRepo map[string]string, requestedTag string |
| 65 | 65 |
return imageList, tagsByImage, nil |
| 66 | 66 |
} |
| 67 | 67 |
|
| 68 |
+func (s *TagStore) getImageTags(localName, askedTag string) ([]string, error) {
|
|
| 69 |
+ localRepo, err := s.Get(localName) |
|
| 70 |
+ if err != nil {
|
|
| 71 |
+ return nil, err |
|
| 72 |
+ } |
|
| 73 |
+ log.Debugf("Checking %s against %#v", askedTag, localRepo)
|
|
| 74 |
+ if len(askedTag) > 0 {
|
|
| 75 |
+ if _, ok := localRepo[askedTag]; !ok {
|
|
| 76 |
+ return nil, fmt.Errorf("Tag does not exist for %s:%s", localName, askedTag)
|
|
| 77 |
+ } |
|
| 78 |
+ return []string{askedTag}, nil
|
|
| 79 |
+ } |
|
| 80 |
+ var tags []string |
|
| 81 |
+ for tag := range localRepo {
|
|
| 82 |
+ tags = append(tags, tag) |
|
| 83 |
+ } |
|
| 84 |
+ return tags, nil |
|
| 85 |
+} |
|
| 86 |
+ |
|
| 68 | 87 |
// createImageIndex returns an index of an image's layer IDs and tags. |
| 69 | 88 |
func (s *TagStore) createImageIndex(images []string, tags map[string][]string) []*registry.ImgData {
|
| 70 | 89 |
var imageIndex []*registry.ImgData |
| ... | ... |
@@ -251,7 +270,7 @@ func (s *TagStore) pushImage(r *registry.Session, out io.Writer, imgID, ep strin |
| 251 | 251 |
return imgData.Checksum, nil |
| 252 | 252 |
} |
| 253 | 253 |
|
| 254 |
-func (s *TagStore) pushV2Repository(r *registry.Session, eng *engine.Engine, out io.Writer, repoInfo *registry.RepositoryInfo, manifestBytes, tag string, sf *utils.StreamFormatter) error {
|
|
| 254 |
+func (s *TagStore) pushV2Repository(r *registry.Session, eng *engine.Engine, out io.Writer, repoInfo *registry.RepositoryInfo, tag string, sf *utils.StreamFormatter) error {
|
|
| 255 | 255 |
if repoInfo.Official {
|
| 256 | 256 |
j := eng.Job("trust_update_base")
|
| 257 | 257 |
if err := j.Run(); err != nil {
|
| ... | ... |
@@ -263,13 +282,22 @@ func (s *TagStore) pushV2Repository(r *registry.Session, eng *engine.Engine, out |
| 263 | 263 |
if err != nil {
|
| 264 | 264 |
return fmt.Errorf("error getting registry endpoint: %s", err)
|
| 265 | 265 |
} |
| 266 |
+ |
|
| 267 |
+ tags, err := s.getImageTags(repoInfo.LocalName, tag) |
|
| 268 |
+ if err != nil {
|
|
| 269 |
+ return err |
|
| 270 |
+ } |
|
| 271 |
+ if len(tags) == 0 {
|
|
| 272 |
+ return fmt.Errorf("No tags to push for %s", repoInfo.LocalName)
|
|
| 273 |
+ } |
|
| 274 |
+ |
|
| 266 | 275 |
auth, err := r.GetV2Authorization(endpoint, repoInfo.RemoteName, false) |
| 267 | 276 |
if err != nil {
|
| 268 | 277 |
return fmt.Errorf("error getting authorization: %s", err)
|
| 269 | 278 |
} |
| 270 | 279 |
|
| 271 |
- // if no manifest is given, generate and sign with the key associated with the local tag store |
|
| 272 |
- if len(manifestBytes) == 0 {
|
|
| 280 |
+ for _, tag := range tags {
|
|
| 281 |
+ log.Debugf("Pushing %s:%s to v2 repository", repoInfo.LocalName, tag)
|
|
| 273 | 282 |
mBytes, err := s.newManifest(repoInfo.LocalName, repoInfo.RemoteName, tag) |
| 274 | 283 |
if err != nil {
|
| 275 | 284 |
return err |
| ... | ... |
@@ -287,63 +315,66 @@ func (s *TagStore) pushV2Repository(r *registry.Session, eng *engine.Engine, out |
| 287 | 287 |
if err != nil {
|
| 288 | 288 |
return err |
| 289 | 289 |
} |
| 290 |
- log.Infof("Signed manifest using daemon's key: %s", s.trustKey.KeyID())
|
|
| 291 |
- |
|
| 292 |
- manifestBytes = string(signedBody) |
|
| 293 |
- } |
|
| 294 |
- |
|
| 295 |
- manifest, verified, err := s.verifyManifest(eng, []byte(manifestBytes)) |
|
| 296 |
- if err != nil {
|
|
| 297 |
- return fmt.Errorf("error verifying manifest: %s", err)
|
|
| 298 |
- } |
|
| 299 |
- |
|
| 300 |
- if err := checkValidManifest(manifest); err != nil {
|
|
| 301 |
- return fmt.Errorf("invalid manifest: %s", err)
|
|
| 302 |
- } |
|
| 303 |
- |
|
| 304 |
- if !verified {
|
|
| 305 |
- log.Debugf("Pushing unverified image")
|
|
| 306 |
- } |
|
| 290 |
+ log.Infof("Signed manifest for %s:%s using daemon's key: %s", repoInfo.LocalName, tag, s.trustKey.KeyID())
|
|
| 307 | 291 |
|
| 308 |
- for i := len(manifest.FSLayers) - 1; i >= 0; i-- {
|
|
| 309 |
- var ( |
|
| 310 |
- sumStr = manifest.FSLayers[i].BlobSum |
|
| 311 |
- imgJSON = []byte(manifest.History[i].V1Compatibility) |
|
| 312 |
- ) |
|
| 292 |
+ manifestBytes := string(signedBody) |
|
| 313 | 293 |
|
| 314 |
- sumParts := strings.SplitN(sumStr, ":", 2) |
|
| 315 |
- if len(sumParts) < 2 {
|
|
| 316 |
- return fmt.Errorf("Invalid checksum: %s", sumStr)
|
|
| 294 |
+ manifest, verified, err := s.loadManifest(eng, signedBody) |
|
| 295 |
+ if err != nil {
|
|
| 296 |
+ return fmt.Errorf("error verifying manifest: %s", err)
|
|
| 317 | 297 |
} |
| 318 |
- manifestSum := sumParts[1] |
|
| 319 | 298 |
|
| 320 |
- img, err := image.NewImgJSON(imgJSON) |
|
| 321 |
- if err != nil {
|
|
| 322 |
- return fmt.Errorf("Failed to parse json: %s", err)
|
|
| 299 |
+ if err := checkValidManifest(manifest); err != nil {
|
|
| 300 |
+ return fmt.Errorf("invalid manifest: %s", err)
|
|
| 323 | 301 |
} |
| 324 | 302 |
|
| 325 |
- // Call mount blob |
|
| 326 |
- exists, err := r.HeadV2ImageBlob(endpoint, repoInfo.RemoteName, sumParts[0], manifestSum, auth) |
|
| 327 |
- if err != nil {
|
|
| 328 |
- out.Write(sf.FormatProgress(utils.TruncateID(img.ID), "Image push failed", nil)) |
|
| 329 |
- return err |
|
| 303 |
+ if verified {
|
|
| 304 |
+ log.Infof("Pushing verified image, key %s is registered for %q", s.trustKey.KeyID(), repoInfo.RemoteName)
|
|
| 330 | 305 |
} |
| 331 | 306 |
|
| 332 |
- if !exists {
|
|
| 333 |
- if err := s.PushV2Image(r, img, endpoint, repoInfo.RemoteName, sumParts[0], manifestSum, sf, out, auth); err != nil {
|
|
| 307 |
+ for i := len(manifest.FSLayers) - 1; i >= 0; i-- {
|
|
| 308 |
+ var ( |
|
| 309 |
+ sumStr = manifest.FSLayers[i].BlobSum |
|
| 310 |
+ imgJSON = []byte(manifest.History[i].V1Compatibility) |
|
| 311 |
+ ) |
|
| 312 |
+ |
|
| 313 |
+ sumParts := strings.SplitN(sumStr, ":", 2) |
|
| 314 |
+ if len(sumParts) < 2 {
|
|
| 315 |
+ return fmt.Errorf("Invalid checksum: %s", sumStr)
|
|
| 316 |
+ } |
|
| 317 |
+ manifestSum := sumParts[1] |
|
| 318 |
+ |
|
| 319 |
+ img, err := image.NewImgJSON(imgJSON) |
|
| 320 |
+ if err != nil {
|
|
| 321 |
+ return fmt.Errorf("Failed to parse json: %s", err)
|
|
| 322 |
+ } |
|
| 323 |
+ |
|
| 324 |
+ // Call mount blob |
|
| 325 |
+ exists, err := r.HeadV2ImageBlob(endpoint, repoInfo.RemoteName, sumParts[0], manifestSum, auth) |
|
| 326 |
+ if err != nil {
|
|
| 327 |
+ out.Write(sf.FormatProgress(utils.TruncateID(img.ID), "Image push failed", nil)) |
|
| 334 | 328 |
return err |
| 335 | 329 |
} |
| 336 |
- } else {
|
|
| 337 |
- out.Write(sf.FormatProgress(utils.TruncateID(img.ID), "Image already exists", nil)) |
|
| 330 |
+ |
|
| 331 |
+ if !exists {
|
|
| 332 |
+ if err := s.pushV2Image(r, img, endpoint, repoInfo.RemoteName, sumParts[0], manifestSum, sf, out, auth); err != nil {
|
|
| 333 |
+ return err |
|
| 334 |
+ } |
|
| 335 |
+ } else {
|
|
| 336 |
+ out.Write(sf.FormatProgress(utils.TruncateID(img.ID), "Image already exists", nil)) |
|
| 337 |
+ } |
|
| 338 | 338 |
} |
| 339 |
- } |
|
| 340 | 339 |
|
| 341 |
- // push the manifest |
|
| 342 |
- return r.PutV2ImageManifest(endpoint, repoInfo.RemoteName, tag, bytes.NewReader([]byte(manifestBytes)), auth) |
|
| 340 |
+ // push the manifest |
|
| 341 |
+ if err := r.PutV2ImageManifest(endpoint, repoInfo.RemoteName, tag, bytes.NewReader([]byte(manifestBytes)), auth); err != nil {
|
|
| 342 |
+ return err |
|
| 343 |
+ } |
|
| 344 |
+ } |
|
| 345 |
+ return nil |
|
| 343 | 346 |
} |
| 344 | 347 |
|
| 345 | 348 |
// PushV2Image pushes the image content to the v2 registry, first buffering the contents to disk |
| 346 |
-func (s *TagStore) PushV2Image(r *registry.Session, img *image.Image, endpoint *registry.Endpoint, imageName, sumType, sumStr string, sf *utils.StreamFormatter, out io.Writer, auth *registry.RequestAuthorization) error {
|
|
| 349 |
+func (s *TagStore) pushV2Image(r *registry.Session, img *image.Image, endpoint *registry.Endpoint, imageName, sumType, sumStr string, sf *utils.StreamFormatter, out io.Writer, auth *registry.RequestAuthorization) error {
|
|
| 347 | 350 |
out.Write(sf.FormatProgress(utils.TruncateID(img.ID), "Buffering to Disk", nil)) |
| 348 | 351 |
|
| 349 | 352 |
image, err := s.graph.Get(img.ID) |
| ... | ... |
@@ -398,7 +429,6 @@ func (s *TagStore) CmdPush(job *engine.Job) engine.Status {
|
| 398 | 398 |
} |
| 399 | 399 |
|
| 400 | 400 |
tag := job.Getenv("tag")
|
| 401 |
- manifestBytes := job.Getenv("manifest")
|
|
| 402 | 401 |
job.GetenvJson("authConfig", authConfig)
|
| 403 | 402 |
job.GetenvJson("metaHeaders", &metaHeaders)
|
| 404 | 403 |
|
| ... | ... |
@@ -418,12 +448,8 @@ func (s *TagStore) CmdPush(job *engine.Job) engine.Status {
|
| 418 | 418 |
return job.Error(err2) |
| 419 | 419 |
} |
| 420 | 420 |
|
| 421 |
- if len(tag) == 0 {
|
|
| 422 |
- tag = DEFAULTTAG |
|
| 423 |
- } |
|
| 424 |
- |
|
| 425 | 421 |
if repoInfo.Index.Official || endpoint.Version == registry.APIVersion2 {
|
| 426 |
- err := s.pushV2Repository(r, job.Eng, job.Stdout, repoInfo, manifestBytes, tag, sf) |
|
| 422 |
+ err := s.pushV2Repository(r, job.Eng, job.Stdout, repoInfo, tag, sf) |
|
| 427 | 423 |
if err == nil {
|
| 428 | 424 |
return engine.StatusOK |
| 429 | 425 |
} |
| ... | ... |
@@ -25,7 +25,6 @@ func (s *TagStore) Install(eng *engine.Engine) error {
|
| 25 | 25 |
"import": s.CmdImport, |
| 26 | 26 |
"pull": s.CmdPull, |
| 27 | 27 |
"push": s.CmdPush, |
| 28 |
- "image_manifest": s.CmdManifest, |
|
| 29 | 28 |
} {
|
| 30 | 29 |
if err := eng.Register(name, handler); err != nil {
|
| 31 | 30 |
return fmt.Errorf("Could not register %q: %v", name, err)
|
| ... | ... |
@@ -24,6 +24,7 @@ func TestPullImageWithAliases(t *testing.T) {
|
| 24 | 24 |
if out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "tag", "busybox", repo)); err != nil {
|
| 25 | 25 |
t.Fatalf("Failed to tag image %v: error %v, output %q", repos, err, out)
|
| 26 | 26 |
} |
| 27 |
+ defer deleteImages(repo) |
|
| 27 | 28 |
if out, err := exec.Command(dockerBinary, "push", repo).CombinedOutput(); err != nil {
|
| 28 | 29 |
t.Fatalf("Failed to push image %v: error %v, output %q", err, string(out))
|
| 29 | 30 |
} |
| ... | ... |
@@ -40,7 +41,6 @@ func TestPullImageWithAliases(t *testing.T) {
|
| 40 | 40 |
if out, _, err := runCommandWithOutput(pullCmd); err != nil {
|
| 41 | 41 |
t.Fatalf("Failed to pull %v: error %v, output %q", repoName, err, out)
|
| 42 | 42 |
} |
| 43 |
- defer deleteImages(repos[0]) |
|
| 44 | 43 |
if err := exec.Command(dockerBinary, "inspect", repos[0]).Run(); err != nil {
|
| 45 | 44 |
t.Fatalf("Image %v was not pulled down", repos[0])
|
| 46 | 45 |
} |
| ... | ... |
@@ -45,7 +45,7 @@ func TestPushUntagged(t *testing.T) {
|
| 45 | 45 |
|
| 46 | 46 |
repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
|
| 47 | 47 |
|
| 48 |
- expected := "does not exist" |
|
| 48 |
+ expected := "No tags to push" |
|
| 49 | 49 |
pushCmd := exec.Command(dockerBinary, "push", repoName) |
| 50 | 50 |
if out, _, err := runCommandWithOutput(pushCmd); err == nil {
|
| 51 | 51 |
t.Fatalf("pushing the image to the private registry should have failed: outuput %q", out)
|
| ... | ... |
@@ -55,6 +55,46 @@ func TestPushUntagged(t *testing.T) {
|
| 55 | 55 |
logDone("push - untagged image")
|
| 56 | 56 |
} |
| 57 | 57 |
|
| 58 |
+func TestPushBadTag(t *testing.T) {
|
|
| 59 |
+ defer setupRegistry(t)() |
|
| 60 |
+ |
|
| 61 |
+ repoName := fmt.Sprintf("%v/dockercli/busybox:latest", privateRegistryURL)
|
|
| 62 |
+ |
|
| 63 |
+ expected := "does not exist" |
|
| 64 |
+ pushCmd := exec.Command(dockerBinary, "push", repoName) |
|
| 65 |
+ if out, _, err := runCommandWithOutput(pushCmd); err == nil {
|
|
| 66 |
+ t.Fatalf("pushing the image to the private registry should have failed: outuput %q", out)
|
|
| 67 |
+ } else if !strings.Contains(out, expected) {
|
|
| 68 |
+ t.Fatalf("pushing the image failed with an unexpected message: expected %q, got %q", expected, out)
|
|
| 69 |
+ } |
|
| 70 |
+ logDone("push - image with bad tag")
|
|
| 71 |
+} |
|
| 72 |
+ |
|
| 73 |
+func TestPushMultipleTags(t *testing.T) {
|
|
| 74 |
+ defer setupRegistry(t)() |
|
| 75 |
+ |
|
| 76 |
+ repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
|
|
| 77 |
+ repoTag1 := fmt.Sprintf("%v/dockercli/busybox:t1", privateRegistryURL)
|
|
| 78 |
+ repoTag2 := fmt.Sprintf("%v/dockercli/busybox:t2", privateRegistryURL)
|
|
| 79 |
+ // tag the image to upload it tot he private registry |
|
| 80 |
+ tagCmd1 := exec.Command(dockerBinary, "tag", "busybox", repoTag1) |
|
| 81 |
+ if out, _, err := runCommandWithOutput(tagCmd1); err != nil {
|
|
| 82 |
+ t.Fatalf("image tagging failed: %s, %v", out, err)
|
|
| 83 |
+ } |
|
| 84 |
+ defer deleteImages(repoTag1) |
|
| 85 |
+ tagCmd2 := exec.Command(dockerBinary, "tag", "busybox", repoTag2) |
|
| 86 |
+ if out, _, err := runCommandWithOutput(tagCmd2); err != nil {
|
|
| 87 |
+ t.Fatalf("image tagging failed: %s, %v", out, err)
|
|
| 88 |
+ } |
|
| 89 |
+ defer deleteImages(repoTag2) |
|
| 90 |
+ |
|
| 91 |
+ pushCmd := exec.Command(dockerBinary, "push", repoName) |
|
| 92 |
+ if out, _, err := runCommandWithOutput(pushCmd); err != nil {
|
|
| 93 |
+ t.Fatalf("pushing the image to the private registry has failed: %s, %v", out, err)
|
|
| 94 |
+ } |
|
| 95 |
+ logDone("push - multiple tags to private registry")
|
|
| 96 |
+} |
|
| 97 |
+ |
|
| 58 | 98 |
func TestPushInterrupt(t *testing.T) {
|
| 59 | 99 |
defer setupRegistry(t)() |
| 60 | 100 |
|