Docker-DCO-1.1-Signed-off-by: Erik Hollensbe <github@hollensbe.org> (github: erikh)
| ... | ... |
@@ -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" ] |