Browse code

Merge pull request #3827 from vieux/mova_api_package

Move api into sub package

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