Browse code

move api to it's own package

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

Victor Vieux authored on 2014/01/30 04:26:54
Showing 10 changed files
1 1
deleted file mode 100644
... ...
@@ -1,1149 +0,0 @@
1
-package docker
2
-
3
-import (
4
-	"bufio"
5
-	"bytes"
6
-	"code.google.com/p/go.net/websocket"
7
-	"encoding/base64"
8
-	"encoding/json"
9
-	"expvar"
10
-	"fmt"
11
-	"github.com/dotcloud/docker/auth"
12
-	"github.com/dotcloud/docker/engine"
13
-	"github.com/dotcloud/docker/pkg/systemd"
14
-	"github.com/dotcloud/docker/utils"
15
-	"github.com/gorilla/mux"
16
-	"io"
17
-	"io/ioutil"
18
-	"log"
19
-	"mime"
20
-	"net"
21
-	"net/http"
22
-	"net/http/pprof"
23
-	"os"
24
-	"regexp"
25
-	"strconv"
26
-	"strings"
27
-	"syscall"
28
-)
29
-
30
-const (
31
-	APIVERSION        = 1.9
32
-	DEFAULTHTTPHOST   = "127.0.0.1"
33
-	DEFAULTHTTPPORT   = 4243
34
-	DEFAULTUNIXSOCKET = "/var/run/docker.sock"
35
-)
36
-
37
-type HttpApiFunc func(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error
38
-
39
-func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) {
40
-	conn, _, err := w.(http.Hijacker).Hijack()
41
-	if err != nil {
42
-		return nil, nil, err
43
-	}
44
-	// Flush the options to make sure the client sets the raw mode
45
-	conn.Write([]byte{})
46
-	return conn, conn, nil
47
-}
48
-
49
-//If we don't do this, POST method without Content-type (even with empty body) will fail
50
-func parseForm(r *http.Request) error {
51
-	if r == nil {
52
-		return nil
53
-	}
54
-	if err := r.ParseForm(); err != nil && !strings.HasPrefix(err.Error(), "mime:") {
55
-		return err
56
-	}
57
-	return nil
58
-}
59
-
60
-func parseMultipartForm(r *http.Request) error {
61
-	if err := r.ParseMultipartForm(4096); err != nil && !strings.HasPrefix(err.Error(), "mime:") {
62
-		return err
63
-	}
64
-	return nil
65
-}
66
-
67
-func httpError(w http.ResponseWriter, err error) {
68
-	statusCode := http.StatusInternalServerError
69
-	// FIXME: this is brittle and should not be necessary.
70
-	// If we need to differentiate between different possible error types, we should
71
-	// create appropriate error types with clearly defined meaning.
72
-	if strings.Contains(err.Error(), "No such") {
73
-		statusCode = http.StatusNotFound
74
-	} else if strings.Contains(err.Error(), "Bad parameter") {
75
-		statusCode = http.StatusBadRequest
76
-	} else if strings.Contains(err.Error(), "Conflict") {
77
-		statusCode = http.StatusConflict
78
-	} else if strings.Contains(err.Error(), "Impossible") {
79
-		statusCode = http.StatusNotAcceptable
80
-	} else if strings.Contains(err.Error(), "Wrong login/password") {
81
-		statusCode = http.StatusUnauthorized
82
-	} else if strings.Contains(err.Error(), "hasn't been activated") {
83
-		statusCode = http.StatusForbidden
84
-	}
85
-
86
-	if err != nil {
87
-		utils.Errorf("HTTP Error: statusCode=%d %s", statusCode, err.Error())
88
-		http.Error(w, err.Error(), statusCode)
89
-	}
90
-}
91
-
92
-func writeJSON(w http.ResponseWriter, code int, v engine.Env) error {
93
-	w.Header().Set("Content-Type", "application/json")
94
-	w.WriteHeader(code)
95
-	return v.Encode(w)
96
-}
97
-
98
-func getBoolParam(value string) (bool, error) {
99
-	if value == "" {
100
-		return false, nil
101
-	}
102
-	ret, err := strconv.ParseBool(value)
103
-	if err != nil {
104
-		return false, fmt.Errorf("Bad parameter")
105
-	}
106
-	return ret, nil
107
-}
108
-
109
-func matchesContentType(contentType, expectedType string) bool {
110
-	mimetype, _, err := mime.ParseMediaType(contentType)
111
-	if err != nil {
112
-		utils.Errorf("Error parsing media type: %s error: %s", contentType, err.Error())
113
-	}
114
-	return err == nil && mimetype == expectedType
115
-}
116
-
117
-func postAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
118
-	var (
119
-		authConfig, err = ioutil.ReadAll(r.Body)
120
-		job             = srv.Eng.Job("auth")
121
-		status          string
122
-	)
123
-	if err != nil {
124
-		return err
125
-	}
126
-	job.Setenv("authConfig", string(authConfig))
127
-	job.Stdout.AddString(&status)
128
-	if err = job.Run(); err != nil {
129
-		return err
130
-	}
131
-	if status != "" {
132
-		var env engine.Env
133
-		env.Set("Status", status)
134
-		return writeJSON(w, http.StatusOK, env)
135
-	}
136
-	w.WriteHeader(http.StatusNoContent)
137
-	return nil
138
-}
139
-
140
-func getVersion(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
141
-	w.Header().Set("Content-Type", "application/json")
142
-	srv.Eng.ServeHTTP(w, r)
143
-	return nil
144
-}
145
-
146
-func postContainersKill(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
147
-	if vars == nil {
148
-		return fmt.Errorf("Missing parameter")
149
-	}
150
-	if err := parseForm(r); err != nil {
151
-		return err
152
-	}
153
-	job := srv.Eng.Job("kill", vars["name"])
154
-	if sig := r.Form.Get("signal"); sig != "" {
155
-		job.Args = append(job.Args, sig)
156
-	}
157
-	if err := job.Run(); err != nil {
158
-		return err
159
-	}
160
-	w.WriteHeader(http.StatusNoContent)
161
-	return nil
162
-}
163
-
164
-func getContainersExport(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
165
-	if vars == nil {
166
-		return fmt.Errorf("Missing parameter")
167
-	}
168
-	job := srv.Eng.Job("export", vars["name"])
169
-	job.Stdout.Add(w)
170
-	if err := job.Run(); err != nil {
171
-		return err
172
-	}
173
-	return nil
174
-}
175
-
176
-func getImagesJSON(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
177
-	if err := parseForm(r); err != nil {
178
-		return err
179
-	}
180
-
181
-	var (
182
-		err  error
183
-		outs *engine.Table
184
-		job  = srv.Eng.Job("images")
185
-	)
186
-
187
-	job.Setenv("filter", r.Form.Get("filter"))
188
-	job.Setenv("all", r.Form.Get("all"))
189
-
190
-	if version > 1.8 {
191
-		job.Stdout.Add(w)
192
-	} else if outs, err = job.Stdout.AddListTable(); err != nil {
193
-		return err
194
-	}
195
-
196
-	if err := job.Run(); err != nil {
197
-		return err
198
-	}
199
-
200
-	if version < 1.8 && outs != nil { // Convert to legacy format
201
-		outsLegacy := engine.NewTable("Created", 0)
202
-		for _, out := range outs.Data {
203
-			for _, repoTag := range out.GetList("RepoTags") {
204
-				parts := strings.Split(repoTag, ":")
205
-				outLegacy := &engine.Env{}
206
-				outLegacy.Set("Repository", parts[0])
207
-				outLegacy.Set("Tag", parts[1])
208
-				outLegacy.Set("ID", out.Get("ID"))
209
-				outLegacy.SetInt64("Created", out.GetInt64("Created"))
210
-				outLegacy.SetInt64("Size", out.GetInt64("Size"))
211
-				outLegacy.SetInt64("VirtualSize", out.GetInt64("VirtualSize"))
212
-				outsLegacy.Add(outLegacy)
213
-			}
214
-		}
215
-		if _, err := outsLegacy.WriteListTo(w); err != nil {
216
-			return err
217
-		}
218
-	}
219
-	return nil
220
-}
221
-
222
-func getImagesViz(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
223
-	if version > 1.6 {
224
-		w.WriteHeader(http.StatusNotFound)
225
-		return fmt.Errorf("This is now implemented in the client.")
226
-	}
227
-	srv.Eng.ServeHTTP(w, r)
228
-	return nil
229
-}
230
-
231
-func getInfo(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
232
-	w.Header().Set("Content-Type", "application/json")
233
-	srv.Eng.ServeHTTP(w, r)
234
-	return nil
235
-}
236
-
237
-func getEvents(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
238
-	if err := parseForm(r); err != nil {
239
-		return err
240
-	}
241
-
242
-	w.Header().Set("Content-Type", "application/json")
243
-	var job = srv.Eng.Job("events", r.RemoteAddr)
244
-	job.Stdout.Add(utils.NewWriteFlusher(w))
245
-	job.Setenv("since", r.Form.Get("since"))
246
-	return job.Run()
247
-}
248
-
249
-func getImagesHistory(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
250
-	if vars == nil {
251
-		return fmt.Errorf("Missing parameter")
252
-	}
253
-
254
-	var job = srv.Eng.Job("history", vars["name"])
255
-	job.Stdout.Add(w)
256
-
257
-	if err := job.Run(); err != nil {
258
-		return err
259
-	}
260
-	return nil
261
-}
262
-
263
-func getContainersChanges(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
264
-	if vars == nil {
265
-		return fmt.Errorf("Missing parameter")
266
-	}
267
-	var job = srv.Eng.Job("changes", vars["name"])
268
-	job.Stdout.Add(w)
269
-
270
-	return job.Run()
271
-}
272
-
273
-func getContainersTop(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
274
-	if version < 1.4 {
275
-		return fmt.Errorf("top was improved a lot since 1.3, Please upgrade your docker client.")
276
-	}
277
-	if vars == nil {
278
-		return fmt.Errorf("Missing parameter")
279
-	}
280
-	if err := parseForm(r); err != nil {
281
-		return err
282
-	}
283
-
284
-	job := srv.Eng.Job("top", vars["name"], r.Form.Get("ps_args"))
285
-	job.Stdout.Add(w)
286
-	return job.Run()
287
-}
288
-
289
-func getContainersJSON(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
290
-	if err := parseForm(r); err != nil {
291
-		return err
292
-	}
293
-	var (
294
-		err  error
295
-		outs *engine.Table
296
-		job  = srv.Eng.Job("containers")
297
-	)
298
-
299
-	job.Setenv("all", r.Form.Get("all"))
300
-	job.Setenv("size", r.Form.Get("size"))
301
-	job.Setenv("since", r.Form.Get("since"))
302
-	job.Setenv("before", r.Form.Get("before"))
303
-	job.Setenv("limit", r.Form.Get("limit"))
304
-
305
-	if version > 1.5 {
306
-		job.Stdout.Add(w)
307
-	} else if outs, err = job.Stdout.AddTable(); err != nil {
308
-		return err
309
-	}
310
-	if err = job.Run(); err != nil {
311
-		return err
312
-	}
313
-	if version < 1.5 { // Convert to legacy format
314
-		for _, out := range outs.Data {
315
-			ports := engine.NewTable("", 0)
316
-			ports.ReadListFrom([]byte(out.Get("Ports")))
317
-			out.Set("Ports", displayablePorts(ports))
318
-		}
319
-		if _, err = outs.WriteListTo(w); err != nil {
320
-			return err
321
-		}
322
-	}
323
-	return nil
324
-}
325
-
326
-func postImagesTag(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
327
-	if err := parseForm(r); err != nil {
328
-		return err
329
-	}
330
-	if vars == nil {
331
-		return fmt.Errorf("Missing parameter")
332
-	}
333
-
334
-	job := srv.Eng.Job("tag", vars["name"], r.Form.Get("repo"), r.Form.Get("tag"))
335
-	job.Setenv("force", r.Form.Get("force"))
336
-	if err := job.Run(); err != nil {
337
-		return err
338
-	}
339
-	w.WriteHeader(http.StatusCreated)
340
-	return nil
341
-}
342
-
343
-func postCommit(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
344
-	if err := parseForm(r); err != nil {
345
-		return err
346
-	}
347
-	var (
348
-		config engine.Env
349
-		env    engine.Env
350
-		job    = srv.Eng.Job("commit", r.Form.Get("container"))
351
-	)
352
-	if err := config.Import(r.Body); err != nil {
353
-		utils.Errorf("%s", err)
354
-	}
355
-
356
-	job.Setenv("repo", r.Form.Get("repo"))
357
-	job.Setenv("tag", r.Form.Get("tag"))
358
-	job.Setenv("author", r.Form.Get("author"))
359
-	job.Setenv("comment", r.Form.Get("comment"))
360
-	job.SetenvSubEnv("config", &config)
361
-
362
-	var id string
363
-	job.Stdout.AddString(&id)
364
-	if err := job.Run(); err != nil {
365
-		return err
366
-	}
367
-	env.Set("Id", id)
368
-	return writeJSON(w, http.StatusCreated, env)
369
-}
370
-
371
-// Creates an image from Pull or from Import
372
-func postImagesCreate(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
373
-	if err := parseForm(r); err != nil {
374
-		return err
375
-	}
376
-
377
-	var (
378
-		image = r.Form.Get("fromImage")
379
-		tag   = r.Form.Get("tag")
380
-		job   *engine.Job
381
-	)
382
-	authEncoded := r.Header.Get("X-Registry-Auth")
383
-	authConfig := &auth.AuthConfig{}
384
-	if authEncoded != "" {
385
-		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
386
-		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
387
-			// for a pull it is not an error if no auth was given
388
-			// to increase compatibility with the existing api it is defaulting to be empty
389
-			authConfig = &auth.AuthConfig{}
390
-		}
391
-	}
392
-	if version > 1.0 {
393
-		w.Header().Set("Content-Type", "application/json")
394
-	}
395
-	if image != "" { //pull
396
-		metaHeaders := map[string][]string{}
397
-		for k, v := range r.Header {
398
-			if strings.HasPrefix(k, "X-Meta-") {
399
-				metaHeaders[k] = v
400
-			}
401
-		}
402
-		job = srv.Eng.Job("pull", r.Form.Get("fromImage"), tag)
403
-		job.SetenvBool("parallel", version > 1.3)
404
-		job.SetenvJson("metaHeaders", metaHeaders)
405
-		job.SetenvJson("authConfig", authConfig)
406
-	} else { //import
407
-		job = srv.Eng.Job("import", r.Form.Get("fromSrc"), r.Form.Get("repo"), tag)
408
-		job.Stdin.Add(r.Body)
409
-	}
410
-
411
-	job.SetenvBool("json", version > 1.0)
412
-	job.Stdout.Add(utils.NewWriteFlusher(w))
413
-	if err := job.Run(); err != nil {
414
-		if !job.Stdout.Used() {
415
-			return err
416
-		}
417
-		sf := utils.NewStreamFormatter(version > 1.0)
418
-		w.Write(sf.FormatError(err))
419
-	}
420
-
421
-	return nil
422
-}
423
-
424
-func getImagesSearch(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
425
-	if err := parseForm(r); err != nil {
426
-		return err
427
-	}
428
-	var (
429
-		authEncoded = r.Header.Get("X-Registry-Auth")
430
-		authConfig  = &auth.AuthConfig{}
431
-		metaHeaders = map[string][]string{}
432
-	)
433
-
434
-	if authEncoded != "" {
435
-		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
436
-		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
437
-			// for a search it is not an error if no auth was given
438
-			// to increase compatibility with the existing api it is defaulting to be empty
439
-			authConfig = &auth.AuthConfig{}
440
-		}
441
-	}
442
-	for k, v := range r.Header {
443
-		if strings.HasPrefix(k, "X-Meta-") {
444
-			metaHeaders[k] = v
445
-		}
446
-	}
447
-
448
-	var job = srv.Eng.Job("search", r.Form.Get("term"))
449
-	job.SetenvJson("metaHeaders", metaHeaders)
450
-	job.SetenvJson("authConfig", authConfig)
451
-	job.Stdout.Add(w)
452
-
453
-	return job.Run()
454
-}
455
-
456
-func postImagesInsert(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
457
-	if err := parseForm(r); err != nil {
458
-		return err
459
-	}
460
-	if vars == nil {
461
-		return fmt.Errorf("Missing parameter")
462
-	}
463
-	if version > 1.0 {
464
-		w.Header().Set("Content-Type", "application/json")
465
-	}
466
-
467
-	job := srv.Eng.Job("insert", vars["name"], r.Form.Get("url"), r.Form.Get("path"))
468
-	job.SetenvBool("json", version > 1.0)
469
-	job.Stdout.Add(w)
470
-	if err := job.Run(); err != nil {
471
-		if !job.Stdout.Used() {
472
-			return err
473
-		}
474
-		sf := utils.NewStreamFormatter(version > 1.0)
475
-		w.Write(sf.FormatError(err))
476
-	}
477
-
478
-	return nil
479
-}
480
-
481
-func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
482
-	if vars == nil {
483
-		return fmt.Errorf("Missing parameter")
484
-	}
485
-
486
-	metaHeaders := map[string][]string{}
487
-	for k, v := range r.Header {
488
-		if strings.HasPrefix(k, "X-Meta-") {
489
-			metaHeaders[k] = v
490
-		}
491
-	}
492
-	if err := parseForm(r); err != nil {
493
-		return err
494
-	}
495
-	authConfig := &auth.AuthConfig{}
496
-
497
-	authEncoded := r.Header.Get("X-Registry-Auth")
498
-	if authEncoded != "" {
499
-		// the new format is to handle the authConfig as a header
500
-		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
501
-		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
502
-			// to increase compatibility to existing api it is defaulting to be empty
503
-			authConfig = &auth.AuthConfig{}
504
-		}
505
-	} else {
506
-		// the old format is supported for compatibility if there was no authConfig header
507
-		if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil {
508
-			return err
509
-		}
510
-	}
511
-
512
-	if version > 1.0 {
513
-		w.Header().Set("Content-Type", "application/json")
514
-	}
515
-	job := srv.Eng.Job("push", vars["name"])
516
-	job.SetenvJson("metaHeaders", metaHeaders)
517
-	job.SetenvJson("authConfig", authConfig)
518
-	job.SetenvBool("json", version > 1.0)
519
-	job.Stdout.Add(utils.NewWriteFlusher(w))
520
-
521
-	if err := job.Run(); err != nil {
522
-		if !job.Stdout.Used() {
523
-			return err
524
-		}
525
-		sf := utils.NewStreamFormatter(version > 1.0)
526
-		w.Write(sf.FormatError(err))
527
-	}
528
-	return nil
529
-}
530
-
531
-func getImagesGet(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
532
-	if vars == nil {
533
-		return fmt.Errorf("Missing parameter")
534
-	}
535
-	if version > 1.0 {
536
-		w.Header().Set("Content-Type", "application/x-tar")
537
-	}
538
-	job := srv.Eng.Job("image_export", vars["name"])
539
-	job.Stdout.Add(w)
540
-	return job.Run()
541
-}
542
-
543
-func postImagesLoad(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
544
-	job := srv.Eng.Job("load")
545
-	job.Stdin.Add(r.Body)
546
-	return job.Run()
547
-}
548
-
549
-func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
550
-	if err := parseForm(r); err != nil {
551
-		return nil
552
-	}
553
-	var (
554
-		out         engine.Env
555
-		job         = srv.Eng.Job("create", r.Form.Get("name"))
556
-		outWarnings []string
557
-		outId       string
558
-		warnings    = bytes.NewBuffer(nil)
559
-	)
560
-	if err := job.DecodeEnv(r.Body); err != nil {
561
-		return err
562
-	}
563
-	// Read container ID from the first line of stdout
564
-	job.Stdout.AddString(&outId)
565
-	// Read warnings from stderr
566
-	job.Stderr.Add(warnings)
567
-	if err := job.Run(); err != nil {
568
-		return err
569
-	}
570
-	// Parse warnings from stderr
571
-	scanner := bufio.NewScanner(warnings)
572
-	for scanner.Scan() {
573
-		outWarnings = append(outWarnings, scanner.Text())
574
-	}
575
-	out.Set("Id", outId)
576
-	out.SetList("Warnings", outWarnings)
577
-	return writeJSON(w, http.StatusCreated, out)
578
-}
579
-
580
-func postContainersRestart(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
581
-	if err := parseForm(r); err != nil {
582
-		return err
583
-	}
584
-	if vars == nil {
585
-		return fmt.Errorf("Missing parameter")
586
-	}
587
-	job := srv.Eng.Job("restart", vars["name"])
588
-	job.Setenv("t", r.Form.Get("t"))
589
-	if err := job.Run(); err != nil {
590
-		return err
591
-	}
592
-	w.WriteHeader(http.StatusNoContent)
593
-	return nil
594
-}
595
-
596
-func deleteContainers(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
597
-	if err := parseForm(r); err != nil {
598
-		return err
599
-	}
600
-	if vars == nil {
601
-		return fmt.Errorf("Missing parameter")
602
-	}
603
-	job := srv.Eng.Job("container_delete", vars["name"])
604
-	job.Setenv("removeVolume", r.Form.Get("v"))
605
-	job.Setenv("removeLink", r.Form.Get("link"))
606
-	if err := job.Run(); err != nil {
607
-		return err
608
-	}
609
-	w.WriteHeader(http.StatusNoContent)
610
-	return nil
611
-}
612
-
613
-func deleteImages(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
614
-	if err := parseForm(r); err != nil {
615
-		return err
616
-	}
617
-	if vars == nil {
618
-		return fmt.Errorf("Missing parameter")
619
-	}
620
-	var job = srv.Eng.Job("image_delete", vars["name"])
621
-	job.Stdout.Add(w)
622
-	job.SetenvBool("autoPrune", version > 1.1)
623
-
624
-	return job.Run()
625
-}
626
-
627
-func postContainersStart(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
628
-	if vars == nil {
629
-		return fmt.Errorf("Missing parameter")
630
-	}
631
-	name := vars["name"]
632
-	job := srv.Eng.Job("start", name)
633
-	// allow a nil body for backwards compatibility
634
-	if r.Body != nil {
635
-		if matchesContentType(r.Header.Get("Content-Type"), "application/json") {
636
-			if err := job.DecodeEnv(r.Body); err != nil {
637
-				return err
638
-			}
639
-		}
640
-	}
641
-	if err := job.Run(); err != nil {
642
-		return err
643
-	}
644
-	w.WriteHeader(http.StatusNoContent)
645
-	return nil
646
-}
647
-
648
-func postContainersStop(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
649
-	if err := parseForm(r); err != nil {
650
-		return err
651
-	}
652
-	if vars == nil {
653
-		return fmt.Errorf("Missing parameter")
654
-	}
655
-	job := srv.Eng.Job("stop", vars["name"])
656
-	job.Setenv("t", r.Form.Get("t"))
657
-	if err := job.Run(); err != nil {
658
-		return err
659
-	}
660
-	w.WriteHeader(http.StatusNoContent)
661
-	return nil
662
-}
663
-
664
-func postContainersWait(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
665
-	if vars == nil {
666
-		return fmt.Errorf("Missing parameter")
667
-	}
668
-	var (
669
-		env    engine.Env
670
-		status string
671
-		job    = srv.Eng.Job("wait", vars["name"])
672
-	)
673
-	job.Stdout.AddString(&status)
674
-	if err := job.Run(); err != nil {
675
-		return err
676
-	}
677
-	// Parse a 16-bit encoded integer to map typical unix exit status.
678
-	_, err := strconv.ParseInt(status, 10, 16)
679
-	if err != nil {
680
-		return err
681
-	}
682
-	env.Set("StatusCode", status)
683
-	return writeJSON(w, http.StatusOK, env)
684
-}
685
-
686
-func postContainersResize(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
687
-	if err := parseForm(r); err != nil {
688
-		return err
689
-	}
690
-	if vars == nil {
691
-		return fmt.Errorf("Missing parameter")
692
-	}
693
-	if err := srv.Eng.Job("resize", vars["name"], r.Form.Get("h"), r.Form.Get("w")).Run(); err != nil {
694
-		return err
695
-	}
696
-	return nil
697
-}
698
-
699
-func postContainersAttach(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
700
-	if err := parseForm(r); err != nil {
701
-		return err
702
-	}
703
-	if vars == nil {
704
-		return fmt.Errorf("Missing parameter")
705
-	}
706
-
707
-	var (
708
-		job    = srv.Eng.Job("inspect", vars["name"], "container")
709
-		c, err = job.Stdout.AddEnv()
710
-	)
711
-	if err != nil {
712
-		return err
713
-	}
714
-	if err = job.Run(); err != nil {
715
-		return err
716
-	}
717
-
718
-	inStream, outStream, err := hijackServer(w)
719
-	if err != nil {
720
-		return err
721
-	}
722
-	defer func() {
723
-		if tcpc, ok := inStream.(*net.TCPConn); ok {
724
-			tcpc.CloseWrite()
725
-		} else {
726
-			inStream.Close()
727
-		}
728
-	}()
729
-	defer func() {
730
-		if tcpc, ok := outStream.(*net.TCPConn); ok {
731
-			tcpc.CloseWrite()
732
-		} else if closer, ok := outStream.(io.Closer); ok {
733
-			closer.Close()
734
-		}
735
-	}()
736
-
737
-	var errStream io.Writer
738
-
739
-	fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
740
-
741
-	if c.GetSubEnv("Config") != nil && !c.GetSubEnv("Config").GetBool("Tty") && version >= 1.6 {
742
-		errStream = utils.NewStdWriter(outStream, utils.Stderr)
743
-		outStream = utils.NewStdWriter(outStream, utils.Stdout)
744
-	} else {
745
-		errStream = outStream
746
-	}
747
-
748
-	job = srv.Eng.Job("attach", vars["name"])
749
-	job.Setenv("logs", r.Form.Get("logs"))
750
-	job.Setenv("stream", r.Form.Get("stream"))
751
-	job.Setenv("stdin", r.Form.Get("stdin"))
752
-	job.Setenv("stdout", r.Form.Get("stdout"))
753
-	job.Setenv("stderr", r.Form.Get("stderr"))
754
-	job.Stdin.Add(inStream)
755
-	job.Stdout.Add(outStream)
756
-	job.Stderr.Set(errStream)
757
-	if err := job.Run(); err != nil {
758
-		fmt.Fprintf(outStream, "Error: %s\n", err)
759
-
760
-	}
761
-	return nil
762
-}
763
-
764
-func wsContainersAttach(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
765
-	if err := parseForm(r); err != nil {
766
-		return err
767
-	}
768
-	if vars == nil {
769
-		return fmt.Errorf("Missing parameter")
770
-	}
771
-
772
-	if err := srv.Eng.Job("inspect", vars["name"], "container").Run(); err != nil {
773
-		return err
774
-	}
775
-
776
-	h := websocket.Handler(func(ws *websocket.Conn) {
777
-		defer ws.Close()
778
-		job := srv.Eng.Job("attach", vars["name"])
779
-		job.Setenv("logs", r.Form.Get("logs"))
780
-		job.Setenv("stream", r.Form.Get("stream"))
781
-		job.Setenv("stdin", r.Form.Get("stdin"))
782
-		job.Setenv("stdout", r.Form.Get("stdout"))
783
-		job.Setenv("stderr", r.Form.Get("stderr"))
784
-		job.Stdin.Add(ws)
785
-		job.Stdout.Add(ws)
786
-		job.Stderr.Set(ws)
787
-		if err := job.Run(); err != nil {
788
-			utils.Errorf("Error: %s", err)
789
-		}
790
-	})
791
-	h.ServeHTTP(w, r)
792
-
793
-	return nil
794
-}
795
-
796
-func getContainersByName(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
797
-	if vars == nil {
798
-		return fmt.Errorf("Missing parameter")
799
-	}
800
-	var job = srv.Eng.Job("inspect", vars["name"], "container")
801
-	job.Stdout.Add(w)
802
-	job.SetenvBool("conflict", true) //conflict=true to detect conflict between containers and images in the job
803
-	return job.Run()
804
-}
805
-
806
-func getImagesByName(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
807
-	if vars == nil {
808
-		return fmt.Errorf("Missing parameter")
809
-	}
810
-	var job = srv.Eng.Job("inspect", vars["name"], "image")
811
-	job.Stdout.Add(w)
812
-	job.SetenvBool("conflict", true) //conflict=true to detect conflict between containers and images in the job
813
-	return job.Run()
814
-}
815
-
816
-func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
817
-	if version < 1.3 {
818
-		return fmt.Errorf("Multipart upload for build is no longer supported. Please upgrade your docker client.")
819
-	}
820
-	var (
821
-		authEncoded       = r.Header.Get("X-Registry-Auth")
822
-		authConfig        = &auth.AuthConfig{}
823
-		configFileEncoded = r.Header.Get("X-Registry-Config")
824
-		configFile        = &auth.ConfigFile{}
825
-		job               = srv.Eng.Job("build")
826
-	)
827
-
828
-	// This block can be removed when API versions prior to 1.9 are deprecated.
829
-	// Both headers will be parsed and sent along to the daemon, but if a non-empty
830
-	// ConfigFile is present, any value provided as an AuthConfig directly will
831
-	// be overridden. See BuildFile::CmdFrom for details.
832
-	if version < 1.9 && authEncoded != "" {
833
-		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
834
-		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
835
-			// for a pull it is not an error if no auth was given
836
-			// to increase compatibility with the existing api it is defaulting to be empty
837
-			authConfig = &auth.AuthConfig{}
838
-		}
839
-	}
840
-
841
-	if configFileEncoded != "" {
842
-		configFileJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(configFileEncoded))
843
-		if err := json.NewDecoder(configFileJson).Decode(configFile); err != nil {
844
-			// for a pull it is not an error if no auth was given
845
-			// to increase compatibility with the existing api it is defaulting to be empty
846
-			configFile = &auth.ConfigFile{}
847
-		}
848
-	}
849
-
850
-	if version >= 1.8 {
851
-		w.Header().Set("Content-Type", "application/json")
852
-		job.SetenvBool("json", true)
853
-	}
854
-
855
-	job.Stdout.Add(utils.NewWriteFlusher(w))
856
-	job.Stdin.Add(r.Body)
857
-	job.Setenv("remote", r.FormValue("remote"))
858
-	job.Setenv("t", r.FormValue("t"))
859
-	job.Setenv("q", r.FormValue("q"))
860
-	job.Setenv("nocache", r.FormValue("nocache"))
861
-	job.Setenv("rm", r.FormValue("rm"))
862
-
863
-	if err := job.Run(); err != nil {
864
-		if !job.Stdout.Used() {
865
-			return err
866
-		}
867
-		sf := utils.NewStreamFormatter(version >= 1.8)
868
-		w.Write(sf.FormatError(err))
869
-	}
870
-	return nil
871
-}
872
-
873
-func postContainersCopy(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
874
-	if vars == nil {
875
-		return fmt.Errorf("Missing parameter")
876
-	}
877
-
878
-	var copyData engine.Env
879
-
880
-	if contentType := r.Header.Get("Content-Type"); contentType == "application/json" {
881
-		if err := copyData.Decode(r.Body); err != nil {
882
-			return err
883
-		}
884
-	} else {
885
-		return fmt.Errorf("Content-Type not supported: %s", contentType)
886
-	}
887
-
888
-	if copyData.Get("Resource") == "" {
889
-		return fmt.Errorf("Path cannot be empty")
890
-	}
891
-	if copyData.Get("Resource")[0] == '/' {
892
-		copyData.Set("Resource", copyData.Get("Resource")[1:])
893
-	}
894
-
895
-	job := srv.Eng.Job("container_copy", vars["name"], copyData.Get("Resource"))
896
-	job.Stdout.Add(w)
897
-	if err := job.Run(); err != nil {
898
-		utils.Errorf("%s", err.Error())
899
-	}
900
-	return nil
901
-}
902
-
903
-func optionsHandler(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
904
-	w.WriteHeader(http.StatusOK)
905
-	return nil
906
-}
907
-func writeCorsHeaders(w http.ResponseWriter, r *http.Request) {
908
-	w.Header().Add("Access-Control-Allow-Origin", "*")
909
-	w.Header().Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
910
-	w.Header().Add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS")
911
-}
912
-
913
-func makeHttpHandler(srv *Server, logging bool, localMethod string, localRoute string, handlerFunc HttpApiFunc, enableCors bool) http.HandlerFunc {
914
-	return func(w http.ResponseWriter, r *http.Request) {
915
-		// log the request
916
-		utils.Debugf("Calling %s %s", localMethod, localRoute)
917
-
918
-		if logging {
919
-			log.Println(r.Method, r.RequestURI)
920
-		}
921
-
922
-		if strings.Contains(r.Header.Get("User-Agent"), "Docker-Client/") {
923
-			userAgent := strings.Split(r.Header.Get("User-Agent"), "/")
924
-			if len(userAgent) == 2 && userAgent[1] != VERSION {
925
-				utils.Debugf("Warning: client and server don't have the same version (client: %s, server: %s)", userAgent[1], VERSION)
926
-			}
927
-		}
928
-		version, err := strconv.ParseFloat(mux.Vars(r)["version"], 64)
929
-		if err != nil {
930
-			version = APIVERSION
931
-		}
932
-		if enableCors {
933
-			writeCorsHeaders(w, r)
934
-		}
935
-
936
-		if version == 0 || version > APIVERSION {
937
-			http.Error(w, fmt.Errorf("client and server don't have same version (client : %g, server: %g)", version, APIVERSION).Error(), http.StatusNotFound)
938
-			return
939
-		}
940
-
941
-		if err := handlerFunc(srv, version, w, r, mux.Vars(r)); err != nil {
942
-			utils.Errorf("Error: %s", err)
943
-			httpError(w, err)
944
-		}
945
-	}
946
-}
947
-
948
-// Replicated from expvar.go as not public.
949
-func expvarHandler(w http.ResponseWriter, r *http.Request) {
950
-	w.Header().Set("Content-Type", "application/json; charset=utf-8")
951
-	fmt.Fprintf(w, "{\n")
952
-	first := true
953
-	expvar.Do(func(kv expvar.KeyValue) {
954
-		if !first {
955
-			fmt.Fprintf(w, ",\n")
956
-		}
957
-		first = false
958
-		fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value)
959
-	})
960
-	fmt.Fprintf(w, "\n}\n")
961
-}
962
-
963
-func AttachProfiler(router *mux.Router) {
964
-	router.HandleFunc("/debug/vars", expvarHandler)
965
-	router.HandleFunc("/debug/pprof/", pprof.Index)
966
-	router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
967
-	router.HandleFunc("/debug/pprof/profile", pprof.Profile)
968
-	router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
969
-	router.HandleFunc("/debug/pprof/heap", pprof.Handler("heap").ServeHTTP)
970
-	router.HandleFunc("/debug/pprof/goroutine", pprof.Handler("goroutine").ServeHTTP)
971
-	router.HandleFunc("/debug/pprof/threadcreate", pprof.Handler("threadcreate").ServeHTTP)
972
-}
973
-
974
-func createRouter(srv *Server, logging, enableCors bool) (*mux.Router, error) {
975
-	r := mux.NewRouter()
976
-	if os.Getenv("DEBUG") != "" {
977
-		AttachProfiler(r)
978
-	}
979
-	m := map[string]map[string]HttpApiFunc{
980
-		"GET": {
981
-			"/events":                         getEvents,
982
-			"/info":                           getInfo,
983
-			"/version":                        getVersion,
984
-			"/images/json":                    getImagesJSON,
985
-			"/images/viz":                     getImagesViz,
986
-			"/images/search":                  getImagesSearch,
987
-			"/images/{name:.*}/get":           getImagesGet,
988
-			"/images/{name:.*}/history":       getImagesHistory,
989
-			"/images/{name:.*}/json":          getImagesByName,
990
-			"/containers/ps":                  getContainersJSON,
991
-			"/containers/json":                getContainersJSON,
992
-			"/containers/{name:.*}/export":    getContainersExport,
993
-			"/containers/{name:.*}/changes":   getContainersChanges,
994
-			"/containers/{name:.*}/json":      getContainersByName,
995
-			"/containers/{name:.*}/top":       getContainersTop,
996
-			"/containers/{name:.*}/attach/ws": wsContainersAttach,
997
-		},
998
-		"POST": {
999
-			"/auth":                         postAuth,
1000
-			"/commit":                       postCommit,
1001
-			"/build":                        postBuild,
1002
-			"/images/create":                postImagesCreate,
1003
-			"/images/{name:.*}/insert":      postImagesInsert,
1004
-			"/images/load":                  postImagesLoad,
1005
-			"/images/{name:.*}/push":        postImagesPush,
1006
-			"/images/{name:.*}/tag":         postImagesTag,
1007
-			"/containers/create":            postContainersCreate,
1008
-			"/containers/{name:.*}/kill":    postContainersKill,
1009
-			"/containers/{name:.*}/restart": postContainersRestart,
1010
-			"/containers/{name:.*}/start":   postContainersStart,
1011
-			"/containers/{name:.*}/stop":    postContainersStop,
1012
-			"/containers/{name:.*}/wait":    postContainersWait,
1013
-			"/containers/{name:.*}/resize":  postContainersResize,
1014
-			"/containers/{name:.*}/attach":  postContainersAttach,
1015
-			"/containers/{name:.*}/copy":    postContainersCopy,
1016
-		},
1017
-		"DELETE": {
1018
-			"/containers/{name:.*}": deleteContainers,
1019
-			"/images/{name:.*}":     deleteImages,
1020
-		},
1021
-		"OPTIONS": {
1022
-			"": optionsHandler,
1023
-		},
1024
-	}
1025
-
1026
-	for method, routes := range m {
1027
-		for route, fct := range routes {
1028
-			utils.Debugf("Registering %s, %s", method, route)
1029
-			// NOTE: scope issue, make sure the variables are local and won't be changed
1030
-			localRoute := route
1031
-			localFct := fct
1032
-			localMethod := method
1033
-
1034
-			// build the handler function
1035
-			f := makeHttpHandler(srv, logging, localMethod, localRoute, localFct, enableCors)
1036
-
1037
-			// add the new route
1038
-			if localRoute == "" {
1039
-				r.Methods(localMethod).HandlerFunc(f)
1040
-			} else {
1041
-				r.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(f)
1042
-				r.Path(localRoute).Methods(localMethod).HandlerFunc(f)
1043
-			}
1044
-		}
1045
-	}
1046
-
1047
-	return r, nil
1048
-}
1049
-
1050
-// ServeRequest processes a single http request to the docker remote api.
1051
-// FIXME: refactor this to be part of Server and not require re-creating a new
1052
-// router each time. This requires first moving ListenAndServe into Server.
1053
-func ServeRequest(srv *Server, apiversion float64, w http.ResponseWriter, req *http.Request) error {
1054
-	router, err := createRouter(srv, false, true)
1055
-	if err != nil {
1056
-		return err
1057
-	}
1058
-	// Insert APIVERSION into the request as a convenience
1059
-	req.URL.Path = fmt.Sprintf("/v%g%s", apiversion, req.URL.Path)
1060
-	router.ServeHTTP(w, req)
1061
-	return nil
1062
-}
1063
-
1064
-// ServeFD creates an http.Server and sets it up to serve given a socket activated
1065
-// argument.
1066
-func ServeFd(addr string, handle http.Handler) error {
1067
-	ls, e := systemd.ListenFD(addr)
1068
-	if e != nil {
1069
-		return e
1070
-	}
1071
-
1072
-	chErrors := make(chan error, len(ls))
1073
-
1074
-	// Since ListenFD will return one or more sockets we have
1075
-	// to create a go func to spawn off multiple serves
1076
-	for i := range ls {
1077
-		listener := ls[i]
1078
-		go func() {
1079
-			httpSrv := http.Server{Handler: handle}
1080
-			chErrors <- httpSrv.Serve(listener)
1081
-		}()
1082
-	}
1083
-
1084
-	for i := 0; i < len(ls); i += 1 {
1085
-		err := <-chErrors
1086
-		if err != nil {
1087
-			return err
1088
-		}
1089
-	}
1090
-
1091
-	return nil
1092
-}
1093
-
1094
-// ListenAndServe sets up the required http.Server and gets it listening for
1095
-// each addr passed in and does protocol specific checking.
1096
-func ListenAndServe(proto, addr string, srv *Server, logging, enableCors bool) error {
1097
-	r, err := createRouter(srv, logging, enableCors)
1098
-	if err != nil {
1099
-		return err
1100
-	}
1101
-
1102
-	if proto == "fd" {
1103
-		return ServeFd(addr, r)
1104
-	}
1105
-
1106
-	if proto == "unix" {
1107
-		if err := syscall.Unlink(addr); err != nil && !os.IsNotExist(err) {
1108
-			return err
1109
-		}
1110
-	}
1111
-
1112
-	l, err := net.Listen(proto, addr)
1113
-	if err != nil {
1114
-		return err
1115
-	}
1116
-
1117
-	// Basic error and sanity checking
1118
-	switch proto {
1119
-	case "tcp":
1120
-		if !strings.HasPrefix(addr, "127.0.0.1") {
1121
-			log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
1122
-		}
1123
-	case "unix":
1124
-		if err := os.Chmod(addr, 0660); err != nil {
1125
-			return err
1126
-		}
1127
-
1128
-		groups, err := ioutil.ReadFile("/etc/group")
1129
-		if err != nil {
1130
-			return err
1131
-		}
1132
-		re := regexp.MustCompile("(^|\n)docker:.*?:([0-9]+)")
1133
-		if gidMatch := re.FindStringSubmatch(string(groups)); gidMatch != nil {
1134
-			gid, err := strconv.Atoi(gidMatch[2])
1135
-			if err != nil {
1136
-				return err
1137
-			}
1138
-			utils.Debugf("docker group found. gid: %d", gid)
1139
-			if err := os.Chown(addr, 0, gid); err != nil {
1140
-				return err
1141
-			}
1142
-		}
1143
-	default:
1144
-		return fmt.Errorf("Invalid protocol format.")
1145
-	}
1146
-
1147
-	httpSrv := http.Server{Addr: addr, Handler: r}
1148
-	return httpSrv.Serve(l)
1149
-}
1150 1
new file mode 100644
... ...
@@ -0,0 +1,1162 @@
0
+package api
1
+
2
+import (
3
+	"bufio"
4
+	"bytes"
5
+	"code.google.com/p/go.net/websocket"
6
+	"encoding/base64"
7
+	"encoding/json"
8
+	"expvar"
9
+	"fmt"
10
+	"github.com/dotcloud/docker/auth"
11
+	"github.com/dotcloud/docker/engine"
12
+	"github.com/dotcloud/docker/pkg/systemd"
13
+	"github.com/dotcloud/docker/utils"
14
+	"github.com/gorilla/mux"
15
+	"io"
16
+	"io/ioutil"
17
+	"log"
18
+	"mime"
19
+	"net"
20
+	"net/http"
21
+	"net/http/pprof"
22
+	"os"
23
+	"regexp"
24
+	"strconv"
25
+	"strings"
26
+	"syscall"
27
+)
28
+
29
+const (
30
+	APIVERSION        = 1.9
31
+	DEFAULTHTTPHOST   = "127.0.0.1"
32
+	DEFAULTHTTPPORT   = 4243
33
+	DEFAULTUNIXSOCKET = "/var/run/docker.sock"
34
+)
35
+
36
+type HttpApiFunc func(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error
37
+
38
+func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) {
39
+	conn, _, err := w.(http.Hijacker).Hijack()
40
+	if err != nil {
41
+		return nil, nil, err
42
+	}
43
+	// Flush the options to make sure the client sets the raw mode
44
+	conn.Write([]byte{})
45
+	return conn, conn, nil
46
+}
47
+
48
+//If we don't do this, POST method without Content-type (even with empty body) will fail
49
+func parseForm(r *http.Request) error {
50
+	if r == nil {
51
+		return nil
52
+	}
53
+	if err := r.ParseForm(); err != nil && !strings.HasPrefix(err.Error(), "mime:") {
54
+		return err
55
+	}
56
+	return nil
57
+}
58
+
59
+func parseMultipartForm(r *http.Request) error {
60
+	if err := r.ParseMultipartForm(4096); err != nil && !strings.HasPrefix(err.Error(), "mime:") {
61
+		return err
62
+	}
63
+	return nil
64
+}
65
+
66
+func httpError(w http.ResponseWriter, err error) {
67
+	statusCode := http.StatusInternalServerError
68
+	// FIXME: this is brittle and should not be necessary.
69
+	// If we need to differentiate between different possible error types, we should
70
+	// create appropriate error types with clearly defined meaning.
71
+	if strings.Contains(err.Error(), "No such") {
72
+		statusCode = http.StatusNotFound
73
+	} else if strings.Contains(err.Error(), "Bad parameter") {
74
+		statusCode = http.StatusBadRequest
75
+	} else if strings.Contains(err.Error(), "Conflict") {
76
+		statusCode = http.StatusConflict
77
+	} else if strings.Contains(err.Error(), "Impossible") {
78
+		statusCode = http.StatusNotAcceptable
79
+	} else if strings.Contains(err.Error(), "Wrong login/password") {
80
+		statusCode = http.StatusUnauthorized
81
+	} else if strings.Contains(err.Error(), "hasn't been activated") {
82
+		statusCode = http.StatusForbidden
83
+	}
84
+
85
+	if err != nil {
86
+		utils.Errorf("HTTP Error: statusCode=%d %s", statusCode, err.Error())
87
+		http.Error(w, err.Error(), statusCode)
88
+	}
89
+}
90
+
91
+func writeJSON(w http.ResponseWriter, code int, v engine.Env) error {
92
+	w.Header().Set("Content-Type", "application/json")
93
+	w.WriteHeader(code)
94
+	return v.Encode(w)
95
+}
96
+
97
+func getBoolParam(value string) (bool, error) {
98
+	if value == "" {
99
+		return false, nil
100
+	}
101
+	ret, err := strconv.ParseBool(value)
102
+	if err != nil {
103
+		return false, fmt.Errorf("Bad parameter")
104
+	}
105
+	return ret, nil
106
+}
107
+
108
+//TODO remove, used on < 1.5 in getContainersJSON
109
+func displayablePorts(ports *engine.Table) string {
110
+	result := []string{}
111
+	for _, port := range ports.Data {
112
+		if port.Get("IP") == "" {
113
+			result = append(result, fmt.Sprintf("%d/%s", port.GetInt("PublicPort"), port.Get("Type")))
114
+		} else {
115
+			result = append(result, fmt.Sprintf("%s:%d->%d/%s", port.Get("IP"), port.GetInt("PublicPort"), port.GetInt("PrivatePort"), port.Get("Type")))
116
+		}
117
+	}
118
+	return strings.Join(result, ", ")
119
+}
120
+
121
+func MatchesContentType(contentType, expectedType string) bool {
122
+	mimetype, _, err := mime.ParseMediaType(contentType)
123
+	if err != nil {
124
+		utils.Errorf("Error parsing media type: %s error: %s", contentType, err.Error())
125
+	}
126
+	return err == nil && mimetype == expectedType
127
+}
128
+
129
+func postAuth(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
130
+	var (
131
+		authConfig, err = ioutil.ReadAll(r.Body)
132
+		job             = eng.Job("auth")
133
+		status          string
134
+	)
135
+	if err != nil {
136
+		return err
137
+	}
138
+	job.Setenv("authConfig", string(authConfig))
139
+	job.Stdout.AddString(&status)
140
+	if err = job.Run(); err != nil {
141
+		return err
142
+	}
143
+	if status != "" {
144
+		var env engine.Env
145
+		env.Set("Status", status)
146
+		return writeJSON(w, http.StatusOK, env)
147
+	}
148
+	w.WriteHeader(http.StatusNoContent)
149
+	return nil
150
+}
151
+
152
+func getVersion(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
153
+	w.Header().Set("Content-Type", "application/json")
154
+	eng.ServeHTTP(w, r)
155
+	return nil
156
+}
157
+
158
+func postContainersKill(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
159
+	if vars == nil {
160
+		return fmt.Errorf("Missing parameter")
161
+	}
162
+	if err := parseForm(r); err != nil {
163
+		return err
164
+	}
165
+	job := eng.Job("kill", vars["name"])
166
+	if sig := r.Form.Get("signal"); sig != "" {
167
+		job.Args = append(job.Args, sig)
168
+	}
169
+	if err := job.Run(); err != nil {
170
+		return err
171
+	}
172
+	w.WriteHeader(http.StatusNoContent)
173
+	return nil
174
+}
175
+
176
+func getContainersExport(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
177
+	if vars == nil {
178
+		return fmt.Errorf("Missing parameter")
179
+	}
180
+	job := eng.Job("export", vars["name"])
181
+	job.Stdout.Add(w)
182
+	if err := job.Run(); err != nil {
183
+		return err
184
+	}
185
+	return nil
186
+}
187
+
188
+func getImagesJSON(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
189
+	if err := parseForm(r); err != nil {
190
+		return err
191
+	}
192
+
193
+	var (
194
+		err  error
195
+		outs *engine.Table
196
+		job  = eng.Job("images")
197
+	)
198
+
199
+	job.Setenv("filter", r.Form.Get("filter"))
200
+	job.Setenv("all", r.Form.Get("all"))
201
+
202
+	if version > 1.8 {
203
+		job.Stdout.Add(w)
204
+	} else if outs, err = job.Stdout.AddListTable(); err != nil {
205
+		return err
206
+	}
207
+
208
+	if err := job.Run(); err != nil {
209
+		return err
210
+	}
211
+
212
+	if version < 1.8 && outs != nil { // Convert to legacy format
213
+		outsLegacy := engine.NewTable("Created", 0)
214
+		for _, out := range outs.Data {
215
+			for _, repoTag := range out.GetList("RepoTags") {
216
+				parts := strings.Split(repoTag, ":")
217
+				outLegacy := &engine.Env{}
218
+				outLegacy.Set("Repository", parts[0])
219
+				outLegacy.Set("Tag", parts[1])
220
+				outLegacy.Set("ID", out.Get("ID"))
221
+				outLegacy.SetInt64("Created", out.GetInt64("Created"))
222
+				outLegacy.SetInt64("Size", out.GetInt64("Size"))
223
+				outLegacy.SetInt64("VirtualSize", out.GetInt64("VirtualSize"))
224
+				outsLegacy.Add(outLegacy)
225
+			}
226
+		}
227
+		if _, err := outsLegacy.WriteListTo(w); err != nil {
228
+			return err
229
+		}
230
+	}
231
+	return nil
232
+}
233
+
234
+func getImagesViz(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
235
+	if version > 1.6 {
236
+		w.WriteHeader(http.StatusNotFound)
237
+		return fmt.Errorf("This is now implemented in the client.")
238
+	}
239
+	eng.ServeHTTP(w, r)
240
+	return nil
241
+}
242
+
243
+func getInfo(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
244
+	w.Header().Set("Content-Type", "application/json")
245
+	eng.ServeHTTP(w, r)
246
+	return nil
247
+}
248
+
249
+func getEvents(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
250
+	if err := parseForm(r); err != nil {
251
+		return err
252
+	}
253
+
254
+	w.Header().Set("Content-Type", "application/json")
255
+	var job = eng.Job("events", r.RemoteAddr)
256
+	job.Stdout.Add(utils.NewWriteFlusher(w))
257
+	job.Setenv("since", r.Form.Get("since"))
258
+	return job.Run()
259
+}
260
+
261
+func getImagesHistory(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
262
+	if vars == nil {
263
+		return fmt.Errorf("Missing parameter")
264
+	}
265
+
266
+	var job = eng.Job("history", vars["name"])
267
+	job.Stdout.Add(w)
268
+
269
+	if err := job.Run(); err != nil {
270
+		return err
271
+	}
272
+	return nil
273
+}
274
+
275
+func getContainersChanges(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
276
+	if vars == nil {
277
+		return fmt.Errorf("Missing parameter")
278
+	}
279
+	var job = eng.Job("changes", vars["name"])
280
+	job.Stdout.Add(w)
281
+
282
+	return job.Run()
283
+}
284
+
285
+func getContainersTop(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
286
+	if version < 1.4 {
287
+		return fmt.Errorf("top was improved a lot since 1.3, Please upgrade your docker client.")
288
+	}
289
+	if vars == nil {
290
+		return fmt.Errorf("Missing parameter")
291
+	}
292
+	if err := parseForm(r); err != nil {
293
+		return err
294
+	}
295
+
296
+	job := eng.Job("top", vars["name"], r.Form.Get("ps_args"))
297
+	job.Stdout.Add(w)
298
+	return job.Run()
299
+}
300
+
301
+func getContainersJSON(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
302
+	if err := parseForm(r); err != nil {
303
+		return err
304
+	}
305
+	var (
306
+		err  error
307
+		outs *engine.Table
308
+		job  = eng.Job("containers")
309
+	)
310
+
311
+	job.Setenv("all", r.Form.Get("all"))
312
+	job.Setenv("size", r.Form.Get("size"))
313
+	job.Setenv("since", r.Form.Get("since"))
314
+	job.Setenv("before", r.Form.Get("before"))
315
+	job.Setenv("limit", r.Form.Get("limit"))
316
+
317
+	if version > 1.5 {
318
+		job.Stdout.Add(w)
319
+	} else if outs, err = job.Stdout.AddTable(); err != nil {
320
+		return err
321
+	}
322
+	if err = job.Run(); err != nil {
323
+		return err
324
+	}
325
+	if version < 1.5 { // Convert to legacy format
326
+		for _, out := range outs.Data {
327
+			ports := engine.NewTable("", 0)
328
+			ports.ReadListFrom([]byte(out.Get("Ports")))
329
+			out.Set("Ports", displayablePorts(ports))
330
+		}
331
+		if _, err = outs.WriteListTo(w); err != nil {
332
+			return err
333
+		}
334
+	}
335
+	return nil
336
+}
337
+
338
+func postImagesTag(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
339
+	if err := parseForm(r); err != nil {
340
+		return err
341
+	}
342
+	if vars == nil {
343
+		return fmt.Errorf("Missing parameter")
344
+	}
345
+
346
+	job := eng.Job("tag", vars["name"], r.Form.Get("repo"), r.Form.Get("tag"))
347
+	job.Setenv("force", r.Form.Get("force"))
348
+	if err := job.Run(); err != nil {
349
+		return err
350
+	}
351
+	w.WriteHeader(http.StatusCreated)
352
+	return nil
353
+}
354
+
355
+func postCommit(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
356
+	if err := parseForm(r); err != nil {
357
+		return err
358
+	}
359
+	var (
360
+		config engine.Env
361
+		env    engine.Env
362
+		job    = eng.Job("commit", r.Form.Get("container"))
363
+	)
364
+	if err := config.Import(r.Body); err != nil {
365
+		utils.Errorf("%s", err)
366
+	}
367
+
368
+	job.Setenv("repo", r.Form.Get("repo"))
369
+	job.Setenv("tag", r.Form.Get("tag"))
370
+	job.Setenv("author", r.Form.Get("author"))
371
+	job.Setenv("comment", r.Form.Get("comment"))
372
+	job.SetenvSubEnv("config", &config)
373
+
374
+	var id string
375
+	job.Stdout.AddString(&id)
376
+	if err := job.Run(); err != nil {
377
+		return err
378
+	}
379
+	env.Set("Id", id)
380
+	return writeJSON(w, http.StatusCreated, env)
381
+}
382
+
383
+// Creates an image from Pull or from Import
384
+func postImagesCreate(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
385
+	if err := parseForm(r); err != nil {
386
+		return err
387
+	}
388
+
389
+	var (
390
+		image = r.Form.Get("fromImage")
391
+		tag   = r.Form.Get("tag")
392
+		job   *engine.Job
393
+	)
394
+	authEncoded := r.Header.Get("X-Registry-Auth")
395
+	authConfig := &auth.AuthConfig{}
396
+	if authEncoded != "" {
397
+		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
398
+		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
399
+			// for a pull it is not an error if no auth was given
400
+			// to increase compatibility with the existing api it is defaulting to be empty
401
+			authConfig = &auth.AuthConfig{}
402
+		}
403
+	}
404
+	if version > 1.0 {
405
+		w.Header().Set("Content-Type", "application/json")
406
+	}
407
+	if image != "" { //pull
408
+		metaHeaders := map[string][]string{}
409
+		for k, v := range r.Header {
410
+			if strings.HasPrefix(k, "X-Meta-") {
411
+				metaHeaders[k] = v
412
+			}
413
+		}
414
+		job = eng.Job("pull", r.Form.Get("fromImage"), tag)
415
+		job.SetenvBool("parallel", version > 1.3)
416
+		job.SetenvJson("metaHeaders", metaHeaders)
417
+		job.SetenvJson("authConfig", authConfig)
418
+	} else { //import
419
+		job = eng.Job("import", r.Form.Get("fromSrc"), r.Form.Get("repo"), tag)
420
+		job.Stdin.Add(r.Body)
421
+	}
422
+
423
+	job.SetenvBool("json", version > 1.0)
424
+	job.Stdout.Add(utils.NewWriteFlusher(w))
425
+	if err := job.Run(); err != nil {
426
+		if !job.Stdout.Used() {
427
+			return err
428
+		}
429
+		sf := utils.NewStreamFormatter(version > 1.0)
430
+		w.Write(sf.FormatError(err))
431
+	}
432
+
433
+	return nil
434
+}
435
+
436
+func getImagesSearch(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
437
+	if err := parseForm(r); err != nil {
438
+		return err
439
+	}
440
+	var (
441
+		authEncoded = r.Header.Get("X-Registry-Auth")
442
+		authConfig  = &auth.AuthConfig{}
443
+		metaHeaders = map[string][]string{}
444
+	)
445
+
446
+	if authEncoded != "" {
447
+		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
448
+		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
449
+			// for a search it is not an error if no auth was given
450
+			// to increase compatibility with the existing api it is defaulting to be empty
451
+			authConfig = &auth.AuthConfig{}
452
+		}
453
+	}
454
+	for k, v := range r.Header {
455
+		if strings.HasPrefix(k, "X-Meta-") {
456
+			metaHeaders[k] = v
457
+		}
458
+	}
459
+
460
+	var job = eng.Job("search", r.Form.Get("term"))
461
+	job.SetenvJson("metaHeaders", metaHeaders)
462
+	job.SetenvJson("authConfig", authConfig)
463
+	job.Stdout.Add(w)
464
+
465
+	return job.Run()
466
+}
467
+
468
+func postImagesInsert(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
469
+	if err := parseForm(r); err != nil {
470
+		return err
471
+	}
472
+	if vars == nil {
473
+		return fmt.Errorf("Missing parameter")
474
+	}
475
+	if version > 1.0 {
476
+		w.Header().Set("Content-Type", "application/json")
477
+	}
478
+
479
+	job := eng.Job("insert", vars["name"], r.Form.Get("url"), r.Form.Get("path"))
480
+	job.SetenvBool("json", version > 1.0)
481
+	job.Stdout.Add(w)
482
+	if err := job.Run(); err != nil {
483
+		if !job.Stdout.Used() {
484
+			return err
485
+		}
486
+		sf := utils.NewStreamFormatter(version > 1.0)
487
+		w.Write(sf.FormatError(err))
488
+	}
489
+
490
+	return nil
491
+}
492
+
493
+func postImagesPush(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
494
+	if vars == nil {
495
+		return fmt.Errorf("Missing parameter")
496
+	}
497
+
498
+	metaHeaders := map[string][]string{}
499
+	for k, v := range r.Header {
500
+		if strings.HasPrefix(k, "X-Meta-") {
501
+			metaHeaders[k] = v
502
+		}
503
+	}
504
+	if err := parseForm(r); err != nil {
505
+		return err
506
+	}
507
+	authConfig := &auth.AuthConfig{}
508
+
509
+	authEncoded := r.Header.Get("X-Registry-Auth")
510
+	if authEncoded != "" {
511
+		// the new format is to handle the authConfig as a header
512
+		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
513
+		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
514
+			// to increase compatibility to existing api it is defaulting to be empty
515
+			authConfig = &auth.AuthConfig{}
516
+		}
517
+	} else {
518
+		// the old format is supported for compatibility if there was no authConfig header
519
+		if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil {
520
+			return err
521
+		}
522
+	}
523
+
524
+	if version > 1.0 {
525
+		w.Header().Set("Content-Type", "application/json")
526
+	}
527
+	job := eng.Job("push", vars["name"])
528
+	job.SetenvJson("metaHeaders", metaHeaders)
529
+	job.SetenvJson("authConfig", authConfig)
530
+	job.SetenvBool("json", version > 1.0)
531
+	job.Stdout.Add(utils.NewWriteFlusher(w))
532
+
533
+	if err := job.Run(); err != nil {
534
+		if !job.Stdout.Used() {
535
+			return err
536
+		}
537
+		sf := utils.NewStreamFormatter(version > 1.0)
538
+		w.Write(sf.FormatError(err))
539
+	}
540
+	return nil
541
+}
542
+
543
+func getImagesGet(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
544
+	if vars == nil {
545
+		return fmt.Errorf("Missing parameter")
546
+	}
547
+	if version > 1.0 {
548
+		w.Header().Set("Content-Type", "application/x-tar")
549
+	}
550
+	job := eng.Job("image_export", vars["name"])
551
+	job.Stdout.Add(w)
552
+	return job.Run()
553
+}
554
+
555
+func postImagesLoad(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
556
+	job := eng.Job("load")
557
+	job.Stdin.Add(r.Body)
558
+	return job.Run()
559
+}
560
+
561
+func postContainersCreate(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
562
+	if err := parseForm(r); err != nil {
563
+		return nil
564
+	}
565
+	var (
566
+		out         engine.Env
567
+		job         = eng.Job("create", r.Form.Get("name"))
568
+		outWarnings []string
569
+		outId       string
570
+		warnings    = bytes.NewBuffer(nil)
571
+	)
572
+	if err := job.DecodeEnv(r.Body); err != nil {
573
+		return err
574
+	}
575
+	// Read container ID from the first line of stdout
576
+	job.Stdout.AddString(&outId)
577
+	// Read warnings from stderr
578
+	job.Stderr.Add(warnings)
579
+	if err := job.Run(); err != nil {
580
+		return err
581
+	}
582
+	// Parse warnings from stderr
583
+	scanner := bufio.NewScanner(warnings)
584
+	for scanner.Scan() {
585
+		outWarnings = append(outWarnings, scanner.Text())
586
+	}
587
+	out.Set("Id", outId)
588
+	out.SetList("Warnings", outWarnings)
589
+	return writeJSON(w, http.StatusCreated, out)
590
+}
591
+
592
+func postContainersRestart(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
593
+	if err := parseForm(r); err != nil {
594
+		return err
595
+	}
596
+	if vars == nil {
597
+		return fmt.Errorf("Missing parameter")
598
+	}
599
+	job := eng.Job("restart", vars["name"])
600
+	job.Setenv("t", r.Form.Get("t"))
601
+	if err := job.Run(); err != nil {
602
+		return err
603
+	}
604
+	w.WriteHeader(http.StatusNoContent)
605
+	return nil
606
+}
607
+
608
+func deleteContainers(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
609
+	if err := parseForm(r); err != nil {
610
+		return err
611
+	}
612
+	if vars == nil {
613
+		return fmt.Errorf("Missing parameter")
614
+	}
615
+	job := eng.Job("container_delete", vars["name"])
616
+	job.Setenv("removeVolume", r.Form.Get("v"))
617
+	job.Setenv("removeLink", r.Form.Get("link"))
618
+	if err := job.Run(); err != nil {
619
+		return err
620
+	}
621
+	w.WriteHeader(http.StatusNoContent)
622
+	return nil
623
+}
624
+
625
+func deleteImages(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
626
+	if err := parseForm(r); err != nil {
627
+		return err
628
+	}
629
+	if vars == nil {
630
+		return fmt.Errorf("Missing parameter")
631
+	}
632
+	var job = eng.Job("image_delete", vars["name"])
633
+	job.Stdout.Add(w)
634
+	job.SetenvBool("autoPrune", version > 1.1)
635
+
636
+	return job.Run()
637
+}
638
+
639
+func postContainersStart(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
640
+	if vars == nil {
641
+		return fmt.Errorf("Missing parameter")
642
+	}
643
+	name := vars["name"]
644
+	job := eng.Job("start", name)
645
+	// allow a nil body for backwards compatibility
646
+	if r.Body != nil {
647
+		if MatchesContentType(r.Header.Get("Content-Type"), "application/json") {
648
+			if err := job.DecodeEnv(r.Body); err != nil {
649
+				return err
650
+			}
651
+		}
652
+	}
653
+	if err := job.Run(); err != nil {
654
+		return err
655
+	}
656
+	w.WriteHeader(http.StatusNoContent)
657
+	return nil
658
+}
659
+
660
+func postContainersStop(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
661
+	if err := parseForm(r); err != nil {
662
+		return err
663
+	}
664
+	if vars == nil {
665
+		return fmt.Errorf("Missing parameter")
666
+	}
667
+	job := eng.Job("stop", vars["name"])
668
+	job.Setenv("t", r.Form.Get("t"))
669
+	if err := job.Run(); err != nil {
670
+		return err
671
+	}
672
+	w.WriteHeader(http.StatusNoContent)
673
+	return nil
674
+}
675
+
676
+func postContainersWait(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
677
+	if vars == nil {
678
+		return fmt.Errorf("Missing parameter")
679
+	}
680
+	var (
681
+		env    engine.Env
682
+		status string
683
+		job    = eng.Job("wait", vars["name"])
684
+	)
685
+	job.Stdout.AddString(&status)
686
+	if err := job.Run(); err != nil {
687
+		return err
688
+	}
689
+	// Parse a 16-bit encoded integer to map typical unix exit status.
690
+	_, err := strconv.ParseInt(status, 10, 16)
691
+	if err != nil {
692
+		return err
693
+	}
694
+	env.Set("StatusCode", status)
695
+	return writeJSON(w, http.StatusOK, env)
696
+}
697
+
698
+func postContainersResize(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
699
+	if err := parseForm(r); err != nil {
700
+		return err
701
+	}
702
+	if vars == nil {
703
+		return fmt.Errorf("Missing parameter")
704
+	}
705
+	if err := eng.Job("resize", vars["name"], r.Form.Get("h"), r.Form.Get("w")).Run(); err != nil {
706
+		return err
707
+	}
708
+	return nil
709
+}
710
+
711
+func postContainersAttach(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
712
+	if err := parseForm(r); err != nil {
713
+		return err
714
+	}
715
+	if vars == nil {
716
+		return fmt.Errorf("Missing parameter")
717
+	}
718
+
719
+	var (
720
+		job    = eng.Job("inspect", vars["name"], "container")
721
+		c, err = job.Stdout.AddEnv()
722
+	)
723
+	if err != nil {
724
+		return err
725
+	}
726
+	if err = job.Run(); err != nil {
727
+		return err
728
+	}
729
+
730
+	inStream, outStream, err := hijackServer(w)
731
+	if err != nil {
732
+		return err
733
+	}
734
+	defer func() {
735
+		if tcpc, ok := inStream.(*net.TCPConn); ok {
736
+			tcpc.CloseWrite()
737
+		} else {
738
+			inStream.Close()
739
+		}
740
+	}()
741
+	defer func() {
742
+		if tcpc, ok := outStream.(*net.TCPConn); ok {
743
+			tcpc.CloseWrite()
744
+		} else if closer, ok := outStream.(io.Closer); ok {
745
+			closer.Close()
746
+		}
747
+	}()
748
+
749
+	var errStream io.Writer
750
+
751
+	fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
752
+
753
+	if c.GetSubEnv("Config") != nil && !c.GetSubEnv("Config").GetBool("Tty") && version >= 1.6 {
754
+		errStream = utils.NewStdWriter(outStream, utils.Stderr)
755
+		outStream = utils.NewStdWriter(outStream, utils.Stdout)
756
+	} else {
757
+		errStream = outStream
758
+	}
759
+
760
+	job = eng.Job("attach", vars["name"])
761
+	job.Setenv("logs", r.Form.Get("logs"))
762
+	job.Setenv("stream", r.Form.Get("stream"))
763
+	job.Setenv("stdin", r.Form.Get("stdin"))
764
+	job.Setenv("stdout", r.Form.Get("stdout"))
765
+	job.Setenv("stderr", r.Form.Get("stderr"))
766
+	job.Stdin.Add(inStream)
767
+	job.Stdout.Add(outStream)
768
+	job.Stderr.Set(errStream)
769
+	if err := job.Run(); err != nil {
770
+		fmt.Fprintf(outStream, "Error: %s\n", err)
771
+
772
+	}
773
+	return nil
774
+}
775
+
776
+func wsContainersAttach(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
777
+	if err := parseForm(r); err != nil {
778
+		return err
779
+	}
780
+	if vars == nil {
781
+		return fmt.Errorf("Missing parameter")
782
+	}
783
+
784
+	if err := eng.Job("inspect", vars["name"], "container").Run(); err != nil {
785
+		return err
786
+	}
787
+
788
+	h := websocket.Handler(func(ws *websocket.Conn) {
789
+		defer ws.Close()
790
+		job := eng.Job("attach", vars["name"])
791
+		job.Setenv("logs", r.Form.Get("logs"))
792
+		job.Setenv("stream", r.Form.Get("stream"))
793
+		job.Setenv("stdin", r.Form.Get("stdin"))
794
+		job.Setenv("stdout", r.Form.Get("stdout"))
795
+		job.Setenv("stderr", r.Form.Get("stderr"))
796
+		job.Stdin.Add(ws)
797
+		job.Stdout.Add(ws)
798
+		job.Stderr.Set(ws)
799
+		if err := job.Run(); err != nil {
800
+			utils.Errorf("Error: %s", err)
801
+		}
802
+	})
803
+	h.ServeHTTP(w, r)
804
+
805
+	return nil
806
+}
807
+
808
+func getContainersByName(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
809
+	if vars == nil {
810
+		return fmt.Errorf("Missing parameter")
811
+	}
812
+	var job = eng.Job("inspect", vars["name"], "container")
813
+	job.Stdout.Add(w)
814
+	job.SetenvBool("conflict", true) //conflict=true to detect conflict between containers and images in the job
815
+	return job.Run()
816
+}
817
+
818
+func getImagesByName(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
819
+	if vars == nil {
820
+		return fmt.Errorf("Missing parameter")
821
+	}
822
+	var job = eng.Job("inspect", vars["name"], "image")
823
+	job.Stdout.Add(w)
824
+	job.SetenvBool("conflict", true) //conflict=true to detect conflict between containers and images in the job
825
+	return job.Run()
826
+}
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 {
830
+		return fmt.Errorf("Multipart upload for build is no longer supported. Please upgrade your docker client.")
831
+	}
832
+	var (
833
+		authEncoded       = r.Header.Get("X-Registry-Auth")
834
+		authConfig        = &auth.AuthConfig{}
835
+		configFileEncoded = r.Header.Get("X-Registry-Config")
836
+		configFile        = &auth.ConfigFile{}
837
+		job               = eng.Job("build")
838
+	)
839
+
840
+	// This block can be removed when API versions prior to 1.9 are deprecated.
841
+	// Both headers will be parsed and sent along to the daemon, but if a non-empty
842
+	// ConfigFile is present, any value provided as an AuthConfig directly will
843
+	// be overridden. See BuildFile::CmdFrom for details.
844
+	if version < 1.9 && authEncoded != "" {
845
+		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
846
+		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
847
+			// for a pull it is not an error if no auth was given
848
+			// to increase compatibility with the existing api it is defaulting to be empty
849
+			authConfig = &auth.AuthConfig{}
850
+		}
851
+	}
852
+
853
+	if configFileEncoded != "" {
854
+		configFileJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(configFileEncoded))
855
+		if err := json.NewDecoder(configFileJson).Decode(configFile); err != nil {
856
+			// for a pull it is not an error if no auth was given
857
+			// to increase compatibility with the existing api it is defaulting to be empty
858
+			configFile = &auth.ConfigFile{}
859
+		}
860
+	}
861
+
862
+	if version >= 1.8 {
863
+		w.Header().Set("Content-Type", "application/json")
864
+		job.SetenvBool("json", true)
865
+	}
866
+
867
+	job.Stdout.Add(utils.NewWriteFlusher(w))
868
+	job.Stdin.Add(r.Body)
869
+	job.Setenv("remote", r.FormValue("remote"))
870
+	job.Setenv("t", r.FormValue("t"))
871
+	job.Setenv("q", r.FormValue("q"))
872
+	job.Setenv("nocache", r.FormValue("nocache"))
873
+	job.Setenv("rm", r.FormValue("rm"))
874
+
875
+	if err := job.Run(); err != nil {
876
+		if !job.Stdout.Used() {
877
+			return err
878
+		}
879
+		sf := utils.NewStreamFormatter(version >= 1.8)
880
+		w.Write(sf.FormatError(err))
881
+	}
882
+	return nil
883
+}
884
+
885
+func postContainersCopy(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
886
+	if vars == nil {
887
+		return fmt.Errorf("Missing parameter")
888
+	}
889
+
890
+	var copyData engine.Env
891
+
892
+	if contentType := r.Header.Get("Content-Type"); contentType == "application/json" {
893
+		if err := copyData.Decode(r.Body); err != nil {
894
+			return err
895
+		}
896
+	} else {
897
+		return fmt.Errorf("Content-Type not supported: %s", contentType)
898
+	}
899
+
900
+	if copyData.Get("Resource") == "" {
901
+		return fmt.Errorf("Path cannot be empty")
902
+	}
903
+	if copyData.Get("Resource")[0] == '/' {
904
+		copyData.Set("Resource", copyData.Get("Resource")[1:])
905
+	}
906
+
907
+	job := eng.Job("container_copy", vars["name"], copyData.Get("Resource"))
908
+	job.Stdout.Add(w)
909
+	if err := job.Run(); err != nil {
910
+		utils.Errorf("%s", err.Error())
911
+	}
912
+	return nil
913
+}
914
+
915
+func optionsHandler(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
916
+	w.WriteHeader(http.StatusOK)
917
+	return nil
918
+}
919
+func writeCorsHeaders(w http.ResponseWriter, r *http.Request) {
920
+	w.Header().Add("Access-Control-Allow-Origin", "*")
921
+	w.Header().Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
922
+	w.Header().Add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS")
923
+}
924
+
925
+func makeHttpHandler(eng *engine.Engine, logging bool, localMethod string, localRoute string, handlerFunc HttpApiFunc, enableCors bool, dockerVersion string) http.HandlerFunc {
926
+	return func(w http.ResponseWriter, r *http.Request) {
927
+		// log the request
928
+		utils.Debugf("Calling %s %s", localMethod, localRoute)
929
+
930
+		if logging {
931
+			log.Println(r.Method, r.RequestURI)
932
+		}
933
+
934
+		if strings.Contains(r.Header.Get("User-Agent"), "Docker-Client/") {
935
+			userAgent := strings.Split(r.Header.Get("User-Agent"), "/")
936
+			if len(userAgent) == 2 && userAgent[1] != dockerVersion {
937
+				utils.Debugf("Warning: client and server don't have the same version (client: %s, server: %s)", userAgent[1], dockerVersion)
938
+			}
939
+		}
940
+		version, err := strconv.ParseFloat(mux.Vars(r)["version"], 64)
941
+		if err != nil {
942
+			version = APIVERSION
943
+		}
944
+		if enableCors {
945
+			writeCorsHeaders(w, r)
946
+		}
947
+
948
+		if version == 0 || version > APIVERSION {
949
+			http.Error(w, fmt.Errorf("client and server don't have same version (client : %g, server: %g)", version, APIVERSION).Error(), http.StatusNotFound)
950
+			return
951
+		}
952
+
953
+		if err := handlerFunc(eng, version, w, r, mux.Vars(r)); err != nil {
954
+			utils.Errorf("Error: %s", err)
955
+			httpError(w, err)
956
+		}
957
+	}
958
+}
959
+
960
+// Replicated from expvar.go as not public.
961
+func expvarHandler(w http.ResponseWriter, r *http.Request) {
962
+	w.Header().Set("Content-Type", "application/json; charset=utf-8")
963
+	fmt.Fprintf(w, "{\n")
964
+	first := true
965
+	expvar.Do(func(kv expvar.KeyValue) {
966
+		if !first {
967
+			fmt.Fprintf(w, ",\n")
968
+		}
969
+		first = false
970
+		fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value)
971
+	})
972
+	fmt.Fprintf(w, "\n}\n")
973
+}
974
+
975
+func AttachProfiler(router *mux.Router) {
976
+	router.HandleFunc("/debug/vars", expvarHandler)
977
+	router.HandleFunc("/debug/pprof/", pprof.Index)
978
+	router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
979
+	router.HandleFunc("/debug/pprof/profile", pprof.Profile)
980
+	router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
981
+	router.HandleFunc("/debug/pprof/heap", pprof.Handler("heap").ServeHTTP)
982
+	router.HandleFunc("/debug/pprof/goroutine", pprof.Handler("goroutine").ServeHTTP)
983
+	router.HandleFunc("/debug/pprof/threadcreate", pprof.Handler("threadcreate").ServeHTTP)
984
+}
985
+
986
+func createRouter(eng *engine.Engine, logging, enableCors bool, dockerVersion string) (*mux.Router, error) {
987
+	r := mux.NewRouter()
988
+	if os.Getenv("DEBUG") != "" {
989
+		AttachProfiler(r)
990
+	}
991
+	m := map[string]map[string]HttpApiFunc{
992
+		"GET": {
993
+			"/events":                         getEvents,
994
+			"/info":                           getInfo,
995
+			"/version":                        getVersion,
996
+			"/images/json":                    getImagesJSON,
997
+			"/images/viz":                     getImagesViz,
998
+			"/images/search":                  getImagesSearch,
999
+			"/images/{name:.*}/get":           getImagesGet,
1000
+			"/images/{name:.*}/history":       getImagesHistory,
1001
+			"/images/{name:.*}/json":          getImagesByName,
1002
+			"/containers/ps":                  getContainersJSON,
1003
+			"/containers/json":                getContainersJSON,
1004
+			"/containers/{name:.*}/export":    getContainersExport,
1005
+			"/containers/{name:.*}/changes":   getContainersChanges,
1006
+			"/containers/{name:.*}/json":      getContainersByName,
1007
+			"/containers/{name:.*}/top":       getContainersTop,
1008
+			"/containers/{name:.*}/attach/ws": wsContainersAttach,
1009
+		},
1010
+		"POST": {
1011
+			"/auth":                         postAuth,
1012
+			"/commit":                       postCommit,
1013
+			"/build":                        postBuild,
1014
+			"/images/create":                postImagesCreate,
1015
+			"/images/{name:.*}/insert":      postImagesInsert,
1016
+			"/images/load":                  postImagesLoad,
1017
+			"/images/{name:.*}/push":        postImagesPush,
1018
+			"/images/{name:.*}/tag":         postImagesTag,
1019
+			"/containers/create":            postContainersCreate,
1020
+			"/containers/{name:.*}/kill":    postContainersKill,
1021
+			"/containers/{name:.*}/restart": postContainersRestart,
1022
+			"/containers/{name:.*}/start":   postContainersStart,
1023
+			"/containers/{name:.*}/stop":    postContainersStop,
1024
+			"/containers/{name:.*}/wait":    postContainersWait,
1025
+			"/containers/{name:.*}/resize":  postContainersResize,
1026
+			"/containers/{name:.*}/attach":  postContainersAttach,
1027
+			"/containers/{name:.*}/copy":    postContainersCopy,
1028
+		},
1029
+		"DELETE": {
1030
+			"/containers/{name:.*}": deleteContainers,
1031
+			"/images/{name:.*}":     deleteImages,
1032
+		},
1033
+		"OPTIONS": {
1034
+			"": optionsHandler,
1035
+		},
1036
+	}
1037
+
1038
+	for method, routes := range m {
1039
+		for route, fct := range routes {
1040
+			utils.Debugf("Registering %s, %s", method, route)
1041
+			// NOTE: scope issue, make sure the variables are local and won't be changed
1042
+			localRoute := route
1043
+			localFct := fct
1044
+			localMethod := method
1045
+
1046
+			// build the handler function
1047
+			f := makeHttpHandler(eng, logging, localMethod, localRoute, localFct, enableCors, dockerVersion)
1048
+
1049
+			// add the new route
1050
+			if localRoute == "" {
1051
+				r.Methods(localMethod).HandlerFunc(f)
1052
+			} else {
1053
+				r.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(f)
1054
+				r.Path(localRoute).Methods(localMethod).HandlerFunc(f)
1055
+			}
1056
+		}
1057
+	}
1058
+
1059
+	return r, nil
1060
+}
1061
+
1062
+// ServeRequest processes a single http request to the docker remote api.
1063
+// FIXME: refactor this to be part of Server and not require re-creating a new
1064
+// router each time. This requires first moving ListenAndServe into Server.
1065
+func ServeRequest(eng *engine.Engine, apiversion float64, w http.ResponseWriter, req *http.Request) error {
1066
+	router, err := createRouter(eng, false, true, "")
1067
+	if err != nil {
1068
+		return err
1069
+	}
1070
+	// Insert APIVERSION into the request as a convenience
1071
+	req.URL.Path = fmt.Sprintf("/v%g%s", apiversion, req.URL.Path)
1072
+	router.ServeHTTP(w, req)
1073
+	return nil
1074
+}
1075
+
1076
+// ServeFD creates an http.Server and sets it up to serve given a socket activated
1077
+// argument.
1078
+func ServeFd(addr string, handle http.Handler) error {
1079
+	ls, e := systemd.ListenFD(addr)
1080
+	if e != nil {
1081
+		return e
1082
+	}
1083
+
1084
+	chErrors := make(chan error, len(ls))
1085
+
1086
+	// Since ListenFD will return one or more sockets we have
1087
+	// to create a go func to spawn off multiple serves
1088
+	for i := range ls {
1089
+		listener := ls[i]
1090
+		go func() {
1091
+			httpSrv := http.Server{Handler: handle}
1092
+			chErrors <- httpSrv.Serve(listener)
1093
+		}()
1094
+	}
1095
+
1096
+	for i := 0; i < len(ls); i += 1 {
1097
+		err := <-chErrors
1098
+		if err != nil {
1099
+			return err
1100
+		}
1101
+	}
1102
+
1103
+	return nil
1104
+}
1105
+
1106
+// ListenAndServe sets up the required http.Server and gets it listening for
1107
+// each addr passed in and does protocol specific checking.
1108
+func ListenAndServe(proto, addr string, eng *engine.Engine, logging, enableCors bool, dockerVersion string) error {
1109
+	r, err := createRouter(eng, logging, enableCors, dockerVersion)
1110
+	if err != nil {
1111
+		return err
1112
+	}
1113
+
1114
+	if proto == "fd" {
1115
+		return ServeFd(addr, r)
1116
+	}
1117
+
1118
+	if proto == "unix" {
1119
+		if err := syscall.Unlink(addr); err != nil && !os.IsNotExist(err) {
1120
+			return err
1121
+		}
1122
+	}
1123
+
1124
+	l, err := net.Listen(proto, addr)
1125
+	if err != nil {
1126
+		return err
1127
+	}
1128
+
1129
+	// Basic error and sanity checking
1130
+	switch proto {
1131
+	case "tcp":
1132
+		if !strings.HasPrefix(addr, "127.0.0.1") {
1133
+			log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
1134
+		}
1135
+	case "unix":
1136
+		if err := os.Chmod(addr, 0660); err != nil {
1137
+			return err
1138
+		}
1139
+
1140
+		groups, err := ioutil.ReadFile("/etc/group")
1141
+		if err != nil {
1142
+			return err
1143
+		}
1144
+		re := regexp.MustCompile("(^|\n)docker:.*?:([0-9]+)")
1145
+		if gidMatch := re.FindStringSubmatch(string(groups)); gidMatch != nil {
1146
+			gid, err := strconv.Atoi(gidMatch[2])
1147
+			if err != nil {
1148
+				return err
1149
+			}
1150
+			utils.Debugf("docker group found. gid: %d", gid)
1151
+			if err := os.Chown(addr, 0, gid); err != nil {
1152
+				return err
1153
+			}
1154
+		}
1155
+	default:
1156
+		return fmt.Errorf("Invalid protocol format.")
1157
+	}
1158
+
1159
+	httpSrv := http.Server{Addr: addr, Handler: r}
1160
+	return httpSrv.Serve(l)
1161
+}
0 1162
new file mode 100644
... ...
@@ -0,0 +1,65 @@
0
+package api
1
+
2
+import (
3
+	"fmt"
4
+	"net/http"
5
+	"net/http/httptest"
6
+	"testing"
7
+)
8
+
9
+func TestJsonContentType(t *testing.T) {
10
+	if !MatchesContentType("application/json", "application/json") {
11
+		t.Fail()
12
+	}
13
+
14
+	if !MatchesContentType("application/json; charset=utf-8", "application/json") {
15
+		t.Fail()
16
+	}
17
+
18
+	if MatchesContentType("dockerapplication/json", "application/json") {
19
+		t.Fail()
20
+	}
21
+}
22
+
23
+func TestGetBoolParam(t *testing.T) {
24
+	if ret, err := getBoolParam("true"); err != nil || !ret {
25
+		t.Fatalf("true -> true, nil | got %t %s", ret, err)
26
+	}
27
+	if ret, err := getBoolParam("True"); err != nil || !ret {
28
+		t.Fatalf("True -> true, nil | got %t %s", ret, err)
29
+	}
30
+	if ret, err := getBoolParam("1"); err != nil || !ret {
31
+		t.Fatalf("1 -> true, nil | got %t %s", ret, err)
32
+	}
33
+	if ret, err := getBoolParam(""); err != nil || ret {
34
+		t.Fatalf("\"\" -> false, nil | got %t %s", ret, err)
35
+	}
36
+	if ret, err := getBoolParam("false"); err != nil || ret {
37
+		t.Fatalf("false -> false, nil | got %t %s", ret, err)
38
+	}
39
+	if ret, err := getBoolParam("0"); err != nil || ret {
40
+		t.Fatalf("0 -> false, nil | got %t %s", ret, err)
41
+	}
42
+	if ret, err := getBoolParam("faux"); err == nil || ret {
43
+		t.Fatalf("faux -> false, err | got %t %s", ret, err)
44
+	}
45
+}
46
+
47
+func TesthttpError(t *testing.T) {
48
+	r := httptest.NewRecorder()
49
+
50
+	httpError(r, fmt.Errorf("No such method"))
51
+	if r.Code != http.StatusNotFound {
52
+		t.Fatalf("Expected %d, got %d", http.StatusNotFound, r.Code)
53
+	}
54
+
55
+	httpError(r, fmt.Errorf("This accound hasn't been activated"))
56
+	if r.Code != http.StatusForbidden {
57
+		t.Fatalf("Expected %d, got %d", http.StatusForbidden, r.Code)
58
+	}
59
+
60
+	httpError(r, fmt.Errorf("Some error"))
61
+	if r.Code != http.StatusInternalServerError {
62
+		t.Fatalf("Expected %d, got %d", http.StatusInternalServerError, r.Code)
63
+	}
64
+}
0 65
deleted file mode 100644
... ...
@@ -1,19 +0,0 @@
1
-package docker
2
-
3
-import (
4
-	"testing"
5
-)
6
-
7
-func TestJsonContentType(t *testing.T) {
8
-	if !matchesContentType("application/json", "application/json") {
9
-		t.Fail()
10
-	}
11
-
12
-	if !matchesContentType("application/json; charset=utf-8", "application/json") {
13
-		t.Fail()
14
-	}
15
-
16
-	if matchesContentType("dockerapplication/json", "application/json") {
17
-		t.Fail()
18
-	}
19
-}
... ...
@@ -8,6 +8,7 @@ import (
8 8
 	"encoding/json"
9 9
 	"errors"
10 10
 	"fmt"
11
+	"github.com/dotcloud/docker/api"
11 12
 	"github.com/dotcloud/docker/archive"
12 13
 	"github.com/dotcloud/docker/auth"
13 14
 	"github.com/dotcloud/docker/engine"
... ...
@@ -79,7 +80,7 @@ func (cli *DockerCli) CmdHelp(args ...string) error {
79 79
 			return nil
80 80
 		}
81 81
 	}
