Merge pull request #15780 from mountkin/build-multi-tags
Add ability to add multiple tags with docker build
This commit is contained in:
commit
448398c2a8
10 changed files with 119 additions and 40 deletions
|
@ -49,7 +49,8 @@ const (
|
|||
// Usage: docker build [OPTIONS] PATH | URL | -
|
||||
func (cli *DockerCli) CmdBuild(args ...string) error {
|
||||
cmd := Cli.Subcmd("build", []string{"PATH | URL | -"}, Cli.DockerCommands["build"].Description, true)
|
||||
tag := cmd.String([]string{"t", "-tag"}, "", "Repository name (and optionally a tag) for the image")
|
||||
flTags := opts.NewListOpts(validateTag)
|
||||
cmd.Var(&flTags, []string{"t", "-tag"}, "Name and optionally a tag in the 'name:tag' format")
|
||||
suppressOutput := cmd.Bool([]string{"q", "-quiet"}, false, "Suppress the verbose output generated by the containers")
|
||||
noCache := cmd.Bool([]string{"#no-cache", "-no-cache"}, false, "Do not use cache when building the image")
|
||||
rm := cmd.Bool([]string{"#rm", "-rm"}, true, "Remove intermediate containers after a successful build")
|
||||
|
@ -207,24 +208,11 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
|||
memorySwap = parsedMemorySwap
|
||||
}
|
||||
}
|
||||
|
||||
// Send the build context
|
||||
v := &url.Values{}
|
||||
|
||||
//Check if the given image name can be resolved
|
||||
if *tag != "" {
|
||||
repository, tag := parsers.ParseRepositoryTag(*tag)
|
||||
if err := registry.ValidateRepositoryName(repository); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(tag) > 0 {
|
||||
if err := tags.ValidateTagName(tag); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
v := url.Values{
|
||||
"t": flTags.GetAll(),
|
||||
}
|
||||
|
||||
v.Set("t", *tag)
|
||||
|
||||
if *suppressOutput {
|
||||
v.Set("q", "1")
|
||||
}
|
||||
|
@ -324,6 +312,24 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// validateTag checks if the given image name can be resolved.
|
||||
func validateTag(rawRepo string) (string, error) {
|
||||
repository, tag := parsers.ParseRepositoryTag(rawRepo)
|
||||
if err := registry.ValidateRepositoryName(repository); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(tag) == 0 {
|
||||
return rawRepo, nil
|
||||
}
|
||||
|
||||
if err := tags.ValidateTagName(tag); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return rawRepo, nil
|
||||
}
|
||||
|
||||
// isUNC returns true if the path is UNC (one starting \\). It always returns
|
||||
// false on Linux.
|
||||
func isUNC(path string) bool {
|
||||
|
|
|
@ -308,16 +308,9 @@ func (s *router) postBuild(ctx context.Context, w http.ResponseWriter, r *http.R
|
|||
buildConfig.Pull = true
|
||||
}
|
||||
|
||||
repoName, tag := parsers.ParseRepositoryTag(r.FormValue("t"))
|
||||
if repoName != "" {
|
||||
if err := registry.ValidateRepositoryName(repoName); err != nil {
|
||||
return errf(err)
|
||||
}
|
||||
if len(tag) > 0 {
|
||||
if err := tags.ValidateTagName(tag); err != nil {
|
||||
return errf(err)
|
||||
}
|
||||
}
|
||||
repoAndTags, err := sanitizeRepoAndTags(r.Form["t"])
|
||||
if err != nil {
|
||||
return errf(err)
|
||||
}
|
||||
|
||||
buildConfig.DockerfileName = r.FormValue("dockerfile")
|
||||
|
@ -369,7 +362,6 @@ func (s *router) postBuild(ctx context.Context, w http.ResponseWriter, r *http.R
|
|||
var (
|
||||
context builder.ModifiableContext
|
||||
dockerfileName string
|
||||
err error
|
||||
)
|
||||
context, dockerfileName, err = daemonbuilder.DetectContextFromRemoteURL(r.Body, remoteURL, pReader)
|
||||
if err != nil {
|
||||
|
@ -418,8 +410,8 @@ func (s *router) postBuild(ctx context.Context, w http.ResponseWriter, r *http.R
|
|||
return errf(err)
|
||||
}
|
||||
|
||||
if repoName != "" {
|
||||
if err := s.daemon.TagImage(repoName, tag, string(imgID), true); err != nil {
|
||||
for _, rt := range repoAndTags {
|
||||
if err := s.daemon.TagImage(rt.repo, rt.tag, string(imgID), true); err != nil {
|
||||
return errf(err)
|
||||
}
|
||||
}
|
||||
|
@ -427,6 +419,48 @@ func (s *router) postBuild(ctx context.Context, w http.ResponseWriter, r *http.R
|
|||
return nil
|
||||
}
|
||||
|
||||
// repoAndTag is a helper struct for holding the parsed repositories and tags of
|
||||
// the input "t" argument.
|
||||
type repoAndTag struct {
|
||||
repo, tag string
|
||||
}
|
||||
|
||||
// sanitizeRepoAndTags parses the raw "t" parameter received from the client
|
||||
// to a slice of repoAndTag.
|
||||
// It also validates each repoName and tag.
|
||||
func sanitizeRepoAndTags(names []string) ([]repoAndTag, error) {
|
||||
var (
|
||||
repoAndTags []repoAndTag
|
||||
// This map is used for deduplicating the "-t" paramter.
|
||||
uniqNames = make(map[string]struct{})
|
||||
)
|
||||
for _, repo := range names {
|
||||
name, tag := parsers.ParseRepositoryTag(repo)
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := registry.ValidateRepositoryName(name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nameWithTag := name
|
||||
if len(tag) > 0 {
|
||||
if err := tags.ValidateTagName(tag); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nameWithTag += ":" + tag
|
||||
} else {
|
||||
nameWithTag += ":" + tags.DefaultTag
|
||||
}
|
||||
if _, exists := uniqNames[nameWithTag]; !exists {
|
||||
uniqNames[nameWithTag] = struct{}{}
|
||||
repoAndTags = append(repoAndTags, repoAndTag{repo: name, tag: tag})
|
||||
}
|
||||
}
|
||||
return repoAndTags, nil
|
||||
}
|
||||
|
||||
func (s *router) getImagesJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
|
|
|
@ -1386,8 +1386,9 @@ Query Parameters:
|
|||
|
||||
- **dockerfile** - Path within the build context to the Dockerfile. This is
|
||||
ignored if `remote` is specified and points to an individual filename.
|
||||
- **t** – A repository name (and optionally a tag) to apply to
|
||||
the resulting image in case of success.
|
||||
- **t** – A name and optional tag to apply to the image in the `name:tag` format.
|
||||
If you omit the `tag` the default `latest` value is assumed.
|
||||
You can provide one or more `t` parameters.
|
||||
- **remote** – A Git repository URI or HTTP/HTTPS URI build source. If the
|
||||
URI specifies a filename, the file's contents are placed into a file
|
||||
called `Dockerfile`.
|
||||
|
|
|
@ -1386,8 +1386,9 @@ Query Parameters:
|
|||
|
||||
- **dockerfile** - Path within the build context to the Dockerfile. This is
|
||||
ignored if `remote` is specified and points to an individual filename.
|
||||
- **t** – A repository name (and optionally a tag) to apply to
|
||||
the resulting image in case of success.
|
||||
- **t** – A name and optional tag to apply to the image in the `name:tag` format.
|
||||
If you omit the `tag` the default `latest` value is assumed.
|
||||
You can provide one or more `t` parameters.
|
||||
- **remote** – A Git repository URI or HTTP/HTTPS URI build source. If the
|
||||
URI specifies a filename, the file's contents are placed into a file
|
||||
called `Dockerfile`.
|
||||
|
|
|
@ -62,6 +62,11 @@ the build succeeds:
|
|||
|
||||
$ docker build -t shykes/myapp .
|
||||
|
||||
To tag the image into multiple repositories after the build,
|
||||
add multiple `-t` parameters when you run the `build` command:
|
||||
|
||||
$ docker build -t shykes/myapp:1.0.2 -t shykes/myapp:latest .
|
||||
|
||||
The Docker daemon runs the instructions in the `Dockerfile` one-by-one,
|
||||
committing the result of each instruction
|
||||
to a new image if necessary, before finally outputting the ID of your
|
||||
|
|
|
@ -31,7 +31,7 @@ parent = "smn_cli"
|
|||
--pull=false Always attempt to pull a newer version of the image
|
||||
-q, --quiet=false Suppress the verbose output generated by the containers
|
||||
--rm=true Remove intermediate containers after a successful build
|
||||
-t, --tag="" Repository name (and optionally a tag) for the image
|
||||
-t, --tag=[] Name and optionally a tag in the 'name:tag' format
|
||||
--ulimit=[] Ulimit options
|
||||
|
||||
Builds Docker images from a Dockerfile and a "context". A build's context is
|
||||
|
@ -227,6 +227,14 @@ uploaded context. The builder reference contains detailed information on
|
|||
This will build like the previous example, but it will then tag the resulting
|
||||
image. The repository name will be `vieux/apache` and the tag will be `2.0`
|
||||
|
||||
You can apply multiple tags to an image. For example, you can apply the `latest`
|
||||
tag to a newly built image and add another tag that references a specific
|
||||
version.
|
||||
For example, to tag an image both as `whenry/fedora-jboss:latest` and
|
||||
`whenry/fedora-jboss:v2.1`, use the following:
|
||||
|
||||
$ docker build -t whenry/fedora-jboss:latest -t whenry/fedora-jboss:v2.1 .
|
||||
|
||||
### Specify Dockerfile (-f)
|
||||
|
||||
$ docker build -f Dockerfile.debug .
|
||||
|
|
|
@ -6258,3 +6258,22 @@ func (s *DockerSuite) TestBuildTagEvent(c *check.C) {
|
|||
c.Fatal("The 'tag' event not heard from the server")
|
||||
}
|
||||
}
|
||||
|
||||
// #15780
|
||||
func (s *DockerSuite) TestBuildMultipleTags(c *check.C) {
|
||||
dockerfile := `
|
||||
FROM busybox
|
||||
MAINTAINER test-15780
|
||||
`
|
||||
cmd := exec.Command(dockerBinary, "build", "-t", "tag1", "-t", "tag2:v2",
|
||||
"-t", "tag1:latest", "-t", "tag1", "--no-cache", "-")
|
||||
cmd.Stdin = strings.NewReader(dockerfile)
|
||||
_, err := runCommand(cmd)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
id1, err := getIDByName("tag1")
|
||||
c.Assert(err, check.IsNil)
|
||||
id2, err := getIDByName("tag2:v2")
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(id1, check.Equals, id2)
|
||||
}
|
||||
|
|
|
@ -1163,7 +1163,6 @@ func buildImageCmd(name, dockerfile string, useCache bool, buildFlags ...string)
|
|||
buildCmd := exec.Command(dockerBinary, args...)
|
||||
buildCmd.Stdin = strings.NewReader(dockerfile)
|
||||
return buildCmd
|
||||
|
||||
}
|
||||
|
||||
func buildImageWithOut(name, dockerfile string, useCache bool, buildFlags ...string) (string, string, error) {
|
||||
|
|
|
@ -16,7 +16,7 @@ docker-build - Build a new image from the source code at PATH
|
|||
[**--pull**[=*false*]]
|
||||
[**-q**|**--quiet**[=*false*]]
|
||||
[**--rm**[=*true*]]
|
||||
[**-t**|**--tag**[=*TAG*]]
|
||||
[**-t**|**--tag**[=*[]*]]
|
||||
[**-m**|**--memory**[=*MEMORY*]]
|
||||
[**--memory-swap**[=*MEMORY-SWAP*]]
|
||||
[**--cpu-period**[=*0*]]
|
||||
|
@ -82,7 +82,7 @@ set as the **URL**, the repository is cloned locally and then sent as the contex
|
|||
Remove intermediate containers after a successful build. The default is *true*.
|
||||
|
||||
**-t**, **--tag**=""
|
||||
Repository name (and optionally a tag) to be applied to the resulting image in case of success
|
||||
Repository names (and optionally with tags) to be applied to the resulting image in case of success.
|
||||
|
||||
**-m**, **--memory**=*MEMORY*
|
||||
Memory limit
|
||||
|
@ -235,6 +235,14 @@ If you do not provide a version tag then Docker will assign `latest`:
|
|||
|
||||
When you list the images, the image above will have the tag `latest`.
|
||||
|
||||
You can apply multiple tags to an image. For example, you can apply the `latest`
|
||||
tag to a newly built image and add another tag that references a specific
|
||||
version.
|
||||
For example, to tag an image both as `whenry/fedora-jboss:latest` and
|
||||
`whenry/fedora-jboss:v2.1`, use the following:
|
||||
|
||||
docker build -t whenry/fedora-jboss:latest -t whenry/fedora-jboss:v2.1 .
|
||||
|
||||
So renaming an image is arbitrary but consideration should be given to
|
||||
a useful convention that makes sense for consumers and should also take
|
||||
into account Docker community conventions.
|
||||
|
|
|
@ -86,7 +86,6 @@ func (opts *ListOpts) Delete(key string) {
|
|||
|
||||
// GetMap returns the content of values in a map in order to avoid
|
||||
// duplicates.
|
||||
// FIXME: can we remove this?
|
||||
func (opts *ListOpts) GetMap() map[string]struct{} {
|
||||
ret := make(map[string]struct{})
|
||||
for _, k := range *opts.values {
|
||||
|
@ -96,7 +95,6 @@ func (opts *ListOpts) GetMap() map[string]struct{} {
|
|||
}
|
||||
|
||||
// GetAll returns the values of slice.
|
||||
// FIXME: Can we remove this?
|
||||
func (opts *ListOpts) GetAll() []string {
|
||||
return (*opts.values)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue