Browse code

bump API version

Docker-DCO-1.1-Signed-off-by: Victor Vieux <victor.vieux@docker.com> (github: vieux)

Victor Vieux authored on 2014/02/15 07:53:53
Showing 7 changed files
... ...
@@ -2038,7 +2038,7 @@ func (cli *DockerCli) call(method, path string, data interface{}, passAuthInfo b
2038 2038
 	re := regexp.MustCompile("/+")
2039 2039
 	path = re.ReplaceAllString(path, "/")
2040 2040
 
2041
-	req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), params)
2041
+	req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", APIVERSION, path), params)
2042 2042
 	if err != nil {
2043 2043
 		return nil, -1, err
2044 2044
 	}
... ...
@@ -2115,7 +2115,7 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, h
2115 2115
 	re := regexp.MustCompile("/+")
2116 2116
 	path = re.ReplaceAllString(path, "/")
2117 2117
 
2118
-	req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), in)
2118
+	req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", APIVERSION, path), in)
2119 2119
 	if err != nil {
2120 2120
 		return err
2121 2121
 	}
... ...
@@ -2179,7 +2179,7 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea
2179 2179
 	re := regexp.MustCompile("/+")
2180 2180
 	path = re.ReplaceAllString(path, "/")
2181 2181
 
2182
-	req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), nil)
2182
+	req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", APIVERSION, path), nil)
2183 2183
 	if err != nil {
2184 2184
 		return err
2185 2185
 	}
... ...
@@ -9,7 +9,7 @@ import (
9 9
 )
10 10
 
11 11
 const (
12
-	APIVERSION        = 1.9
12
+	APIVERSION        = "1.10"
13 13
 	DEFAULTHTTPHOST   = "127.0.0.1"
14 14
 	DEFAULTUNIXSOCKET = "/var/run/docker.sock"
15 15
 )
... ...
@@ -32,7 +32,7 @@ var (
32 32
 	activationLock chan struct{}
33 33
 )
34 34
 
