Browse code

Fix arg in from when arg is not defined

Add mock builder backend
Add tests for ARG in FROM

Signed-off-by: Daniel Nephin <dnephin@docker.com>

Daniel Nephin authored on 2017/04/05 02:40:37
Showing 6 changed files
... ...
@@ -22,7 +22,6 @@ import (
22 22
 	"github.com/docker/docker/api/types/container"
23 23
 	"github.com/docker/docker/api/types/strslice"
24 24
 	"github.com/docker/docker/builder"
25
-	"github.com/docker/docker/pkg/shellvar"
26 25
 	"github.com/docker/docker/pkg/signal"
27 26
 	"github.com/docker/go-connections/nat"
28 27
 	"github.com/pkg/errors"
... ...
@@ -219,14 +218,8 @@ func from(b *Builder, args []string, attributes map[string]bool, original string
219 219
 		return err
220 220
 	}
221 221
 
222
-	getBuildArg := func(key string) (string, bool) {
223
-		value, ok := b.options.BuildArgs[key]
224
-		if value != nil {
225
-			return *value, ok
226
-		}
227
-		return "", ok
228
-	}
229
-	name, err := shellvar.Substitute(args[0], getBuildArg)
222
+	substituionArgs := b.buildArgsWithoutConfigEnv()
223
+	name, err := ProcessWord(args[0], substituionArgs, b.directive.EscapeToken)
230 224
 	if err != nil {
231 225
 		return err
232 226
 	}
... ...
@@ -9,6 +9,8 @@ import (
9 9
 	"github.com/docker/docker/api/types"
10 10
 	"github.com/docker/docker/api/types/container"
11 11
 	"github.com/docker/docker/api/types/strslice"
12
+	"github.com/docker/docker/builder"
13
+	"github.com/docker/docker/pkg/testutil/assert"
12 14
 	"github.com/docker/go-connections/nat"
13 15
 )
14 16
 
... ...
@@ -189,35 +191,68 @@ func TestLabel(t *testing.T) {
189 189
 	}
190 190
 }
191 191
 
192
-func TestFrom(t *testing.T) {
193
-	b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true}
192
+func newBuilderWithMockBackend() *Builder {
193
+	b := &Builder{
194
+		flags:            &BFlags{},
195
+		runConfig:        &container.Config{},
196
+		options:          &types.ImageBuildOptions{},
197
+		docker:           &MockBackend{},
198
+		allowedBuildArgs: make(map[string]*string),
199
+		allBuildArgs:     make(map[string]struct{}),
200
+	}
194 201
 	b.imageContexts = &imageContexts{b: b}
202
+	return b
203
+}
204
+
205
+func TestFromScratch(t *testing.T) {
206
+	b := newBuilderWithMockBackend()
195 207
 
196 208
 	err := from(b, []string{"scratch"}, nil, "")
197 209
 
198 210
 	if runtime.GOOS == "windows" {
199
-		if err == nil {
200
-			t.Fatal("Error not set on Windows")
201
-		}
211
+		assert.Error(t, err, "Windows does not support FROM scratch")
212
+		return
213
+	}
202 214
 
203
-		expectedError := "Windows does not support FROM scratch"
215
+	assert.NilError(t, err)
216
+	assert.Equal(t, b.image, "")
217
+	assert.Equal(t, b.noBaseImage, true)
218
+}
204 219
 
