Browse code

Add named context support

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>

Tonis Tiigi authored on 2017/03/21 02:28:21
Showing 4 changed files
... ...
@@ -189,15 +189,20 @@ func dispatchCopy(b *Builder, args []string, attributes map[string]bool, origina
189 189
 
190 190
 	var contextID *int
191 191
 	if flFrom.IsUsed() {
192
-		var err error
193
-		context, err := strconv.Atoi(flFrom.Value)
194
-		if err != nil {
195
-			return errors.Wrap(err, "from expects an integer value corresponding to the context number")
196
-		}
197
-		if err := b.imageContexts.validate(context); err != nil {
198
-			return err
192
+		flFrom.Value = strings.ToLower(flFrom.Value)
193
+		if context, ok := b.imageContexts.byName[flFrom.Value]; ok {
194
+			contextID = &context
195
+		} else {
196
+			var err error
197
+			context, err := strconv.Atoi(flFrom.Value)
198
+			if err != nil {
199
+				return errors.Wrap(err, "from expects an integer value corresponding to the context number")
200
+			}
201
+			if err := b.imageContexts.validate(context); err != nil {
202
+				return err
203
+			}
204
+			contextID = &context
199 205
 		}
200
-		contextID = &context
201 206
 	}
202 207
 
203 208
 	return b.runContextCommand(args, false, false, "COPY", contextID)
... ...
@@ -208,7 +213,13 @@ func dispatchCopy(b *Builder, args []string, attributes map[string]bool, origina
208 208
 // This sets the image the dockerfile will build on top of.
209 209
 //
210 210
 func from(b *Builder, args []string, attributes map[string]bool, original string) error {
211
-	if len(args) != 1 {
211
+	ctxName := ""
212
+	if len(args) == 3 && strings.EqualFold(args[1], "as") {
213
+		ctxName = strings.ToLower(args[2])
214
+		if ok, _ := regexp.MatchString("^[a-z][a-z0-9-_\\.]*$", ctxName); !ok {
215
+			return errors.Errorf("invalid name for build stage: %q, name can't start with a number or contain symbols", ctxName)
216
+		}
217
+	} else if len(args) != 1 {
212 218
 		return errExactlyOneArgument("FROM")
213 219
 	}
214 220
 
... ...
@@ -221,7 +232,9 @@ func from(b *Builder, args []string, attributes map[string]bool, original string
221 221
 	var image builder.Image
222 222
 
223 223
 	b.resetImageCache()
224
-	b.imageContexts.new()
224
+	if err := b.imageContexts.new(ctxName); err != nil {
225
+		return err
226
+	}
225 227
 
226 228
 	// Windows cannot support a container with no base image.
227 229
 	if name == api.NoBaseImageSpecifier {
... ...
@@ -12,9 +12,10 @@ import (
12 12
 // imageContexts is a helper for stacking up built image rootfs and reusing
13 13
 // them as contexts
14 14
 type imageContexts struct {
15
-	b     *Builder
16
-	list  []*imageMount
17
-	cache *pathCache
15
+	b      *Builder
16
+	list   []*imageMount
17
+	byName map[string]int
18
+	cache  *pathCache
18 19
 }
19 20
 
20 21
 type imageMount struct {
... ...
@@ -23,8 +24,18 @@ type imageMount struct {
23 23
 	release func() error
24 24
 }
25 25
 
26
-func (ic *imageContexts) new() {
26
+func (ic *imageContexts) new(name string) error {
27
+	if len(name) > 0 {
28
+		if ic.byName == nil {
29
+			ic.byName = make(map[string]int)
30
+		}
31
+		if _, ok := ic.byName[name]; ok {
32
+			return errors.Errorf("duplicate name %s", name)
33
+		}
34
+		ic.byName[name] = len(ic.list)
35
+	}
27 36
 	ic.list = append(ic.list, &imageMount{})
37
+	return nil
28 38
 }
29 39
 
30 40
 func (ic *imageContexts) update(imageID string) {
... ...
@@ -80,7 +80,7 @@ func init() {
80 80
 		command.Entrypoint:  parseMaybeJSON,
81 81
 		command.Env:         parseEnv,
82 82
 		command.Expose:      parseStringsWhitespaceDelimited,
83
-		command.From:        parseString,
83
+		command.From:        parseStringsWhitespaceDelimited,
84 84
 		command.Healthcheck: parseHealthConfig,
85 85
 		command.Label:       parseLabel,
86 86
 		command.Maintainer:  parseString,
... ...
@@ -5752,7 +5752,7 @@ func (s *DockerSuite) TestBuildContChar(c *check.C) {
5752 5752
 
5753 5753
 func (s *DockerSuite) TestBuildCopyFromPreviousRootFS(c *check.C) {
5754 5754
 	dockerfile := `
5755
-		FROM busybox
5755
+		FROM busybox AS first
5756 5756
 		COPY foo bar
5757 5757
 		FROM busybox
5758 5758
     %s
... ...
@@ -5762,7 +5762,8 @@ func (s *DockerSuite) TestBuildCopyFromPreviousRootFS(c *check.C) {
5762 5762
     COPY bar /
5763 5763
     COPY --from=1 baz sub/
5764 5764
     COPY --from=0 bar baz
5765
-    COPY --from=0 bar bay`
5765
+    COPY --from=first bar bay`
5766
+
5766 5767
 	ctx := fakeContext(c, fmt.Sprintf(dockerfile, ""), map[string]string{
5767 5768
 		"Dockerfile": dockerfile,
5768 5769
 		"foo":        "abc",
... ...
@@ -5847,6 +5848,36 @@ func (s *DockerSuite) TestBuildCopyFromPreviousRootFSErrors(c *check.C) {
5847 5847
 		ExitCode: 1,
5848 5848
 		Err:      "invalid from flag value 0 refers current build block",
5849 5849
 	})
5850
+
5851
+	dockerfile = `
5852
+		FROM busybox AS foo
5853
+		COPY --from=bar foo bar`
5854
+
5855
+	ctx = fakeContext(c, dockerfile, map[string]string{
5856
+		"Dockerfile": dockerfile,
5857
+		"foo":        "abc",
5858
+	})
5859
+	defer ctx.Close()
5860
+
5861
+	buildImage("build1", withExternalBuildContext(ctx)).Assert(c, icmd.Expected{
5862
+		ExitCode: 1,
5863
+		Err:      "invalid context value bar",
5864
+	})
5865
+
5866
+	dockerfile = `
5867
+		FROM busybox AS 1
5868
+		COPY --from=1 foo bar`
5869
+
5870
+	ctx = fakeContext(c, dockerfile, map[string]string{
5871
+		"Dockerfile": dockerfile,
5872
+		"foo":        "abc",
5873
+	})
5874
+	defer ctx.Close()
5875
+
5876
+	buildImage("build1", withExternalBuildContext(ctx)).Assert(c, icmd.Expected{
5877
+		ExitCode: 1,
5878
+		Err:      "invalid name for build stage",
5879
+	})
5850 5880
 }
5851 5881
 
5852 5882
 func (s *DockerSuite) TestBuildCopyFromPreviousFrom(c *check.C) {
... ...
@@ -5863,9 +5894,9 @@ func (s *DockerSuite) TestBuildCopyFromPreviousFrom(c *check.C) {
5863 5863
 	result.Assert(c, icmd.Success)
5864 5864
 
5865 5865
 	dockerfile = `
5866
-		FROM build1:latest
5866
+		FROM build1:latest AS foo
5867 5867
     FROM busybox
5868
-		COPY --from=0 bar /
5868
+		COPY --from=foo bar /
5869 5869
 		COPY foo /`
5870 5870
 	ctx = fakeContext(c, dockerfile, map[string]string{
5871 5871
 		"Dockerfile": dockerfile,