Browse code

Allow for env vars to have spaces in some cases

Closes #17781

This allows for env vars in EXPOSE to be parsed for spaces so that each
"word" is then treated independently instead of as a single word/arg.

Signed-off-by: Doug Davis <dug@us.ibm.com>

Doug Davis authored on 2015/11/08 05:05:55
Showing 7 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,112 @@
0
+hello                    |     hello
1
+he'll'o                  |     hello
2
+he'llo                   |     hello
3
+he\'llo                  |     he'llo
4
+he\\'llo                 |     he\llo
5
+abc\tdef                 |     abctdef
6
+"abc\tdef"               |     abc\tdef
7
+'abc\tdef'               |     abc\tdef
8
+hello\                   |     hello
9
+hello\\                  |     hello\
10
+"hello                   |     hello
11
+"hello\"                 |     hello"
12
+"hel'lo"                 |     hel'lo
13
+'hello                   |     hello
14
+'hello\'                 |     hello\
15
+"''"                     |     ''
16
+$.                       |     $.
17
+$1                       |
18
+he$1x                    |     hex
19
+he$.x                    |     he$.x
20
+he$pwd.                  |     he.
21
+he$PWD                   |     he/home
22
+he\$PWD                  |     he$PWD
23
+he\\$PWD                 |     he\/home
24
+he\${}                   |     he${}
25
+he\${}xx                 |     he${}xx
26
+he${}                    |     he
27
+he${}xx                  |     hexx
28
+he${hi}                  |     he
29
+he${hi}xx                |     hexx
30
+he${PWD}                 |     he/home
31
+he${.}                   |     error
32
+he${XXX:-000}xx          |     he000xx
33
+he${PWD:-000}xx          |     he/homexx
34
+he${XXX:-$PWD}xx         |     he/homexx
35
+he${XXX:-${PWD:-yyy}}xx  |     he/homexx
36
+he${XXX:-${YYY:-yyy}}xx  |     heyyyxx
37
+he${XXX:YYY}             |     error
38
+he${XXX:+${PWD}}xx       |     hexx
39
+he${PWD:+${XXX}}xx       |     hexx
40
+he${PWD:+${SHELL}}xx     |     hebashxx
41
+he${XXX:+000}xx          |     hexx
42
+he${PWD:+000}xx          |     he000xx
43
+'he${XX}'                |     he${XX}
44
+"he${PWD}"               |     he/home
45
+"he'$PWD'"               |     he'/home'
46
+"$PWD"                   |     /home
47
+'$PWD'                   |     $PWD
48
+'\$PWD'                  |     \$PWD
49
+'"hello"'                |     "hello"
50
+he\$PWD                  |     he$PWD
51
+"he\$PWD"                |     he$PWD
52
+'he\$PWD'                |     he\$PWD
53
+he${PWD                  |     error
54
+he${PWD:=000}xx          |     error
55
+he${PWD:+${PWD}:}xx      |     he/home:xx
56
+he${XXX:-\$PWD:}xx       |     he$PWD:xx
57
+he${XXX:-\${PWD}z}xx     |     he${PWDz}xx
58
+안녕하세요                 |     안녕하세요
59
+안'녕'하세요               |     안녕하세요
60
+안'녕하세요                |     안녕하세요
61
+안녕\'하세요               |     안녕'하세요
62
+안\\'녕하세요              |     안\녕하세요
63
+안녕\t하세요               |     안녕t하세요
64
+"안녕\t하세요"             |     안녕\t하세요
65
+'안녕\t하세요              |     안녕\t하세요
66
+안녕하세요\                |     안녕하세요
67
+안녕하세요\\               |     안녕하세요\
68
+"안녕하세요                |     안녕하세요
69
+"안녕하세요\"              |     안녕하세요"
70
+"안녕'하세요"              |     안녕'하세요
71
+'안녕하세요                |     안녕하세요
72
+'안녕하세요\'              |     안녕하세요\
73
+안녕$1x                    |     안녕x
74
+안녕$.x                    |     안녕$.x
75
+안녕$pwd.                  |     안녕.
76
+안녕$PWD                   |     안녕/home
77
+안녕\$PWD                  |     안녕$PWD
78
+안녕\\$PWD                 |     안녕\/home
79
+안녕\${}                   |     안녕${}
80
+안녕\${}xx                 |     안녕${}xx
81
+안녕${}                    |     안녕
82
+안녕${}xx                  |     안녕xx
83
+안녕${hi}                  |     안녕
84
+안녕${hi}xx                |     안녕xx
85
+안녕${PWD}                 |     안녕/home
86
+안녕${.}                   |     error
87
+안녕${XXX:-000}xx          |     안녕000xx
88
+안녕${PWD:-000}xx          |     안녕/homexx
89
+안녕${XXX:-$PWD}xx         |     안녕/homexx
90
+안녕${XXX:-${PWD:-yyy}}xx  |     안녕/homexx
91
+안녕${XXX:-${YYY:-yyy}}xx  |     안녕yyyxx
92
+안녕${XXX:YYY}             |     error
93
+안녕${XXX:+${PWD}}xx       |     안녕xx
94
+안녕${PWD:+${XXX}}xx       |     안녕xx
95
+안녕${PWD:+${SHELL}}xx     |     안녕bashxx
96
+안녕${XXX:+000}xx          |     안녕xx
97
+안녕${PWD:+000}xx          |     안녕000xx
98
+'안녕${XX}'                |     안녕${XX}
99
+"안녕${PWD}"               |     안녕/home
100
+"안녕'$PWD'"               |     안녕'/home'
101
+'"안녕"'                   |     "안녕"
102
+안녕\$PWD                  |     안녕$PWD
103
+"안녕\$PWD"                |     안녕$PWD
104
+'안녕\$PWD'                |     안녕\$PWD
105
+안녕${PWD                  |     error
106
+안녕${PWD:=000}xx          |     error
107
+안녕${PWD:+${PWD}:}xx      |     안녕/home:xx
108
+안녕${XXX:-\$PWD:}xx       |     안녕$PWD:xx
109
+안녕${XXX:-\${PWD}z}xx     |     안녕${PWDz}xx
110
+$KOREAN                    |     한국어
111
+안녕$KOREAN                |     안녕한국어
... ...
@@ -42,6 +42,19 @@ var replaceEnvAllowed = map[string]struct{}{
42 42
 	command.Arg:        {},
43 43
 }
44 44
 
45
+// Certain commands are allowed to have their args split into more
46
+// words after env var replacements. Meaning:
47
+//   ENV foo="123 456"
48
+//   EXPOSE $foo
49
+// should result in the same thing as:
50
+//   EXPOSE 123 456
51
+// and not treat "123 456" as a single word.
52
+// Note that: EXPOSE "$foo" and EXPOSE $foo are not the same thing.
53
+// Quotes will cause it to still be treated as single word.
54
+var allowWordExpansion = map[string]bool{
55
+	command.Expose: true,
56
+}
57
+
45 58
 var evaluateTable map[string]func(*Builder, []string, map[string]bool, string) error
46 59
 
47 60
 func init() {
... ...
@@ -92,7 +105,7 @@ func (b *Builder) dispatch(stepN int, ast *parser.Node) error {
92 92
 	attrs := ast.Attributes
93 93
 	original := ast.Original
94 94
 	flags := ast.Flags
95
-	strs := []string{}
95
+	strList := []string{}
96 96
 	msg := fmt.Sprintf("Step %d : %s", stepN+1, upperCasedCmd)
97 97
 
98 98
 	if len(ast.Flags) > 0 {
... ...
@@ -104,7 +117,7 @@ func (b *Builder) dispatch(stepN int, ast *parser.Node) error {
104 104
 			return fmt.Errorf("ONBUILD requires at least one argument")
105 105
 		}
106 106
 		ast = ast.Next.Children[0]
107
-		strs = append(strs, ast.Value)
107
+		strList = append(strList, ast.Value)
108 108
 		msg += " " + ast.Value
109 109
 
110 110
 		if len(ast.Flags) > 0 {
... ...
@@ -122,9 +135,6 @@ func (b *Builder) dispatch(stepN int, ast *parser.Node) error {
122 122
 		cursor = cursor.Next
123 123
 		n++
124 124
 	}
125
-	l := len(strs)
126
-	strList := make([]string, n+l)
127
-	copy(strList, strs)
128 125
 	msgList := make([]string, n)
129 126
 
130 127
 	var i int
... ...
@@ -155,12 +165,24 @@ func (b *Builder) dispatch(stepN int, ast *parser.Node) error {
155 155
 		str = ast.Value
156 156
 		if _, ok := replaceEnvAllowed[cmd]; ok {
157 157
 			var err error
158
-			str, err = ProcessWord(ast.Value, envs)
159
-			if err != nil {
160
-				return err
158
+			var words []string
159
+
160
+			if _, ok := allowWordExpansion[cmd]; ok {
161
+				words, err = ProcessWords(str, envs)
162
+				if err != nil {
163
+					return err
164
+				}
165
+				strList = append(strList, words...)
166
+			} else {
167
+				str, err = ProcessWord(str, envs)
168
+				if err != nil {
169
+					return err
170
+				}
171
+				strList = append(strList, str)
161 172
 			}
173
+		} else {
174
+			strList = append(strList, str)
162 175
 		}
163
-		strList[i+l] = str
164 176
 		msgList[i] = ast.Value
165 177
 		i++
166 178
 	}
... ...
@@ -29,17 +29,85 @@ func ProcessWord(word string, env []string) (string, error) {
29 29
 		pos:  0,
30 30
 	}
31 31
 	sw.scanner.Init(strings.NewReader(word))
32
-	return sw.process()
32
+	word, _, err := sw.process()
33
+	return word, err
33 34
 }
34 35
 
35
-func (sw *shellWord) process() (string, error) {
36
+// ProcessWords will use the 'env' list of environment variables,
37
+// and replace any env var references in 'word' then it will also
38
+// return a slice of strings which represents the 'word'
39
+// split up based on spaces - taking into account quotes.  Note that
40
+// this splitting is done **after** the env var substitutions are done.
41
+// Note, each one is trimmed to remove leading and trailing spaces (unless
42
+// they are quoted", but ProcessWord retains spaces between words.
43
+func ProcessWords(word string, env []string) ([]string, error) {
44
+	sw := &shellWord{
45
+		word: word,
46
+		envs: env,
47
+		pos:  0,
48
+	}
49
+	sw.scanner.Init(strings.NewReader(word))
50
+	_, words, err := sw.process()
51
+	return words, err
52
+}
53
+
54
+func (sw *shellWord) process() (string, []string, error) {
36 55
 	return sw.processStopOn(scanner.EOF)
37 56
 }
38 57
 
58
+type wordsStruct struct {
59
+	word   string
60
+	words  []string
61
+	inWord bool
62
+}
63
+
64
+func (w *wordsStruct) addChar(ch rune) {
65
+	if unicode.IsSpace(ch) && w.inWord {
66
+		if len(w.word) != 0 {
67
+			w.words = append(w.words, w.word)
68
+			w.word = ""
69
+			w.inWord = false
70
+		}
71
+	} else if !unicode.IsSpace(ch) {
72
+		w.addRawChar(ch)
73
+	}
74
+}
75
+
76
+func (w *wordsStruct) addRawChar(ch rune) {
77
+	w.word += string(ch)
78
+	w.inWord = true
79
+}
80
+
81
+func (w *wordsStruct) addString(str string) {
82
+	var scan scanner.Scanner
83
+	scan.Init(strings.NewReader(str))
84
+	for scan.Peek() != scanner.EOF {
85
+		w.addChar(scan.Next())
86
+	}
87
+}
88
+
89
+func (w *wordsStruct) addRawString(str string) {
90
+	w.word += str
91
+	w.inWord = true
92
+}
93
+
94
+func (w *wordsStruct) getWords() []string {
95
+	if len(w.word) > 0 {
96
+		w.words = append(w.words, w.word)
97
+
98
+		// Just in case we're called again by mistake
99
+		w.word = ""
100
+		w.inWord = false
101
+	}
102
+	return w.words
103
+}
104
+
39 105
 // Process the word, starting at 'pos', and stop when we get to the
40 106
 // end of the word or the 'stopChar' character
41
-func (sw *shellWord) processStopOn(stopChar rune) (string, error) {
107
+func (sw *shellWord) processStopOn(stopChar rune) (string, []string, error) {
42 108
 	var result string
109
+	var words wordsStruct
110
+
43 111
 	var charFuncMapping = map[rune]func() (string, error){
44 112
 		'\'': sw.processSingleQuote,
45 113
 		'"':  sw.processDoubleQuote,
... ...
@@ -57,9 +125,15 @@ func (sw *shellWord) processStopOn(stopChar rune) (string, error) {
57 57
 			// Call special processing func for certain chars
58 58
 			tmp, err := fn()
59 59
 			if err != nil {
60
-				return "", err
60
+				return "", []string{}, err
61 61
 			}
62 62
 			result += tmp
63
+
64
+			if ch == rune('$') {
65
+				words.addString(tmp)
66
+			} else {
67
+				words.addRawString(tmp)
68
+			}
63 69
 		} else {
64 70
 			// Not special, just add it to the result
65 71
 			ch = sw.scanner.Next()
... ...
@@ -73,13 +147,16 @@ func (sw *shellWord) processStopOn(stopChar rune) (string, error) {
73 73
 					break
74 74
 				}
75 75
 
76
+				words.addRawChar(ch)
77
+			} else {
78
+				words.addChar(ch)
76 79
 			}
77 80
 
78 81
 			result += string(ch)
79 82
 		}
80 83
 	}
81 84
 
82
-	return result, nil
85
+	return result, words.getWords(), nil
83 86
 }
84 87
 
85 88
 func (sw *shellWord) processSingleQuote() (string, error) {
... ...
@@ -160,7 +237,7 @@ func (sw *shellWord) processDollar() (string, error) {
160 160
 			sw.scanner.Next() // skip over :
161 161
 			modifier := sw.scanner.Next()
162 162
 
163
-			word, err := sw.processStopOn('}')
163
+			word, _, err := sw.processStopOn('}')
164 164
 			if err != nil {
165 165
 				return "", err
166 166
 			}
... ...
@@ -7,10 +7,12 @@ import (
7 7
 	"testing"
8 8
 )
9 9
 
10
-func TestShellParser(t *testing.T) {
11
-	file, err := os.Open("words")
10
+func TestShellParser4EnvVars(t *testing.T) {
11
+	fn := "envVarTest"
12
+
13
+	file, err := os.Open(fn)
12 14
 	if err != nil {
13
-		t.Fatalf("Can't open 'words': %s", err)
15
+		t.Fatalf("Can't open '%s': %s", err, fn)
14 16
 	}
15 17
 	defer file.Close()
16 18
 
... ...
@@ -32,7 +34,7 @@ func TestShellParser(t *testing.T) {
32 32
 
33 33
 		words := strings.Split(line, "|")
34 34
 		if len(words) != 2 {
35
-			t.Fatalf("Error in 'words' - should be 2 words:%q", words)
35
+			t.Fatalf("Error in '%s' - should be exactly one | in:%q", fn, line)
36 36
 		}
37 37
 
38 38
 		words[0] = strings.TrimSpace(words[0])
... ...
@@ -50,6 +52,54 @@ func TestShellParser(t *testing.T) {
50 50
 	}
51 51
 }
52 52
 
53
+func TestShellParser4Words(t *testing.T) {
54
+	fn := "wordsTest"
55
+
56
+	file, err := os.Open(fn)
57
+	if err != nil {
58
+		t.Fatalf("Can't open '%s': %s", err, fn)
59
+	}
60
+	defer file.Close()
61
+
62
+	envs := []string{}
63
+	scanner := bufio.NewScanner(file)
64
+	for scanner.Scan() {
65
+		line := scanner.Text()
66
+
67
+		if strings.HasPrefix(line, "#") {
68
+			continue
69
+		}
70
+
71
+		if strings.HasPrefix(line, "ENV ") {
72
+			line = strings.TrimLeft(line[3:], " ")
73
+			envs = append(envs, line)
74
+			continue
75
+		}
76
+
77
+		words := strings.Split(line, "|")
78
+		if len(words) != 2 {
79
+			t.Fatalf("Error in '%s' - should be exactly one | in: %q", fn, line)
80
+		}
81
+		test := strings.TrimSpace(words[0])
82
+		expected := strings.Split(strings.TrimLeft(words[1], " "), ",")
83
+
84
+		result, err := ProcessWords(test, envs)
85
+
86
+		if err != nil {
87
+			result = []string{"error"}
88
+		}
89
+
90
+		if len(result) != len(expected) {
91
+			t.Fatalf("Error. %q was suppose to result in %q, but got %q instead", test, expected, result)
92
+		}
93
+		for i, w := range expected {
94
+			if w != result[i] {
95
+				t.Fatalf("Error. %q was suppose to result in %q, but got %q instead", test, expected, result)
96
+			}
97
+		}
98
+	}
99
+}
100
+
53 101
 func TestGetEnv(t *testing.T) {
54 102
 	sw := &shellWord{
55 103
 		word: "",
56 104
deleted file mode 100644
... ...
@@ -1,112 +0,0 @@
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                |     안녕한국어
113 1
new file mode 100644
... ...
@@ -0,0 +1,25 @@
0
+hello | hello
1
+hello${hi}bye | hellobye
2
+ENV hi=hi
3
+hello${hi}bye | hellohibye
4
+ENV space=abc  def
5
+hello${space}bye | helloabc,defbye
6
+hello"${space}"bye | helloabc  defbye
7
+hello "${space}"bye | hello,abc  defbye
8
+ENV leading=  ab c
9
+hello${leading}def | hello,ab,cdef
10
+hello"${leading}" def | hello  ab c,def
11
+hello"${leading}" | hello  ab c
12
+hello${leading} | hello,ab,c
13
+# next line MUST have 3 trailing spaces, don't erase them!
14
+ENV trailing=ab c   
15
+hello${trailing} | helloab,c
16
+hello${trailing}d | helloab,c,d
17
+hello"${trailing}"d | helloab c   d
18
+# next line MUST have 3 trailing spaces, don't erase them!
19
+hel"lo${trailing}" | helloab c   
20
+hello" there  " | hello there  
21
+hello there     | hello,there
22
+hello\ there | hello there
23
+hello" there | hello there
24
+hello\" there | hello",there
... ...
@@ -150,6 +150,8 @@ func (s *DockerSuite) TestBuildEnvironmentReplacementExpose(c *check.C) {
150 150
   FROM scratch
151 151
   ENV port 80
152 152
   EXPOSE ${port}
153
+  ENV ports "  99   100 "
154
+  EXPOSE ${ports}
153 155
   `, true)
154 156
 	if err != nil {
155 157
 		c.Fatal(err)
... ...
@@ -166,8 +168,13 @@ func (s *DockerSuite) TestBuildEnvironmentReplacementExpose(c *check.C) {
166 166
 		c.Fatal(err)
167 167
 	}
168 168
 
169
-	if _, ok := exposedPorts["80/tcp"]; !ok {
170
-		c.Fatal("Exposed port 80 from environment not in Config.ExposedPorts on image")
169
+	exp := []int{80, 99, 100}
170
+
171
+	for _, p := range exp {
172
+		tmp := fmt.Sprintf("%d/tcp", p)
173
+		if _, ok := exposedPorts[tmp]; !ok {
174
+			c.Fatalf("Exposed port %d from environment not in Config.ExposedPorts on image", p)
175
+		}
171 176
 	}
172 177
 
173 178
 }