diff --git a/builder/dockerfile/builder.go b/builder/dockerfile/builder.go index da43513fff..8c4fbc9ff1 100644 --- a/builder/dockerfile/builder.go +++ b/builder/dockerfile/builder.go @@ -7,6 +7,7 @@ import ( "io" "io/ioutil" "os" + "sort" "strings" "github.com/Sirupsen/logrus" @@ -203,6 +204,28 @@ func sanitizeRepoAndTags(names []string) ([]reference.Named, error) { return repoAndTags, nil } +func (b *Builder) processLabels() error { + if len(b.options.Labels) == 0 { + return nil + } + + var labels []string + for k, v := range b.options.Labels { + labels = append(labels, fmt.Sprintf("%q='%s'", k, v)) + } + // Sort the label to have a repeatable order + sort.Strings(labels) + + line := "LABEL " + strings.Join(labels, " ") + _, node, err := parser.ParseLine(line, &b.directive, false) + if err != nil { + return err + } + b.dockerfile.Children = append(b.dockerfile.Children, node) + + return nil +} + // build runs the Dockerfile builder from a context and a docker object that allows to make calls // to Docker. // @@ -233,16 +256,8 @@ func (b *Builder) build(stdout io.Writer, stderr io.Writer, out io.Writer) (stri return "", err } - if len(b.options.Labels) > 0 { - line := "LABEL " - for k, v := range b.options.Labels { - line += fmt.Sprintf("%q='%s' ", k, v) - } - _, node, err := parser.ParseLine(line, &b.directive, false) - if err != nil { - return "", err - } - b.dockerfile.Children = append(b.dockerfile.Children, node) + if err := b.processLabels(); err != nil { + return "", err } var shortImgID string diff --git a/builder/dockerfile/builder_test.go b/builder/dockerfile/builder_test.go new file mode 100644 index 0000000000..8f6b9e9883 --- /dev/null +++ b/builder/dockerfile/builder_test.go @@ -0,0 +1,54 @@ +package dockerfile + +import ( + "strings" + + "testing" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/builder/dockerfile/parser" +) + +func TestBuildProcessLabels(t *testing.T) { + dockerfile := "FROM scratch" + d := parser.Directive{} + parser.SetEscapeToken(parser.DefaultEscapeToken, &d) + n, err := parser.Parse(strings.NewReader(dockerfile), &d) + if err != nil { + t.Fatalf("Error when parsing Dockerfile: %s", err) + } + + options := &types.ImageBuildOptions{ + Labels: map[string]string{ + "org.e": "cli-e", + "org.d": "cli-d", + "org.c": "cli-c", + "org.b": "cli-b", + "org.a": "cli-a", + }, + } + b := &Builder{ + runConfig: &container.Config{}, + options: options, + directive: d, + dockerfile: n, + } + err = b.processLabels() + if err != nil { + t.Fatalf("Error when processing labels: %s", err) + } + + expected := []string{ + "FROM scratch", + `LABEL "org.a"='cli-a' "org.b"='cli-b' "org.c"='cli-c' "org.d"='cli-d' "org.e"='cli-e'`, + } + if len(b.dockerfile.Children) != 2 { + t.Fatalf("Expect 2, got %d", len(b.dockerfile.Children)) + } + for i, v := range b.dockerfile.Children { + if v.Original != expected[i] { + t.Fatalf("Expect '%s' for %dth children, got, '%s'", expected[i], i, v.Original) + } + } +}