Browse code

Allow push of a single tag

Docker-DCO-1.1-Signed-off-by: Guillaume J. Charmes <guillaume@charmes.net> (github: creack)

Guillaume J. Charmes authored on 2014/04/01 10:50:10
Showing 3 changed files
... ...
@@ -1000,7 +1000,7 @@ func (cli *DockerCli) CmdImport(args ...string) error {
1000 1000
 }
1001 1001
 
1002 1002
 func (cli *DockerCli) CmdPush(args ...string) error {
1003
-	cmd := cli.Subcmd("push", "NAME", "Push an image or a repository to the registry")
1003
+	cmd := cli.Subcmd("push", "NAME[:TAG]", "Push an image or a repository to the registry")
1004 1004
 	if err := cmd.Parse(args); err != nil {
1005 1005
 		return nil
1006 1006
 	}
... ...
@@ -1013,8 +1013,10 @@ func (cli *DockerCli) CmdPush(args ...string) error {
1013 1013
 
1014 1014
 	cli.LoadConfigFile()
1015 1015
 
1016
+	remote, tag := utils.ParseRepositoryTag(name)
1017
+
1016 1018
 	// Resolve the Repository name from fqn to hostname + name
1017
-	hostname, _, err := registry.ResolveRepositoryName(name)
1019
+	hostname, _, err := registry.ResolveRepositoryName(remote)
1018 1020
 	if err != nil {
1019 1021
 		return err
1020 1022
 	}
... ...
@@ -1033,6 +1035,7 @@ func (cli *DockerCli) CmdPush(args ...string) error {
1033 1033
 	}
1034 1034
 
1035 1035
 	v := url.Values{}
1036
+	v.Set("tag", tag)
1036 1037
 	push := func(authConfig registry.AuthConfig) error {
1037 1038
 		buf, err := json.Marshal(authConfig)
1038 1039
 		if err != nil {
... ...
@@ -1042,7 +1045,7 @@ func (cli *DockerCli) CmdPush(args ...string) error {
1042 1042
 			base64.URLEncoding.EncodeToString(buf),
1043 1043
 		}
1044 1044
 
1045
-		return cli.stream("POST", "/images/"+name+"/push?"+v.Encode(), nil, cli.out, map[string][]string{
1045
+		return cli.stream("POST", "/images/"+remote+"/push?"+v.Encode(), nil, cli.out, map[string][]string{
1046 1046
 			"X-Registry-Auth": registryAuthHeader,
1047 1047
 		})
1048 1048
 	}
... ...
@@ -517,6 +517,7 @@ func postImagesPush(eng *engine.Engine, version version.Version, w http.Response
517 517
 	job := eng.Job("push", vars["name"])
518 518
 	job.SetenvJson("metaHeaders", metaHeaders)
519 519
 	job.SetenvJson("authConfig", authConfig)
520
+	job.Setenv("tag", r.Form.Get("tag"))
520 521
 	if version.GreaterThan("1.0") {
521 522
 		job.SetenvBool("json", true)
522 523
 		streamJSON(job, w, true)
... ...
@@ -1401,7 +1401,7 @@ func (srv *Server) ImagePull(job *engine.Job) engine.Status {
1401 1401
 }
1402 1402
 
1403 1403
 // Retrieve the all the images to be uploaded in the correct order
1404
-func (srv *Server) getImageList(localRepo map[string]string) ([]string, map[string][]string, error) {
1404
+func (srv *Server) getImageList(localRepo map[string]string, requestedTag string) ([]string, map[string][]string, error) {
1405 1405
 	var (
1406 1406
 		imageList   []string
1407 1407
 		imagesSeen  map[string]bool     = make(map[string]bool)
... ...
@@ -1409,6 +1409,9 @@ func (srv *Server) getImageList(localRepo map[string]string) ([]string, map[stri
1409 1409
 	)
1410 1410
 
1411 1411
 	for tag, id := range localRepo {
1412
+		if requestedTag != "" && requestedTag != tag {
1413
+			continue
1414
+		}
1412 1415
 		var imageListForThisTag []string
1413 1416
 
1414 1417
 		tagsByImage[id] = append(tagsByImage[id], tag)
... ...
@@ -1435,25 +1438,29 @@ func (srv *Server) getImageList(localRepo map[string]string) ([]string, map[stri
1435 1435
 		// append to main image list
1436 1436
 		imageList = append(imageList, imageListForThisTag...)
1437 1437
 	}
1438
-
1438
+	if len(imageList) == 0 {
1439
+		return nil, nil, fmt.Errorf("No images found for the requested repository / tag")
1440
+	}
1439 1441
 	utils.Debugf("Image list: %v", imageList)
1440 1442
 	utils.Debugf("Tags by image: %v", tagsByImage)
1441 1443
 
1442 1444
 	return imageList, tagsByImage, nil
1443 1445
 }
1444 1446
 
1445
-func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, localName, remoteName string, localRepo map[string]string, sf *utils.StreamFormatter) error {
1447
+func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, localName, remoteName string, localRepo map[string]string, tag string, sf *utils.StreamFormatter) error {
1446 1448
 	out = utils.NewWriteFlusher(out)
1447 1449
 	utils.Debugf("Local repo: %s", localRepo)
1448
-	imgList, tagsByImage, err := srv.getImageList(localRepo)
1450
+	imgList, tagsByImage, err := srv.getImageList(localRepo, tag)
1449 1451
 	if err != nil {
1450 1452
 		return err
1451 1453
 	}
1452 1454
 
1453 1455
 	out.Write(sf.FormatStatus("", "Sending image list"))
1454 1456
 
1455
-	var repoData *registry.RepositoryData
1456
-	var imageIndex []*registry.ImgData
1457
+	var (
1458
+		repoData   *registry.RepositoryData
1459
+		imageIndex []*registry.ImgData
1460
+	)
1457 1461
 
1458 1462
 	for _, imgId := range imgList {
1459 1463
 		if tags, exists := tagsByImage[imgId]; exists {
... ...
@@ -1488,8 +1495,12 @@ func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, localName
1488 1488
 		return err
1489 1489
 	}
1490 1490
 
1491
+	nTag := 1
1492
+	if tag == "" {
1493
+		nTag = len(localRepo)
1494
+	}
1491 1495
 	for _, ep := range repoData.Endpoints {
1492
-		out.Write(sf.FormatStatus("", "Pushing repository %s (%d tags)", localName, len(localRepo)))
1496
+		out.Write(sf.FormatStatus("", "Pushing repository %s (%d tags)", localName, nTag))
1493 1497
 
1494 1498
 		for _, imgId := range imgList {
1495 1499
 			if r.LookupRemoteImage(imgId, ep, repoData.Tokens) {
... ...
@@ -1575,6 +1586,7 @@ func (srv *Server) ImagePush(job *engine.Job) engine.Status {
1575 1575
 		metaHeaders map[string][]string
1576 1576
 	)
1577 1577
 
1578
+	tag := job.Getenv("tag")
1578 1579
 	job.GetenvJson("authConfig", authConfig)
1579 1580
 	job.GetenvJson("metaHeaders", metaHeaders)
1580 1581
 	if _, err := srv.poolAdd("push", localName); err != nil {
... ...
@@ -1600,11 +1612,14 @@ func (srv *Server) ImagePush(job *engine.Job) engine.Status {
1600 1600
 	}
1601 1601
 
1602 1602
 	if err != nil {
1603
-		reposLen := len(srv.runtime.Repositories().Repositories[localName])
1603
+		reposLen := 1
1604
+		if tag == "" {
1605
+			reposLen = len(srv.runtime.Repositories().Repositories[localName])
1606
+		}
1604 1607
 		job.Stdout.Write(sf.FormatStatus("", "The push refers to a repository [%s] (len: %d)", localName, reposLen))
1605 1608
 		// If it fails, try to get the repository
1606 1609
 		if localRepo, exists := srv.runtime.Repositories().Repositories[localName]; exists {
1607
-			if err := srv.pushRepository(r, job.Stdout, localName, remoteName, localRepo, sf); err != nil {
1610
+			if err := srv.pushRepository(r, job.Stdout, localName, remoteName, localRepo, tag, sf); err != nil {
1608 1611
 				return job.Error(err)
1609 1612
 			}
1610 1613
 			return engine.StatusOK