| ... | ... |
@@ -1004,7 +1004,7 @@ func (cli *DockerCli) CmdImport(args ...string) error {
|
| 1004 | 1004 |
} |
| 1005 | 1005 |
|
| 1006 | 1006 |
func (cli *DockerCli) CmdPush(args ...string) error {
|
| 1007 |
- cmd := cli.Subcmd("push", "NAME", "Push an image or a repository to the registry")
|
|
| 1007 |
+ cmd := cli.Subcmd("push", "NAME[:TAG]", "Push an image or a repository to the registry")
|
|
| 1008 | 1008 |
if err := cmd.Parse(args); err != nil {
|
| 1009 | 1009 |
return nil |
| 1010 | 1010 |
} |
| ... | ... |
@@ -1017,8 +1017,10 @@ func (cli *DockerCli) CmdPush(args ...string) error {
|
| 1017 | 1017 |
|
| 1018 | 1018 |
cli.LoadConfigFile() |
| 1019 | 1019 |
|
| 1020 |
+ remote, tag := utils.ParseRepositoryTag(name) |
|
| 1021 |
+ |
|
| 1020 | 1022 |
// Resolve the Repository name from fqn to hostname + name |
| 1021 |
- hostname, _, err := registry.ResolveRepositoryName(name) |
|
| 1023 |
+ hostname, _, err := registry.ResolveRepositoryName(remote) |
|
| 1022 | 1024 |
if err != nil {
|
| 1023 | 1025 |
return err |
| 1024 | 1026 |
} |
| ... | ... |
@@ -1037,6 +1039,7 @@ func (cli *DockerCli) CmdPush(args ...string) error {
|
| 1037 | 1037 |
} |
| 1038 | 1038 |
|
| 1039 | 1039 |
v := url.Values{}
|
| 1040 |
+ v.Set("tag", tag)
|
|
| 1040 | 1041 |
push := func(authConfig registry.AuthConfig) error {
|
| 1041 | 1042 |
buf, err := json.Marshal(authConfig) |
| 1042 | 1043 |
if err != nil {
|
| ... | ... |
@@ -1046,7 +1049,7 @@ func (cli *DockerCli) CmdPush(args ...string) error {
|
| 1046 | 1046 |
base64.URLEncoding.EncodeToString(buf), |
| 1047 | 1047 |
} |
| 1048 | 1048 |
|
| 1049 |
- return cli.stream("POST", "/images/"+name+"/push?"+v.Encode(), nil, cli.out, map[string][]string{
|
|
| 1049 |
+ return cli.stream("POST", "/images/"+remote+"/push?"+v.Encode(), nil, cli.out, map[string][]string{
|
|
| 1050 | 1050 |
"X-Registry-Auth": registryAuthHeader, |
| 1051 | 1051 |
}) |
| 1052 | 1052 |
} |
| ... | ... |
@@ -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) |
| ... | ... |
@@ -1007,12 +1007,10 @@ The last container is marked as a ``Ghost`` container. It is a container that wa |
| 1007 | 1007 |
|
| 1008 | 1008 |
:: |
| 1009 | 1009 |
|
| 1010 |
- Usage: docker pull NAME |
|
| 1010 |
+ Usage: docker pull NAME[:TAG] |
|
| 1011 | 1011 |
|
| 1012 | 1012 |
Pull an image or a repository from the registry |
| 1013 | 1013 |
|
| 1014 |
- -t, --tag="": Download tagged image in repository |
|
| 1015 |
- |
|
| 1016 | 1014 |
|
| 1017 | 1015 |
.. _cli_push: |
| 1018 | 1016 |
|
| ... | ... |
@@ -1021,7 +1019,7 @@ The last container is marked as a ``Ghost`` container. It is a container that wa |
| 1021 | 1021 |
|
| 1022 | 1022 |
:: |
| 1023 | 1023 |
|
| 1024 |
- Usage: docker push NAME |
|
| 1024 |
+ Usage: docker push NAME[:TAG] |
|
| 1025 | 1025 |
|
| 1026 | 1026 |
Push an image or a repository to the registry |
| 1027 | 1027 |
|
| ... | ... |
@@ -1405,7 +1405,7 @@ func (srv *Server) ImagePull(job *engine.Job) engine.Status {
|
| 1405 | 1405 |
} |
| 1406 | 1406 |
|
| 1407 | 1407 |
// Retrieve the all the images to be uploaded in the correct order |
| 1408 |
-func (srv *Server) getImageList(localRepo map[string]string) ([]string, map[string][]string, error) {
|
|
| 1408 |
+func (srv *Server) getImageList(localRepo map[string]string, requestedTag string) ([]string, map[string][]string, error) {
|
|
| 1409 | 1409 |
var ( |
| 1410 | 1410 |
imageList []string |
| 1411 | 1411 |
imagesSeen map[string]bool = make(map[string]bool) |
| ... | ... |
@@ -1413,6 +1413,9 @@ func (srv *Server) getImageList(localRepo map[string]string) ([]string, map[stri |
| 1413 | 1413 |
) |
| 1414 | 1414 |
|
| 1415 | 1415 |
for tag, id := range localRepo {
|
| 1416 |
+ if requestedTag != "" && requestedTag != tag {
|
|
| 1417 |
+ continue |
|
| 1418 |
+ } |
|
| 1416 | 1419 |
var imageListForThisTag []string |
| 1417 | 1420 |
|
| 1418 | 1421 |
tagsByImage[id] = append(tagsByImage[id], tag) |
| ... | ... |
@@ -1439,25 +1442,29 @@ func (srv *Server) getImageList(localRepo map[string]string) ([]string, map[stri |
| 1439 | 1439 |
// append to main image list |
| 1440 | 1440 |
imageList = append(imageList, imageListForThisTag...) |
| 1441 | 1441 |
} |
| 1442 |
- |
|
| 1442 |
+ if len(imageList) == 0 {
|
|
| 1443 |
+ return nil, nil, fmt.Errorf("No images found for the requested repository / tag")
|
|
| 1444 |
+ } |
|
| 1443 | 1445 |
utils.Debugf("Image list: %v", imageList)
|
| 1444 | 1446 |
utils.Debugf("Tags by image: %v", tagsByImage)
|
| 1445 | 1447 |
|
| 1446 | 1448 |
return imageList, tagsByImage, nil |
| 1447 | 1449 |
} |
| 1448 | 1450 |
|
| 1449 |
-func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, localName, remoteName string, localRepo map[string]string, sf *utils.StreamFormatter) error {
|
|
| 1451 |
+func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, localName, remoteName string, localRepo map[string]string, tag string, sf *utils.StreamFormatter) error {
|
|
| 1450 | 1452 |
out = utils.NewWriteFlusher(out) |
| 1451 | 1453 |
utils.Debugf("Local repo: %s", localRepo)
|
| 1452 |
- imgList, tagsByImage, err := srv.getImageList(localRepo) |
|
| 1454 |
+ imgList, tagsByImage, err := srv.getImageList(localRepo, tag) |
|
| 1453 | 1455 |
if err != nil {
|
| 1454 | 1456 |
return err |
| 1455 | 1457 |
} |
| 1456 | 1458 |
|
| 1457 | 1459 |
out.Write(sf.FormatStatus("", "Sending image list"))
|
| 1458 | 1460 |
|
| 1459 |
- var repoData *registry.RepositoryData |
|
| 1460 |
- var imageIndex []*registry.ImgData |
|
| 1461 |
+ var ( |
|
| 1462 |
+ repoData *registry.RepositoryData |
|
| 1463 |
+ imageIndex []*registry.ImgData |
|
| 1464 |
+ ) |
|
| 1461 | 1465 |
|
| 1462 | 1466 |
for _, imgId := range imgList {
|
| 1463 | 1467 |
if tags, exists := tagsByImage[imgId]; exists {
|
| ... | ... |
@@ -1492,8 +1499,12 @@ func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, localName |
| 1492 | 1492 |
return err |
| 1493 | 1493 |
} |
| 1494 | 1494 |
|
| 1495 |
+ nTag := 1 |
|
| 1496 |
+ if tag == "" {
|
|
| 1497 |
+ nTag = len(localRepo) |
|
| 1498 |
+ } |
|
| 1495 | 1499 |
for _, ep := range repoData.Endpoints {
|
| 1496 |
- out.Write(sf.FormatStatus("", "Pushing repository %s (%d tags)", localName, len(localRepo)))
|
|
| 1500 |
+ out.Write(sf.FormatStatus("", "Pushing repository %s (%d tags)", localName, nTag))
|
|
| 1497 | 1501 |
|
| 1498 | 1502 |
for _, imgId := range imgList {
|
| 1499 | 1503 |
if r.LookupRemoteImage(imgId, ep, repoData.Tokens) {
|
| ... | ... |
@@ -1579,6 +1590,7 @@ func (srv *Server) ImagePush(job *engine.Job) engine.Status {
|
| 1579 | 1579 |
metaHeaders map[string][]string |
| 1580 | 1580 |
) |
| 1581 | 1581 |
|
| 1582 |
+ tag := job.Getenv("tag")
|
|
| 1582 | 1583 |
job.GetenvJson("authConfig", authConfig)
|
| 1583 | 1584 |
job.GetenvJson("metaHeaders", metaHeaders)
|
| 1584 | 1585 |
if _, err := srv.poolAdd("push", localName); err != nil {
|
| ... | ... |
@@ -1604,11 +1616,14 @@ func (srv *Server) ImagePush(job *engine.Job) engine.Status {
|
| 1604 | 1604 |
} |
| 1605 | 1605 |
|
| 1606 | 1606 |
if err != nil {
|
| 1607 |
- reposLen := len(srv.runtime.Repositories().Repositories[localName]) |
|
| 1607 |
+ reposLen := 1 |
|
| 1608 |
+ if tag == "" {
|
|
| 1609 |
+ reposLen = len(srv.runtime.Repositories().Repositories[localName]) |
|
| 1610 |
+ } |
|
| 1608 | 1611 |
job.Stdout.Write(sf.FormatStatus("", "The push refers to a repository [%s] (len: %d)", localName, reposLen))
|
| 1609 | 1612 |
// If it fails, try to get the repository |
| 1610 | 1613 |
if localRepo, exists := srv.runtime.Repositories().Repositories[localName]; exists {
|
| 1611 |
- if err := srv.pushRepository(r, job.Stdout, localName, remoteName, localRepo, sf); err != nil {
|
|
| 1614 |
+ if err := srv.pushRepository(r, job.Stdout, localName, remoteName, localRepo, tag, sf); err != nil {
|
|
| 1612 | 1615 |
return job.Error(err) |
| 1613 | 1616 |
} |
| 1614 | 1617 |
return engine.StatusOK |