diff --git a/builder/dockerfile/builder.go b/builder/dockerfile/builder.go index 7c8c1624e0..4144876708 100644 --- a/builder/dockerfile/builder.go +++ b/builder/dockerfile/builder.go @@ -222,6 +222,17 @@ func (b *Builder) build(config *types.ImageBuildOptions, context builder.Context } return "", err } + + // Commit the layer when there are only one children in + // the dockerfile, this is only the `FROM` tag, and + // build labels. Otherwise, the new image won't be + // labeled properly. + // Commit here, so the ID of the final image is reported + // properly. + if len(b.dockerfile.Children) == 1 && len(b.options.Labels) > 0 { + b.commit("", b.runConfig.Cmd, "") + } + shortImgID = stringid.TruncateID(b.image) fmt.Fprintf(b.Stdout, " ---> %s\n", shortImgID) if b.options.Remove { diff --git a/builder/dockerfile/internals.go b/builder/dockerfile/internals.go index 62752ae261..6a937707f8 100644 --- a/builder/dockerfile/internals.go +++ b/builder/dockerfile/internals.go @@ -418,7 +418,20 @@ func (b *Builder) processImageFrom(img builder.Image) error { b.image = img.ImageID() if img.RunConfig() != nil { - b.runConfig = img.RunConfig() + imgConfig := *img.RunConfig() + // inherit runConfig labels from the current + // state if they've been set already. + // Ensures that images with only a FROM + // get the labels populated properly. + if b.runConfig.Labels != nil { + if imgConfig.Labels == nil { + imgConfig.Labels = make(map[string]string) + } + for k, v := range b.runConfig.Labels { + imgConfig.Labels[k] = v + } + } + b.runConfig = &imgConfig } } diff --git a/integration-cli/docker_cli_build_test.go b/integration-cli/docker_cli_build_test.go index 6d67861d0b..fa0bf46118 100644 --- a/integration-cli/docker_cli_build_test.go +++ b/integration-cli/docker_cli_build_test.go @@ -6680,11 +6680,9 @@ func (s *DockerSuite) TestBuildLabel(c *check.C) { _, err := buildImage(name, ` FROM `+minimalBaseImage()+` LABEL default foo -`, false, []string{"--label", testLabel}...) +`, false, "--label", testLabel) - if err != nil { - c.Fatal("error building image with labels", err) - } + c.Assert(err, checker.IsNil) res := inspectFieldJSON(c, name, "Config.Labels") @@ -6699,6 +6697,28 @@ func (s *DockerSuite) TestBuildLabel(c *check.C) { } } +func (s *DockerSuite) TestBuildLabelOneNode(c *check.C) { + name := "testbuildlabel" + + _, err := buildImage(name, "FROM busybox", false, "--label", "foo=bar") + + c.Assert(err, checker.IsNil) + + res, err := inspectImage(name, "json .Config.Labels") + c.Assert(err, checker.IsNil) + var labels map[string]string + + if err := json.Unmarshal([]byte(res), &labels); err != nil { + c.Fatal(err) + } + + v, ok := labels["foo"] + if !ok { + c.Fatal("label `foo` not found in image") + } + c.Assert(v, checker.Equals, "bar") +} + func (s *DockerSuite) TestBuildLabelCacheCommit(c *check.C) { name := "testbuildlabelcachecommit" testLabel := "foo" @@ -6713,11 +6733,9 @@ func (s *DockerSuite) TestBuildLabelCacheCommit(c *check.C) { _, err := buildImage(name, ` FROM `+minimalBaseImage()+` LABEL default foo -`, true, []string{"--label", testLabel}...) +`, true, "--label", testLabel) - if err != nil { - c.Fatal("error building image with labels", err) - } + c.Assert(err, checker.IsNil) res := inspectFieldJSON(c, name, "Config.Labels") diff --git a/integration-cli/docker_utils.go b/integration-cli/docker_utils.go index 23313c9d3c..a7a692a43b 100644 --- a/integration-cli/docker_utils.go +++ b/integration-cli/docker_utils.go @@ -890,6 +890,21 @@ func inspectMountPointJSON(j, destination string) (types.MountPoint, error) { return *m, nil } +func inspectImage(name, filter string) (string, error) { + args := []string{"inspect", "--type", "image"} + if filter != "" { + format := fmt.Sprintf("{{%s}}", filter) + args = append(args, "-f", format) + } + args = append(args, name) + inspectCmd := exec.Command(dockerBinary, args...) + out, exitCode, err := runCommandWithOutput(inspectCmd) + if err != nil || exitCode != 0 { + return "", fmt.Errorf("failed to inspect %s: %s", name, out) + } + return strings.TrimSpace(out), nil +} + func getIDByName(name string) (string, error) { return inspectFieldWithError(name, "Id") }