Browse Source

Add 'LABEL' command from '--label' to the last stage

This PR is tring to fix issue #36996.

Currently for multi-stage build, if `--target` specified, the `--label` option
will be ignored. The root cause is the last stage build will remove the `LABEL`
command(s) node created from the `--label` option. In order to address this issue,
we can create `LABEL` command(s) and add it/tem to the last stage.

Signed-off-by: Dennis Chen <dennis.chen@arm.com>
Dennis Chen 7 năm trước cách đây
mục cha
commit
9c238ebd55

+ 19 - 2
builder/dockerfile/builder.go

@@ -6,6 +6,7 @@ import (
 	"fmt"
 	"fmt"
 	"io"
 	"io"
 	"io/ioutil"
 	"io/ioutil"
+	"sort"
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
@@ -208,13 +209,26 @@ func newBuilder(clientCtx context.Context, options builderOptions) *Builder {
 	return b
 	return b
 }
 }
 
 
+// Build 'LABEL' command(s) from '--label' options and add to the last stage
+func buildLabelOptions(labels map[string]string, stages []instructions.Stage) {
+	keys := []string{}
+	for key := range labels {
+		keys = append(keys, key)
+	}
+
+	// Sort the label to have a repeatable order
+	sort.Strings(keys)
+	for _, key := range keys {
+		value := labels[key]
+		stages[len(stages)-1].AddCommand(instructions.NewLabelCommand(key, value, true))
+	}
+}
+
 // Build runs the Dockerfile builder by parsing the Dockerfile and executing
 // Build runs the Dockerfile builder by parsing the Dockerfile and executing
 // the instructions from the file.
 // the instructions from the file.
 func (b *Builder) build(source builder.Source, dockerfile *parser.Result) (*builder.Result, error) {
 func (b *Builder) build(source builder.Source, dockerfile *parser.Result) (*builder.Result, error) {
 	defer b.imageSources.Unmount()
 	defer b.imageSources.Unmount()
 
 
-	addNodesForLabelOption(dockerfile.AST, b.options.Labels)
-
 	stages, metaArgs, err := instructions.Parse(dockerfile.AST)
 	stages, metaArgs, err := instructions.Parse(dockerfile.AST)
 	if err != nil {
 	if err != nil {
 		if instructions.IsUnknownInstruction(err) {
 		if instructions.IsUnknownInstruction(err) {
@@ -231,6 +245,9 @@ func (b *Builder) build(source builder.Source, dockerfile *parser.Result) (*buil
 		stages = stages[:targetIx+1]
 		stages = stages[:targetIx+1]
 	}
 	}
 
 
+	// Add 'LABEL' command specified by '--label' option to the last stage
+	buildLabelOptions(b.options.Labels, stages)
+
 	dockerfile.PrintWarnings(b.Stderr)
 	dockerfile.PrintWarnings(b.Stderr)
 	dispatchState, err := b.dispatchDockerfileWithCancellation(stages, metaArgs, dockerfile.EscapeToken, source)
 	dispatchState, err := b.dispatchDockerfileWithCancellation(stages, metaArgs, dockerfile.EscapeToken, source)
 	if err != nil {
 	if err != nil {

+ 21 - 1
builder/dockerfile/instructions/commands.go

@@ -110,17 +110,37 @@ type MaintainerCommand struct {
 	Maintainer string
 	Maintainer string
 }
 }
 
 
+// NewLabelCommand creates a new 'LABEL' command
+func NewLabelCommand(k string, v string, NoExp bool) *LabelCommand {
+	kvp := KeyValuePair{Key: k, Value: v}
+	c := "LABEL "
+	c += kvp.String()
+	nc := withNameAndCode{code: c, name: "label"}
+	cmd := &LabelCommand{
+		withNameAndCode: nc,
+		Labels: KeyValuePairs{
+			kvp,
+		},
+		noExpand: NoExp,
+	}
+	return cmd
+}
+
 // LabelCommand : LABEL some json data describing the image
 // LabelCommand : LABEL some json data describing the image
 //
 //
 // Sets the Label variable foo to bar,
 // Sets the Label variable foo to bar,
 //
 //
 type LabelCommand struct {
 type LabelCommand struct {
 	withNameAndCode
 	withNameAndCode
-	Labels KeyValuePairs // kvp slice instead of map to preserve ordering
+	Labels   KeyValuePairs // kvp slice instead of map to preserve ordering
+	noExpand bool
 }
 }
 
 
 // Expand variables
 // Expand variables
 func (c *LabelCommand) Expand(expander SingleWordExpander) error {
 func (c *LabelCommand) Expand(expander SingleWordExpander) error {
+	if c.noExpand {
+		return nil
+	}
 	return expandKvpsInPlace(c.Labels, expander)
 	return expandKvpsInPlace(c.Labels, expander)
 }
 }