Signed-off-by: John Howard <jhoward@microsoft.com>
| ... | ... |
@@ -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 |
+} |