205
-		if !strings.Contains(err.Error(), expectedError) {
206
-			t.Fatalf("Error message not correct on Windows. Should be: %s, got: %s", expectedError, err.Error())
207
-		}
208
-	} else {
209
-		if err != nil {
210
-			t.Fatalf("Error when executing from: %s", err.Error())
211
-		}
220
+func TestFromWithArg(t *testing.T) {
221
+	tag, expected := ":sometag", "expectedthisid"
212 222
 
213
-		if b.image != "" {
214
-			t.Fatalf("Image should be empty, got: %s", b.image)
215
-		}
223
+	getImage := func(name string) (builder.Image, error) {
224
+		assert.Equal(t, name, "alpine"+tag)
225
+		return &mockImage{id: "expectedthisid"}, nil
226
+	}
227
+	b := newBuilderWithMockBackend()
228
+	b.docker.(*MockBackend).getImageOnBuildFunc = getImage
216 229
 
217
-		if b.noBaseImage != true {
218
-			t.Fatalf("Image should not have any base image, got: %v", b.noBaseImage)
219
-		}
230
+	assert.NilError(t, arg(b, []string{"THETAG=" + tag}, nil, ""))
231
+	err := from(b, []string{"alpine${THETAG}"}, nil, "")
232
+
233
+	assert.NilError(t, err)
234
+	assert.Equal(t, b.image, expected)
235
+	assert.Equal(t, b.from.ImageID(), expected)
236
+	assert.NotNil(t, b.allowedBuildArgs)
237
+	assert.Equal(t, len(b.allowedBuildArgs), 0)
238
+}
239
+
240
+func TestFromWithUndefinedArg(t *testing.T) {
241
+	tag, expected := "sometag", "expectedthisid"
242
+
243
+	getImage := func(name string) (builder.Image, error) {
244
+		assert.Equal(t, name, "alpine")
245
+		return &mockImage{id: "expectedthisid"}, nil
220 246
 	}
247
+	b := newBuilderWithMockBackend()
248
+	b.docker.(*MockBackend).getImageOnBuildFunc = getImage
249
+	b.options.BuildArgs = map[string]*string{"THETAG": &tag}
250
+
251
+	err := from(b, []string{"alpine${THETAG}"}, nil, "")
252
+	assert.NilError(t, err)
253
+	assert.Equal(t, b.image, expected)
221 254
 }
222 255
 
223 256
 func TestOnbuildIllegalTriggers(t *testing.T) {
... ...
@@ -175,7 +175,10 @@ func (b *Builder) evaluateEnv(cmd string, str string, envs []string) ([]string,
175 175
 	if allowWordExpansion[cmd] {
176 176
 		processFunc = ProcessWords
177 177
 	} else {
178
-		processFunc = ProcessWord
178
+		processFunc = func(word string, envs []string, escape rune) ([]string, error) {
179
+			word, err := ProcessWord(word, envs, escape)
180
+			return []string{word}, err
181
+		}
179 182
 	}
180 183
 	return processFunc(str, envs, b.directive.EscapeToken)
181 184
 }
182 185
new file mode 100644
... ...
@@ -0,0 +1,99 @@
0
+package dockerfile
1
+
2
+import (
3
+	"io"
4
+	"time"
5
+
6
+	"github.com/docker/distribution/reference"
7
+	"github.com/docker/docker/api/types"
8
+	"github.com/docker/docker/api/types/backend"
9
+	"github.com/docker/docker/api/types/container"
10
+	"github.com/docker/docker/builder"
11
+	"github.com/docker/docker/image"
12
+	"golang.org/x/net/context"
13
+)
14
+
15
+// MockBackend implements the builder.Backend interface for unit testing
16
+type MockBackend struct {
17
+	getImageOnBuildFunc func(string) (builder.Image, error)
18
+}
19
+
20
+func (m *MockBackend) GetImageOnBuild(name string) (builder.Image, error) {
21
+	if m.getImageOnBuildFunc != nil {
22
+		return m.getImageOnBuildFunc(name)
23
+	}
24
+	return &mockImage{id: "theid"}, nil
25
+}
26
+
27
+func (m *MockBackend) TagImageWithReference(image.ID, reference.Named) error {
28
+	return nil
29
+}
30
+
31
+func (m *MockBackend) PullOnBuild(ctx context.Context, name string, authConfigs map[string]types.AuthConfig, output io.Writer) (builder.Image, error) {
32
+	return nil, nil
33
+}
34
+
35
+func (m *MockBackend) ContainerAttachRaw(cID string, stdin io.ReadCloser, stdout, stderr io.Writer, stream bool) error {
36
+	return nil
37
+}
38
+
39
+func (m *MockBackend) ContainerCreate(config types.ContainerCreateConfig) (container.ContainerCreateCreatedBody, error) {
40
+	return container.ContainerCreateCreatedBody{}, nil
41
+}
42
+
43
+func (m *MockBackend) ContainerRm(name string, config *types.ContainerRmConfig) error {
44
+	return nil
45
+}
46
+
47
+func (m *MockBackend) Commit(string, *backend.ContainerCommitConfig) (string, error) {
48
+	return "", nil
49
+}
50
+
51
+func (m *MockBackend) ContainerKill(containerID string, sig uint64) error {
52
+	return nil
53
+}
54
+
55
+func (m *MockBackend) ContainerStart(containerID string, hostConfig *container.HostConfig, checkpoint string, checkpointDir string) error {
56
+	return nil
57
+}
58
+
59
+func (m *MockBackend) ContainerWait(containerID string, timeout time.Duration) (int, error) {
60
+	return 0, nil
61
+}
62
+
63
+func (m *MockBackend) ContainerUpdateCmdOnBuild(containerID string, cmd []string) error {
64
+	return nil
65
+}
66
+
67
+func (m *MockBackend) ContainerCreateWorkdir(containerID string) error {
68
+	return nil
69
+}
70
+
71
+func (m *MockBackend) CopyOnBuild(containerID string, destPath string, src builder.FileInfo, decompress bool) error {
72
+	return nil
73
+}
74
+
75
+func (m *MockBackend) HasExperimental() bool {
76
+	return false
77
+}
78
+
79
+func (m *MockBackend) SquashImage(from string, to string) (string, error) {
80
+	return "", nil
81
+}
82
+
83
+func (m *MockBackend) MountImage(name string) (string, func() error, error) {
84
+	return "", func() error { return nil }, nil
85
+}
86
+
87
+type mockImage struct {
88
+	id     string
89
+	config *container.Config
90
+}
91
+
92
+func (i *mockImage) ImageID() string {
93
+	return i.id
94
+}
95
+
96
+func (i *mockImage) RunConfig() *container.Config {
97
+	return i.config
98
+}
... ...
@@ -24,9 +24,9 @@ type shellWord struct {
24 24
 
25 25
 // ProcessWord will use the 'env' list of environment variables,
26 26
 // and replace any env var references in 'word'.
27
-func ProcessWord(word string, env []string, escapeToken rune) ([]string, error) {
27
+func ProcessWord(word string, env []string, escapeToken rune) (string, error) {
28 28
 	word, _, err := process(word, env, escapeToken)
29
-	return []string{word}, err
29
+	return word, err
30 30
 }
31 31
 
32 32
 // ProcessWords will use the 'env' list of environment variables,
... ...
@@ -54,7 +54,7 @@ func TestShellParser4EnvVars(t *testing.T) {
54 54
 				assert.Error(t, err, "")
55 55
 			} else {
56 56
 				assert.NilError(t, err)
57
-				assert.DeepEqual(t, newWord, []string{expected})
57
+				assert.Equal(t, newWord, expected)
58 58
 			}
59 59
 		}
60 60
 	}