Browse code

Windows: Builder case insensitive env

Signed-off-by: John Howard <jhoward@microsoft.com>

John Howard authored on 2016/11/23 04:26:02
Showing 6 changed files
... ...
@@ -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
+}