82
-	help := fmt.Sprintf("Usage: docker [OPTIONS] COMMAND [arg...]\n -H=[unix://%s]: tcp://host:port to bind/connect to or unix://path/to/socket to use\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n", DEFAULTUNIXSOCKET)
82
+	help := fmt.Sprintf("Usage: docker [OPTIONS] COMMAND [arg...]\n -H=[unix://%s]: tcp://host:port to bind/connect to or unix://path/to/socket to use\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n", api.DEFAULTUNIXSOCKET)
83 83
 	for _, command := range [][]string{
84 84
 		{"attach", "Attach to a running container"},
85 85
 		{"build", "Build a container from a Dockerfile"},
... ...
@@ -2283,7 +2284,7 @@ func (cli *DockerCli) call(method, path string, data interface{}, passAuthInfo b
2283 2283
 	re := regexp.MustCompile("/+")
2284 2284
 	path = re.ReplaceAllString(path, "/")
2285 2285
 
2286
-	req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), params)
2286
+	req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", api.APIVERSION, path), params)
2287 2287
 	if err != nil {
2288 2288
 		return nil, -1, err
2289 2289
 	}
... ...
@@ -2360,7 +2361,7 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, h
2360 2360
 	re := regexp.MustCompile("/+")
2361 2361
 	path = re.ReplaceAllString(path, "/")
