Signed-off-by: John Howard <jhoward@microsoft.com>
John Howard authored on 2016/11/23 04:26:02... | ... |
@@ -71,14 +71,20 @@ func env(b *Builder, args []string, attributes map[string]bool, original string) |
71 | 71 |
if len(args[j]) == 0 { |
72 | 72 |
return errBlankCommandNames("ENV") |
73 | 73 |
} |
74 |
- |
|
75 | 74 |
newVar := args[j] + "=" + args[j+1] + "" |
76 | 75 |
commitStr += " " + newVar |
77 | 76 |
|
78 | 77 |
gotOne := false |
79 | 78 |
for i, envVar := range b.runConfig.Env { |
80 | 79 |
envParts := strings.SplitN(envVar, "=", 2) |
81 |
- if envParts[0] == args[j] { |
|
80 |
+ compareFrom := envParts[0] |
|
81 |
+ compareTo := args[j] |
|
82 |
+ if runtime.GOOS == "windows" { |
|
83 |
+ // Case insensitive environment variables on Windows |
|
84 |
+ compareFrom = strings.ToUpper(compareFrom) |
|
85 |
+ compareTo = strings.ToUpper(compareTo) |
|
86 |
+ } |
|
87 |
+ if compareFrom == compareTo { |
|
82 | 88 |
b.runConfig.Env[i] = newVar |
83 | 89 |
gotOne = true |
84 | 90 |
break |
... | ... |
@@ -1,112 +1,116 @@ |
1 |
-hello | hello |
|
2 |
-he'll'o | hello |
|
3 |
-he'llo | hello |
|
4 |
-he\'llo | he'llo |
|
5 |
-he\\'llo | he\llo |
|
6 |
-abc\tdef | abctdef |
|
7 |
-"abc\tdef" | abc\tdef |
|
8 |
-'abc\tdef' | abc\tdef |
|
9 |
-hello\ | hello |
|
10 |
-hello\\ | hello\ |
|
11 |
-"hello | hello |
|
12 |
-"hello\" | hello" |
|
13 |
-"hel'lo" | hel'lo |
|
14 |
-'hello | hello |
|
15 |
-'hello\' | hello\ |
|
16 |
-"''" | '' |
|
17 |
-$. | $. |
|
18 |
-$1 | |
|
19 |
-he$1x | hex |
|
20 |
-he$.x | he$.x |
|
21 |
-he$pwd. | he. |
|
22 |
-he$PWD | he/home |
|
23 |
-he\$PWD | he$PWD |
|
24 |
-he\\$PWD | he\/home |
|
25 |
-he\${} | he${} |
|
26 |
-he\${}xx | he${}xx |
|
27 |
-he${} | he |
|
28 |
-he${}xx | hexx |
|
29 |
-he${hi} | he |
|
30 |
-he${hi}xx | hexx |
|
31 |
-he${PWD} | he/home |
|
32 |
-he${.} | error |
|
33 |
-he${XXX:-000}xx | he000xx |
|
34 |
-he${PWD:-000}xx | he/homexx |
|
35 |
-he${XXX:-$PWD}xx | he/homexx |
|
36 |
-he${XXX:-${PWD:-yyy}}xx | he/homexx |
|
37 |
-he${XXX:-${YYY:-yyy}}xx | heyyyxx |
|
38 |
-he${XXX:YYY} | error |
|
39 |
-he${XXX:+${PWD}}xx | hexx |
|
40 |
-he${PWD:+${XXX}}xx | hexx |
|
41 |
-he${PWD:+${SHELL}}xx | hebashxx |
|
42 |
-he${XXX:+000}xx | hexx |
|
43 |
-he${PWD:+000}xx | he000xx |
|
44 |
-'he${XX}' | he${XX} |
|
45 |
-"he${PWD}" | he/home |
|
46 |
-"he'$PWD'" | he'/home' |
|
47 |
-"$PWD" | /home |
|
48 |
-'$PWD' | $PWD |
|
49 |
-'\$PWD' | \$PWD |
|
50 |
-'"hello"' | "hello" |
|
51 |
-he\$PWD | he$PWD |
|
52 |
-"he\$PWD" | he$PWD |
|
53 |
-'he\$PWD' | he\$PWD |
|
54 |
-he${PWD | error |
|
55 |
-he${PWD:=000}xx | error |
|
56 |
-he${PWD:+${PWD}:}xx | he/home:xx |
|
57 |
-he${XXX:-\$PWD:}xx | he$PWD:xx |
|
58 |
-he${XXX:-\${PWD}z}xx | he${PWDz}xx |
|
59 |
-안녕하세요 | 안녕하세요 |
|
60 |
-안'녕'하세요 | 안녕하세요 |
|
61 |
-안'녕하세요 | 안녕하세요 |
|
62 |
-안녕\'하세요 | 안녕'하세요 |
|
63 |
-안\\'녕하세요 | 안\녕하세요 |
|
64 |
-안녕\t하세요 | 안녕t하세요 |
|
65 |
-"안녕\t하세요" | 안녕\t하세요 |
|
66 |
-'안녕\t하세요 | 안녕\t하세요 |
|
67 |
-안녕하세요\ | 안녕하세요 |
|
68 |
-안녕하세요\\ | 안녕하세요\ |
|
69 |
-"안녕하세요 | 안녕하세요 |
|
70 |
-"안녕하세요\" | 안녕하세요" |
|
71 |
-"안녕'하세요" | 안녕'하세요 |
|
72 |
-'안녕하세요 | 안녕하세요 |
|
73 |
-'안녕하세요\' | 안녕하세요\ |
|
74 |
-안녕$1x | 안녕x |
|
75 |
-안녕$.x | 안녕$.x |
|
76 |
-안녕$pwd. | 안녕. |
|
77 |
-안녕$PWD | 안녕/home |
|
78 |
-안녕\$PWD | 안녕$PWD |
|
79 |
-안녕\\$PWD | 안녕\/home |
|
80 |
-안녕\${} | 안녕${} |
|
81 |
-안녕\${}xx | 안녕${}xx |
|
82 |
-안녕${} | 안녕 |
|
83 |
-안녕${}xx | 안녕xx |
|
84 |
-안녕${hi} | 안녕 |
|
85 |
-안녕${hi}xx | 안녕xx |
|
86 |
-안녕${PWD} | 안녕/home |
|
87 |
-안녕${.} | error |
|
88 |
-안녕${XXX:-000}xx | 안녕000xx |
|
89 |
-안녕${PWD:-000}xx | 안녕/homexx |
|
90 |
-안녕${XXX:-$PWD}xx | 안녕/homexx |
|
91 |
-안녕${XXX:-${PWD:-yyy}}xx | 안녕/homexx |
|
92 |
-안녕${XXX:-${YYY:-yyy}}xx | 안녕yyyxx |
|
93 |
-안녕${XXX:YYY} | error |
|
94 |
-안녕${XXX:+${PWD}}xx | 안녕xx |
|
95 |
-안녕${PWD:+${XXX}}xx | 안녕xx |
|
96 |
-안녕${PWD:+${SHELL}}xx | 안녕bashxx |
|
97 |
-안녕${XXX:+000}xx | 안녕xx |
|
98 |
-안녕${PWD:+000}xx | 안녕000xx |
|
99 |
-'안녕${XX}' | 안녕${XX} |
|
100 |
-"안녕${PWD}" | 안녕/home |
|
101 |
-"안녕'$PWD'" | 안녕'/home' |
|
102 |
-'"안녕"' | "안녕" |
|
103 |
-안녕\$PWD | 안녕$PWD |
|
104 |
-"안녕\$PWD" | 안녕$PWD |
|
105 |
-'안녕\$PWD' | 안녕\$PWD |
|
106 |
-안녕${PWD | error |
|
107 |
-안녕${PWD:=000}xx | error |
|
108 |
-안녕${PWD:+${PWD}:}xx | 안녕/home:xx |
|
109 |
-안녕${XXX:-\$PWD:}xx | 안녕$PWD:xx |
|
110 |
-안녕${XXX:-\${PWD}z}xx | 안녕${PWDz}xx |
|
111 |
-$KOREAN | 한국어 |
|
112 |
-안녕$KOREAN | 안녕한국어 |
|
1 |
+A|hello | hello |
|
2 |
+A|he'll'o | hello |
|
3 |
+A|he'llo | hello |
|
4 |
+A|he\'llo | he'llo |
|
5 |
+A|he\\'llo | he\llo |
|
6 |
+A|abc\tdef | abctdef |
|
7 |
+A|"abc\tdef" | abc\tdef |
|
8 |
+A|'abc\tdef' | abc\tdef |
|
9 |
+A|hello\ | hello |
|
10 |
+A|hello\\ | hello\ |
|
11 |
+A|"hello | hello |
|
12 |
+A|"hello\" | hello" |
|
13 |
+A|"hel'lo" | hel'lo |
|
14 |
+A|'hello | hello |
|
15 |
+A|'hello\' | hello\ |
|
16 |
+A|"''" | '' |
|
17 |
+A|$. | $. |
|
18 |
+A|$1 | |
|
19 |
+A|he$1x | hex |
|
20 |
+A|he$.x | he$.x |
|
21 |
+# Next one is different on Windows as $pwd==$PWD |
|
22 |
+U|he$pwd. | he. |
|
23 |
+W|he$pwd. | he/home. |
|
24 |
+A|he$PWD | he/home |
|
25 |
+A|he\$PWD | he$PWD |
|
26 |
+A|he\\$PWD | he\/home |
|
27 |
+A|he\${} | he${} |
|
28 |
+A|he\${}xx | he${}xx |
|
29 |
+A|he${} | he |
|
30 |
+A|he${}xx | hexx |
|
31 |
+A|he${hi} | he |
|
32 |
+A|he${hi}xx | hexx |
|
33 |
+A|he${PWD} | he/home |
|
34 |
+A|he${.} | error |
|
35 |
+A|he${XXX:-000}xx | he000xx |
|
36 |
+A|he${PWD:-000}xx | he/homexx |
|
37 |
+A|he${XXX:-$PWD}xx | he/homexx |
|
38 |
+A|he${XXX:-${PWD:-yyy}}xx | he/homexx |
|
39 |
+A|he${XXX:-${YYY:-yyy}}xx | heyyyxx |
|
40 |
+A|he${XXX:YYY} | error |
|
41 |
+A|he${XXX:+${PWD}}xx | hexx |
|
42 |
+A|he${PWD:+${XXX}}xx | hexx |
|
43 |
+A|he${PWD:+${SHELL}}xx | hebashxx |
|
44 |
+A|he${XXX:+000}xx | hexx |
|
45 |
+A|he${PWD:+000}xx | he000xx |
|
46 |
+A|'he${XX}' | he${XX} |
|
47 |
+A|"he${PWD}" | he/home |
|
48 |
+A|"he'$PWD'" | he'/home' |
|
49 |
+A|"$PWD" | /home |
|
50 |
+A|'$PWD' | $PWD |
|
51 |
+A|'\$PWD' | \$PWD |
|
52 |
+A|'"hello"' | "hello" |
|
53 |
+A|he\$PWD | he$PWD |
|
54 |
+A|"he\$PWD" | he$PWD |
|
55 |
+A|'he\$PWD' | he\$PWD |
|
56 |
+A|he${PWD | error |
|
57 |
+A|he${PWD:=000}xx | error |
|
58 |
+A|he${PWD:+${PWD}:}xx | he/home:xx |
|
59 |
+A|he${XXX:-\$PWD:}xx | he$PWD:xx |
|
60 |
+A|he${XXX:-\${PWD}z}xx | he${PWDz}xx |
|
61 |
+A|안녕하세요 | 안녕하세요 |
|
62 |
+A|안'녕'하세요 | 안녕하세요 |
|
63 |
+A|안'녕하세요 | 안녕하세요 |
|
64 |
+A|안녕\'하세요 | 안녕'하세요 |
|
65 |
+A|안\\'녕하세요 | 안\녕하세요 |
|
66 |
+A|안녕\t하세요 | 안녕t하세요 |
|
67 |
+A|"안녕\t하세요" | 안녕\t하세요 |
|
68 |
+A|'안녕\t하세요 | 안녕\t하세요 |
|
69 |
+A|안녕하세요\ | 안녕하세요 |
|
70 |
+A|안녕하세요\\ | 안녕하세요\ |
|
71 |
+A|"안녕하세요 | 안녕하세요 |
|
72 |
+A|"안녕하세요\" | 안녕하세요" |
|
73 |
+A|"안녕'하세요" | 안녕'하세요 |
|
74 |
+A|'안녕하세요 | 안녕하세요 |
|
75 |
+A|'안녕하세요\' | 안녕하세요\ |
|
76 |
+A|안녕$1x | 안녕x |
|
77 |
+A|안녕$.x | 안녕$.x |
|
78 |
+# Next one is different on Windows as $pwd==$PWD |
|
79 |
+U|안녕$pwd. | 안녕. |
|
80 |
+W|안녕$pwd. | 안녕/home. |
|
81 |
+A|안녕$PWD | 안녕/home |
|
82 |
+A|안녕\$PWD | 안녕$PWD |
|
83 |
+A|안녕\\$PWD | 안녕\/home |
|
84 |
+A|안녕\${} | 안녕${} |
|
85 |
+A|안녕\${}xx | 안녕${}xx |
|
86 |
+A|안녕${} | 안녕 |
|
87 |
+A|안녕${}xx | 안녕xx |
|
88 |
+A|안녕${hi} | 안녕 |
|
89 |
+A|안녕${hi}xx | 안녕xx |
|
90 |
+A|안녕${PWD} | 안녕/home |
|
91 |
+A|안녕${.} | error |
|
92 |
+A|안녕${XXX:-000}xx | 안녕000xx |
|
93 |
+A|안녕${PWD:-000}xx | 안녕/homexx |
|
94 |
+A|안녕${XXX:-$PWD}xx | 안녕/homexx |
|
95 |
+A|안녕${XXX:-${PWD:-yyy}}xx | 안녕/homexx |
|
96 |
+A|안녕${XXX:-${YYY:-yyy}}xx | 안녕yyyxx |
|
97 |
+A|안녕${XXX:YYY} | error |
|
98 |
+A|안녕${XXX:+${PWD}}xx | 안녕xx |
|
99 |
+A|안녕${PWD:+${XXX}}xx | 안녕xx |
|
100 |
+A|안녕${PWD:+${SHELL}}xx | 안녕bashxx |
|
101 |
+A|안녕${XXX:+000}xx | 안녕xx |
|
102 |
+A|안녕${PWD:+000}xx | 안녕000xx |
|
103 |
+A|'안녕${XX}' | 안녕${XX} |
|
104 |
+A|"안녕${PWD}" | 안녕/home |
|
105 |
+A|"안녕'$PWD'" | 안녕'/home' |
|
106 |
+A|'"안녕"' | "안녕" |
|
107 |
+A|안녕\$PWD | 안녕$PWD |
|
108 |
+A|"안녕\$PWD" | 안녕$PWD |
|
109 |
+A|'안녕\$PWD' | 안녕\$PWD |
|
110 |
+A|안녕${PWD | error |
|
111 |
+A|안녕${PWD:=000}xx | error |
|
112 |
+A|안녕${PWD:+${PWD}:}xx | 안녕/home:xx |
|
113 |
+A|안녕${XXX:-\$PWD:}xx | 안녕$PWD:xx |
|
114 |
+A|안녕${XXX:-\${PWD}z}xx | 안녕${PWDz}xx |
|
115 |
+A|$KOREAN | 한국어 |
|
116 |
+A|안녕$KOREAN | 안녕한국어 |
... | ... |
@@ -8,6 +8,7 @@ package dockerfile |
8 | 8 |
|
9 | 9 |
import ( |
10 | 10 |
"fmt" |
11 |
+ "runtime" |
|
11 | 12 |
"strings" |
12 | 13 |
"text/scanner" |
13 | 14 |
"unicode" |
... | ... |
@@ -298,9 +299,16 @@ func (sw *shellWord) processName() string { |
298 | 298 |
} |
299 | 299 |
|
300 | 300 |
func (sw *shellWord) getEnv(name string) string { |
301 |
+ if runtime.GOOS == "windows" { |
|
302 |
+ // Case-insensitive environment variables on Windows |
|
303 |
+ name = strings.ToUpper(name) |
|
304 |
+ } |
|
301 | 305 |
for _, env := range sw.envs { |
302 | 306 |
i := strings.Index(env, "=") |
303 | 307 |
if i < 0 { |
308 |
+ if runtime.GOOS == "windows" { |
|
309 |
+ env = strings.ToUpper(env) |
|
310 |
+ } |
|
304 | 311 |
if name == env { |
305 | 312 |
// Should probably never get here, but just in case treat |
306 | 313 |
// it like "var" and "var=" are the same |
... | ... |
@@ -308,7 +316,11 @@ func (sw *shellWord) getEnv(name string) string { |
308 | 308 |
} |
309 | 309 |
continue |
310 | 310 |
} |
311 |
- if name != env[:i] { |
|
311 |
+ compareName := env[:i] |
|
312 |
+ if runtime.GOOS == "windows" { |
|
313 |
+ compareName = strings.ToUpper(compareName) |
|
314 |
+ } |
|
315 |
+ if name != compareName { |
|
312 | 316 |
continue |
313 | 317 |
} |
314 | 318 |
return env[i+1:] |
... | ... |
@@ -3,12 +3,14 @@ package dockerfile |
3 | 3 |
import ( |
4 | 4 |
"bufio" |
5 | 5 |
"os" |
6 |
+ "runtime" |
|
6 | 7 |
"strings" |
7 | 8 |
"testing" |
8 | 9 |
) |
9 | 10 |
|
10 | 11 |
func TestShellParser4EnvVars(t *testing.T) { |
11 | 12 |
fn := "envVarTest" |
13 |
+ lineCount := 0 |
|
12 | 14 |
|
13 | 15 |
file, err := os.Open(fn) |
14 | 16 |
if err != nil { |
... | ... |
@@ -20,6 +22,7 @@ func TestShellParser4EnvVars(t *testing.T) { |
20 | 20 |
envs := []string{"PWD=/home", "SHELL=bash", "KOREAN=한국어"} |
21 | 21 |
for scanner.Scan() { |
22 | 22 |
line := scanner.Text() |
23 |
+ lineCount++ |
|
23 | 24 |
|
24 | 25 |
// Trim comments and blank lines |
25 | 26 |
i := strings.Index(line, "#") |
... | ... |
@@ -33,21 +36,30 @@ func TestShellParser4EnvVars(t *testing.T) { |
33 | 33 |
} |
34 | 34 |
|
35 | 35 |
words := strings.Split(line, "|") |
36 |
- if len(words) != 2 { |
|
36 |
+ if len(words) != 3 { |
|
37 | 37 |
t.Fatalf("Error in '%s' - should be exactly one | in:%q", fn, line) |
38 | 38 |
} |
39 | 39 |
|
40 | 40 |
words[0] = strings.TrimSpace(words[0]) |
41 | 41 |
words[1] = strings.TrimSpace(words[1]) |
42 |
+ words[2] = strings.TrimSpace(words[2]) |
|
42 | 43 |
|
43 |
- newWord, err := ProcessWord(words[0], envs, '\\') |
|
44 |
- |
|
45 |
- if err != nil { |
|
46 |
- newWord = "error" |
|
44 |
+ // Key W=Windows; A=All; U=Unix |
|
45 |
+ if (words[0] != "W") && (words[0] != "A") && (words[0] != "U") { |
|
46 |
+ t.Fatalf("Invalid tag %s at line %d of %s. Must be W, A or U", words[0], lineCount, fn) |
|
47 | 47 |
} |
48 | 48 |
|
49 |
- if newWord != words[1] { |
|
50 |
- t.Fatalf("Error. Src: %s Calc: %s Expected: %s", words[0], newWord, words[1]) |
|
49 |
+ if ((words[0] == "W" || words[0] == "A") && runtime.GOOS == "windows") || |
|
50 |
+ ((words[0] == "U" || words[0] == "A") && runtime.GOOS != "windows") { |
|
51 |
+ newWord, err := ProcessWord(words[1], envs, '\\') |
|
52 |
+ |
|
53 |
+ if err != nil { |
|
54 |
+ newWord = "error" |
|
55 |
+ } |
|
56 |
+ |
|
57 |
+ if newWord != words[2] { |
|
58 |
+ t.Fatalf("Error. Src: %s Calc: %s Expected: %s at line %d", words[1], newWord, words[2], lineCount) |
|
59 |
+ } |
|
51 | 60 |
} |
52 | 61 |
} |
53 | 62 |
} |
... | ... |
@@ -46,6 +46,11 @@ func merge(userConf, imageConf *containertypes.Config) error { |
46 | 46 |
imageEnvKey := strings.Split(imageEnv, "=")[0] |
47 | 47 |
for _, userEnv := range userConf.Env { |
48 | 48 |
userEnvKey := strings.Split(userEnv, "=")[0] |
49 |
+ if runtime.GOOS == "windows" { |
|
50 |
+ // Case insensitive environment variables on Windows |
|
51 |
+ imageEnvKey = strings.ToUpper(imageEnvKey) |
|
52 |
+ userEnvKey = strings.ToUpper(userEnvKey) |
|
53 |
+ } |
|
49 | 54 |
if imageEnvKey == userEnvKey { |
50 | 55 |
found = true |
51 | 56 |
break |
... | ... |
@@ -7311,3 +7311,19 @@ RUN ["cat", "/foo/file"] |
7311 | 7311 |
c.Fatal(err) |
7312 | 7312 |
} |
7313 | 7313 |
} |
7314 |
+ |
|
7315 |
+// Case-insensitive environment variables on Windows |
|
7316 |
+func (s *DockerSuite) TestBuildWindowsEnvCaseInsensitive(c *check.C) { |
|
7317 |
+ testRequires(c, DaemonIsWindows) |
|
7318 |
+ name := "testbuildwindowsenvcaseinsensitive" |
|
7319 |
+ if _, err := buildImage(name, ` |
|
7320 |
+ FROM `+WindowsBaseImage+` |
|
7321 |
+ ENV FOO=bar foo=bar |
|
7322 |
+ `, true); err != nil { |
|
7323 |
+ c.Fatal(err) |
|
7324 |
+ } |
|
7325 |
+ res := inspectFieldJSON(c, name, "Config.Env") |
|
7326 |
+ if res != `["foo=bar"]` { // Should not have FOO=bar in it - takes the last one processed. And only one entry as deduped. |
|
7327 |
+ c.Fatalf("Case insensitive environment variables on Windows failed. Got %s", res) |
|
7328 |
+ } |
|
7329 |
+} |