Browse code

Merge pull request #25142 from tiborvass/1.12.0-final-cherry-picks

1.12.0 final cherry picks

Justin Cormack authored on 2016/07/28 08:21:36
Showing 19 changed files
... ...
@@ -71,6 +71,7 @@ type Builder struct {
71 71
 	disableCommit    bool
72 72
 	cacheBusted      bool
73 73
 	allowedBuildArgs map[string]bool // list of build-time args that are allowed for expansion/substitution and passing to commands in 'run'.
74
+	directive        parser.Directive
74 75
 
75 76
 	// TODO: remove once docker.Commit can receive a tag
76 77
 	id string
... ...
@@ -130,9 +131,15 @@ func NewBuilder(clientCtx context.Context, config *types.ImageBuildOptions, back
130 130
 		tmpContainers:    map[string]struct{}{},
131 131
 		id:               stringid.GenerateNonCryptoID(),
132 132
 		allowedBuildArgs: make(map[string]bool),
133
+		directive: parser.Directive{
134
+			EscapeSeen:           false,
135
+			LookingForDirectives: true,
136
+		},
133 137
 	}
138
+	parser.SetEscapeToken(parser.DefaultEscapeToken, &b.directive) // Assume the default token for escape
139
+
134 140
 	if dockerfile != nil {
135
-		b.dockerfile, err = parser.Parse(dockerfile)
141
+		b.dockerfile, err = parser.Parse(dockerfile, &b.directive)
136 142
 		if err != nil {
137 143
 			return nil, err
138 144
 		}
... ...
@@ -218,7 +225,7 @@ func (b *Builder) build(stdout io.Writer, stderr io.Writer, out io.Writer) (stri
218 218
 		for k, v := range b.options.Labels {
219 219
 			line += fmt.Sprintf("%q=%q ", k, v)
220 220
 		}
221
-		_, node, err := parser.ParseLine(line)
221
+		_, node, err := parser.ParseLine(line, &b.directive)
222 222
 		if err != nil {
223 223
 			return "", err
224 224
 		}
... ...
@@ -291,7 +298,12 @@ func (b *Builder) Cancel() {
291 291
 //
292 292
 // TODO: Remove?
293 293
 func BuildFromConfig(config *container.Config, changes []string) (*container.Config, error) {
294
-	ast, err := parser.Parse(bytes.NewBufferString(strings.Join(changes, "\n")))
294
+	b, err := NewBuilder(context.Background(), nil, nil, nil, nil)
295
+	if err != nil {
296
+		return nil, err
297
+	}
298
+
299
+	ast, err := parser.Parse(bytes.NewBufferString(strings.Join(changes, "\n")), &b.directive)
295 300
 	if err != nil {
296 301
 		return nil, err
297 302
 	}
... ...
@@ -303,10 +315,6 @@ func BuildFromConfig(config *container.Config, changes []string) (*container.Con
303 303
 		}
304 304
 	}
305 305
 
306
-	b, err := NewBuilder(context.Background(), nil, nil, nil, nil)
307
-	if err != nil {
308
-		return nil, err
309
-	}
310 306
 	b.runConfig = config
311 307
 	b.Stdout = ioutil.Discard
312 308
 	b.Stderr = ioutil.Discard
... ...
@@ -173,7 +173,9 @@ func executeTestCase(t *testing.T, testCase dispatchTestCase) {
173 173
 	}()
174 174
 
175 175
 	r := strings.NewReader(testCase.dockerfile)
176
-	n, err := parser.Parse(r)
176
+	d := parser.Directive{}
177
+	parser.SetEscapeToken(parser.DefaultEscapeToken, &d)
178
+	n, err := parser.Parse(r, &d)
177 179
 
178 180
 	if err != nil {
179 181
 		t.Fatalf("Error when parsing Dockerfile: %s", err)
... ...
@@ -427,7 +427,7 @@ func (b *Builder) processImageFrom(img builder.Image) error {
427 427
 
428 428
 	// parse the ONBUILD triggers by invoking the parser
429 429
 	for _, step := range onBuildTriggers {
430
-		ast, err := parser.Parse(strings.NewReader(step))
430
+		ast, err := parser.Parse(strings.NewReader(step), &b.directive)
431 431
 		if err != nil {
432 432
 			return err
433 433
 		}
... ...
@@ -648,7 +648,7 @@ func (b *Builder) parseDockerfile() error {
648 648
 			return fmt.Errorf("The Dockerfile (%s) cannot be empty", b.options.Dockerfile)
649 649
 		}
650 650
 	}
651
-	b.dockerfile, err = parser.Parse(f)
651
+	b.dockerfile, err = parser.Parse(f, &b.directive)
652 652
 	if err != nil {
653 653
 		return err
654 654
 	}
... ...
@@ -22,7 +22,10 @@ func main() {
22 22
 			panic(err)
23 23
 		}
24 24
 
25
-		ast, err := parser.Parse(f)
25
+		d := parser.Directive{LookingForDirectives: true}
26
+		parser.SetEscapeToken(parser.DefaultEscapeToken, &d)
27
+
28
+		ast, err := parser.Parse(f, &d)
26 29
 		if err != nil {
27 30
 			panic(err)
28 31
 		} else {
... ...
@@ -28,7 +28,10 @@ var validJSONArraysOfStrings = map[string][]string{
28 28
 
29 29
 func TestJSONArraysOfStrings(t *testing.T) {
30 30
 	for json, expected := range validJSONArraysOfStrings {
31
-		if node, _, err := parseJSON(json); err != nil {
31
+		d := Directive{}
32
+		SetEscapeToken(DefaultEscapeToken, &d)
33
+
34
+		if node, _, err := parseJSON(json, &d); err != nil {
32 35
 			t.Fatalf("%q should be a valid JSON array of strings, but wasn't! (err: %q)", json, err)
33 36
 		} else {
34 37
 			i := 0
... ...
@@ -48,7 +51,10 @@ func TestJSONArraysOfStrings(t *testing.T) {
48 48
 		}
49 49
 	}
50 50
 	for _, json := range invalidJSONArraysOfStrings {
51
-		if _, _, err := parseJSON(json); err != errDockerfileNotStringArray {
51
+		d := Directive{}
52
+		SetEscapeToken(DefaultEscapeToken, &d)
53
+
54
+		if _, _, err := parseJSON(json, &d); err != errDockerfileNotStringArray {
52 55
 			t.Fatalf("%q should be an invalid JSON array of strings, but wasn't!", json)
53 56
 		}
54 57
 	}
... ...
@@ -21,7 +21,7 @@ var (
21 21
 
22 22
 // ignore the current argument. This will still leave a command parsed, but
23 23
 // will not incorporate the arguments into the ast.
24
-func parseIgnore(rest string) (*Node, map[string]bool, error) {
24
+func parseIgnore(rest string, d *Directive) (*Node, map[string]bool, error) {
25 25
 	return &Node{}, nil, nil
26 26
 }
27 27
 
... ...
@@ -30,12 +30,12 @@ func parseIgnore(rest string) (*Node, map[string]bool, error) {
30 30
 //
31 31
 // ONBUILD RUN foo bar -> (onbuild (run foo bar))
32 32
 //
33
-func parseSubCommand(rest string) (*Node, map[string]bool, error) {
33
+func parseSubCommand(rest string, d *Directive) (*Node, map[string]bool, error) {
34 34
 	if rest == "" {
35 35
 		return nil, nil, nil
36 36
 	}
37 37
 
38
-	_, child, err := ParseLine(rest)
38
+	_, child, err := ParseLine(rest, d)
39 39
 	if err != nil {
40 40
 		return nil, nil, err
41 41
 	}
... ...
@@ -46,7 +46,7 @@ func parseSubCommand(rest string) (*Node, map[string]bool, error) {
46 46
 // helper to parse words (i.e space delimited or quoted strings) in a statement.
47 47
 // The quotes are preserved as part of this function and they are stripped later
48 48
 // as part of processWords().
49
-func parseWords(rest string) []string {
49
+func parseWords(rest string, d *Directive) []string {
50 50
 	const (
51 51
 		inSpaces = iota // looking for start of a word
52 52
 		inWord
... ...
@@ -96,7 +96,7 @@ func parseWords(rest string) []string {
96 96
 				blankOK = true
97 97
 				phase = inQuote
98 98
 			}
99
-			if ch == tokenEscape {
99
+			if ch == d.EscapeToken {
100 100
 				if pos+chWidth == len(rest) {
101 101
 					continue // just skip an escape token at end of line
102 102
 				}
... ...
@@ -115,7 +115,7 @@ func parseWords(rest string) []string {
115 115
 				phase = inWord
116 116
 			}
117 117
 			// The escape token is special except for ' quotes - can't escape anything for '
118
-			if ch == tokenEscape && quote != '\'' {
118
+			if ch == d.EscapeToken && quote != '\'' {
119 119
 				if pos+chWidth == len(rest) {
120 120
 					phase = inWord
121 121
 					continue // just skip the escape token at end
... ...
@@ -133,14 +133,14 @@ func parseWords(rest string) []string {
133 133
 
134 134
 // parse environment like statements. Note that this does *not* handle
135 135
 // variable interpolation, which will be handled in the evaluator.
136
-func parseNameVal(rest string, key string) (*Node, map[string]bool, error) {
136
+func parseNameVal(rest string, key string, d *Directive) (*Node, map[string]bool, error) {
137 137
 	// This is kind of tricky because we need to support the old
138 138
 	// variant:   KEY name value
139 139
 	// as well as the new one:    KEY name=value ...
140 140
 	// The trigger to know which one is being used will be whether we hit
141 141
 	// a space or = first.  space ==> old, "=" ==> new
142 142
 
143
-	words := parseWords(rest)
143
+	words := parseWords(rest, d)
144 144
 	if len(words) == 0 {
145 145
 		return nil, nil, nil
146 146
 	}
... ...
@@ -187,12 +187,12 @@ func parseNameVal(rest string, key string) (*Node, map[string]bool, error) {
187 187
 	return rootnode, nil, nil
188 188
 }
189 189
 
190
-func parseEnv(rest string) (*Node, map[string]bool, error) {
191
-	return parseNameVal(rest, "ENV")
190
+func parseEnv(rest string, d *Directive) (*Node, map[string]bool, error) {
191
+	return parseNameVal(rest, "ENV", d)
192 192
 }
193 193
 
194
-func parseLabel(rest string) (*Node, map[string]bool, error) {
195
-	return parseNameVal(rest, "LABEL")
194
+func parseLabel(rest string, d *Directive) (*Node, map[string]bool, error) {
195
+	return parseNameVal(rest, "LABEL", d)
196 196
 }
197 197
 
198 198
 // parses a statement containing one or more keyword definition(s) and/or
... ...
@@ -203,8 +203,8 @@ func parseLabel(rest string) (*Node, map[string]bool, error) {
203 203
 // In addition, a keyword definition alone is of the form `keyword` like `name1`
204 204
 // above. And the assignments `name2=` and `name3=""` are equivalent and
205 205
 // assign an empty value to the respective keywords.
206
-func parseNameOrNameVal(rest string) (*Node, map[string]bool, error) {
207
-	words := parseWords(rest)
206
+func parseNameOrNameVal(rest string, d *Directive) (*Node, map[string]bool, error) {
207
+	words := parseWords(rest, d)
208 208
 	if len(words) == 0 {
209 209
 		return nil, nil, nil
210 210
 	}
... ...
@@ -229,7 +229,7 @@ func parseNameOrNameVal(rest string) (*Node, map[string]bool, error) {
229 229
 
230 230
 // parses a whitespace-delimited set of arguments. The result is effectively a
231 231
 // linked list of string arguments.
232
-func parseStringsWhitespaceDelimited(rest string) (*Node, map[string]bool, error) {
232
+func parseStringsWhitespaceDelimited(rest string, d *Directive) (*Node, map[string]bool, error) {
233 233
 	if rest == "" {
234 234
 		return nil, nil, nil
235 235
 	}
... ...
@@ -253,7 +253,7 @@ func parseStringsWhitespaceDelimited(rest string) (*Node, map[string]bool, error
253 253
 }
254 254
 
255 255
 // parsestring just wraps the string in quotes and returns a working node.
256
-func parseString(rest string) (*Node, map[string]bool, error) {
256
+func parseString(rest string, d *Directive) (*Node, map[string]bool, error) {
257 257
 	if rest == "" {
258 258
 		return nil, nil, nil
259 259
 	}
... ...
@@ -263,7 +263,7 @@ func parseString(rest string) (*Node, map[string]bool, error) {
263 263
 }
264 264
 
265 265
 // parseJSON converts JSON arrays to an AST.
266
-func parseJSON(rest string) (*Node, map[string]bool, error) {
266
+func parseJSON(rest string, d *Directive) (*Node, map[string]bool, error) {
267 267
 	rest = strings.TrimLeftFunc(rest, unicode.IsSpace)
268 268
 	if !strings.HasPrefix(rest, "[") {
269 269
 		return nil, nil, fmt.Errorf(`Error parsing "%s" as a JSON array`, rest)
... ...
@@ -296,12 +296,12 @@ func parseJSON(rest string) (*Node, map[string]bool, error) {
296 296
 // parseMaybeJSON determines if the argument appears to be a JSON array. If
297 297
 // so, passes to parseJSON; if not, quotes the result and returns a single
298 298
 // node.
299
-func parseMaybeJSON(rest string) (*Node, map[string]bool, error) {
299
+func parseMaybeJSON(rest string, d *Directive) (*Node, map[string]bool, error) {
300 300
 	if rest == "" {
301 301
 		return nil, nil, nil
302 302
 	}
303 303
 
304
-	node, attrs, err := parseJSON(rest)
304
+	node, attrs, err := parseJSON(rest, d)
305 305
 
306 306
 	if err == nil {
307 307
 		return node, attrs, nil
... ...
@@ -318,8 +318,8 @@ func parseMaybeJSON(rest string) (*Node, map[string]bool, error) {
318 318
 // parseMaybeJSONToList determines if the argument appears to be a JSON array. If
319 319
 // so, passes to parseJSON; if not, attempts to parse it as a whitespace
320 320
 // delimited string.
321
-func parseMaybeJSONToList(rest string) (*Node, map[string]bool, error) {
322
-	node, attrs, err := parseJSON(rest)
321
+func parseMaybeJSONToList(rest string, d *Directive) (*Node, map[string]bool, error) {
322
+	node, attrs, err := parseJSON(rest, d)
323 323
 
324 324
 	if err == nil {
325 325
 		return node, attrs, nil
... ...
@@ -328,11 +328,11 @@ func parseMaybeJSONToList(rest string) (*Node, map[string]bool, error) {
328 328
 		return nil, nil, err
329 329
 	}
330 330
 
331
-	return parseStringsWhitespaceDelimited(rest)
331
+	return parseStringsWhitespaceDelimited(rest, d)
332 332
 }
333 333
 
334 334
 // The HEALTHCHECK command is like parseMaybeJSON, but has an extra type argument.
335
-func parseHealthConfig(rest string) (*Node, map[string]bool, error) {
335
+func parseHealthConfig(rest string, d *Directive) (*Node, map[string]bool, error) {
336 336
 	// Find end of first argument
337 337
 	var sep int
338 338
 	for ; sep < len(rest); sep++ {
... ...
@@ -352,7 +352,7 @@ func parseHealthConfig(rest string) (*Node, map[string]bool, error) {
352 352
 	}
353 353
 
354 354
 	typ := rest[:sep]
355
-	cmd, attrs, err := parseMaybeJSON(rest[next:])
355
+	cmd, attrs, err := parseMaybeJSON(rest[next:], d)
356 356
 	if err != nil {
357 357
 		return nil, nil, err
358 358
 	}
... ...
@@ -36,26 +36,32 @@ type Node struct {
36 36
 	EndLine    int             // the line in the original dockerfile where the node ends
37 37
 }
38 38
 
39
+// Directive is the structure used during a build run to hold the state of
40
+// parsing directives.
41
+type Directive struct {
42
+	EscapeToken           rune           // Current escape token
43
+	LineContinuationRegex *regexp.Regexp // Current line contination regex
44
+	LookingForDirectives  bool           // Whether we are currently looking for directives
45
+	EscapeSeen            bool           // Whether the escape directive has been seen
46
+}
47
+
39 48
 var (
40
-	dispatch              map[string]func(string) (*Node, map[string]bool, error)
41
-	tokenWhitespace       = regexp.MustCompile(`[\t\v\f\r ]+`)
42
-	tokenLineContinuation *regexp.Regexp
43
-	tokenEscape           rune
44
-	tokenEscapeCommand    = regexp.MustCompile(`^#[ \t]*escape[ \t]*=[ \t]*(?P<escapechar>.).*$`)
45
-	tokenComment          = regexp.MustCompile(`^#.*$`)
46
-	lookingForDirectives  bool
47
-	directiveEscapeSeen   bool
49
+	dispatch           map[string]func(string, *Directive) (*Node, map[string]bool, error)
50
+	tokenWhitespace    = regexp.MustCompile(`[\t\v\f\r ]+`)
51
+	tokenEscapeCommand = regexp.MustCompile(`^#[ \t]*escape[ \t]*=[ \t]*(?P<escapechar>.).*$`)
52
+	tokenComment       = regexp.MustCompile(`^#.*$`)
48 53
 )
49 54
 
50
-const defaultTokenEscape = "\\"
55
+// DefaultEscapeToken is the default escape token
56
+const DefaultEscapeToken = "\\"
51 57
 
52
-// setTokenEscape sets the default token for escaping characters in a Dockerfile.
53
-func setTokenEscape(s string) error {
58
+// SetEscapeToken sets the default token for escaping characters in a Dockerfile.
59
+func SetEscapeToken(s string, d *Directive) error {
54 60
 	if s != "`" && s != "\\" {
55 61
 		return fmt.Errorf("invalid ESCAPE '%s'. Must be ` or \\", s)
56 62
 	}
57
-	tokenEscape = rune(s[0])
58
-	tokenLineContinuation = regexp.MustCompile(`\` + s + `[ \t]*$`)
63
+	d.EscapeToken = rune(s[0])
64
+	d.LineContinuationRegex = regexp.MustCompile(`\` + s + `[ \t]*$`)
59 65
 	return nil
60 66
 }
61 67
 
... ...
@@ -66,7 +72,7 @@ func init() {
66 66
 	// reformulating the arguments according to the rules in the parser
67 67
 	// functions. Errors are propagated up by Parse() and the resulting AST can
68 68
 	// be incorporated directly into the existing AST as a next.
69
-	dispatch = map[string]func(string) (*Node, map[string]bool, error){
69
+	dispatch = map[string]func(string, *Directive) (*Node, map[string]bool, error){
70 70
 		command.Add:         parseMaybeJSONToList,
71 71
 		command.Arg:         parseNameOrNameVal,
72 72
 		command.Cmd:         parseMaybeJSON,
... ...
@@ -89,36 +95,35 @@ func init() {
89 89
 }
90 90
 
91 91
 // ParseLine parse a line and return the remainder.
92
-func ParseLine(line string) (string, *Node, error) {
93
-
92
+func ParseLine(line string, d *Directive) (string, *Node, error) {
94 93
 	// Handle the parser directive '# escape=<char>. Parser directives must precede
95 94
 	// any builder instruction or other comments, and cannot be repeated.
96
-	if lookingForDirectives {
95
+	if d.LookingForDirectives {
97 96
 		tecMatch := tokenEscapeCommand.FindStringSubmatch(strings.ToLower(line))
98 97
 		if len(tecMatch) > 0 {
99
-			if directiveEscapeSeen == true {
98
+			if d.EscapeSeen == true {
100 99
 				return "", nil, fmt.Errorf("only one escape parser directive can be used")
101 100
 			}
102 101
 			for i, n := range tokenEscapeCommand.SubexpNames() {
103 102
 				if n == "escapechar" {
104
-					if err := setTokenEscape(tecMatch[i]); err != nil {
103
+					if err := SetEscapeToken(tecMatch[i], d); err != nil {
105 104
 						return "", nil, err
106 105
 					}
107
-					directiveEscapeSeen = true
106
+					d.EscapeSeen = true
108 107
 					return "", nil, nil
109 108
 				}
110 109
 			}
111 110
 		}
112 111
 	}
113 112
 
114
-	lookingForDirectives = false
113
+	d.LookingForDirectives = false
115 114
 
116 115
 	if line = stripComments(line); line == "" {
117 116
 		return "", nil, nil
118 117
 	}
119 118
 
120
-	if tokenLineContinuation.MatchString(line) {
121
-		line = tokenLineContinuation.ReplaceAllString(line, "")
119
+	if d.LineContinuationRegex.MatchString(line) {
120
+		line = d.LineContinuationRegex.ReplaceAllString(line, "")
122 121
 		return line, nil, nil
123 122
 	}
124 123
 
... ...
@@ -130,7 +135,7 @@ func ParseLine(line string) (string, *Node, error) {
130 130
 	node := &Node{}
131 131
 	node.Value = cmd
132 132
 
133
-	sexp, attrs, err := fullDispatch(cmd, args)
133
+	sexp, attrs, err := fullDispatch(cmd, args, d)
134 134
 	if err != nil {
135 135
 		return "", nil, err
136 136
 	}
... ...
@@ -145,10 +150,7 @@ func ParseLine(line string) (string, *Node, error) {
145 145
 
146 146
 // Parse is the main parse routine.
147 147
 // It handles an io.ReadWriteCloser and returns the root of the AST.
148
-func Parse(rwc io.Reader) (*Node, error) {
149
-	directiveEscapeSeen = false
150
-	lookingForDirectives = true
151
-	setTokenEscape(defaultTokenEscape) // Assume the default token for escape
148
+func Parse(rwc io.Reader, d *Directive) (*Node, error) {
152 149
 	currentLine := 0
153 150
 	root := &Node{}
154 151
 	root.StartLine = -1
... ...
@@ -163,7 +165,7 @@ func Parse(rwc io.Reader) (*Node, error) {
163 163
 		}
164 164
 		scannedLine := strings.TrimLeftFunc(string(scannedBytes), unicode.IsSpace)
165 165
 		currentLine++
166
-		line, child, err := ParseLine(scannedLine)
166
+		line, child, err := ParseLine(scannedLine, d)
167 167
 		if err != nil {
168 168
 			return nil, err
169 169
 		}
... ...
@@ -178,7 +180,7 @@ func Parse(rwc io.Reader) (*Node, error) {
178 178
 					continue
179 179
 				}
180 180
 
181
-				line, child, err = ParseLine(line + newline)
181
+				line, child, err = ParseLine(line+newline, d)
182 182
 				if err != nil {
183 183
 					return nil, err
184 184
 				}
... ...
@@ -188,7 +190,7 @@ func Parse(rwc io.Reader) (*Node, error) {
188 188
 				}
189 189
 			}
190 190
 			if child == nil && line != "" {
191
-				_, child, err = ParseLine(line)
191
+				_, child, err = ParseLine(line, d)
192 192
 				if err != nil {
193 193
 					return nil, err
194 194
 				}
... ...
@@ -39,7 +39,9 @@ func TestTestNegative(t *testing.T) {
39 39
 			t.Fatalf("Dockerfile missing for %s: %v", dir, err)
40 40
 		}
41 41
 
42
-		_, err = Parse(df)
42
+		d := Directive{LookingForDirectives: true}
43
+		SetEscapeToken(DefaultEscapeToken, &d)
44
+		_, err = Parse(df, &d)
43 45
 		if err == nil {
44 46
 			t.Fatalf("No error parsing broken dockerfile for %s", dir)
45 47
 		}
... ...
@@ -59,7 +61,9 @@ func TestTestData(t *testing.T) {
59 59
 		}
60 60
 		defer df.Close()
61 61
 
62
-		ast, err := Parse(df)
62
+		d := Directive{LookingForDirectives: true}
63
+		SetEscapeToken(DefaultEscapeToken, &d)
64
+		ast, err := Parse(df, &d)
63 65
 		if err != nil {
64 66
 			t.Fatalf("Error parsing %s's dockerfile: %v", dir, err)
65 67
 		}
... ...
@@ -119,13 +123,15 @@ func TestParseWords(t *testing.T) {
119 119
 	}
120 120
 
121 121
 	for _, test := range tests {
122
-		words := parseWords(test["input"][0])
122
+		d := Directive{LookingForDirectives: true}
123
+		SetEscapeToken(DefaultEscapeToken, &d)
124
+		words := parseWords(test["input"][0], &d)
123 125
 		if len(words) != len(test["expect"]) {
124
-			t.Fatalf("length check failed. input: %v, expect: %v, output: %v", test["input"][0], test["expect"], words)
126
+			t.Fatalf("length check failed. input: %v, expect: %q, output: %q", test["input"][0], test["expect"], words)
125 127
 		}
126 128
 		for i, word := range words {
127 129
 			if word != test["expect"][i] {
128
-				t.Fatalf("word check failed for word: %q. input: %v, expect: %v, output: %v", word, test["input"][0], test["expect"], words)
130
+				t.Fatalf("word check failed for word: %q. input: %q, expect: %q, output: %q", word, test["input"][0], test["expect"], words)
129 131
 			}
130 132
 		}
131 133
 	}
... ...
@@ -138,7 +144,9 @@ func TestLineInformation(t *testing.T) {
138 138
 	}
139 139
 	defer df.Close()
140 140
 
141
-	ast, err := Parse(df)
141
+	d := Directive{LookingForDirectives: true}
142
+	SetEscapeToken(DefaultEscapeToken, &d)
143
+	ast, err := Parse(df, &d)
142 144
 	if err != nil {
143 145
 		t.Fatalf("Error parsing dockerfile %s: %v", testFileLineInfo, err)
144 146
 	}
... ...
@@ -36,7 +36,7 @@ func (node *Node) Dump() string {
36 36
 
37 37
 // performs the dispatch based on the two primal strings, cmd and args. Please
38 38
 // look at the dispatch table in parser.go to see how these dispatchers work.
39
-func fullDispatch(cmd, args string) (*Node, map[string]bool, error) {
39
+func fullDispatch(cmd, args string, d *Directive) (*Node, map[string]bool, error) {
40 40
 	fn := dispatch[cmd]
41 41
 
42 42
 	// Ignore invalid Dockerfile instructions
... ...
@@ -44,7 +44,7 @@ func fullDispatch(cmd, args string) (*Node, map[string]bool, error) {
44 44
 		fn = parseIgnore
45 45
 	}
46 46
 
47
-	sexp, attrs, err := fn(args)
47
+	sexp, attrs, err := fn(args, d)
48 48
 	if err != nil {
49 49
 		return nil, nil, err
50 50
 	}
... ...
@@ -1741,6 +1741,7 @@ _docker_service_update() {
1741 1741
 
1742 1742
 	if [ "$subcommand" = "create" ] ; then
1743 1743
 		options_with_args="$options_with_args
1744
+			--container-label
1744 1745
 			--mode
1745 1746
 		"
1746 1747
 
... ...
@@ -1754,6 +1755,8 @@ _docker_service_update() {
1754 1754
 	if [ "$subcommand" = "update" ] ; then
1755 1755
 		options_with_args="$options_with_args
1756 1756
 			--arg
1757
+			--container-label-add
1758
+			--container-label-rm
1757 1759
 			--image
1758 1760
 		"
1759 1761
 
... ...
@@ -1814,7 +1817,6 @@ _docker_service_update() {
1814 1814
 _docker_swarm() {
1815 1815
 	local subcommands="
1816 1816
 		init
1817
-		inspect
1818 1817
 		join
1819 1818
 		join-token
1820 1819
 		leave
... ...
@@ -1855,20 +1857,6 @@ _docker_swarm_init() {
1855 1855
 	esac
1856 1856
 }
1857 1857
 
1858
-_docker_swarm_inspect() {
1859
-	case "$prev" in
1860
-		--format|-f)
1861
-			return
1862
-			;;
1863
-	esac
1864
-
1865
-	case "$cur" in
1866
-		-*)
1867
-			COMPREPLY=( $( compgen -W "--format -f --help" -- "$cur" ) )
1868
-			;;
1869
-	esac
1870
-}
1871
-
1872 1858
 _docker_swarm_join() {
1873 1859
 	case "$prev" in
1874 1860
 		--token)
... ...
@@ -2,7 +2,6 @@
2 2
 Description=Docker Application Container Engine
3 3
 Documentation=https://docs.docker.com
4 4
 After=network.target
5
-Requires=docker.socket
6 5
 
7 6
 [Service]
8 7
 Type=notify
... ...
@@ -199,6 +199,7 @@ func DriverTestDiffApply(t testing.TB, fileCount int, drivername string, driverO
199 199
 	upper := stringid.GenerateRandomID()
200 200
 	deleteFile := "file-remove.txt"
201 201
 	deleteFileContent := []byte("This file should get removed in upper!")
202
+	deleteDir := "var/lib"
202 203
 
203 204
 	if err := driver.Create(base, "", "", nil); err != nil {
204 205
 		t.Fatal(err)
... ...
@@ -212,6 +213,10 @@ func DriverTestDiffApply(t testing.TB, fileCount int, drivername string, driverO
212 212
 		t.Fatal(err)
213 213
 	}
214 214
 
215
+	if err := addDirectory(driver, base, deleteDir); err != nil {
216
+		t.Fatal(err)
217
+	}
218
+
215 219
 	if err := driver.Create(upper, base, "", nil); err != nil {
216 220
 		t.Fatal(err)
217 221
 	}
... ...
@@ -220,7 +225,7 @@ func DriverTestDiffApply(t testing.TB, fileCount int, drivername string, driverO
220 220
 		t.Fatal(err)
221 221
 	}
222 222
 
223
-	if err := removeFile(driver, upper, deleteFile); err != nil {
223
+	if err := removeAll(driver, upper, deleteFile, deleteDir); err != nil {
224 224
 		t.Fatal(err)
225 225
 	}
226 226
 
... ...
@@ -271,6 +276,10 @@ func DriverTestDiffApply(t testing.TB, fileCount int, drivername string, driverO
271 271
 	if err := checkFileRemoved(driver, diff, deleteFile); err != nil {
272 272
 		t.Fatal(err)
273 273
 	}
274
+
275
+	if err := checkFileRemoved(driver, diff, deleteDir); err != nil {
276
+		t.Fatal(err)
277
+	}
274 278
 }
275 279
 
276 280
 // DriverTestChanges tests computed changes on a layer matches changes made
... ...
@@ -78,14 +78,29 @@ func addFile(drv graphdriver.Driver, layer, filename string, content []byte) err
78 78
 	return ioutil.WriteFile(path.Join(root, filename), content, 0755)
79 79
 }
80 80
 
81
-func removeFile(drv graphdriver.Driver, layer, filename string) error {
81
+func addDirectory(drv graphdriver.Driver, layer, dir string) error {
82 82
 	root, err := drv.Get(layer, "")
83 83
 	if err != nil {
84 84
 		return err
85 85
 	}
86 86
 	defer drv.Put(layer)
87 87
 
88
-	return os.Remove(path.Join(root, filename))
88
+	return os.MkdirAll(path.Join(root, dir), 0755)
89
+}
90
+
91
+func removeAll(drv graphdriver.Driver, layer string, names ...string) error {
92
+	root, err := drv.Get(layer, "")
93
+	if err != nil {
94
+		return err
95
+	}
96
+	defer drv.Put(layer)
97
+
98
+	for _, filename := range names {
99
+		if err := os.RemoveAll(path.Join(root, filename)); err != nil {
100
+			return err
101
+		}
102
+	}
103
+	return nil
89 104
 }
90 105
 
91 106
 func checkFileRemoved(drv graphdriver.Driver, layer, filename string) error {
... ...
@@ -29,7 +29,6 @@ update delay:
29 29
       --replicas 3 \
30 30
       --name redis \
31 31
       --update-delay 10s \
32
-      --update-parallelism 1 \
33 32
       redis:3.0.6
34 33
 
35 34
     0u6a4s31ybk7yw2wyvtikmu50
... ...
@@ -37,18 +36,21 @@ update delay:
37 37
 
38 38
     You configure the rolling update policy at service deployment time.
39 39
 
40
-    The `--update-parallelism` flag configures the number of service tasks that
41
-    the scheduler can update simultaneously. When updates to individual tasks
42
-    return a state of `RUNNING` or `FAILED`, the scheduler schedules another
43
-    task to update until all tasks are updated.
44
-
45 40
     The `--update-delay` flag configures the time delay between updates to a
46
-    service task or sets of tasks.
41
+    service task or sets of tasks. You can describe the time `T` as a
42
+    combination of the number of seconds `Ts`, minutes `Tm`, or hours `Th`. So
43
+    `10m30s` indicates a 10 minute 30 second delay.
47 44
 
48
-    You can describe the time `T` as a combination of the number of seconds
49
-    `Ts`, minutes `Tm`, or hours `Th`. So `10m30s` indicates a 10 minute 30
50
-    second delay.
45
+    By default the scheduler updates 1 task at a time. You can pass the
46
+    `--update-parallelism` flag to configure the maximum number of service tasks
47
+    that the scheduler updates simultaneously.
51 48
 
49
+    By default, when an update to an individual task returns a state of
50
+    `RUNNING`, the scheduler schedules another task to update until all tasks
51
+    are updated. If, at any time during an update a task returns `FAILED`, the
52
+    scheduler pauses the update. You can control the behavior using the
53
+    `--update-failure-action` flag for `docker service create` or
54
+    `docker service update`.
52 55
 
53 56
 3. Inspect the `redis` service:
54 57
 
... ...
@@ -77,13 +79,15 @@ applies the update to nodes according to the `UpdateConfig` policy:
77 77
     redis
78 78
     ```
79 79
 
80
-    The scheduler applies rolling updates as follows:
80
+    The scheduler applies rolling updates as follows by default:
81 81
 
82
-    * Stop the initial number of tasks according to `--update-parallelism`.
83
-    * Schedule updates for the stopped tasks.
84
-    * Start the containers for the updated tasks.
85
-    * After an update to a task completes, wait for the specified delay
86
-    period before stopping the next task.
82
+    * Stop the first task.
83
+    * Schedule update for the stopped task.
84
+    * Start the container for the updated task.
85
+    * If the update to a task returns `RUNNING`, wait for the
86
+    specified delay period then stop the next task.
87
+    * If, at any time during the update, a task returns `FAILED`, pause the
88
+    update.
87 89
 
88 90
 5. Run `docker service inspect --pretty redis` to see the new image in the
89 91
 desired state:
... ...
@@ -65,7 +65,7 @@ clone git github.com/RackSec/srslog 259aed10dfa74ea2961eddd1d9847619f6e98837
65 65
 clone git github.com/imdario/mergo 0.2.1
66 66
 
67 67
 #get libnetwork packages
68
-clone git github.com/docker/libnetwork c7dc6dc476a5f00f9b28efebe591347dd64264fc
68
+clone git github.com/docker/libnetwork 443b7be96fdf0ed8f65ec92953aa8df4f9a725dc
69 69
 clone git github.com/docker/go-events afb2b9f2c23f33ada1a22b03651775fdc65a5089
70 70
 clone git github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
71 71
 clone git github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
... ...
@@ -563,6 +563,8 @@ func (clnt *client) Restore(containerID string, options ...CreateOption) error {
563 563
 		clnt.remote.Lock()
564 564
 		return nil
565 565
 	}
566
+	// relock because of the defer
567
+	clnt.remote.Lock()
566 568
 
567 569
 	clnt.deleteContainer(containerID)
568 570
 
... ...
@@ -23,7 +23,8 @@ func (overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi os
23 23
 	// convert whiteouts to AUFS format
24 24
 	if fi.Mode()&os.ModeCharDevice != 0 && hdr.Devmajor == 0 && hdr.Devminor == 0 {
25 25
 		// we just rename the file and make it normal
26
-		hdr.Name = WhiteoutPrefix + hdr.Name
26
+		dir, filename := filepath.Split(hdr.Name)
27
+		hdr.Name = filepath.Join(dir, WhiteoutPrefix+filename)
27 28
 		hdr.Mode = 0600
28 29
 		hdr.Typeflag = tar.TypeReg
29 30
 		hdr.Size = 0
... ...
@@ -726,6 +726,12 @@ func (sb *sandbox) restoreOslSandbox() error {
726 726
 		joinInfo := ep.joinInfo
727 727
 		i := ep.iface
728 728
 		ep.Unlock()
729
+
730
+		if i == nil {
731
+			log.Errorf("error restoring endpoint %s for container %s", ep.Name(), sb.ContainerID())
732
+			continue
733
+		}
734
+
729 735
 		ifaceOptions = append(ifaceOptions, sb.osSbox.InterfaceOptions().Address(i.addr), sb.osSbox.InterfaceOptions().Routes(i.routes))
730 736
 		if i.addrv6 != nil && i.addrv6.IP.To16() != nil {
731 737
 			ifaceOptions = append(ifaceOptions, sb.osSbox.InterfaceOptions().AddressIPv6(i.addrv6))
... ...
@@ -245,6 +245,10 @@ func (c *controller) sandboxCleanup(activeSandboxes map[string]interface{}) {
245 245
 					ep = &endpoint{id: eps.Eid, network: n, sandboxID: sbs.ID}
246 246
 				}
247 247
 			}
248
+			if _, ok := activeSandboxes[sb.ID()]; ok && err != nil {
249
+				logrus.Errorf("failed to restore endpoint %s in %s for container %s due to %v", eps.Eid, eps.Nid, sb.ContainerID(), err)
250
+				continue
251
+			}
248 252
 			heap.Push(&sb.endpoints, ep)
249 253
 		}
250 254