Browse code

builder: whitelist verbs useful for environment replacement.

Docker-DCO-1.1-Signed-off-by: Erik Hollensbe <github@hollensbe.org> (github: erikh)

Erik Hollensbe authored on 2014/10/26 03:29:18
Showing 2 changed files
... ...
@@ -41,6 +41,17 @@ var (
41 41
 	ErrDockerfileEmpty = errors.New("Dockerfile cannot be empty")
42 42
 )
43 43
 
44
+// Environment variable interpolation will happen on these statements only.
45
+var replaceEnvAllowed = map[string]struct{}{
46
+	"env":     {},
47
+	"add":     {},
48
+	"copy":    {},
49
+	"workdir": {},
50
+	"expose":  {},
51
+	"volume":  {},
52
+	"user":    {},
53
+}
54
+
44 55
 var evaluateTable map[string]func(*Builder, []string, map[string]bool, string) error
45 56
 
46 57
 func init() {
... ...
@@ -196,13 +207,18 @@ func (b *Builder) dispatch(stepN int, ast *parser.Node) error {
196 196
 
197 197
 	if cmd == "onbuild" {
198 198
 		ast = ast.Next.Children[0]
199
-		strs = append(strs, b.replaceEnv(ast.Value))
199
+		strs = append(strs, ast.Value)
200 200
 		msg += " " + ast.Value
201 201
 	}
202 202
 
203 203
 	for ast.Next != nil {
204 204
 		ast = ast.Next
205
-		strs = append(strs, b.replaceEnv(ast.Value))
205
+		var str string
206
+		str = ast.Value
207
+		if _, ok := replaceEnvAllowed[cmd]; ok {
208
+			str = b.replaceEnv(ast.Value)
209
+		}
210
+		strs = append(strs, str)
206 211
 		msg += " " + ast.Value
207 212
 	}
208 213
 
... ...
@@ -2,6 +2,7 @@ package main
2 2
 
3 3
 import (
4 4
 	"archive/tar"
5
+	"encoding/json"
5 6
 	"fmt"
6 7
 	"io/ioutil"
7 8
 	"os"
... ...
@@ -15,6 +16,186 @@ import (
15 15
 	"github.com/docker/docker/pkg/archive"
16 16
 )
17 17
 
18
+func TestBuildEnvironmentReplacementUser(t *testing.T) {
19
+	name := "testbuildenvironmentreplacement"
20
+	defer deleteImages(name)
21
+
22
+	_, err := buildImage(name, `
23
+  FROM scratch
24
+  ENV user foo
25
+  USER ${user}
26
+  `, true)
27
+	if err != nil {
28
+		t.Fatal(err)
29
+	}
30
+
31
+	res, err := inspectFieldJSON(name, "Config.User")
32
+	if err != nil {
33
+		t.Fatal(err)
34
+	}
35
+
36
+	if res != `"foo"` {
37
+		t.Fatal("User foo from environment not in Config.User on image")
38
+	}
39
+
40
+	logDone("build - user environment replacement")
41
+}
42
+
43
+func TestBuildEnvironmentReplacementVolume(t *testing.T) {
44
+	name := "testbuildenvironmentreplacement"
45
+	defer deleteImages(name)
46
+
47
+	_, err := buildImage(name, `
48
+  FROM scratch
49
+  ENV volume /quux
50
+  VOLUME ${volume}
51
+  `, true)
52
+	if err != nil {
53
+		t.Fatal(err)
54
+	}
55
+
56
+	res, err := inspectFieldJSON(name, "Config.Volumes")
57
+	if err != nil {
58
+		t.Fatal(err)
59
+	}
60
+
61
+	var volumes map[string]interface{}
62
+
63
+	if err := json.Unmarshal([]byte(res), &volumes); err != nil {
64
+		t.Fatal(err)
65
+	}
66
+
67
+	if _, ok := volumes["/quux"]; !ok {
68
+		t.Fatal("Volume /quux from environment not in Config.Volumes on image")
69
+	}
70
+
71
+	logDone("build - volume environment replacement")
72
+}
73
+
74
+func TestBuildEnvironmentReplacementExpose(t *testing.T) {
75
+	name := "testbuildenvironmentreplacement"
76
+	defer deleteImages(name)
77
+
78
+	_, err := buildImage(name, `
79
+  FROM scratch
80
+  ENV port 80
81
+  EXPOSE ${port}
82
+  `, true)
83
+	if err != nil {
84
+		t.Fatal(err)
85
+	}
86
+
87
+	res, err := inspectFieldJSON(name, "Config.ExposedPorts")
88
+	if err != nil {
89
+		t.Fatal(err)
90
+	}
91
+
92
+	var exposedPorts map[string]interface{}
93
+
94
+	if err := json.Unmarshal([]byte(res), &exposedPorts); err != nil {
95
+		t.Fatal(err)
96
+	}
97
+
98
+	if _, ok := exposedPorts["80/tcp"]; !ok {
99
+		t.Fatal("Exposed port 80 from environment not in Config.ExposedPorts on image")
100
+	}
101
+
102
+	logDone("build - expose environment replacement")
103
+}
104
+
105
+func TestBuildEnvironmentReplacementWorkdir(t *testing.T) {
106
+	name := "testbuildenvironmentreplacement"
107
+	defer deleteImages(name)
108
+
109
+	_, err := buildImage(name, `
110
+  FROM busybox
111
+  ENV MYWORKDIR /work
112
+  RUN mkdir ${MYWORKDIR}
113
+  WORKDIR ${MYWORKDIR}
114
+  `, true)
115
+
116
+	if err != nil {
117
+		t.Fatal(err)
118
+	}
119
+
120
+	logDone("build - workdir environment replacement")
121
+}
122
+
123
+func TestBuildEnvironmentReplacementAddCopy(t *testing.T) {
124
+	name := "testbuildenvironmentreplacement"
125
+	defer deleteImages(name)
126
+
127
+	ctx, err := fakeContext(`
128
+  FROM scratch
129
+  ENV baz foo
130
+  ENV quux bar
131
+  ENV dot .
132
+
133
+  ADD ${baz} ${dot}
134
+  COPY ${quux} ${dot}
135
+  `,
136
+		map[string]string{
137
+			"foo": "test1",
138
+			"bar": "test2",
139
+		})
140
+
141
+	if err != nil {
142
+		t.Fatal(err)
143
+	}
144
+
145
+	if _, err := buildImageFromContext(name, ctx, true); err != nil {
146
+		t.Fatal(err)
147
+	}
148
+
149
+	logDone("build - add/copy environment replacement")
150
+}
151
+
152
+func TestBuildEnvironmentReplacementEnv(t *testing.T) {
153
+	name := "testbuildenvironmentreplacement"
154
+
155
+	defer deleteImages(name)
156
+
157
+	_, err := buildImage(name,
158
+		`
159
+  FROM scratch
160
+  ENV foo foo
161
+  ENV bar ${foo}
162
+  `, true)
163
+
164
+	if err != nil {
165
+		t.Fatal(err)
166
+	}
167
+
168
+	res, err := inspectFieldJSON(name, "Config.Env")
169
+	if err != nil {
170
+		t.Fatal(err)
171
+	}
172
+
173
+	envResult := []string{}
174
+
175
+	if err = unmarshalJSON([]byte(res), &envResult); err != nil {
176
+		t.Fatal(err)
177
+	}
178
+
179
+	found := false
180
+
181
+	for _, env := range envResult {
182
+		parts := strings.SplitN(env, "=", 2)
183
+		if parts[0] == "bar" {
184
+			found = true
185
+			if parts[1] != "foo" {
186
+				t.Fatal("Could not find replaced var for env `bar`: got %q instead of `foo`", parts[1])
187
+			}
188
+		}
189
+	}
190
+
191
+	if !found {
192
+		t.Fatal("Never found the `bar` env variable")
193
+	}
194
+
195
+	logDone("build - env environment replacement")
196
+}
197
+
18 198
 func TestBuildHandleEscapes(t *testing.T) {
19 199
 	name := "testbuildhandleescapes"
20 200
 
... ...
@@ -170,7 +351,7 @@ func TestBuildEnvOverwrite(t *testing.T) {
170 170
 		`
171 171
     FROM busybox
172 172
     ENV TEST foo
173
-    CMD echo \${TEST}
173
+    CMD echo ${TEST}
174 174
     `,
175 175
 		true)
176 176
 
... ...
@@ -2618,6 +2799,7 @@ func TestBuildEnvUsage(t *testing.T) {
2618 2618
 	name := "testbuildenvusage"
2619 2619
 	defer deleteImages(name)
2620 2620
 	dockerfile := `FROM busybox
2621
+ENV    HOME /root
2621 2622
 ENV    PATH $HOME/bin:$PATH
2622 2623
 ENV    PATH /tmp:$PATH
2623 2624
 RUN    [ "$PATH" = "/tmp:$HOME/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" ]