35
-type HttpApiFunc func(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error
35
+type HttpApiFunc func(eng *engine.Engine, version string, w http.ResponseWriter, r *http.Request, vars map[string]string) error
36 36
 
37 37
 func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) {
38 38
 	conn, _, err := w.(http.Hijacker).Hijack()
... ...
@@ -113,7 +113,7 @@ func getBoolParam(value string) (bool, error) {
113 113
 	return ret, nil
114 114
 }
115 115
 
116
-func postAuth(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
116
+func postAuth(eng *engine.Engine, version string, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
117 117
 	var (
118 118
 		authConfig, err = ioutil.ReadAll(r.Body)
119 119
 		job             = eng.Job("auth")
... ...
@@ -136,13 +136,13 @@ func postAuth(eng *engine.Engine, version float64, w http.ResponseWriter, r *htt
136 136
 	return nil
137 137
 }
138 138
 
139
-func getVersion(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
139
+func getVersion(eng *engine.Engine, version string, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
140 140
 	w.Header().Set("Content-Type", "application/json")
141 141
 	eng.ServeHTTP(w, r)
142 142
 	return nil
143 143
 }
144 144
 
145
-func postContainersKill(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
145
+func postContainersKill(eng *engine.Engine, version string, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
146 146
 	if vars == nil {
147 147
 		return fmt.Errorf("Missing parameter")
148 148
 	}
... ...
@@ -160,7 +160,7 @@ func postContainersKill(eng *engine.Engine, version float64, w http.ResponseWrit
160 160
 	return nil
161 161
 }
162 162
 
163
-func getContainersExport(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
163
+func getContainersExport(eng *engine.Engine, version string, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
164 164
 	if vars == nil {
165 165
 		return fmt.Errorf("Missing parameter")
166 166
 	}
... ...
@@ -172,7 +172,7 @@ func getContainersExport(eng *engine.Engine, version float64, w http.ResponseWri
172 172
 	return nil
173 173
 }
174 174
 
175
-func getImagesJSON(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
175
+func getImagesJSON(eng *engine.Engine, version string, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
176 176
 	if err := parseForm(r); err != nil {
177 177
 		return err
178 178
 	}
... ...
@@ -186,7 +186,7 @@ func getImagesJSON(eng *engine.Engine, version float64, w http.ResponseWriter, r
186 186
 	job.Setenv("filter", r.Form.Get("filter"))
187 187
 	job.Setenv("all", r.Form.Get("all"))
188 188
 
189
-	if version >= 1.7 {
189
+	if utils.CompareVersion(version, "1.7") >= 0 {
190 190
 		streamJSON(job, w, false)
191 191
 	} else if outs, err = job.Stdout.AddListTable(); err != nil {
192 192
 		return err
... ...
@@ -196,7 +196,7 @@ func getImagesJSON(eng *engine.Engine, version float64, w http.ResponseWriter, r
196 196
 		return err
197 197
 	}
198 198
 
199
-	if version < 1.7 && outs != nil { // Convert to legacy format
199
+	if utils.CompareVersion(version, "1.7") < 0 && outs != nil { // Convert to legacy format
200 200
 		outsLegacy := engine.NewTable("Created", 0)
201 201
 		for _, out := range outs.Data {
202 202
 			for _, repoTag := range out.GetList("RepoTags") {
... ...
@@ -219,8 +219,8 @@ func getImagesJSON(eng *engine.Engine, version float64, w http.ResponseWriter, r
219 219
 	return nil
220 220
 }
221 221
 
222
-func getImagesViz(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
223
-	if version > 1.6 {
222
+func getImagesViz(eng *engine.Engine, version string, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
223
+	if utils.CompareVersion(version, "1.6") > 0 {
224 224
 		w.WriteHeader(http.StatusNotFound)
225 225
 		return fmt.Errorf("This is now implemented in the client.")
226 226
 	}
... ...
@@ -228,13 +228,13 @@ func getImagesViz(eng *engine.Engine, version float64, w http.ResponseWriter, r
228 228
 	return nil
229 229
 }
230 230
 
231
-func getInfo(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
231
+func getInfo(eng *engine.Engine, version string, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
232 232
 	w.Header().Set("Content-Type", "application/json")
233 233
 	eng.ServeHTTP(w, r)
234 234
 	return nil
235 235
 }
236 236
 
237
-func getEvents(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
237
+func getEvents(eng *engine.Engine, version string, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
238 238
 	if err := parseForm(r); err != nil {
239 239
 		return err
240 240
 	}
... ...
@@ -245,7 +245,7 @@ func getEvents(eng *engine.Engine, version float64, w http.ResponseWriter, r *ht
245 245
 	return job.Run()
246 246
 }
247 247
 
248
-func getImagesHistory(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
248
+func getImagesHistory(eng *engine.Engine, version string, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
249 249
 	if vars == nil {
250 250
 		return fmt.Errorf("Missing parameter")
251 251
 	}
... ...
@@ -259,7 +259,7 @@ func getImagesHistory(eng *engine.Engine, version float64, w http.ResponseWriter
259 259
 	return nil
260 260
 }
261 261
 
262
-func getContainersChanges(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
262
+func getContainersChanges(eng *engine.Engine, version string, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
263 263
 	if vars == nil {
264 264
 		return fmt.Errorf("Missing parameter")
265 265
 	}
... ...
@@ -269,8 +269,8 @@ func getContainersChanges(eng *engine.Engine, version float64, w http.ResponseWr
269 269
 	return job.Run()
270 270
 }
271 271
 
272
-func getContainersTop(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
273
-	if version < 1.4 {
272
+func getContainersTop(eng *engine.Engine, version string, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
273
+	if utils.CompareVersion(version, "1.4") < 0 {
274 274
 		return fmt.Errorf("top was improved a lot since 1.3, Please upgrade your docker client.")
275 275
 	}
276 276
 	if vars == nil {
... ...
@@ -285,7 +285,7 @@ func getContainersTop(eng *engine.Engine, version float64, w http.ResponseWriter
285 285
 	return job.Run()
286 286
 }
287 287
 
288
-func getContainersJSON(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
288
+func getContainersJSON(eng *engine.Engine, version string, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
289 289
 	if err := parseForm(r); err != nil {
290 290
 		return err
291 291
 	}
... ...
@@ -301,7 +301,7 @@ func getContainersJSON(eng *engine.Engine, version float64, w http.ResponseWrite
301 301
 	job.Setenv("before", r.Form.Get("before"))
302 302
 	job.Setenv("limit", r.Form.Get("limit"))
303 303
 
304
-	if version >= 1.5 {
304
+	if utils.CompareVersion(version, "1.5") >= 0 {
305 305
 		streamJSON(job, w, false)
306 306
 	} else if outs, err = job.Stdout.AddTable(); err != nil {
307 307
 		return err
... ...
@@ -309,7 +309,7 @@ func getContainersJSON(eng *engine.Engine, version float64, w http.ResponseWrite
309 309
 	if err = job.Run(); err != nil {
310 310
 		return err
311 311
 	}
312
-	if version < 1.5 { // Convert to legacy format
312
+	if utils.CompareVersion(version, "1.5") < 0 { // Convert to legacy format
313 313
 		for _, out := range outs.Data {
314 314
 			ports := engine.NewTable("", 0)
315 315
 			ports.ReadListFrom([]byte(out.Get("Ports")))
... ...
@@ -323,7 +323,7 @@ func getContainersJSON(eng *engine.Engine, version float64, w http.ResponseWrite
323 323
 	return nil
324 324
 }
325 325
 
326
-func postImagesTag(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
326
+func postImagesTag(eng *engine.Engine, version string, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
327 327
 	if err := parseForm(r); err != nil {
328 328
 		return err
329 329
 	}
... ...
@@ -340,7 +340,7 @@ func postImagesTag(eng *engine.Engine, version float64, w http.ResponseWriter, r
340 340
 	return nil
341 341
 }
342 342
 
343
-func postCommit(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
343
+func postCommit(eng *engine.Engine, version string, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
344 344
 	if err := parseForm(r); err != nil {
345 345
 		return err
346 346
 	}
... ...
@@ -369,7 +369,7 @@ func postCommit(eng *engine.Engine, version float64, w http.ResponseWriter, r *h
369 369
 }
370 370
 
371 371
 // Creates an image from Pull or from Import
372
-func postImagesCreate(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
372
+func postImagesCreate(eng *engine.Engine, version string, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
373 373
 	if err := parseForm(r); err != nil {
374 374
 		return err
375 375
 	}
... ...
@@ -389,9 +389,6 @@ func postImagesCreate(eng *engine.Engine, version float64, w http.ResponseWriter
389 389
 			authConfig = &auth.AuthConfig{}
390 390
 		}
391 391
 	}
392
-	if version > 1.0 {
393
-		w.Header().Set("Content-Type", "application/json")
394
-	}
395 392
 	if image != "" { //pull
396 393
 		metaHeaders := map[string][]string{}
397 394
 		for k, v := range r.Header {
... ...
@@ -400,7 +397,7 @@ func postImagesCreate(eng *engine.Engine, version float64, w http.ResponseWriter
400 400
 			}
401 401
 		}
402 402
 		job = eng.Job("pull", r.Form.Get("fromImage"), tag)
403
-		job.SetenvBool("parallel", version > 1.3)
403
+		job.SetenvBool("parallel", utils.CompareVersion(version, "1.3") > 0)
404 404
 		job.SetenvJson("metaHeaders", metaHeaders)
405 405
 		job.SetenvJson("authConfig", authConfig)
406 406
 	} else { //import
... ...
@@ -408,7 +405,7 @@ func postImagesCreate(eng *engine.Engine, version float64, w http.ResponseWriter
408 408
 		job.Stdin.Add(r.Body)
409 409
 	}
410 410
 
411
-	if version > 1.0 {
411
+	if utils.CompareVersion(version, "1.0") > 0 {
412 412
 		job.SetenvBool("json", true)
413 413
 		streamJSON(job, w, true)
414 414
 	} else {
... ...
@@ -418,14 +415,14 @@ func postImagesCreate(eng *engine.Engine, version float64, w http.ResponseWriter
418 418
 		if !job.Stdout.Used() {
419 419
 			return err
420 420
 		}
421
-		sf := utils.NewStreamFormatter(version > 1.0)
421
+		sf := utils.NewStreamFormatter(utils.CompareVersion(version, "1.0") > 0)
422 422
 		w.Write(sf.FormatError(err))
423 423
 	}
424 424
 
425 425
 	return nil
426 426
 }
427 427
 
428
-func getImagesSearch(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
428
+func getImagesSearch(eng *engine.Engine, version string, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
429 429
 	if err := parseForm(r); err != nil {
430 430
 		return err
431 431
 	}
... ...
@@ -457,19 +454,15 @@ func getImagesSearch(eng *engine.Engine, version float64, w http.ResponseWriter,
457 457
 	return job.Run()
458 458
 }
459 459
 
460
-func postImagesInsert(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
460
+func postImagesInsert(eng *engine.Engine, version string, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
461 461
 	if err := parseForm(r); err != nil {
462 462
 		return err
463 463
 	}
464 464
 	if vars == nil {
465 465
 		return fmt.Errorf("Missing parameter")
466 466
 	}
467
-	if version > 1.0 {
468
-		w.Header().Set("Content-Type", "application/json")
469
-	}
470
-
471 467
 	job := eng.Job("insert", vars["name"], r.Form.Get("url"), r.Form.Get("path"))
472
-	if version > 1.0 {
468
+	if utils.CompareVersion(version, "1.0") > 0 {
473 469
 		job.SetenvBool("json", true)
474 470
 		streamJSON(job, w, false)
475 471
 	} else {
... ...
@@ -479,14 +472,14 @@ func postImagesInsert(eng *engine.Engine, version float64, w http.ResponseWriter
479 479
 		if !job.Stdout.Used() {
480 480
 			return err
481 481
 		}
482
-		sf := utils.NewStreamFormatter(version > 1.0)
482
+		sf := utils.NewStreamFormatter(utils.CompareVersion(version, "1.0") > 0)
483 483
 		w.Write(sf.FormatError(err))
484 484
 	}
485 485
 
486 486
 	return nil
487 487
 }
488 488
 
489
-func postImagesPush(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
489
+func postImagesPush(eng *engine.Engine, version string, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
490 490
 	if vars == nil {
491 491
 		return fmt.Errorf("Missing parameter")
492 492
 	}
... ...
@@ -517,13 +510,10 @@ func postImagesPush(eng *engine.Engine, version float64, w http.ResponseWriter,
517 517
 		}
518 518
 	}
519 519
 
520
-	if version > 1.0 {
521
-		w.Header().Set("Content-Type", "application/json")
522
-	}
523 520
 	job := eng.Job("push", vars["name"])
524 521
 	job.SetenvJson("metaHeaders", metaHeaders)
525 522
 	job.SetenvJson("authConfig", authConfig)
526
-	if version > 1.0 {
523
+	if utils.CompareVersion(version, "1.0") > 0 {
527 524
 		job.SetenvBool("json", true)
528 525
 		streamJSON(job, w, true)
529 526
 	} else {
... ...
@@ -534,17 +524,17 @@ func postImagesPush(eng *engine.Engine, version float64, w http.ResponseWriter,
534 534
 		if !job.Stdout.Used() {
535 535
 			return err
536 536
 		}
537
-		sf := utils.NewStreamFormatter(version > 1.0)
537
+		sf := utils.NewStreamFormatter(utils.CompareVersion(version, "1.0") > 0)
538 538
 		w.Write(sf.FormatError(err))
539 539
 	}
540 540
 	return nil
541 541
 }
542 542
 
543
-func getImagesGet(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
543
+func getImagesGet(eng *engine.Engine, version string, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
544 544
 	if vars == nil {
545 545
 		return fmt.Errorf("Missing parameter")
546 546
 	}
547
-	if version > 1.0 {
547
+	if utils.CompareVersion(version, "1.0") > 0 {
548 548
 		w.Header().Set("Content-Type", "application/x-tar")
549 549
 	}
550 550
 	job := eng.Job("image_export", vars["name"])
... ...
@@ -552,13 +542,13 @@ func getImagesGet(eng *engine.Engine, version float64, w http.ResponseWriter, r
552 552
 	return job.Run()
553 553
 }
554 554
 
555
-func postImagesLoad(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
555
+func postImagesLoad(eng *engine.Engine, version string, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
556 556
 	job := eng.Job("load")
557 557
 	job.Stdin.Add(r.Body)
558 558
 	return job.Run()
559 559
 }
560 560
 
561
-func postContainersCreate(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
561
+func postContainersCreate(eng *engine.Engine, version string, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
562 562
 	if err := parseForm(r); err != nil {
563 563
 		return nil
564 564
 	}
... ...
@@ -589,7 +579,7 @@ func postContainersCreate(eng *engine.Engine, version float64, w http.ResponseWr
589 589
 	return writeJSON(w, http.StatusCreated, out)
590 590
 }
591 591
 
592
-func postContainersRestart(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
592
+func postContainersRestart(eng *engine.Engine, version string, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
593 593
 	if err := parseForm(r); err != nil {
594 594
 		return err
595 595
 	}
... ...
@@ -605,7 +595,7 @@ func postContainersRestart(eng *engine.Engine, version float64, w http.ResponseW
605 605
 	return nil
606 606
 }
607 607
 
608
-func deleteContainers(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
608
+func deleteContainers(eng *engine.Engine, version string, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
609 609
 	if err := parseForm(r); err != nil {
610 610
 		return err
611 611
 	}
... ...
@@ -622,7 +612,7 @@ func deleteContainers(eng *engine.Engine, version float64, w http.ResponseWriter
622 622
 	return nil
623 623
 }
624 624
 
625
-func deleteImages(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
625
+func deleteImages(eng *engine.Engine, version string, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
626 626
 	if err := parseForm(r); err != nil {
627 627
 		return err
628 628
 	}
... ...
@@ -636,7 +626,7 @@ func deleteImages(eng *engine.Engine, version float64, w http.ResponseWriter, r
636 636
 	return job.Run()
637 637
 }
638 638
 
639
-func postContainersStart(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
639
+func postContainersStart(eng *engine.Engine, version string, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
640 640
 	if vars == nil {
641 641
 		return fmt.Errorf("Missing parameter")
642 642
 	}
... ...
@@ -657,7 +647,7 @@ func postContainersStart(eng *engine.Engine, version float64, w http.ResponseWri
657 657
 	return nil
658 658
 }
659 659
 
660
-func postContainersStop(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
660
+func postContainersStop(eng *engine.Engine, version string, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
661 661
 	if err := parseForm(r); err != nil {
662 662
 		return err
663 663
 	}
... ...
@@ -673,7 +663,7 @@ func postContainersStop(eng *engine.Engine, version float64, w http.ResponseWrit
673 673
 	return nil
674 674
 }
675 675
 
676
-func postContainersWait(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
676
+func postContainersWait(eng *engine.Engine, version string, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
677 677
 	if vars == nil {
678 678
 		return fmt.Errorf("Missing parameter")
679 679
 	}
... ...
@@ -695,7 +685,7 @@ func postContainersWait(eng *engine.Engine, version float64, w http.ResponseWrit
695 695
 	return writeJSON(w, http.StatusOK, env)
696 696
 }
697 697
 
698
-func postContainersResize(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
698
+func postContainersResize(eng *engine.Engine, version string, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
699 699
 	if err := parseForm(r); err != nil {
700 700
 		return err
701 701
 	}
... ...
@@ -708,7 +698,7 @@ func postContainersResize(eng *engine.Engine, version float64, w http.ResponseWr
708 708
 	return nil
709 709
 }
710 710
 
711
-func postContainersAttach(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
711
+func postContainersAttach(eng *engine.Engine, version string, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
712 712
 	if err := parseForm(r); err != nil {
713 713
 		return err
714 714
 	}
... ...
@@ -750,7 +740,7 @@ func postContainersAttach(eng *engine.Engine, version float64, w http.ResponseWr
750 750
 
751 751
 	fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
752 752
 
753
-	if c.GetSubEnv("Config") != nil && !c.GetSubEnv("Config").GetBool("Tty") && version >= 1.6 {
753
+	if c.GetSubEnv("Config") != nil && !c.GetSubEnv("Config").GetBool("Tty") && utils.CompareVersion(version, "1.6") >= 0 {
754 754
 		errStream = utils.NewStdWriter(outStream, utils.Stderr)
755 755
 		outStream = utils.NewStdWriter(outStream, utils.Stdout)
756 756
 	} else {
... ...
@@ -773,7 +763,7 @@ func postContainersAttach(eng *engine.Engine, version float64, w http.ResponseWr
773 773
 	return nil
774 774
 }
775 775
 
776
-func wsContainersAttach(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
776
+func wsContainersAttach(eng *engine.Engine, version string, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
777 777
 	if err := parseForm(r); err != nil {
778 778
 		return err
779 779
 	}
... ...
@@ -805,7 +795,7 @@ func wsContainersAttach(eng *engine.Engine, version float64, w http.ResponseWrit
805 805
 	return nil
806 806
 }
807 807
 
808
-func getContainersByName(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
808
+func getContainersByName(eng *engine.Engine, version string, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
809 809
 	if vars == nil {
810 810
 		return fmt.Errorf("Missing parameter")
811 811
 	}
... ...
@@ -815,7 +805,7 @@ func getContainersByName(eng *engine.Engine, version float64, w http.ResponseWri
815 815
 	return job.Run()
816 816
 }
817 817
 
818
-func getImagesByName(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
818
+func getImagesByName(eng *engine.Engine, version string, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
819 819
 	if vars == nil {
820 820
 		return fmt.Errorf("Missing parameter")
821 821
 	}
... ...
@@ -825,8 +815,8 @@ func getImagesByName(eng *engine.Engine, version float64, w http.ResponseWriter,
825 825
 	return job.Run()
826 826
 }
827 827
 
828
-func postBuild(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
829
-	if version < 1.3 {
828
+func postBuild(eng *engine.Engine, version string, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
829
+	if utils.CompareVersion(version, "1.3") < 0 {
830 830
 		return fmt.Errorf("Multipart upload for build is no longer supported. Please upgrade your docker client.")
831 831
 	}
832 832
 	var (
... ...
@@ -841,7 +831,7 @@ func postBuild(eng *engine.Engine, version float64, w http.ResponseWriter, r *ht
841 841
 	// Both headers will be parsed and sent along to the daemon, but if a non-empty
842 842
 	// ConfigFile is present, any value provided as an AuthConfig directly will
843 843
 	// be overridden. See BuildFile::CmdFrom for details.
844
-	if version < 1.9 && authEncoded != "" {
844
+	if utils.CompareVersion(version, "1.9") < 0 && authEncoded != "" {
845 845
 		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
846 846
 		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
847 847
 			// for a pull it is not an error if no auth was given
... ...
@@ -859,7 +849,7 @@ func postBuild(eng *engine.Engine, version float64, w http.ResponseWriter, r *ht
859 859
 		}
860 860
 	}
861 861
 
862
-	if version >= 1.8 {
862
+	if utils.CompareVersion(version, "1.8") >= 0 {
863 863
 		job.SetenvBool("json", true)
864 864
 		streamJSON(job, w, true)
865 865
 	} else {
... ...
@@ -878,13 +868,13 @@ func postBuild(eng *engine.Engine, version float64, w http.ResponseWriter, r *ht
878 878
 		if !job.Stdout.Used() {
879 879
 			return err
880 880
 		}
881
-		sf := utils.NewStreamFormatter(version >= 1.8)
881
+		sf := utils.NewStreamFormatter(utils.CompareVersion(version, "1.8") >= 0)
882 882
 		w.Write(sf.FormatError(err))
883 883
 	}
884 884
 	return nil
885 885
 }
886 886
 
887
-func postContainersCopy(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
887
+func postContainersCopy(eng *engine.Engine, version string, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
888 888
 	if vars == nil {
889 889
 		return fmt.Errorf("Missing parameter")
890 890
 	}
... ...
@@ -917,7 +907,7 @@ func postContainersCopy(eng *engine.Engine, version float64, w http.ResponseWrit
917 917
 	return nil
918 918
 }
919 919
 
920
-func optionsHandler(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
920
+func optionsHandler(eng *engine.Engine, version string, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
921 921
 	w.WriteHeader(http.StatusOK)
922 922
 	return nil
923 923
 }
... ...
@@ -942,16 +932,16 @@ func makeHttpHandler(eng *engine.Engine, logging bool, localMethod string, local
942 942
 				utils.Debugf("Warning: client and server don't have the same version (client: %s, server: %s)", userAgent[1], dockerVersion)
943 943
 			}
944 944
 		}
945
-		version, err := strconv.ParseFloat(mux.Vars(r)["version"], 64)
946
-		if err != nil {
945
+		version := mux.Vars(r)["version"]
946
+		if version == "" {
947 947
 			version = APIVERSION
948 948
 		}
949 949
 		if enableCors {
950 950
 			writeCorsHeaders(w, r)
951 951
 		}
952 952
 
953
-		if version == 0 || version > APIVERSION {
954
-			http.Error(w, fmt.Errorf("client and server don't have same version (client : %g, server: %g)", version, APIVERSION).Error(), http.StatusNotFound)
953
+		if utils.CompareVersion(version, APIVERSION) == 1 {
954
+			http.Error(w, fmt.Errorf("client and server don't have same version (client : %s, server: %s)", version, APIVERSION).Error(), http.StatusNotFound)
955 955
 			return
956 956
 		}
957 957
 
... ...
@@ -26,15 +26,31 @@ Docker Remote API
26 26
 2. Versions
27 27
 ===========
28 28
 
29
-The current version of the API is 1.9
29
+The current version of the API is 1.10
30 30
 
31 31
 Calling /images/<name>/insert is the same as calling
32
-/v1.9/images/<name>/insert
32
+/v1.10/images/<name>/insert
33 33
 
34 34
 You can still call an old version of the api using
35 35
 /v1.0/images/<name>/insert
36 36
 
37 37
 
38
+v1.10
39
+****
40
+
41
+Full Documentation
42
+------------------
43
+
44
+:doc:`docker_remote_api_v1.10`
45
+
46
+What's new
47
+----------
48
+
49
+.. http:delete:: /images/(name)
50
+
51
+   **New!** You can now use the force parameter to force delete of an image, even if it's
52
+   tagged in multiple repositories.
53
+
38 54
 v1.9
39 55
 ****
40 56
 
41 57
new file mode 100644
... ...
@@ -0,0 +1,1282 @@
0
+:title: Remote API v1.9
1
+:description: API Documentation for Docker
2
+:keywords: API, Docker, rcli, REST, documentation
3
+
4
+:orphan:
5
+
6
+======================
7
+Docker Remote API v1.9
8
+======================
9
+
10
+.. contents:: Table of Contents
11
+
12
+1. Brief introduction
13
+=====================
14
+
15
+- The Remote API has replaced rcli
16
+- The daemon listens on ``unix:///var/run/docker.sock``, but you can
17
+  :ref:`bind_docker`.
18
+- The API tends to be REST, but for some complex commands, like
19
+  ``attach`` or ``pull``, the HTTP connection is hijacked to transport
20
+  ``stdout, stdin`` and ``stderr``
21
+
22
+2. Endpoints
23
+============
24
+
25
+2.1 Containers
26
+--------------
27
+
28
+List containers
29
+***************
30
+
31
+.. http:get:: /containers/json
32
+
33
+        List containers
34
+
35
+        **Example request**:
36
+
37
+        .. sourcecode:: http
38
+
39
+           GET /containers/json?all=1&before=8dfafdbc3a40&size=1 HTTP/1.1
40
+
41
+        **Example response**:
42
+
43
+        .. sourcecode:: http
44
+
45
+           HTTP/1.1 200 OK
46
+           Content-Type: application/json
47
+
48
+           [
49
+                {
50
+                        "Id": "8dfafdbc3a40",
51
+                        "Image": "base:latest",
52
+                        "Command": "echo 1",
53
+                        "Created": 1367854155,
54
+                        "Status": "Exit 0",
55
+                        "Ports":[{"PrivatePort": 2222, "PublicPort": 3333, "Type": "tcp"}],
56
+                        "SizeRw":12288,
57
+                        "SizeRootFs":0
58
+                },
59
+                {
60
+                        "Id": "9cd87474be90",
61
+                        "Image": "base:latest",
62
+                        "Command": "echo 222222",
63
+                        "Created": 1367854155,
64
+                        "Status": "Exit 0",
65
+                        "Ports":[],
66
+                        "SizeRw":12288,
67
+                        "SizeRootFs":0
68
+                },
69
+                {
70
+                        "Id": "3176a2479c92",
71
+                        "Image": "base:latest",
72
+                        "Command": "echo 3333333333333333",
73
+                        "Created": 1367854154,
74
+                        "Status": "Exit 0",
75
+                        "Ports":[],
76
+                        "SizeRw":12288,
77
+                        "SizeRootFs":0
78
+                },
79
+                {
80
+                        "Id": "4cb07b47f9fb",
81
+                        "Image": "base:latest",
82
+                        "Command": "echo 444444444444444444444444444444444",
83
+                        "Created": 1367854152,
84
+                        "Status": "Exit 0",
85
+                        "Ports":[],
86
+                        "SizeRw":12288,
87
+                        "SizeRootFs":0
88
+                }
89
+           ]
90
+
91
+        :query all: 1/True/true or 0/False/false, Show all containers. Only running containers are shown by default
92
+        :query limit: Show ``limit`` last created containers, include non-running ones.
93
+        :query since: Show only containers created since Id, include non-running ones.
94
+        :query before: Show only containers created before Id, include non-running ones.
95
+        :query size: 1/True/true or 0/False/false, Show the containers sizes
96
+        :statuscode 200: no error
97
+        :statuscode 400: bad parameter
98
+        :statuscode 500: server error
99
+
100
+
101
+Create a container
102
+******************
103
+
104
+.. http:post:: /containers/create
105
+
106
+        Create a container
107
+
108
+        **Example request**:
109
+
110
+        .. sourcecode:: http
111
+
112
+           POST /containers/create HTTP/1.1
113
+           Content-Type: application/json
114
+
115
+           {
116
+                "Hostname":"",
117
+                "User":"",
118
+                "Memory":0,
119
+                "MemorySwap":0,
120
+                "AttachStdin":false,
121
+                "AttachStdout":true,
122
+                "AttachStderr":true,
123
+                "PortSpecs":null,
124
+                "Tty":false,
125
+                "OpenStdin":false,
126
+                "StdinOnce":false,
127
+                "Env":null,
128
+                "Cmd":[
129
+                        "date"
130
+                ],
131
+                "Dns":null,
132
+                "Image":"base",
133
+                "Volumes":{
134
+                        "/tmp": {}
135
+                },
136
+                "VolumesFrom":"",
137
+                "WorkingDir":"",
138
+                "ExposedPorts":{
139
+                        "22/tcp": {}
140
+                }
141
+           }
142
+
143
+        **Example response**:
144
+
145
+        .. sourcecode:: http
146
+
147
+           HTTP/1.1 201 OK
148
+           Content-Type: application/json
149
+
150
+           {
151
+                "Id":"e90e34656806"
152
+                "Warnings":[]
153
+           }
154
+
155
+        :jsonparam config: the container's configuration
156
+        :query name: Assign the specified name to the container. Must match ``/?[a-zA-Z0-9_-]+``.
157
+        :statuscode 201: no error
158
+        :statuscode 404: no such container
159
+        :statuscode 406: impossible to attach (container not running)
160
+        :statuscode 500: server error
161
+
162
+
163
+Inspect a container
164
+*******************
165
+
166
+.. http:get:: /containers/(id)/json
167
+
168
+        Return low-level information on the container ``id``
169
+
170
+        **Example request**:
171
+
172
+        .. sourcecode:: http
173
+
174
+           GET /containers/4fa6e0f0c678/json HTTP/1.1
175
+
176
+        **Example response**:
177
+
178
+        .. sourcecode:: http
179
+
180
+           HTTP/1.1 200 OK
181
+           Content-Type: application/json
182
+
183
+           {
184
+                        "Id": "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2",
185
+                        "Created": "2013-05-07T14:51:42.041847+02:00",
186
+                        "Path": "date",
187
+                        "Args": [],
188
+                        "Config": {
189
+                                "Hostname": "4fa6e0f0c678",
190
+                                "User": "",
191
+                                "Memory": 0,
192
+                                "MemorySwap": 0,
193
+                                "AttachStdin": false,
194
+                                "AttachStdout": true,
195
+                                "AttachStderr": true,
196
+                                "PortSpecs": null,
197
+                                "Tty": false,
198
+                                "OpenStdin": false,
199
+                                "StdinOnce": false,
200
+                                "Env": null,
201
+                                "Cmd": [
202
+                                        "date"
203
+                                ],
204
+                                "Dns": null,
205
+                                "Image": "base",
206
+                                "Volumes": {},
207
+                                "VolumesFrom": "",
208
+                                "WorkingDir":""
209
+
210
+                        },
211
+                        "State": {
212
+                                "Running": false,
213
+                                "Pid": 0,
214
+                                "ExitCode": 0,
215
+                                "StartedAt": "2013-05-07T14:51:42.087658+02:01360",
216
+                                "Ghost": false
217
+                        },
218
+                        "Image": "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
219
+                        "NetworkSettings": {
220
+                                "IpAddress": "",
221
+                                "IpPrefixLen": 0,
222
+                                "Gateway": "",
223
+                                "Bridge": "",
224
+                                "PortMapping": null
225
+                        },
226
+                        "SysInitPath": "/home/kitty/go/src/github.com/dotcloud/docker/bin/docker",
227
+                        "ResolvConfPath": "/etc/resolv.conf",
228
+                        "Volumes": {},
229
+                        "HostConfig": {
230
+                            "Binds": null,
231
+                            "ContainerIDFile": "",
232
+                            "LxcConf": [],
233
+                            "Privileged": false,
234
+                            "PortBindings": {
235
+                               "80/tcp": [
236
+                                   {
237
+                                       "HostIp": "0.0.0.0",
238
+                                       "HostPort": "49153"
239
+                                   }
240
+                               ]
241
+                            },
242
+                            "Links": null,
243
+                            "PublishAllPorts": false
244
+                        }
245
+           }
246
+
247
+        :statuscode 200: no error
248
+        :statuscode 404: no such container
249
+        :statuscode 500: server error
250
+
251
+
252
+List processes running inside a container
253
+*****************************************
254
+
255
+.. http:get:: /containers/(id)/top
256
+
257
+        List processes running inside the container ``id``
258
+
259
+        **Example request**:
260
+
261
+        .. sourcecode:: http
262
+
263
+           GET /containers/4fa6e0f0c678/top HTTP/1.1
264
+
265
+        **Example response**:
266
+
267
+        .. sourcecode:: http
268
+
269
+           HTTP/1.1 200 OK
270
+           Content-Type: application/json
271
+
272
+           {
273
+                "Titles":[
274
+                        "USER",
275
+                        "PID",
276
+                        "%CPU",
277
+                        "%MEM",
278
+                        "VSZ",
279
+                        "RSS",
280
+                        "TTY",
281
+                        "STAT",
282
+                        "START",
283
+                        "TIME",
284
+                        "COMMAND"
285
+                        ],
286
+                "Processes":[
287
+                        ["root","20147","0.0","0.1","18060","1864","pts/4","S","10:06","0:00","bash"],
288
+                        ["root","20271","0.0","0.0","4312","352","pts/4","S+","10:07","0:00","sleep","10"]
289
+                ]
290
+           }
291
+
292
+        :query ps_args: ps arguments to use (eg. aux)
293
+        :statuscode 200: no error
294
+        :statuscode 404: no such container
295
+        :statuscode 500: server error
296
+
297
+
298
+Inspect changes on a container's filesystem
299
+*******************************************
300
+
301
+.. http:get:: /containers/(id)/changes
302
+
303
+        Inspect changes on container ``id`` 's filesystem
304
+
305
+        **Example request**:
306
+
307
+        .. sourcecode:: http
308
+
309
+           GET /containers/4fa6e0f0c678/changes HTTP/1.1
310
+
311
+
312
+        **Example response**:
313
+
314
+        .. sourcecode:: http
315
+
316
+           HTTP/1.1 200 OK
317
+           Content-Type: application/json
318
+
319
+           [
320
+                {
321
+                        "Path":"/dev",
322
+                        "Kind":0
323
+                },
324
+                {
325
+                        "Path":"/dev/kmsg",
326
+                        "Kind":1
327
+                },
328
+                {
329
+                        "Path":"/test",
330
+                        "Kind":1
331
+                }
332
+           ]
333
+
334
+        :statuscode 200: no error
335
+        :statuscode 404: no such container
336
+        :statuscode 500: server error
337
+
338
+
339
+Export a container
340
+******************
341
+
342
+.. http:get:: /containers/(id)/export
343
+
344
+        Export the contents of container ``id``
345
+
346
+        **Example request**:
347
+
348
+        .. sourcecode:: http
349
+
350
+           GET /containers/4fa6e0f0c678/export HTTP/1.1
351
+
352
+
353
+        **Example response**:
354
+
355
+        .. sourcecode:: http
356
+
357
+           HTTP/1.1 200 OK
358
+           Content-Type: application/octet-stream
359
+
360
+           {{ STREAM }}
361
+
362
+        :statuscode 200: no error
363
+        :statuscode 404: no such container
364
+        :statuscode 500: server error
365
+
366
+
367
+Start a container
368
+*****************
369
+
370
+.. http:post:: /containers/(id)/start
371
+
372
+        Start the container ``id``
373
+
374
+        **Example request**:
375
+
376
+        .. sourcecode:: http
377
+
378
+           POST /containers/(id)/start HTTP/1.1
379
+           Content-Type: application/json
380
+
381
+           {
382
+                "Binds":["/tmp:/tmp"],
383
+                "LxcConf":{"lxc.utsname":"docker"},
384
+                "PortBindings":{ "22/tcp": [{ "HostPort": "11022" }] },
385
+                "PublishAllPorts":false,
386
+                "Privileged":false
387
+           }
388
+
389
+        **Example response**:
390
+
391
+        .. sourcecode:: http
392
+
393
+           HTTP/1.1 204 No Content
394
+           Content-Type: text/plain
395
+
396
+        :jsonparam hostConfig: the container's host configuration (optional)
397
+        :statuscode 204: no error
398
+        :statuscode 404: no such container
399
+        :statuscode 500: server error
400
+
401
+
402
+Stop a container
403
+****************
404
+
405
+.. http:post:: /containers/(id)/stop
406
+
407
+        Stop the container ``id``
408
+
409
+        **Example request**:
410
+
411
+        .. sourcecode:: http
412
+
413
+           POST /containers/e90e34656806/stop?t=5 HTTP/1.1
414
+
415
+        **Example response**:
416
+
417
+        .. sourcecode:: http
418
+
419
+           HTTP/1.1 204 OK
420
+
421
+        :query t: number of seconds to wait before killing the container
422
+        :statuscode 204: no error
423
+        :statuscode 404: no such container
424
+        :statuscode 500: server error
425
+
426
+
427
+Restart a container
428
+*******************
429
+
430
+.. http:post:: /containers/(id)/restart
431
+
432
+        Restart the container ``id``
433
+
434
+        **Example request**:
435
+
436
+        .. sourcecode:: http
437
+
438
+           POST /containers/e90e34656806/restart?t=5 HTTP/1.1
439
+
440
+        **Example response**:
441
+
442
+        .. sourcecode:: http
443
+
444
+           HTTP/1.1 204 OK
445
+
446
+        :query t: number of seconds to wait before killing the container
447
+        :statuscode 204: no error
448
+        :statuscode 404: no such container
449
+        :statuscode 500: server error
450
+
451
+
452
+Kill a container
453
+****************
454
+
455
+.. http:post:: /containers/(id)/kill
456
+
457
+        Kill the container ``id``
458
+
459
+        **Example request**:
460
+
461
+        .. sourcecode:: http
462
+
463
+           POST /containers/e90e34656806/kill HTTP/1.1
464
+
465
+        **Example response**:
466
+
467
+        .. sourcecode:: http
468
+
469
+           HTTP/1.1 204 OK
470
+
471
+        :statuscode 204: no error
472
+        :statuscode 404: no such container
473
+        :statuscode 500: server error
474
+
475
+
476
+Attach to a container
477
+*********************
478
+
479
+.. http:post:: /containers/(id)/attach
480
+
481
+        Attach to the container ``id``
482
+
483
+        **Example request**:
484
+
485
+        .. sourcecode:: http
486
+
487
+           POST /containers/16253994b7c4/attach?logs=1&stream=0&stdout=1 HTTP/1.1
488
+
489
+        **Example response**:
490
+
491
+        .. sourcecode:: http
492
+
493
+           HTTP/1.1 200 OK
494
+           Content-Type: application/vnd.docker.raw-stream
495
+
496
+           {{ STREAM }}
497
+
498
+        :query logs: 1/True/true or 0/False/false, return logs. Default false
499
+        :query stream: 1/True/true or 0/False/false, return stream. Default false
500
+        :query stdin: 1/True/true or 0/False/false, if stream=true, attach to stdin. Default false
501
+        :query stdout: 1/True/true or 0/False/false, if logs=true, return stdout log, if stream=true, attach to stdout. Default false
502
+        :query stderr: 1/True/true or 0/False/false, if logs=true, return stderr log, if stream=true, attach to stderr. Default false
503
+        :statuscode 200: no error
504
+        :statuscode 400: bad parameter
505
+        :statuscode 404: no such container
506
+        :statuscode 500: server error
507
+
508
+        **Stream details**:
509
+
510
+        When using the TTY setting is enabled in
511
+        :http:post:`/containers/create`, the stream is the raw data
512
+        from the process PTY and client's stdin.  When the TTY is
513
+        disabled, then the stream is multiplexed to separate stdout
514
+        and stderr.
515
+
516
+        The format is a **Header** and a **Payload** (frame).
517
+
518
+        **HEADER**
519
+
520
+        The header will contain the information on which stream write
521
+        the stream (stdout or stderr). It also contain the size of
522
+        the associated frame encoded on the last 4 bytes (uint32).
523
+
524
+        It is encoded on the first 8 bytes like this::
525
+
526
+            header := [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4}
527
+
528
+        ``STREAM_TYPE`` can be:
529
+
530
+        - 0: stdin (will be writen on stdout)
531
+        - 1: stdout
532
+        - 2: stderr
533
+
534
+        ``SIZE1, SIZE2, SIZE3, SIZE4`` are the 4 bytes of the uint32 size encoded as big endian.
535
+
536
+        **PAYLOAD**
537
+
538
+        The payload is the raw stream.
539
+
540
+        **IMPLEMENTATION**
541
+
542
+        The simplest way to implement the Attach protocol is the following:
543
+
544
+        1) Read 8 bytes
545
+        2) chose stdout or stderr depending on the first byte
546
+        3) Extract the frame size from the last 4 byets
547
+        4) Read the extracted size and output it on the correct output
548
+        5) Goto 1)
549
+
550
+
551
+
552
+Wait a container
553
+****************
554
+
555
+.. http:post:: /containers/(id)/wait
556
+
557
+        Block until container ``id`` stops, then returns the exit code
558
+
559
+        **Example request**:
560
+
561
+        .. sourcecode:: http
562
+
563
+           POST /containers/16253994b7c4/wait HTTP/1.1
564
+
565
+        **Example response**:
566
+
567
+        .. sourcecode:: http
568
+
569
+           HTTP/1.1 200 OK
570
+           Content-Type: application/json
571
+
572
+           {"StatusCode":0}
573
+
574
+        :statuscode 200: no error
575
+        :statuscode 404: no such container
576
+        :statuscode 500: server error
577
+
578
+
579
+Remove a container
580
+*******************
581
+
582
+.. http:delete:: /containers/(id)
583
+
584
+        Remove the container ``id`` from the filesystem
585
+
586
+        **Example request**:
587
+
588
+        .. sourcecode:: http
589
+
590
+           DELETE /containers/16253994b7c4?v=1 HTTP/1.1
591
+
592
+        **Example response**:
593
+
594
+        .. sourcecode:: http
595
+
596
+           HTTP/1.1 204 OK
597
+
598
+        :query v: 1/True/true or 0/False/false, Remove the volumes associated to the container. Default false
599
+        :statuscode 204: no error
600
+        :statuscode 400: bad parameter
601
+        :statuscode 404: no such container
602
+        :statuscode 500: server error
603
+
604
+
605
+Copy files or folders from a container
606
+**************************************
607
+
608
+.. http:post:: /containers/(id)/copy
609
+
610
+        Copy files or folders of container ``id``
611
+
612
+        **Example request**:
613
+
614
+        .. sourcecode:: http
615
+
616
+           POST /containers/4fa6e0f0c678/copy HTTP/1.1
617
+           Content-Type: application/json
618
+
619
+           {
620
+                "Resource":"test.txt"
621
+           }
622
+
623
+        **Example response**:
624
+
625
+        .. sourcecode:: http
626
+
627
+           HTTP/1.1 200 OK
628
+           Content-Type: application/octet-stream
629
+
630
+           {{ STREAM }}
631
+
632
+        :statuscode 200: no error
633
+        :statuscode 404: no such container
634
+        :statuscode 500: server error
635
+
636
+
637
+2.2 Images
638
+----------
639
+
640
+List Images
641
+***********
642
+
643
+.. http:get:: /images/json
644
+
645
+        **Example request**:
646
+
647
+        .. sourcecode:: http
648
+
649
+           GET /images/json?all=0 HTTP/1.1
650
+
651
+        **Example response**:
652
+
653
+        .. sourcecode:: http
654
+
655
+           HTTP/1.1 200 OK
656
+           Content-Type: application/json
657
+
658
+           [
659
+             {
660
+                "RepoTags": [
661
+                  "ubuntu:12.04",
662
+                  "ubuntu:precise",
663
+                  "ubuntu:latest"
664
+                ],
665
+                "Id": "8dbd9e392a964056420e5d58ca5cc376ef18e2de93b5cc90e868a1bbc8318c1c",
666
+                "Created": 1365714795,
667
+                "Size": 131506275,
668
+                "VirtualSize": 131506275
669
+             },
670
+             {
671
+                "RepoTags": [
672
+                  "ubuntu:12.10",
673
+                  "ubuntu:quantal"
674
+                ],
675
+                "ParentId": "27cf784147099545",
676
+                "Id": "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
677
+                "Created": 1364102658,
678
+                "Size": 24653,
679
+                "VirtualSize": 180116135
680
+             }
681
+           ]
682
+
683
+
684
+Create an image
685
+***************
686
+
687
+.. http:post:: /images/create
688
+
689
+        Create an image, either by pull it from the registry or by importing it
690
+
691
+        **Example request**:
692
+
693
+        .. sourcecode:: http
694
+
695
+           POST /images/create?fromImage=base HTTP/1.1
696
+
697
+        **Example response**:
698
+
699
+        .. sourcecode:: http
700
+
701
+           HTTP/1.1 200 OK
702
+           Content-Type: application/json
703
+
704
+           {"status":"Pulling..."}
705
+           {"status":"Pulling", "progress":"1 B/ 100 B", "progressDetail":{"current":1, "total":100}}
706
+           {"error":"Invalid..."}
707
+           ...
708
+
709
+        When using this endpoint to pull an image from the registry,
710
+        the ``X-Registry-Auth`` header can be used to include a
711
+        base64-encoded AuthConfig object.
712
+
713
+        :query fromImage: name of the image to pull
714
+        :query fromSrc: source to import, - means stdin
715
+        :query repo: repository
716
+        :query tag: tag
717
+        :query registry: the registry to pull from
718
+        :reqheader X-Registry-Auth: base64-encoded AuthConfig object
719
+        :statuscode 200: no error
720
+        :statuscode 500: server error
721
+
722
+
723
+
724
+Insert a file in an image
725
+*************************
726
+
727
+.. http:post:: /images/(name)/insert
728
+
729
+        Insert a file from ``url`` in the image ``name`` at ``path``
730
+
731
+        **Example request**:
732
+
733
+        .. sourcecode:: http
734
+
735
+           POST /images/test/insert?path=/usr&url=myurl HTTP/1.1
736
+
737
+        **Example response**:
738
+
739
+        .. sourcecode:: http
740
+
741
+           HTTP/1.1 200 OK
742
+           Content-Type: application/json
743
+
744
+           {"status":"Inserting..."}
745
+           {"status":"Inserting", "progress":"1/? (n/a)", "progressDetail":{"current":1}}
746
+           {"error":"Invalid..."}
747
+           ...
748
+
749
+        :statuscode 200: no error
750
+        :statuscode 500: server error
751
+
752
+
753
+Inspect an image
754
+****************
755
+
756
+.. http:get:: /images/(name)/json
757
+
758
+        Return low-level information on the image ``name``
759
+
760
+        **Example request**:
761
+
762
+        .. sourcecode:: http
763
+
764
+           GET /images/base/json HTTP/1.1
765
+
766
+        **Example response**:
767
+
768
+        .. sourcecode:: http
769
+
770
+           HTTP/1.1 200 OK
771
+           Content-Type: application/json
772
+
773
+           {
774
+                "id":"b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
775
+                "parent":"27cf784147099545",
776
+                "created":"2013-03-23T22:24:18.818426-07:00",
777
+                "container":"3d67245a8d72ecf13f33dffac9f79dcdf70f75acb84d308770391510e0c23ad0",
778
+                "container_config":
779
+                        {
780
+                                "Hostname":"",
781
+                                "User":"",
782
+                                "Memory":0,
783
+                                "MemorySwap":0,
784
+                                "AttachStdin":false,
785
+                                "AttachStdout":false,
786
+                                "AttachStderr":false,
787
+                                "PortSpecs":null,
788
+                                "Tty":true,
789
+                                "OpenStdin":true,
790
+                                "StdinOnce":false,
791
+                                "Env":null,
792
+                                "Cmd": ["/bin/bash"]
793
+                                ,"Dns":null,
794
+                                "Image":"base",
795
+                                "Volumes":null,
796
+                                "VolumesFrom":"",
797
+                                "WorkingDir":""
798
+                        },
799
+                "Size": 6824592
800
+           }
801
+
802
+        :statuscode 200: no error
803
+        :statuscode 404: no such image
804
+        :statuscode 500: server error
805
+
806
+
807
+Get the history of an image
808
+***************************
809
+
810
+.. http:get:: /images/(name)/history
811
+
812
+        Return the history of the image ``name``
813
+
814
+        **Example request**:
815
+
816
+        .. sourcecode:: http
817
+
818
+           GET /images/base/history HTTP/1.1
819
+
820
+        **Example response**:
821
+
822
+        .. sourcecode:: http
823
+
824
+           HTTP/1.1 200 OK
825
+           Content-Type: application/json
826
+
827
+           [
828
+                {
829
+                        "Id":"b750fe79269d",
830
+                        "Created":1364102658,
831
+                        "CreatedBy":"/bin/bash"
832
+                },
833
+                {
834
+                        "Id":"27cf78414709",
835
+                        "Created":1364068391,
836
+                        "CreatedBy":""
837
+                }
838
+           ]
839
+
840
+        :statuscode 200: no error
841
+        :statuscode 404: no such image
842
+        :statuscode 500: server error
843
+
844
+
845
+Push an image on the registry
846
+*****************************
847
+
848
+.. http:post:: /images/(name)/push
849
+
850
+   Push the image ``name`` on the registry
851
+
852
+   **Example request**:
853
+
854
+   .. sourcecode:: http
855
+
856
+      POST /images/test/push HTTP/1.1
857
+
858
+   **Example response**:
859
+
860
+   .. sourcecode:: http
861
+
862
+    HTTP/1.1 200 OK
863
+    Content-Type: application/json
864
+
865
+    {"status":"Pushing..."}
866
+    {"status":"Pushing", "progress":"1/? (n/a)", "progressDetail":{"current":1}}}
867
+    {"error":"Invalid..."}
868
+    ...
869
+
870
+   :query registry: the registry you wan to push, optional
871
+   :reqheader X-Registry-Auth: include a base64-encoded AuthConfig object.
872
+   :statuscode 200: no error
873
+   :statuscode 404: no such image
874
+   :statuscode 500: server error
875
+
876
+
877
+Tag an image into a repository
878
+******************************
879
+
880
+.. http:post:: /images/(name)/tag
881
+
882
+        Tag the image ``name`` into a repository
883
+
884
+        **Example request**:
885
+
886
+        .. sourcecode:: http
887
+
888
+           POST /images/test/tag?repo=myrepo&force=0 HTTP/1.1
889
+
890
+        **Example response**:
891
+
892
+        .. sourcecode:: http
893
+
894
+           HTTP/1.1 201 OK
895
+
896
+        :query repo: The repository to tag in
897
+        :query force: 1/True/true or 0/False/false, default false
898
+        :statuscode 201: no error
899
+        :statuscode 400: bad parameter
900
+        :statuscode 404: no such image
901
+        :statuscode 409: conflict
902
+        :statuscode 500: server error
903
+
904
+
905
+Remove an image
906
+***************
907
+
908
+.. http:delete:: /images/(name)
909
+
910
+        Remove the image ``name`` from the filesystem
911
+
912
+        **Example request**:
913
+
914
+        .. sourcecode:: http
915
+
916
+           DELETE /images/test HTTP/1.1
917
+
918
+        **Example response**:
919
+
920
+        .. sourcecode:: http
921
+
922
+           HTTP/1.1 200 OK
923
+           Content-type: application/json
924
+
925
+           [
926
+            {"Untagged":"3e2f21a89f"},
927
+            {"Deleted":"3e2f21a89f"},
928
+            {"Deleted":"53b4f83ac9"}
929
+           ]
930
+
931
+        :query force: 1/True/true or 0/False/false, default false
932
+        :statuscode 200: no error
933
+        :statuscode 404: no such image
934
+        :statuscode 409: conflict
935
+        :statuscode 500: server error
936
+
937
+
938
+Search images
939
+*************
940
+
941
+.. http:get:: /images/search
942
+
943
+        Search for an image in the docker index.
944
+
945
+        .. note::
946
+
947
+           The response keys have changed from API v1.6 to reflect the JSON
948
+           sent by the registry server to the docker daemon's request.
949
+
950
+        **Example request**:
951
+
952
+        .. sourcecode:: http
953
+
954
+           GET /images/search?term=sshd HTTP/1.1
955
+
956
+        **Example response**:
957
+
958
+        .. sourcecode:: http
959
+
960
+           HTTP/1.1 200 OK
961
+           Content-Type: application/json
962
+
963
+           [
964
+                   {
965
+                       "description": "",
966
+                       "is_official": false,
967
+                       "is_trusted": false,
968
+                       "name": "wma55/u1210sshd",
969
+                       "star_count": 0
970
+                   },
971
+                   {
972
+                       "description": "",
973
+                       "is_official": false,
974
+                       "is_trusted": false,
975
+                       "name": "jdswinbank/sshd",
976
+                       "star_count": 0
977
+                   },
978
+                   {
979
+                       "description": "",
980
+                       "is_official": false,
981
+                       "is_trusted": false,
982
+                       "name": "vgauthier/sshd",
983
+                       "star_count": 0
984
+                   }
985
+           ...
986
+           ]
987
+
988
+        :query term: term to search
989
+        :statuscode 200: no error
990
+        :statuscode 500: server error
991
+
992
+
993
+2.3 Misc
994
+--------
995
+
996
+Build an image from Dockerfile via stdin
997
+****************************************
998
+
999
+.. http:post:: /build
1000
+
1001
+   Build an image from Dockerfile via stdin
1002
+
1003
+   **Example request**:
1004
+
1005
+   .. sourcecode:: http
1006
+
1007
+      POST /build HTTP/1.1
1008
+
1009
+      {{ STREAM }}
1010
+
1011
+   **Example response**:
1012
+
1013
+   .. sourcecode:: http
1014
+
1015
+      HTTP/1.1 200 OK
1016
+      Content-Type: application/json
1017
+
1018
+      {"stream":"Step 1..."}
1019
+      {"stream":"..."}
1020
+      {"error":"Error...", "errorDetail":{"code": 123, "message": "Error..."}}
1021
+
1022
+
1023
+   The stream must be a tar archive compressed with one of the
1024
+   following algorithms: identity (no compression), gzip, bzip2,
1025
+   xz.
1026
+
1027
+   The archive must include a file called ``Dockerfile`` at its
1028
+   root. It may include any number of other files, which will be
1029
+   accessible in the build context (See the :ref:`ADD build command
1030
+   <dockerbuilder>`).
1031
+
1032
+   :query t: repository name (and optionally a tag) to be applied to the resulting image in case of success
1033
+   :query q: suppress verbose build output
1034
+   :query nocache: do not use the cache when building the image
1035
+   :reqheader Content-type: should be set to ``"application/tar"``.
1036
+   :reqheader X-Registry-Config: base64-encoded ConfigFile object
1037
+   :statuscode 200: no error
1038
+   :statuscode 500: server error
1039
+
1040
+
1041
+
1042
+Check auth configuration
1043
+************************
1044
+
1045
+.. http:post:: /auth
1046
+
1047
+        Get the default username and email
1048
+
1049
+        **Example request**:
1050
+
1051
+        .. sourcecode:: http
1052
+
1053
+           POST /auth HTTP/1.1
1054
+           Content-Type: application/json
1055
+
1056
+           {
1057
+                "username":"hannibal",
1058
+                "password:"xxxx",
1059
+                "email":"hannibal@a-team.com",
1060
+                "serveraddress":"https://index.docker.io/v1/"
1061
+           }
1062
+
1063
+        **Example response**:
1064
+
1065
+        .. sourcecode:: http
1066
+
1067
+           HTTP/1.1 200 OK
1068
+
1069
+        :statuscode 200: no error
1070
+        :statuscode 204: no error
1071
+        :statuscode 500: server error
1072
+
1073
+
1074
+Display system-wide information
1075
+*******************************
1076
+
1077
+.. http:get:: /info
1078
+
1079
+        Display system-wide information
1080
+
1081
+        **Example request**:
1082
+
1083
+        .. sourcecode:: http
1084
+
1085
+           GET /info HTTP/1.1
1086
+
1087
+        **Example response**:
1088
+
1089
+        .. sourcecode:: http
1090
+
1091
+           HTTP/1.1 200 OK
1092
+           Content-Type: application/json
1093
+
1094
+           {
1095
+                "Containers":11,
1096
+                "Images":16,
1097
+                "Debug":false,
1098
+                "NFd": 11,
1099
+                "NGoroutines":21,
1100
+                "MemoryLimit":true,
1101
+                "SwapLimit":false,
1102
+                "IPv4Forwarding":true
1103
+           }
1104
+
1105
+        :statuscode 200: no error
1106
+        :statuscode 500: server error
1107
+
1108
+
1109
+Show the docker version information
1110
+***********************************
1111
+
1112
+.. http:get:: /version
1113
+
1114
+        Show the docker version information
1115
+
1116
+        **Example request**:
1117
+
1118
+        .. sourcecode:: http
1119
+
1120
+           GET /version HTTP/1.1
1121
+
1122
+        **Example response**:
1123
+
1124
+        .. sourcecode:: http
1125
+
1126
+           HTTP/1.1 200 OK
1127
+           Content-Type: application/json
1128
+
1129
+           {
1130
+                "Version":"0.2.2",
1131
+                "GitCommit":"5a2a5cc+CHANGES",
1132
+                "GoVersion":"go1.0.3"
1133
+           }
1134
+
1135
+        :statuscode 200: no error
1136
+        :statuscode 500: server error
1137
+
1138
+
1139
+Create a new image from a container's changes
1140
+*********************************************
1141
+
1142
+.. http:post:: /commit
1143
+
1144
+    Create a new image from a container's changes
1145
+
1146
+    **Example request**:
1147
+
1148
+    .. sourcecode:: http
1149
+
1150
+        POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1
1151
+
1152
+    **Example response**:
1153
+
1154
+    .. sourcecode:: http
1155
+
1156
+        HTTP/1.1 201 OK
1157
+            Content-Type: application/vnd.docker.raw-stream
1158
+
1159
+        {"Id":"596069db4bf5"}
1160
+
1161
+    :query container: source container
1162
+    :query repo: repository
1163
+    :query tag: tag
1164
+    :query m: commit message
1165
+    :query author: author (eg. "John Hannibal Smith <hannibal@a-team.com>")
1166
+    :query run: config automatically applied when the image is run. (ex: {"Cmd": ["cat", "/world"], "PortSpecs":["22"]})
1167
+    :statuscode 201: no error
1168
+    :statuscode 404: no such container
1169
+    :statuscode 500: server error
1170
+
1171
+
1172
+Monitor Docker's events
1173
+***********************
1174
+
1175
+.. http:get:: /events
1176
+
1177
+        Get events from docker, either in real time via streaming, or via polling (using `since`)
1178
+
1179
+        **Example request**:
1180
+
1181
+        .. sourcecode:: http
1182
+
1183
+           GET /events?since=1374067924
1184
+
1185
+        **Example response**:
1186
+
1187
+        .. sourcecode:: http
1188
+
1189
+           HTTP/1.1 200 OK
1190
+           Content-Type: application/json
1191
+
1192
+           {"status":"create","id":"dfdf82bd3881","from":"base:latest","time":1374067924}
1193
+           {"status":"start","id":"dfdf82bd3881","from":"base:latest","time":1374067924}
1194
+           {"status":"stop","id":"dfdf82bd3881","from":"base:latest","time":1374067966}
1195
+           {"status":"destroy","id":"dfdf82bd3881","from":"base:latest","time":1374067970}
1196
+
1197
+        :query since: timestamp used for polling
1198
+        :statuscode 200: no error
1199
+        :statuscode 500: server error
1200
+
1201
+Get a tarball containing all images and tags in a repository
1202
+************************************************************
1203
+
1204
+.. http:get:: /images/(name)/get
1205
+
1206
+        Get a tarball containing all images and metadata for the repository specified by ``name``.
1207
+
1208
+        **Example request**
1209
+
1210
+        .. sourcecode:: http
1211
+
1212
+           GET /images/ubuntu/get
1213
+
1214
+        **Example response**:
1215
+
1216
+        .. sourcecode:: http
1217
+
1218
+           HTTP/1.1 200 OK
1219
+           Content-Type: application/x-tar
1220
+
1221
+           Binary data stream
1222
+
1223
+        :statuscode 200: no error
1224
+        :statuscode 500: server error
1225
+
1226
+Load a tarball with a set of images and tags into docker
1227
+********************************************************
1228
+
1229
+.. http:post:: /images/load
1230
+
1231
+   Load a set of images and tags into the docker repository.
1232
+
1233
+   **Example request**
1234
+
1235
+   .. sourcecode:: http
1236
+
1237
+      POST /images/load
1238
+
1239
+      Tarball in body
1240
+
1241
+   **Example response**:
1242
+
1243
+   .. sourcecode:: http
1244
+
1245
+      HTTP/1.1 200 OK
1246
+
1247
+   :statuscode 200: no error
1248
+   :statuscode 500: server error
1249
+
1250
+3. Going further
1251
+================
1252
+
1253
+3.1 Inside 'docker run'
1254
+-----------------------
1255
+
1256
+Here are the steps of 'docker run' :
1257
+
1258
+* Create the container
1259
+* If the status code is 404, it means the image doesn't exists:
1260
+        * Try to pull it
1261
+        * Then retry to create the container
1262
+* Start the container
1263
+* If you are not in detached mode:
1264
+        * Attach to the container, using logs=1 (to have stdout and stderr from the container's start) and stream=1
1265
+* If in detached mode or only stdin is attached:
1266
+        * Display the container's id
1267
+
1268
+
1269
+3.2 Hijacking
1270
+-------------
1271
+
1272
+In this version of the API, /attach, uses hijacking to transport stdin, stdout and stderr on the same socket. This might change in the future.
1273
+
1274
+3.3 CORS Requests
1275
+-----------------
1276
+
1277
+To enable cross origin requests to the remote api add the flag "-api-enable-cors" when running docker in daemon mode.
1278
+
1279
+.. code-block:: bash
1280
+
1281
+   docker -d -H="192.168.1.9:4243" -api-enable-cors
... ...
@@ -972,3 +972,25 @@ func NewReadCloserWrapper(r io.Reader, closer func() error) io.ReadCloser {
972 972
 		closer: closer,
973 973
 	}
974 974
 }
975
+
976
+func CompareVersion(a, b string) int {
977
+	aa := strings.Split(a, ".")
978
+	bb := strings.Split(b, ".")
979
+	for i, s := range aa {
980
+		var ai, bi int
981
+		ai, _ = strconv.Atoi(s)
982
+		if len(bb) > i {
983
+			bi, _ = strconv.Atoi(bb[i])
984
+		}
985
+		if ai > bi {
986
+			return 1
987
+		}
988
+		if bi > ai {
989
+			return -1
990
+		}
991
+	}
992
+	if len(bb) > len(aa) {
993
+		return -1
994
+	}
995
+	return 0
996
+}
... ...
@@ -479,3 +479,23 @@ func StrSlicesEqual(a, b []string) bool {
479 479
 
480 480
 	return true
481 481
 }
482
+
483
+func asserVersion(t *testing.T, a, b string, result int) {
484
+	if r := CompareVersion(a, b); r != result {
485
+		t.Fatalf("Unexpected version comparison result. Found %d, expected %d", r, result)
486
+	}
487
+}
488
+
489
+func TestCompareVersion(t *testing.T) {
490
+	asserVersion(t, "1.12", "1.12", 0)
491
+	asserVersion(t, "1.05.00.0156", "1.0.221.9289", 1)
492
+	asserVersion(t, "1", "1.0.1", -1)
493
+	asserVersion(t, "1.0.1", "1", 1)
494
+	asserVersion(t, "1.0.1", "1.0.2", -1)
495
+	asserVersion(t, "1.0.2", "1.0.3", -1)
496
+	asserVersion(t, "1.0.3", "1.1", -1)
497
+	asserVersion(t, "1.1", "1.1.1", -1)
498
+	asserVersion(t, "1.1.1", "1.1.2", -1)
499
+	asserVersion(t, "1.1.2", "1.2", -1)
500
+
501
+}