2362 2362
 
2363
-	req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), in)
2363
+	req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", api.APIVERSION, path), in)
2364 2364
 	if err != nil {
2365 2365
 		return err
2366 2366
 	}
... ...
@@ -2405,7 +2406,7 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, h
2405 2405
 		return fmt.Errorf("Error: %s", bytes.TrimSpace(body))
2406 2406
 	}
2407 2407
 
2408
-	if matchesContentType(resp.Header.Get("Content-Type"), "application/json") {
2408
+	if api.MatchesContentType(resp.Header.Get("Content-Type"), "application/json") {
2409 2409
 		return utils.DisplayJSONMessagesStream(resp.Body, out, cli.terminalFd, cli.isTerminal)
2410 2410
 	}
2411 2411
 	if _, err := io.Copy(out, resp.Body); err != nil {
... ...
@@ -2424,7 +2425,7 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea
2424 2424
 	re := regexp.MustCompile("/+")
2425 2425
 	path = re.ReplaceAllString(path, "/")
2426 2426
 
2427
-	req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), nil)
2427
+	req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", api.APIVERSION, path), nil)
2428 2428
 	if err != nil {
2429 2429
 		return err
2430 2430
 	}
... ...
@@ -3,6 +3,7 @@ package main
3 3
 import (
4 4
 	"fmt"
5 5
 	"github.com/dotcloud/docker"
6
+	"github.com/dotcloud/docker/api"
6 7
 	"github.com/dotcloud/docker/engine"
7 8
 	flag "github.com/dotcloud/docker/pkg/mflag"
8 9
 	"github.com/dotcloud/docker/sysinit"
... ...
@@ -57,7 +58,7 @@ func main() {
57 57
 
58 58
 		if defaultHost == "" || *flDaemon {
59 59
 			// If we do not have a host, default to unix socket
60
-			defaultHost = fmt.Sprintf("unix://%s", docker.DEFAULTUNIXSOCKET)
60
+			defaultHost = fmt.Sprintf("unix://%s", api.DEFAULTUNIXSOCKET)
61 61
 		}
62 62
 		flHosts.Set(defaultHost)
63 63
 	}
64 64
deleted file mode 100644
... ...
@@ -1,51 +0,0 @@
1
-package docker
2
-
3
-import (
4
-	"fmt"
5
-	"net/http"
6
-	"net/http/httptest"
7
-	"testing"
8
-)
9
-
10
-func TestGetBoolParam(t *testing.T) {
11
-	if ret, err := getBoolParam("true"); err != nil || !ret {
12
-		t.Fatalf("true -> true, nil | got %t %s", ret, err)
13
-	}
14
-	if ret, err := getBoolParam("True"); err != nil || !ret {
15
-		t.Fatalf("True -> true, nil | got %t %s", ret, err)
16
-	}
17
-	if ret, err := getBoolParam("1"); err != nil || !ret {
18
-		t.Fatalf("1 -> true, nil | got %t %s", ret, err)
19
-	}
20
-	if ret, err := getBoolParam(""); err != nil || ret {
21
-		t.Fatalf("\"\" -> false, nil | got %t %s", ret, err)
22
-	}
23
-	if ret, err := getBoolParam("false"); err != nil || ret {
24
-		t.Fatalf("false -> false, nil | got %t %s", ret, err)
25
-	}
26
-	if ret, err := getBoolParam("0"); err != nil || ret {
27
-		t.Fatalf("0 -> false, nil | got %t %s", ret, err)
28
-	}
29
-	if ret, err := getBoolParam("faux"); err == nil || ret {
30
-		t.Fatalf("faux -> false, err | got %t %s", ret, err)
31
-	}
32
-}
33
-
34
-func TesthttpError(t *testing.T) {
35
-	r := httptest.NewRecorder()
36
-
37
-	httpError(r, fmt.Errorf("No such method"))
38
-	if r.Code != http.StatusNotFound {
39
-		t.Fatalf("Expected %d, got %d", http.StatusNotFound, r.Code)
40
-	}
41
-
42
-	httpError(r, fmt.Errorf("This accound hasn't been activated"))
43
-	if r.Code != http.StatusForbidden {
44
-		t.Fatalf("Expected %d, got %d", http.StatusForbidden, r.Code)
45
-	}
46
-
47
-	httpError(r, fmt.Errorf("Some error"))
48
-	if r.Code != http.StatusInternalServerError {
49
-		t.Fatalf("Expected %d, got %d", http.StatusInternalServerError, r.Code)
50
-	}
51
-}
... ...
@@ -7,6 +7,7 @@ import (
7 7
 	"encoding/json"
8 8
 	"fmt"
9 9
 	"github.com/dotcloud/docker"
10
+	"github.com/dotcloud/docker/api"
10 11
 	"github.com/dotcloud/docker/engine"
11 12
 	"github.com/dotcloud/docker/utils"
12 13
 	"io"
... ...
@@ -21,7 +22,6 @@ import (
21 21
 func TestGetVersion(t *testing.T) {
22 22
 	eng := NewTestEngine(t)
23 23
 	defer mkRuntimeFromEngine(eng, t).Nuke()
24
-	srv := mkServerFromEngine(eng, t)
25 24
 
26 25
 	var err error
27 26
 	r := httptest.NewRecorder()
... ...
@@ -31,7 +31,7 @@ func TestGetVersion(t *testing.T) {
31 31
 		t.Fatal(err)
32 32
 	}
33 33
 	// FIXME getting the version should require an actual running Server
34
-	if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
34
+	if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
35 35
 		t.Fatal(err)
36 36
 	}
37 37
 	assertHttpNotError(r, t)
... ...
@@ -58,7 +58,6 @@ func TestGetVersion(t *testing.T) {
58 58
 func TestGetInfo(t *testing.T) {
59 59
 	eng := NewTestEngine(t)
60 60
 	defer mkRuntimeFromEngine(eng, t).Nuke()
61
-	srv := mkServerFromEngine(eng, t)
62 61
 
63 62
 	job := eng.Job("images")
64 63
 	initialImages, err := job.Stdout.AddListTable()
... ...
@@ -74,7 +73,7 @@ func TestGetInfo(t *testing.T) {
74 74
 	}
75 75
 	r := httptest.NewRecorder()
76 76
 
77
-	if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
77
+	if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
78 78
 		t.Fatal(err)
79 79
 	}
80 80
 	assertHttpNotError(r, t)
... ...
@@ -122,7 +121,7 @@ func TestGetEvents(t *testing.T) {
122 122
 
123 123
 	r := httptest.NewRecorder()
124 124
 	setTimeout(t, "", 500*time.Millisecond, func() {
125
-		if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
125
+		if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
126 126
 			t.Fatal(err)
127 127
 		}
128 128
 		assertHttpNotError(r, t)
... ...
@@ -146,7 +145,6 @@ func TestGetEvents(t *testing.T) {
146 146
 func TestGetImagesJSON(t *testing.T) {
147 147
 	eng := NewTestEngine(t)
148 148
 	defer mkRuntimeFromEngine(eng, t).Nuke()
149
-	srv := mkServerFromEngine(eng, t)
150 149
 
151 150
 	job := eng.Job("images")
152 151
 	initialImages, err := job.Stdout.AddListTable()
... ...
@@ -164,7 +162,7 @@ func TestGetImagesJSON(t *testing.T) {
164 164
 
165 165
 	r := httptest.NewRecorder()
166 166
 
167
-	if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
167
+	if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
168 168
 		t.Fatal(err)
169 169
 	}
170 170
 	assertHttpNotError(r, t)
... ...
@@ -199,7 +197,7 @@ func TestGetImagesJSON(t *testing.T) {
199 199
 	if err != nil {
200 200
 		t.Fatal(err)
201 201
 	}
202
-	if err := docker.ServeRequest(srv, docker.APIVERSION, r2, req2); err != nil {
202
+	if err := api.ServeRequest(eng, api.APIVERSION, r2, req2); err != nil {
203 203
 		t.Fatal(err)
204 204
 	}
205 205
 	assertHttpNotError(r2, t)
... ...
@@ -232,7 +230,7 @@ func TestGetImagesJSON(t *testing.T) {
232 232
 		t.Fatal(err)
233 233
 	}
234 234
 
235
-	if err := docker.ServeRequest(srv, docker.APIVERSION, r3, req3); err != nil {
235
+	if err := api.ServeRequest(eng, api.APIVERSION, r3, req3); err != nil {
236 236
 		t.Fatal(err)
237 237
 	}
238 238
 	assertHttpNotError(r3, t)
... ...
@@ -250,7 +248,6 @@ func TestGetImagesJSON(t *testing.T) {
250 250
 func TestGetImagesHistory(t *testing.T) {
251 251
 	eng := NewTestEngine(t)
252 252
 	defer mkRuntimeFromEngine(eng, t).Nuke()
253
-	srv := mkServerFromEngine(eng, t)
254 253
 
255 254
 	r := httptest.NewRecorder()
256 255
 
... ...
@@ -258,7 +255,7 @@ func TestGetImagesHistory(t *testing.T) {
258 258
 	if err != nil {
259 259
 		t.Fatal(err)
260 260
 	}
261
-	if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
261
+	if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
262 262
 		t.Fatal(err)
263 263
 	}
264 264
 	assertHttpNotError(r, t)
... ...
@@ -275,7 +272,6 @@ func TestGetImagesHistory(t *testing.T) {
275 275
 func TestGetImagesByName(t *testing.T) {
276 276
 	eng := NewTestEngine(t)
277 277
 	defer mkRuntimeFromEngine(eng, t).Nuke()
278
-	srv := mkServerFromEngine(eng, t)
279 278
 
280 279
 	req, err := http.NewRequest("GET", "/images/"+unitTestImageName+"/json", nil)
281 280
 	if err != nil {
... ...
@@ -283,7 +279,7 @@ func TestGetImagesByName(t *testing.T) {
283 283
 	}
284 284
 
285 285
 	r := httptest.NewRecorder()
286
-	if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
286
+	if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
287 287
 		t.Fatal(err)
288 288
 	}
289 289
 	assertHttpNotError(r, t)
... ...
@@ -300,7 +296,6 @@ func TestGetImagesByName(t *testing.T) {
300 300
 func TestGetContainersJSON(t *testing.T) {
301 301
 	eng := NewTestEngine(t)
302 302
 	defer mkRuntimeFromEngine(eng, t).Nuke()
303
-	srv := mkServerFromEngine(eng, t)
304 303
 
305 304
 	job := eng.Job("containers")
306 305
 	job.SetenvBool("all", true)
... ...
@@ -328,7 +323,7 @@ func TestGetContainersJSON(t *testing.T) {
328 328
 	}
329 329
 
330 330
 	r := httptest.NewRecorder()
331
-	if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
331
+	if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
332 332
 		t.Fatal(err)
333 333
 	}
334 334
 	assertHttpNotError(r, t)
... ...
@@ -347,7 +342,6 @@ func TestGetContainersJSON(t *testing.T) {
347 347
 func TestGetContainersExport(t *testing.T) {
348 348
 	eng := NewTestEngine(t)
349 349
 	defer mkRuntimeFromEngine(eng, t).Nuke()
350
-	srv := mkServerFromEngine(eng, t)
351 350
 
352 351
 	// Create a container and remove a file
353 352
 	containerID := createTestContainer(eng,
... ...
@@ -365,7 +359,7 @@ func TestGetContainersExport(t *testing.T) {
365 365
 	if err != nil {
366 366
 		t.Fatal(err)
367 367
 	}
368
-	if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
368
+	if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
369 369
 		t.Fatal(err)
370 370
 	}
371 371
 	assertHttpNotError(r, t)
... ...
@@ -396,7 +390,6 @@ func TestGetContainersExport(t *testing.T) {
396 396
 func TestGetContainersChanges(t *testing.T) {
397 397
 	eng := NewTestEngine(t)
398 398
 	defer mkRuntimeFromEngine(eng, t).Nuke()
399
-	srv := mkServerFromEngine(eng, t)
400 399
 
401 400
 	// Create a container and remove a file
402 401
 	containerID := createTestContainer(eng,
... ...
@@ -413,7 +406,7 @@ func TestGetContainersChanges(t *testing.T) {
413 413
 	if err != nil {
414 414
 		t.Fatal(err)
415 415
 	}
416
-	if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
416
+	if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
417 417
 		t.Fatal(err)
418 418
 	}
419 419
 	assertHttpNotError(r, t)
... ...
@@ -437,7 +430,6 @@ func TestGetContainersChanges(t *testing.T) {
437 437
 func TestGetContainersTop(t *testing.T) {
438 438
 	eng := NewTestEngine(t)
439 439
 	defer mkRuntimeFromEngine(eng, t).Nuke()
440
-	srv := mkServerFromEngine(eng, t)
441 440
 
442 441
 	containerID := createTestContainer(eng,
443 442
 		&docker.Config{
... ...
@@ -481,7 +473,7 @@ func TestGetContainersTop(t *testing.T) {
481 481
 	if err != nil {
482 482
 		t.Fatal(err)
483 483
 	}
484
-	if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
484
+	if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
485 485
 		t.Fatal(err)
486 486
 	}
487 487
 	assertHttpNotError(r, t)
... ...
@@ -514,7 +506,6 @@ func TestGetContainersTop(t *testing.T) {
514 514
 func TestGetContainersByName(t *testing.T) {
515 515
 	eng := NewTestEngine(t)
516 516
 	defer mkRuntimeFromEngine(eng, t).Nuke()
517
-	srv := mkServerFromEngine(eng, t)
518 517
 
519 518
 	// Create a container and remove a file
520 519
 	containerID := createTestContainer(eng,
... ...
@@ -530,7 +521,7 @@ func TestGetContainersByName(t *testing.T) {
530 530
 	if err != nil {
531 531
 		t.Fatal(err)
532 532
 	}
533
-	if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
533
+	if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
534 534
 		t.Fatal(err)
535 535
 	}
536 536
 	assertHttpNotError(r, t)
... ...
@@ -565,7 +556,7 @@ func TestPostCommit(t *testing.T) {
565 565
 	}
566 566
 
567 567
 	r := httptest.NewRecorder()
568
-	if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
568
+	if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
569 569
 		t.Fatal(err)
570 570
 	}
571 571
 	assertHttpNotError(r, t)
... ...
@@ -585,7 +576,6 @@ func TestPostCommit(t *testing.T) {
585 585
 func TestPostContainersCreate(t *testing.T) {
586 586
 	eng := NewTestEngine(t)
587 587
 	defer mkRuntimeFromEngine(eng, t).Nuke()
588
-	srv := mkServerFromEngine(eng, t)
589 588
 
590 589
 	configJSON, err := json.Marshal(&docker.Config{
591 590
 		Image:  unitTestImageID,
... ...
@@ -602,7 +592,7 @@ func TestPostContainersCreate(t *testing.T) {
602 602
 	}
603 603
 
604 604
 	r := httptest.NewRecorder()
605
-	if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
605
+	if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
606 606
 		t.Fatal(err)
607 607
 	}
608 608
 	assertHttpNotError(r, t)
... ...
@@ -627,7 +617,6 @@ func TestPostContainersCreate(t *testing.T) {
627 627
 func TestPostContainersKill(t *testing.T) {
628 628
 	eng := NewTestEngine(t)
629 629
 	defer mkRuntimeFromEngine(eng, t).Nuke()
630
-	srv := mkServerFromEngine(eng, t)
631 630
 
632 631
 	containerID := createTestContainer(eng,
633 632
 		&docker.Config{
... ...
@@ -652,7 +641,7 @@ func TestPostContainersKill(t *testing.T) {
652 652
 	if err != nil {
653 653
 		t.Fatal(err)
654 654
 	}
655
-	if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
655
+	if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
656 656
 		t.Fatal(err)
657 657
 	}
658 658
 	assertHttpNotError(r, t)
... ...
@@ -667,7 +656,6 @@ func TestPostContainersKill(t *testing.T) {
667 667
 func TestPostContainersRestart(t *testing.T) {
668 668
 	eng := NewTestEngine(t)
669 669
 	defer mkRuntimeFromEngine(eng, t).Nuke()
670
-	srv := mkServerFromEngine(eng, t)
671 670
 
672 671
 	containerID := createTestContainer(eng,
673 672
 		&docker.Config{
... ...
@@ -692,7 +680,7 @@ func TestPostContainersRestart(t *testing.T) {
692 692
 		t.Fatal(err)
693 693
 	}
694 694
 	r := httptest.NewRecorder()
695
-	if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
695
+	if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
696 696
 		t.Fatal(err)
697 697
 	}
698 698
 	assertHttpNotError(r, t)
... ...
@@ -713,7 +701,6 @@ func TestPostContainersRestart(t *testing.T) {
713 713
 func TestPostContainersStart(t *testing.T) {
714 714
 	eng := NewTestEngine(t)
715 715
 	defer mkRuntimeFromEngine(eng, t).Nuke()
716
-	srv := mkServerFromEngine(eng, t)
717 716
 
718 717
 	containerID := createTestContainer(
719 718
 		eng,
... ...
@@ -735,7 +722,7 @@ func TestPostContainersStart(t *testing.T) {
735 735
 	req.Header.Set("Content-Type", "application/json")
736 736
 
737 737
 	r := httptest.NewRecorder()
738
-	if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
738
+	if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
739 739
 		t.Fatal(err)
740 740
 	}
741 741
 	assertHttpNotError(r, t)
... ...
@@ -752,7 +739,7 @@ func TestPostContainersStart(t *testing.T) {
752 752
 	}
753 753
 
754 754
 	r = httptest.NewRecorder()
755
-	if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
755
+	if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
756 756
 		t.Fatal(err)
757 757
 	}
758 758
 	// Starting an already started container should return an error
... ...
@@ -767,7 +754,6 @@ func TestPostContainersStart(t *testing.T) {
767 767
 func TestRunErrorBindMountRootSource(t *testing.T) {
768 768
 	eng := NewTestEngine(t)
769 769
 	defer mkRuntimeFromEngine(eng, t).Nuke()
770
-	srv := mkServerFromEngine(eng, t)
771 770
 
772 771
 	containerID := createTestContainer(
773 772
 		eng,
... ...
@@ -791,7 +777,7 @@ func TestRunErrorBindMountRootSource(t *testing.T) {
791 791
 	req.Header.Set("Content-Type", "application/json")
792 792
 
793 793
 	r := httptest.NewRecorder()
794
-	if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
794
+	if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
795 795
 		t.Fatal(err)
796 796
 	}
797 797
 	if r.Code != http.StatusInternalServerError {
... ...
@@ -803,7 +789,6 @@ func TestRunErrorBindMountRootSource(t *testing.T) {
803 803
 func TestPostContainersStop(t *testing.T) {
804 804
 	eng := NewTestEngine(t)
805 805
 	defer mkRuntimeFromEngine(eng, t).Nuke()
806
-	srv := mkServerFromEngine(eng, t)
807 806
 
808 807
 	containerID := createTestContainer(eng,
809 808
 		&docker.Config{
... ...
@@ -829,7 +814,7 @@ func TestPostContainersStop(t *testing.T) {
829 829
 		t.Fatal(err)
830 830
 	}
831 831
 	r := httptest.NewRecorder()
832
-	if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
832
+	if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
833 833
 		t.Fatal(err)
834 834
 	}
835 835
 	assertHttpNotError(r, t)
... ...
@@ -844,7 +829,6 @@ func TestPostContainersStop(t *testing.T) {
844 844
 func TestPostContainersWait(t *testing.T) {
845 845
 	eng := NewTestEngine(t)
846 846
 	defer mkRuntimeFromEngine(eng, t).Nuke()
847
-	srv := mkServerFromEngine(eng, t)
848 847
 
849 848
 	containerID := createTestContainer(eng,
850 849
 		&docker.Config{
... ...
@@ -862,7 +846,7 @@ func TestPostContainersWait(t *testing.T) {
862 862
 		if err != nil {
863 863
 			t.Fatal(err)
864 864
 		}
865
-		if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
865
+		if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
866 866
 			t.Fatal(err)
867 867
 		}
868 868
 		assertHttpNotError(r, t)
... ...
@@ -883,7 +867,6 @@ func TestPostContainersWait(t *testing.T) {
883 883
 func TestPostContainersAttach(t *testing.T) {
884 884
 	eng := NewTestEngine(t)
885 885
 	defer mkRuntimeFromEngine(eng, t).Nuke()
886
-	srv := mkServerFromEngine(eng, t)
887 886
 
888 887
 	containerID := createTestContainer(eng,
889 888
 		&docker.Config{
... ...
@@ -921,7 +904,7 @@ func TestPostContainersAttach(t *testing.T) {
921 921
 			t.Fatal(err)
922 922
 		}
923 923
 
924
-		if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
924
+		if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
925 925
 			t.Fatal(err)
926 926
 		}
927 927
 		assertHttpNotError(r.ResponseRecorder, t)
... ...
@@ -962,7 +945,6 @@ func TestPostContainersAttach(t *testing.T) {
962 962
 func TestPostContainersAttachStderr(t *testing.T) {
963 963
 	eng := NewTestEngine(t)
964 964
 	defer mkRuntimeFromEngine(eng, t).Nuke()
965
-	srv := mkServerFromEngine(eng, t)
966 965
 
967 966
 	containerID := createTestContainer(eng,
968 967
 		&docker.Config{
... ...
@@ -1000,7 +982,7 @@ func TestPostContainersAttachStderr(t *testing.T) {
1000 1000
 			t.Fatal(err)
1001 1001
 		}
1002 1002
 
1003
-		if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
1003
+		if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
1004 1004
 			t.Fatal(err)
1005 1005
 		}
1006 1006
 		assertHttpNotError(r.ResponseRecorder, t)
... ...
@@ -1044,7 +1026,6 @@ func TestPostContainersAttachStderr(t *testing.T) {
1044 1044
 func TestDeleteContainers(t *testing.T) {
1045 1045
 	eng := NewTestEngine(t)
1046 1046
 	defer mkRuntimeFromEngine(eng, t).Nuke()
1047
-	srv := mkServerFromEngine(eng, t)
1048 1047
 
1049 1048
 	containerID := createTestContainer(eng,
1050 1049
 		&docker.Config{
... ...
@@ -1058,7 +1039,7 @@ func TestDeleteContainers(t *testing.T) {
1058 1058
 		t.Fatal(err)
1059 1059
 	}
1060 1060
 	r := httptest.NewRecorder()
1061
-	if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
1061
+	if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
1062 1062
 		t.Fatal(err)
1063 1063
 	}
1064 1064
 	assertHttpNotError(r, t)
... ...
@@ -1071,13 +1052,13 @@ func TestDeleteContainers(t *testing.T) {
1071 1071
 func TestOptionsRoute(t *testing.T) {
1072 1072
 	eng := NewTestEngine(t)
1073 1073
 	defer mkRuntimeFromEngine(eng, t).Nuke()
1074
-	srv := mkServerFromEngine(eng, t)
1074
+
1075 1075
 	r := httptest.NewRecorder()
1076 1076
 	req, err := http.NewRequest("OPTIONS", "/", nil)
1077 1077
 	if err != nil {
1078 1078
 		t.Fatal(err)
1079 1079
 	}
1080
-	if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
1080
+	if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
1081 1081
 		t.Fatal(err)
1082 1082
 	}
1083 1083
 	assertHttpNotError(r, t)
... ...
@@ -1089,14 +1070,14 @@ func TestOptionsRoute(t *testing.T) {
1089 1089
 func TestGetEnabledCors(t *testing.T) {
1090 1090
 	eng := NewTestEngine(t)
1091 1091
 	defer mkRuntimeFromEngine(eng, t).Nuke()
1092
-	srv := mkServerFromEngine(eng, t)
1092
+
1093 1093
 	r := httptest.NewRecorder()
1094 1094
 
1095 1095
 	req, err := http.NewRequest("GET", "/version", nil)
1096 1096
 	if err != nil {
1097 1097
 		t.Fatal(err)
1098 1098
 	}
1099
-	if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
1099
+	if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
1100 1100
 		t.Fatal(err)
1101 1101
 	}
1102 1102
 	assertHttpNotError(r, t)
... ...
@@ -1122,7 +1103,6 @@ func TestGetEnabledCors(t *testing.T) {
1122 1122
 func TestDeleteImages(t *testing.T) {
1123 1123
 	eng := NewTestEngine(t)
1124 1124
 	defer mkRuntimeFromEngine(eng, t).Nuke()
1125
-	srv := mkServerFromEngine(eng, t)
1126 1125
 
1127 1126
 	initialImages := getImages(eng, t, true, "")
1128 1127
 
... ...
@@ -1142,7 +1122,7 @@ func TestDeleteImages(t *testing.T) {
1142 1142
 	}
1143 1143
 
1144 1144
 	r := httptest.NewRecorder()
1145
-	if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
1145
+	if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
1146 1146
 		t.Fatal(err)
1147 1147
 	}
1148 1148
 	if r.Code != http.StatusConflict {
... ...
@@ -1155,7 +1135,7 @@ func TestDeleteImages(t *testing.T) {
1155 1155
 	}
1156 1156
 
1157 1157
 	r2 := httptest.NewRecorder()
1158
-	if err := docker.ServeRequest(srv, docker.APIVERSION, r2, req2); err != nil {
1158
+	if err := api.ServeRequest(eng, api.APIVERSION, r2, req2); err != nil {
1159 1159
 		t.Fatal(err)
1160 1160
 	}
1161 1161
 	assertHttpNotError(r2, t)
... ...
@@ -1180,7 +1160,6 @@ func TestDeleteImages(t *testing.T) {
1180 1180
 func TestPostContainersCopy(t *testing.T) {
1181 1181
 	eng := NewTestEngine(t)
1182 1182
 	defer mkRuntimeFromEngine(eng, t).Nuke()
1183
-	srv := mkServerFromEngine(eng, t)
1184 1183
 
1185 1184
 	// Create a container and remove a file
1186 1185
 	containerID := createTestContainer(eng,
... ...
@@ -1208,7 +1187,7 @@ func TestPostContainersCopy(t *testing.T) {
1208 1208
 		t.Fatal(err)
1209 1209
 	}
1210 1210
 	req.Header.Add("Content-Type", "application/json")
1211
-	if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
1211
+	if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
1212 1212
 		t.Fatal(err)
1213 1213
 	}
1214 1214
 	assertHttpNotError(r, t)
... ...
@@ -2,6 +2,7 @@ package docker
2 2
 
3 3
 import (
4 4
 	"fmt"
5
+	"github.com/dotcloud/docker/api"
5 6
 	"github.com/dotcloud/docker/utils"
6 7
 	"os"
7 8
 	"path/filepath"
... ...
@@ -129,7 +130,7 @@ func ValidateEnv(val string) (string, error) {
129 129
 }
130 130
 
131 131
 func ValidateHost(val string) (string, error) {
132
-	host, err := utils.ParseHost(DEFAULTHTTPHOST, DEFAULTHTTPPORT, DEFAULTUNIXSOCKET, val)
132
+	host, err := utils.ParseHost(api.DEFAULTHTTPHOST, api.DEFAULTHTTPPORT, api.DEFAULTUNIXSOCKET, val)
133 133
 	if err != nil {
134 134
 		return val, err
135 135
 	}
... ...
@@ -4,6 +4,7 @@ import (
4 4
 	"encoding/json"
5 5
 	"errors"
6 6
 	"fmt"
7
+	"github.com/dotcloud/docker/api"
7 8
 	"github.com/dotcloud/docker/archive"
8 9
 	"github.com/dotcloud/docker/auth"
9 10
 	"github.com/dotcloud/docker/engine"
... ...
@@ -122,7 +123,7 @@ func (srv *Server) ListenAndServe(job *engine.Job) engine.Status {
122 122
 		protoAddrParts := strings.SplitN(protoAddr, "://", 2)
123 123
 		go func() {
124 124
 			log.Printf("Listening for HTTP on %s (%s)\n", protoAddrParts[0], protoAddrParts[1])
125
-			chErrors <- ListenAndServe(protoAddrParts[0], protoAddrParts[1], srv, job.GetenvBool("Logging"), job.GetenvBool("EnableCors"))
125
+			chErrors <- api.ListenAndServe(protoAddrParts[0], protoAddrParts[1], srv.Eng, job.GetenvBool("Logging"), job.GetenvBool("EnableCors"), VERSION)
126 126
 		}()
127 127
 	}
128 128