Browse code

split API into 2 go packages

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

Victor Vieux authored on 2014/03/29 07:59:29
Showing 7 changed files
1 1
deleted file mode 100644
... ...
@@ -1,2546 +0,0 @@
1
-package api
2
-
3
-import (
4
-	"bufio"
5
-	"bytes"
6
-	"crypto/tls"
7
-	"encoding/base64"
8
-	"encoding/json"
9
-	"errors"
10
-	"fmt"
11
-	"github.com/dotcloud/docker/archive"
12
-	"github.com/dotcloud/docker/dockerversion"
13
-	"github.com/dotcloud/docker/engine"
14
-	"github.com/dotcloud/docker/nat"
15
-	flag "github.com/dotcloud/docker/pkg/mflag"
16
-	"github.com/dotcloud/docker/pkg/signal"
17
-	"github.com/dotcloud/docker/pkg/term"
18
-	"github.com/dotcloud/docker/registry"
19
-	"github.com/dotcloud/docker/runconfig"
20
-	"github.com/dotcloud/docker/utils"
21
-	"io"
22
-	"io/ioutil"
23
-	"net"
24
-	"net/http"
25
-	"net/http/httputil"
26
-	"net/url"
27
-	"os"
28
-	"os/exec"
29
-	gosignal "os/signal"
30
-	"path"
31
-	"reflect"
32
-	"regexp"
33
-	goruntime "runtime"
34
-	"strconv"
35
-	"strings"
36
-	"syscall"
37
-	"text/tabwriter"
38
-	"text/template"
39
-	"time"
40
-)
41
-
42
-var funcMap = template.FuncMap{
43
-	"json": func(v interface{}) string {
44
-		a, _ := json.Marshal(v)
45
-		return string(a)
46
-	},
47
-}
48
-
49
-var (
50
-	ErrConnectionRefused = errors.New("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
51
-)
52
-
53
-func (cli *DockerCli) getMethod(name string) (func(...string) error, bool) {
54
-	methodName := "Cmd" + strings.ToUpper(name[:1]) + strings.ToLower(name[1:])
55
-	method := reflect.ValueOf(cli).MethodByName(methodName)
56
-	if !method.IsValid() {
57
-		return nil, false
58
-	}
59
-	return method.Interface().(func(...string) error), true
60
-}
61
-
62
-func (cli *DockerCli) ParseCommands(args ...string) error {
63
-	if len(args) > 0 {
64
-		method, exists := cli.getMethod(args[0])
65
-		if !exists {
66
-			fmt.Println("Error: Command not found:", args[0])
67
-			return cli.CmdHelp(args[1:]...)
68
-		}
69
-		return method(args[1:]...)
70
-	}
71
-	return cli.CmdHelp(args...)
72
-}
73
-
74
-func (cli *DockerCli) CmdHelp(args ...string) error {
75
-	if len(args) > 0 {
76
-		method, exists := cli.getMethod(args[0])
77
-		if !exists {
78
-			fmt.Fprintf(cli.err, "Error: Command not found: %s\n", args[0])
79
-		} else {
80
-			method("--help")
81
-			return nil
82
-		}
83
-	}
84
-	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)
85
-	for _, command := range [][]string{
86
-		{"attach", "Attach to a running container"},
87
-		{"build", "Build a container from a Dockerfile"},
88
-		{"commit", "Create a new image from a container's changes"},
89
-		{"cp", "Copy files/folders from the containers filesystem to the host path"},
90
-		{"diff", "Inspect changes on a container's filesystem"},
91
-		{"events", "Get real time events from the server"},
92
-		{"export", "Stream the contents of a container as a tar archive"},
93
-		{"history", "Show the history of an image"},
94
-		{"images", "List images"},
95
-		{"import", "Create a new filesystem image from the contents of a tarball"},
96
-		{"info", "Display system-wide information"},
97
-		{"insert", "Insert a file in an image"},
98
-		{"inspect", "Return low-level information on a container"},
99
-		{"kill", "Kill a running container"},
100
-		{"load", "Load an image from a tar archive"},
101
-		{"login", "Register or Login to the docker registry server"},
102
-		{"logs", "Fetch the logs of a container"},
103
-		{"port", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT"},
104
-		{"ps", "List containers"},
105
-		{"pull", "Pull an image or a repository from the docker registry server"},
106
-		{"push", "Push an image or a repository to the docker registry server"},
107
-		{"restart", "Restart a running container"},
108
-		{"rm", "Remove one or more containers"},
109
-		{"rmi", "Remove one or more images"},
110
-		{"run", "Run a command in a new container"},
111
-		{"save", "Save an image to a tar archive"},
112
-		{"search", "Search for an image in the docker index"},
113
-		{"start", "Start a stopped container"},
114
-		{"stop", "Stop a running container"},
115
-		{"tag", "Tag an image into a repository"},
116
-		{"top", "Lookup the running processes of a container"},
117
-		{"version", "Show the docker version information"},
118
-		{"wait", "Block until a container stops, then print its exit code"},
119
-	} {
120
-		help += fmt.Sprintf("    %-10.10s%s\n", command[0], command[1])
121
-	}
122
-	fmt.Fprintf(cli.err, "%s\n", help)
123
-	return nil
124
-}
125
-
126
-func (cli *DockerCli) CmdInsert(args ...string) error {
127
-	cmd := cli.Subcmd("insert", "IMAGE URL PATH", "Insert a file from URL in the IMAGE at PATH")
128
-	if err := cmd.Parse(args); err != nil {
129
-		return nil
130
-	}
131
-	if cmd.NArg() != 3 {
132
-		cmd.Usage()
133
-		return nil
134
-	}
135
-
136
-	v := url.Values{}
137
-	v.Set("url", cmd.Arg(1))
138
-	v.Set("path", cmd.Arg(2))
139
-
140
-	return cli.stream("POST", "/images/"+cmd.Arg(0)+"/insert?"+v.Encode(), nil, cli.out, nil)
141
-}
142
-
143
-func (cli *DockerCli) CmdBuild(args ...string) error {
144
-	cmd := cli.Subcmd("build", "[OPTIONS] PATH | URL | -", "Build a new container image from the source code at PATH")
145
-	tag := cmd.String([]string{"t", "-tag"}, "", "Repository name (and optionally a tag) to be applied to the resulting image in case of success")
146
-	suppressOutput := cmd.Bool([]string{"q", "-quiet"}, false, "Suppress the verbose output generated by the containers")
147
-	noCache := cmd.Bool([]string{"#no-cache", "-no-cache"}, false, "Do not use cache when building the image")
148
-	rm := cmd.Bool([]string{"#rm", "-rm"}, true, "Remove intermediate containers after a successful build")
149
-	if err := cmd.Parse(args); err != nil {
150
-		return nil
151
-	}
152
-	if cmd.NArg() != 1 {
153
-		cmd.Usage()
154
-		return nil
155
-	}
156
-
157
-	var (
158
-		context  archive.Archive
159
-		isRemote bool
160
-		err      error
161
-	)
162
-
163
-	_, err = exec.LookPath("git")
164
-	hasGit := err == nil
165
-	if cmd.Arg(0) == "-" {
166
-		// As a special case, 'docker build -' will build from an empty context with the
167
-		// contents of stdin as a Dockerfile
168
-		dockerfile, err := ioutil.ReadAll(cli.in)
169
-		if err != nil {
170
-			return err
171
-		}
172
-		context, err = archive.Generate("Dockerfile", string(dockerfile))
173
-	} else if utils.IsURL(cmd.Arg(0)) && (!utils.IsGIT(cmd.Arg(0)) || !hasGit) {
174
-		isRemote = true
175
-	} else {
176
-		root := cmd.Arg(0)
177
-		if utils.IsGIT(root) {
178
-			remoteURL := cmd.Arg(0)
179
-			if !strings.HasPrefix(remoteURL, "git://") && !strings.HasPrefix(remoteURL, "git@") && !utils.IsURL(remoteURL) {
180
-				remoteURL = "https://" + remoteURL
181
-			}
182
-
183
-			root, err = ioutil.TempDir("", "docker-build-git")
184
-			if err != nil {
185
-				return err
186
-			}
187
-			defer os.RemoveAll(root)
188
-
189
-			if output, err := exec.Command("git", "clone", "--recursive", remoteURL, root).CombinedOutput(); err != nil {
190
-				return fmt.Errorf("Error trying to use git: %s (%s)", err, output)
191
-			}
192
-		}
193
-		if _, err := os.Stat(root); err != nil {
194
-			return err
195
-		}
196
-		filename := path.Join(root, "Dockerfile")
197
-		if _, err = os.Stat(filename); os.IsNotExist(err) {
198
-			return fmt.Errorf("no Dockerfile found in %s", cmd.Arg(0))
199
-		}
200
-		context, err = archive.Tar(root, archive.Uncompressed)
201
-	}
202
-	var body io.Reader
203
-	// Setup an upload progress bar
204
-	// FIXME: ProgressReader shouldn't be this annoying to use
205
-	if context != nil {
206
-		sf := utils.NewStreamFormatter(false)
207
-		body = utils.ProgressReader(context, 0, cli.err, sf, true, "", "Uploading context")
208
-	}
209
-	// Upload the build context
210
-	v := &url.Values{}
211
-
212
-	//Check if the given image name can be resolved
213
-	if *tag != "" {
214
-		repository, _ := utils.ParseRepositoryTag(*tag)
215
-		if _, _, err := registry.ResolveRepositoryName(repository); err != nil {
216
-			return err
217
-		}
218
-	}
219
-
220
-	v.Set("t", *tag)
221
-
222
-	if *suppressOutput {
223
-		v.Set("q", "1")
224
-	}
225
-	if isRemote {
226
-		v.Set("remote", cmd.Arg(0))
227
-	}
228
-	if *noCache {
229
-		v.Set("nocache", "1")
230
-	}
231
-	if *rm {
232
-		v.Set("rm", "1")
233
-	}
234
-
235
-	cli.LoadConfigFile()
236
-
237
-	headers := http.Header(make(map[string][]string))
238
-	buf, err := json.Marshal(cli.configFile)
239
-	if err != nil {
240
-		return err
241
-	}
242
-	headers.Add("X-Registry-Config", base64.URLEncoding.EncodeToString(buf))
243
-
244
-	if context != nil {
245
-		headers.Set("Content-Type", "application/tar")
246
-	}
247
-	err = cli.stream("POST", fmt.Sprintf("/build?%s", v.Encode()), body, cli.out, headers)
248
-	if jerr, ok := err.(*utils.JSONError); ok {
249
-		// If no error code is set, default to 1
250
-		if jerr.Code == 0 {
251
-			jerr.Code = 1
252
-		}
253
-		return &utils.StatusError{Status: jerr.Message, StatusCode: jerr.Code}
254
-	}
255
-	return err
256
-}
257
-
258
-// 'docker login': login / register a user to registry service.
259
-func (cli *DockerCli) CmdLogin(args ...string) error {
260
-	cmd := cli.Subcmd("login", "[OPTIONS] [SERVER]", "Register or Login to a docker registry server, if no server is specified \""+registry.IndexServerAddress()+"\" is the default.")
261
-
262
-	var username, password, email string
263
-
264
-	cmd.StringVar(&username, []string{"u", "-username"}, "", "Username")
265
-	cmd.StringVar(&password, []string{"p", "-password"}, "", "Password")
266
-	cmd.StringVar(&email, []string{"e", "-email"}, "", "Email")
267
-	err := cmd.Parse(args)
268
-	if err != nil {
269
-		return nil
270
-	}
271
-	serverAddress := registry.IndexServerAddress()
272
-	if len(cmd.Args()) > 0 {
273
-		serverAddress = cmd.Arg(0)
274
-	}
275
-
276
-	promptDefault := func(prompt string, configDefault string) {
277
-		if configDefault == "" {
278
-			fmt.Fprintf(cli.out, "%s: ", prompt)
279
-		} else {
280
-			fmt.Fprintf(cli.out, "%s (%s): ", prompt, configDefault)
281
-		}
282
-	}
283
-
284
-	readInput := func(in io.Reader, out io.Writer) string {
285
-		reader := bufio.NewReader(in)
286
-		line, _, err := reader.ReadLine()
287
-		if err != nil {
288
-			fmt.Fprintln(out, err.Error())
289
-			os.Exit(1)
290
-		}
291
-		return string(line)
292
-	}
293
-
294
-	cli.LoadConfigFile()
295
-	authconfig, ok := cli.configFile.Configs[serverAddress]
296
-	if !ok {
297
-		authconfig = registry.AuthConfig{}
298
-	}
299
-
300
-	if username == "" {
301
-		promptDefault("Username", authconfig.Username)
302
-		username = readInput(cli.in, cli.out)
303
-		if username == "" {
304
-			username = authconfig.Username
305
-		}
306
-	}
307
-	if username != authconfig.Username {
308
-		if password == "" {
309
-			oldState, _ := term.SaveState(cli.terminalFd)
310
-			fmt.Fprintf(cli.out, "Password: ")
311
-			term.DisableEcho(cli.terminalFd, oldState)
312
-
313
-			password = readInput(cli.in, cli.out)
314
-			fmt.Fprint(cli.out, "\n")
315
-
316
-			term.RestoreTerminal(cli.terminalFd, oldState)
317
-			if password == "" {
318
-				return fmt.Errorf("Error : Password Required")
319
-			}
320
-		}
321
-
322
-		if email == "" {
323
-			promptDefault("Email", authconfig.Email)
324
-			email = readInput(cli.in, cli.out)
325
-			if email == "" {
326
-				email = authconfig.Email
327
-			}
328
-		}
329
-	} else {
330
-		password = authconfig.Password
331
-		email = authconfig.Email
332
-	}
333
-	authconfig.Username = username
334
-	authconfig.Password = password
335
-	authconfig.Email = email
336
-	authconfig.ServerAddress = serverAddress
337
-	cli.configFile.Configs[serverAddress] = authconfig
338
-
339
-	stream, statusCode, err := cli.call("POST", "/auth", cli.configFile.Configs[serverAddress], false)
340
-	if statusCode == 401 {
341
-		delete(cli.configFile.Configs, serverAddress)
342
-		registry.SaveConfig(cli.configFile)
343
-		return err
344
-	}
345
-	if err != nil {
346
-		return err
347
-	}
348
-	var out2 engine.Env
349
-	err = out2.Decode(stream)
350
-	if err != nil {
351
-		cli.configFile, _ = registry.LoadConfig(os.Getenv("HOME"))
352
-		return err
353
-	}
354
-	registry.SaveConfig(cli.configFile)
355
-	if out2.Get("Status") != "" {
356
-		fmt.Fprintf(cli.out, "%s\n", out2.Get("Status"))
357
-	}
358
-	return nil
359
-}
360
-
361
-// 'docker wait': block until a container stops
362
-func (cli *DockerCli) CmdWait(args ...string) error {
363
-	cmd := cli.Subcmd("wait", "CONTAINER [CONTAINER...]", "Block until a container stops, then print its exit code.")
364
-	if err := cmd.Parse(args); err != nil {
365
-		return nil
366
-	}
367
-	if cmd.NArg() < 1 {
368
-		cmd.Usage()
369
-		return nil
370
-	}
371
-	var encounteredError error
372
-	for _, name := range cmd.Args() {
373
-		status, err := waitForExit(cli, name)
374
-		if err != nil {
375
-			fmt.Fprintf(cli.err, "%s\n", err)
376
-			encounteredError = fmt.Errorf("Error: failed to wait one or more containers")
377
-		} else {
378
-			fmt.Fprintf(cli.out, "%d\n", status)
379
-		}
380
-	}
381
-	return encounteredError
382
-}
383
-
384
-// 'docker version': show version information
385
-func (cli *DockerCli) CmdVersion(args ...string) error {
386
-	cmd := cli.Subcmd("version", "", "Show the docker version information.")
387
-	if err := cmd.Parse(args); err != nil {
388
-		return nil
389
-	}
390
-
391
-	if cmd.NArg() > 0 {
392
-		cmd.Usage()
393
-		return nil
394
-	}
395
-	if dockerversion.VERSION != "" {
396
-		fmt.Fprintf(cli.out, "Client version: %s\n", dockerversion.VERSION)
397
-	}
398
-	fmt.Fprintf(cli.out, "Go version (client): %s\n", goruntime.Version())
399
-	if dockerversion.GITCOMMIT != "" {
400
-		fmt.Fprintf(cli.out, "Git commit (client): %s\n", dockerversion.GITCOMMIT)
401
-	}
402
-
403
-	body, _, err := readBody(cli.call("GET", "/version", nil, false))
404
-	if err != nil {
405
-		return err
406
-	}
407
-
408
-	out := engine.NewOutput()
409
-	remoteVersion, err := out.AddEnv()
410
-	if err != nil {
411
-		utils.Errorf("Error reading remote version: %s\n", err)
412
-		return err
413
-	}
414
-	if _, err := out.Write(body); err != nil {
415
-		utils.Errorf("Error reading remote version: %s\n", err)
416
-		return err
417
-	}
418
-	out.Close()
419
-	fmt.Fprintf(cli.out, "Server version: %s\n", remoteVersion.Get("Version"))
420
-	fmt.Fprintf(cli.out, "Git commit (server): %s\n", remoteVersion.Get("GitCommit"))
421
-	fmt.Fprintf(cli.out, "Go version (server): %s\n", remoteVersion.Get("GoVersion"))
422
-	release := utils.GetReleaseVersion()
423
-	if release != "" {
424
-		fmt.Fprintf(cli.out, "Last stable version: %s", release)
425
-		if (dockerversion.VERSION != "" || remoteVersion.Exists("Version")) && (strings.Trim(dockerversion.VERSION, "-dev") != release || strings.Trim(remoteVersion.Get("Version"), "-dev") != release) {
426
-			fmt.Fprintf(cli.out, ", please update docker")
427
-		}
428
-		fmt.Fprintf(cli.out, "\n")
429
-	}
430
-	return nil
431
-}
432
-
433
-// 'docker info': display system-wide information.
434
-func (cli *DockerCli) CmdInfo(args ...string) error {
435
-	cmd := cli.Subcmd("info", "", "Display system-wide information")
436
-	if err := cmd.Parse(args); err != nil {
437
-		return nil
438
-	}
439
-	if cmd.NArg() > 0 {
440
-		cmd.Usage()
441
-		return nil
442
-	}
443
-
444
-	body, _, err := readBody(cli.call("GET", "/info", nil, false))
445
-	if err != nil {
446
-		return err
447
-	}
448
-
449
-	out := engine.NewOutput()
450
-	remoteInfo, err := out.AddEnv()
451
-	if err != nil {
452
-		return err
453
-	}
454
-
455
-	if _, err := out.Write(body); err != nil {
456
-		utils.Errorf("Error reading remote info: %s\n", err)
457
-		return err
458
-	}
459
-	out.Close()
460
-
461
-	fmt.Fprintf(cli.out, "Containers: %d\n", remoteInfo.GetInt("Containers"))
462
-	fmt.Fprintf(cli.out, "Images: %d\n", remoteInfo.GetInt("Images"))
463
-	fmt.Fprintf(cli.out, "Storage Driver: %s\n", remoteInfo.Get("Driver"))
464
-	var driverStatus [][2]string
465
-	if err := remoteInfo.GetJson("DriverStatus", &driverStatus); err != nil {
466
-		return err
467
-	}
468
-	for _, pair := range driverStatus {
469
-		fmt.Fprintf(cli.out, " %s: %s\n", pair[0], pair[1])
470
-	}
471
-	fmt.Fprintf(cli.out, "Execution Driver: %s\n", remoteInfo.Get("ExecutionDriver"))
472
-	fmt.Fprintf(cli.out, "Kernel Version: %s\n", remoteInfo.Get("KernelVersion"))
473
-
474
-	if remoteInfo.GetBool("Debug") || os.Getenv("DEBUG") != "" {
475
-		fmt.Fprintf(cli.out, "Debug mode (server): %v\n", remoteInfo.GetBool("Debug"))
476
-		fmt.Fprintf(cli.out, "Debug mode (client): %v\n", os.Getenv("DEBUG") != "")
477
-		fmt.Fprintf(cli.out, "Fds: %d\n", remoteInfo.GetInt("NFd"))
478
-		fmt.Fprintf(cli.out, "Goroutines: %d\n", remoteInfo.GetInt("NGoroutines"))
479
-		fmt.Fprintf(cli.out, "EventsListeners: %d\n", remoteInfo.GetInt("NEventsListener"))
480
-
481
-		if initSha1 := remoteInfo.Get("InitSha1"); initSha1 != "" {
482
-			fmt.Fprintf(cli.out, "Init SHA1: %s\n", initSha1)
483
-		}
484
-		if initPath := remoteInfo.Get("InitPath"); initPath != "" {
485
-			fmt.Fprintf(cli.out, "Init Path: %s\n", initPath)
486
-		}
487
-	}
488
-
489
-	if len(remoteInfo.GetList("IndexServerAddress")) != 0 {
490
-		cli.LoadConfigFile()
491
-		u := cli.configFile.Configs[remoteInfo.Get("IndexServerAddress")].Username
492
-		if len(u) > 0 {
493
-			fmt.Fprintf(cli.out, "Username: %v\n", u)
494
-			fmt.Fprintf(cli.out, "Registry: %v\n", remoteInfo.GetList("IndexServerAddress"))
495
-		}
496
-	}
497
-	if !remoteInfo.GetBool("MemoryLimit") {
498
-		fmt.Fprintf(cli.err, "WARNING: No memory limit support\n")
499
-	}
500
-	if !remoteInfo.GetBool("SwapLimit") {
501
-		fmt.Fprintf(cli.err, "WARNING: No swap limit support\n")
502
-	}
503
-	if !remoteInfo.GetBool("IPv4Forwarding") {
504
-		fmt.Fprintf(cli.err, "WARNING: IPv4 forwarding is disabled.\n")
505
-	}
506
-	return nil
507
-}
508
-
509
-func (cli *DockerCli) CmdStop(args ...string) error {
510
-	cmd := cli.Subcmd("stop", "[OPTIONS] CONTAINER [CONTAINER...]", "Stop a running container (Send SIGTERM, and then SIGKILL after grace period)")
511
-	nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Number of seconds to wait for the container to stop before killing it.")
512
-	if err := cmd.Parse(args); err != nil {
513
-		return nil
514
-	}
515
-	if cmd.NArg() < 1 {
516
-		cmd.Usage()
517
-		return nil
518
-	}
519
-
520
-	v := url.Values{}
521
-	v.Set("t", strconv.Itoa(*nSeconds))
522
-
523
-	var encounteredError error
524
-	for _, name := range cmd.Args() {
525
-		_, _, err := readBody(cli.call("POST", "/containers/"+name+"/stop?"+v.Encode(), nil, false))
526
-		if err != nil {
527
-			fmt.Fprintf(cli.err, "%s\n", err)
528
-			encounteredError = fmt.Errorf("Error: failed to stop one or more containers")
529
-		} else {
530
-			fmt.Fprintf(cli.out, "%s\n", name)
531
-		}
532
-	}
533
-	return encounteredError
534
-}
535
-
536
-func (cli *DockerCli) CmdRestart(args ...string) error {
537
-	cmd := cli.Subcmd("restart", "[OPTIONS] CONTAINER [CONTAINER...]", "Restart a running container")
538
-	nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Number of seconds to try to stop for before killing the container. Once killed it will then be restarted. Default=10")
539
-	if err := cmd.Parse(args); err != nil {
540
-		return nil
541
-	}
542
-	if cmd.NArg() < 1 {
543
-		cmd.Usage()
544
-		return nil
545
-	}
546
-
547
-	v := url.Values{}
548
-	v.Set("t", strconv.Itoa(*nSeconds))
549
-
550
-	var encounteredError error
551
-	for _, name := range cmd.Args() {
552
-		_, _, err := readBody(cli.call("POST", "/containers/"+name+"/restart?"+v.Encode(), nil, false))
553
-		if err != nil {
554
-			fmt.Fprintf(cli.err, "%s\n", err)
555
-			encounteredError = fmt.Errorf("Error: failed to restart one or more containers")
556
-		} else {
557
-			fmt.Fprintf(cli.out, "%s\n", name)
558
-		}
559
-	}
560
-	return encounteredError
561
-}
562
-
563
-func (cli *DockerCli) forwardAllSignals(cid string) chan os.Signal {
564
-	sigc := make(chan os.Signal, 1)
565
-	signal.CatchAll(sigc)
566
-	go func() {
567
-		for s := range sigc {
568
-			if s == syscall.SIGCHLD {
569
-				continue
570
-			}
571
-			var sig string
572
-			for sigStr, sigN := range signal.SignalMap {
573
-				if sigN == s {
574
-					sig = sigStr
575
-					break
576
-				}
577
-			}
578
-			if sig == "" {
579
-				utils.Errorf("Unsupported signal: %d. Discarding.", s)
580
-			}
581
-			if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", cid, sig), nil, false)); err != nil {
582
-				utils.Debugf("Error sending signal: %s", err)
583
-			}
584
-		}
585
-	}()
586
-	return sigc
587
-}
588
-
589
-func (cli *DockerCli) CmdStart(args ...string) error {
590
-	cmd := cli.Subcmd("start", "CONTAINER [CONTAINER...]", "Restart a stopped container")
591
-	attach := cmd.Bool([]string{"a", "-attach"}, false, "Attach container's stdout/stderr and forward all signals to the process")
592
-	openStdin := cmd.Bool([]string{"i", "-interactive"}, false, "Attach container's stdin")
593
-	if err := cmd.Parse(args); err != nil {
594
-		return nil
595
-	}
596
-	if cmd.NArg() < 1 {
597
-		cmd.Usage()
598
-		return nil
599
-	}
600
-
601
-	var cErr chan error
602
-	var tty bool
603
-	if *attach || *openStdin {
604
-		if cmd.NArg() > 1 {
605
-			return fmt.Errorf("You cannot start and attach multiple containers at once.")
606
-		}
607
-
608
-		body, _, err := readBody(cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, false))
609
-		if err != nil {
610
-			return err
611
-		}
612
-
613
-		container := &Container{}
614
-		err = json.Unmarshal(body, container)
615
-		if err != nil {
616
-			return err
617
-		}
618
-
619
-		tty = container.Config.Tty
620
-
621
-		if !container.Config.Tty {
622
-			sigc := cli.forwardAllSignals(cmd.Arg(0))
623
-			defer signal.StopCatch(sigc)
624
-		}
625
-
626
-		var in io.ReadCloser
627
-
628
-		v := url.Values{}
629
-		v.Set("stream", "1")
630
-		if *openStdin && container.Config.OpenStdin {
631
-			v.Set("stdin", "1")
632
-			in = cli.in
633
-		}
634
-		v.Set("stdout", "1")
635
-		v.Set("stderr", "1")
636
-
637
-		cErr = utils.Go(func() error {
638
-			return cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), container.Config.Tty, in, cli.out, cli.err, nil)
639
-		})
640
-	}
641
-
642
-	var encounteredError error
643
-	for _, name := range cmd.Args() {
644
-		_, _, err := readBody(cli.call("POST", "/containers/"+name+"/start", nil, false))
645
-		if err != nil {
646
-			if !*attach || !*openStdin {
647
-				fmt.Fprintf(cli.err, "%s\n", err)
648
-				encounteredError = fmt.Errorf("Error: failed to start one or more containers")
649
-			}
650
-		} else {
651
-			if !*attach || !*openStdin {
652
-				fmt.Fprintf(cli.out, "%s\n", name)
653
-			}
654
-		}
655
-	}
656
-	if encounteredError != nil {
657
-		if *openStdin || *attach {
658
-			cli.in.Close()
659
-			<-cErr
660
-		}
661
-		return encounteredError
662
-	}
663
-
664
-	if *openStdin || *attach {
665
-		if tty && cli.isTerminal {
666
-			if err := cli.monitorTtySize(cmd.Arg(0)); err != nil {
667
-				utils.Errorf("Error monitoring TTY size: %s\n", err)
668
-			}
669
-		}
670
-		return <-cErr
671
-	}
672
-	return nil
673
-}
674
-
675
-func (cli *DockerCli) CmdInspect(args ...string) error {
676
-	cmd := cli.Subcmd("inspect", "CONTAINER|IMAGE [CONTAINER|IMAGE...]", "Return low-level information on a container/image")
677
-	tmplStr := cmd.String([]string{"f", "#format", "-format"}, "", "Format the output using the given go template.")
678
-	if err := cmd.Parse(args); err != nil {
679
-		return nil
680
-	}
681
-	if cmd.NArg() < 1 {
682
-		cmd.Usage()
683
-		return nil
684
-	}
685
-
686
-	var tmpl *template.Template
687
-	if *tmplStr != "" {
688
-		var err error
689
-		if tmpl, err = template.New("").Funcs(funcMap).Parse(*tmplStr); err != nil {
690
-			fmt.Fprintf(cli.err, "Template parsing error: %v\n", err)
691
-			return &utils.StatusError{StatusCode: 64,
692
-				Status: "Template parsing error: " + err.Error()}
693
-		}
694
-	}
695
-
696
-	indented := new(bytes.Buffer)
697
-	indented.WriteByte('[')
698
-	status := 0
699
-
700
-	for _, name := range cmd.Args() {
701
-		obj, _, err := readBody(cli.call("GET", "/containers/"+name+"/json", nil, false))
702
-		if err != nil {
703
-			obj, _, err = readBody(cli.call("GET", "/images/"+name+"/json", nil, false))
704
-			if err != nil {
705
-				if strings.Contains(err.Error(), "No such") {
706
-					fmt.Fprintf(cli.err, "Error: No such image or container: %s\n", name)
707
-				} else {
708
-					fmt.Fprintf(cli.err, "%s", err)
709
-				}
710
-				status = 1
711
-				continue
712
-			}
713
-		}
714
-
715
-		if tmpl == nil {
716
-			if err = json.Indent(indented, obj, "", "    "); err != nil {
717
-				fmt.Fprintf(cli.err, "%s\n", err)
718
-				status = 1
719
-				continue
720
-			}
721
-		} else {
722
-			// Has template, will render
723
-			var value interface{}
724
-			if err := json.Unmarshal(obj, &value); err != nil {
725
-				fmt.Fprintf(cli.err, "%s\n", err)
726
-				status = 1
727
-				continue
728
-			}
729
-			if err := tmpl.Execute(cli.out, value); err != nil {
730
-				return err
731
-			}
732
-			cli.out.Write([]byte{'\n'})
733
-		}
734
-		indented.WriteString(",")
735
-	}
736
-
737
-	if indented.Len() > 1 {
738
-		// Remove trailing ','
739
-		indented.Truncate(indented.Len() - 1)
740
-	}
741
-	indented.WriteByte(']')
742
-
743
-	if tmpl == nil {
744
-		if _, err := io.Copy(cli.out, indented); err != nil {
745
-			return err
746
-		}
747
-	}
748
-
749
-	if status != 0 {
750
-		return &utils.StatusError{StatusCode: status}
751
-	}
752
-	return nil
753
-}
754
-
755
-func (cli *DockerCli) CmdTop(args ...string) error {
756
-	cmd := cli.Subcmd("top", "CONTAINER [ps OPTIONS]", "Lookup the running processes of a container")
757
-	if err := cmd.Parse(args); err != nil {
758
-		return nil
759
-	}
760
-	if cmd.NArg() == 0 {
761
-		cmd.Usage()
762
-		return nil
763
-	}
764
-	val := url.Values{}
765
-	if cmd.NArg() > 1 {
766
-		val.Set("ps_args", strings.Join(cmd.Args()[1:], " "))
767
-	}
768
-
769
-	stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/top?"+val.Encode(), nil, false)
770
-	if err != nil {
771
-		return err
772
-	}
773
-	var procs engine.Env
774
-	if err := procs.Decode(stream); err != nil {
775
-		return err
776
-	}
777
-	w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
778
-	fmt.Fprintln(w, strings.Join(procs.GetList("Titles"), "\t"))
779
-	processes := [][]string{}
780
-	if err := procs.GetJson("Processes", &processes); err != nil {
781
-		return err
782
-	}
783
-	for _, proc := range processes {
784
-		fmt.Fprintln(w, strings.Join(proc, "\t"))
785
-	}
786
-	w.Flush()
787
-	return nil
788
-}
789
-
790
-func (cli *DockerCli) CmdPort(args ...string) error {
791
-	cmd := cli.Subcmd("port", "CONTAINER PRIVATE_PORT", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT")
792
-	if err := cmd.Parse(args); err != nil {
793
-		return nil
794
-	}
795
-	if cmd.NArg() != 2 {
796
-		cmd.Usage()
797
-		return nil
798
-	}
799
-
800
-	port := cmd.Arg(1)
801
-	proto := "tcp"
802
-	parts := strings.SplitN(port, "/", 2)
803
-	if len(parts) == 2 && len(parts[1]) != 0 {
804
-		port = parts[0]
805
-		proto = parts[1]
806
-	}
807
-	body, _, err := readBody(cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, false))
808
-	if err != nil {
809
-		return err
810
-	}
811
-	var out Container
812
-	err = json.Unmarshal(body, &out)
813
-	if err != nil {
814
-		return err
815
-	}
816
-
817
-	if frontends, exists := out.NetworkSettings.Ports[nat.Port(port+"/"+proto)]; exists && frontends != nil {
818
-		for _, frontend := range frontends {
819
-			fmt.Fprintf(cli.out, "%s:%s\n", frontend.HostIp, frontend.HostPort)
820
-		}
821
-	} else {
822
-		return fmt.Errorf("Error: No public port '%s' published for %s", cmd.Arg(1), cmd.Arg(0))
823
-	}
824
-	return nil
825
-}
826
-
827
-// 'docker rmi IMAGE' removes all images with the name IMAGE
828
-func (cli *DockerCli) CmdRmi(args ...string) error {
829
-	var (
830
-		cmd     = cli.Subcmd("rmi", "IMAGE [IMAGE...]", "Remove one or more images")
831
-		force   = cmd.Bool([]string{"f", "-force"}, false, "Force")
832
-		noprune = cmd.Bool([]string{"-no-prune"}, false, "Do not delete untagged parents")
833
-	)
834
-	if err := cmd.Parse(args); err != nil {
835
-		return nil
836
-	}
837
-	if cmd.NArg() < 1 {
838
-		cmd.Usage()
839
-		return nil
840
-	}
841
-
842
-	v := url.Values{}
843
-	if *force {
844
-		v.Set("force", "1")
845
-	}
846
-	if *noprune {
847
-		v.Set("noprune", "1")
848
-	}
849
-
850
-	var encounteredError error
851
-	for _, name := range cmd.Args() {
852
-		body, _, err := readBody(cli.call("DELETE", "/images/"+name+"?"+v.Encode(), nil, false))
853
-		if err != nil {
854
-			fmt.Fprintf(cli.err, "%s\n", err)
855
-			encounteredError = fmt.Errorf("Error: failed to remove one or more images")
856
-		} else {
857
-			outs := engine.NewTable("Created", 0)
858
-			if _, err := outs.ReadListFrom(body); err != nil {
859
-				fmt.Fprintf(cli.err, "%s\n", err)
860
-				encounteredError = fmt.Errorf("Error: failed to remove one or more images")
861
-				continue
862
-			}
863
-			for _, out := range outs.Data {
864
-				if out.Get("Deleted") != "" {
865
-					fmt.Fprintf(cli.out, "Deleted: %s\n", out.Get("Deleted"))
866
-				} else {
867
-					fmt.Fprintf(cli.out, "Untagged: %s\n", out.Get("Untagged"))
868
-				}
869
-			}
870
-		}
871
-	}
872
-	return encounteredError
873
-}
874
-
875
-func (cli *DockerCli) CmdHistory(args ...string) error {
876
-	cmd := cli.Subcmd("history", "[OPTIONS] IMAGE", "Show the history of an image")
877
-	quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs")
878
-	noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
879
-
880
-	if err := cmd.Parse(args); err != nil {
881
-		return nil
882
-	}
883
-	if cmd.NArg() != 1 {
884
-		cmd.Usage()
885
-		return nil
886
-	}
887
-
888
-	body, _, err := readBody(cli.call("GET", "/images/"+cmd.Arg(0)+"/history", nil, false))
889
-	if err != nil {
890
-		return err
891
-	}
892
-
893
-	outs := engine.NewTable("Created", 0)
894
-	if _, err := outs.ReadListFrom(body); err != nil {
895
-		return err
896
-	}
897
-
898
-	w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
899
-	if !*quiet {
900
-		fmt.Fprintln(w, "IMAGE\tCREATED\tCREATED BY\tSIZE")
901
-	}
902
-
903
-	for _, out := range outs.Data {
904
-		outID := out.Get("Id")
905
-		if !*quiet {
906
-			if *noTrunc {
907
-				fmt.Fprintf(w, "%s\t", outID)
908
-			} else {
909
-				fmt.Fprintf(w, "%s\t", utils.TruncateID(outID))
910
-			}
911
-
912
-			fmt.Fprintf(w, "%s ago\t", utils.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0))))
913
-
914
-			if *noTrunc {
915
-				fmt.Fprintf(w, "%s\t", out.Get("CreatedBy"))
916
-			} else {
917
-				fmt.Fprintf(w, "%s\t", utils.Trunc(out.Get("CreatedBy"), 45))
918
-			}
919
-			fmt.Fprintf(w, "%s\n", utils.HumanSize(out.GetInt64("Size")))
920
-		} else {
921
-			if *noTrunc {
922
-				fmt.Fprintln(w, outID)
923
-			} else {
924
-				fmt.Fprintln(w, utils.TruncateID(outID))
925
-			}
926
-		}
927
-	}
928
-	w.Flush()
929
-	return nil
930
-}
931
-
932
-func (cli *DockerCli) CmdRm(args ...string) error {
933
-	cmd := cli.Subcmd("rm", "[OPTIONS] CONTAINER [CONTAINER...]", "Remove one or more containers")
934
-	v := cmd.Bool([]string{"v", "-volumes"}, false, "Remove the volumes associated to the container")
935
-	link := cmd.Bool([]string{"l", "#link", "-link"}, false, "Remove the specified link and not the underlying container")
936
-	force := cmd.Bool([]string{"f", "-force"}, false, "Force removal of running container")
937
-
938
-	if err := cmd.Parse(args); err != nil {
939
-		return nil
940
-	}
941
-	if cmd.NArg() < 1 {
942
-		cmd.Usage()
943
-		return nil
944
-	}
945
-	val := url.Values{}
946
-	if *v {
947
-		val.Set("v", "1")
948
-	}
949
-	if *link {
950
-		val.Set("link", "1")
951
-	}
952
-	if *force {
953
-		val.Set("force", "1")
954
-	}
955
-
956
-	var encounteredError error
957
-	for _, name := range cmd.Args() {
958
-		_, _, err := readBody(cli.call("DELETE", "/containers/"+name+"?"+val.Encode(), nil, false))
959
-		if err != nil {
960
-			fmt.Fprintf(cli.err, "%s\n", err)
961
-			encounteredError = fmt.Errorf("Error: failed to remove one or more containers")
962
-		} else {
963
-			fmt.Fprintf(cli.out, "%s\n", name)
964
-		}
965
-	}
966
-	return encounteredError
967
-}
968
-
969
-// 'docker kill NAME' kills a running container
970
-func (cli *DockerCli) CmdKill(args ...string) error {
971
-	cmd := cli.Subcmd("kill", "[OPTIONS] CONTAINER [CONTAINER...]", "Kill a running container (send SIGKILL, or specified signal)")
972
-	signal := cmd.String([]string{"s", "-signal"}, "KILL", "Signal to send to the container")
973
-
974
-	if err := cmd.Parse(args); err != nil {
975
-		return nil
976
-	}
977
-	if cmd.NArg() < 1 {
978
-		cmd.Usage()
979
-		return nil
980
-	}
981
-
982
-	var encounteredError error
983
-	for _, name := range cmd.Args() {
984
-		if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", name, *signal), nil, false)); err != nil {
985
-			fmt.Fprintf(cli.err, "%s\n", err)
986
-			encounteredError = fmt.Errorf("Error: failed to kill one or more containers")
987
-		} else {
988
-			fmt.Fprintf(cli.out, "%s\n", name)
989
-		}
990
-	}
991
-	return encounteredError
992
-}
993
-
994
-func (cli *DockerCli) CmdImport(args ...string) error {
995
-	cmd := cli.Subcmd("import", "URL|- [REPOSITORY[:TAG]]", "Create an empty filesystem image and import the contents of the tarball (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz) into it, then optionally tag it.")
996
-
997
-	if err := cmd.Parse(args); err != nil {
998
-		return nil
999
-	}
1000
-	if cmd.NArg() < 1 {
1001
-		cmd.Usage()
1002
-		return nil
1003
-	}
1004
-
1005
-	var src, repository, tag string
1006
-
1007
-	if cmd.NArg() == 3 {
1008
-		fmt.Fprintf(cli.err, "[DEPRECATED] The format 'URL|- [REPOSITORY [TAG]]' as been deprecated. Please use URL|- [REPOSITORY[:TAG]]\n")
1009
-		src, repository, tag = cmd.Arg(0), cmd.Arg(1), cmd.Arg(2)
1010
-	} else {
1011
-		src = cmd.Arg(0)
1012
-		repository, tag = utils.ParseRepositoryTag(cmd.Arg(1))
1013
-	}
1014
-	v := url.Values{}
1015
-
1016
-	if repository != "" {
1017
-		//Check if the given image name can be resolved
1018
-		if _, _, err := registry.ResolveRepositoryName(repository); err != nil {
1019
-			return err
1020
-		}
1021
-	}
1022
-
1023
-	v.Set("repo", repository)
1024
-	v.Set("tag", tag)
1025
-	v.Set("fromSrc", src)
1026
-
1027
-	var in io.Reader
1028
-
1029
-	if src == "-" {
1030
-		in = cli.in
1031
-	}
1032
-
1033
-	return cli.stream("POST", "/images/create?"+v.Encode(), in, cli.out, nil)
1034
-}
1035
-
1036
-func (cli *DockerCli) CmdPush(args ...string) error {
1037
-	cmd := cli.Subcmd("push", "NAME", "Push an image or a repository to the registry")
1038
-	if err := cmd.Parse(args); err != nil {
1039
-		return nil
1040
-	}
1041
-	name := cmd.Arg(0)
1042
-
1043
-	if name == "" {
1044
-		cmd.Usage()
1045
-		return nil
1046
-	}
1047
-
1048
-	cli.LoadConfigFile()
1049
-
1050
-	// Resolve the Repository name from fqn to hostname + name
1051
-	hostname, _, err := registry.ResolveRepositoryName(name)
1052
-	if err != nil {
1053
-		return err
1054
-	}
1055
-	// Resolve the Auth config relevant for this server
1056
-	authConfig := cli.configFile.ResolveAuthConfig(hostname)
1057
-	// If we're not using a custom registry, we know the restrictions
1058
-	// applied to repository names and can warn the user in advance.
1059
-	// Custom repositories can have different rules, and we must also
1060
-	// allow pushing by image ID.
1061
-	if len(strings.SplitN(name, "/", 2)) == 1 {
1062
-		username := cli.configFile.Configs[registry.IndexServerAddress()].Username
1063
-		if username == "" {
1064
-			username = "<user>"
1065
-		}
1066
-		return fmt.Errorf("You cannot push a \"root\" repository. Please rename your repository in <user>/<repo> (ex: %s/%s)", username, name)
1067
-	}
1068
-
1069
-	v := url.Values{}
1070
-	push := func(authConfig registry.AuthConfig) error {
1071
-		buf, err := json.Marshal(authConfig)
1072
-		if err != nil {
1073
-			return err
1074
-		}
1075
-		registryAuthHeader := []string{
1076
-			base64.URLEncoding.EncodeToString(buf),
1077
-		}
1078
-
1079
-		return cli.stream("POST", "/images/"+name+"/push?"+v.Encode(), nil, cli.out, map[string][]string{
1080
-			"X-Registry-Auth": registryAuthHeader,
1081
-		})
1082
-	}
1083
-
1084
-	if err := push(authConfig); err != nil {
1085
-		if strings.Contains(err.Error(), "Status 401") {
1086
-			fmt.Fprintln(cli.out, "\nPlease login prior to push:")
1087
-			if err := cli.CmdLogin(hostname); err != nil {
1088
-				return err
1089
-			}
1090
-			authConfig := cli.configFile.ResolveAuthConfig(hostname)
1091
-			return push(authConfig)
1092
-		}
1093
-		return err
1094
-	}
1095
-	return nil
1096
-}
1097
-
1098
-func (cli *DockerCli) CmdPull(args ...string) error {
1099
-	cmd := cli.Subcmd("pull", "NAME[:TAG]", "Pull an image or a repository from the registry")
1100
-	tag := cmd.String([]string{"#t", "#-tag"}, "", "Download tagged image in repository")
1101
-	if err := cmd.Parse(args); err != nil {
1102
-		return nil
1103
-	}
1104
-
1105
-	if cmd.NArg() != 1 {
1106
-		cmd.Usage()
1107
-		return nil
1108
-	}
1109
-
1110
-	remote, parsedTag := utils.ParseRepositoryTag(cmd.Arg(0))
1111
-	if *tag == "" {
1112
-		*tag = parsedTag
1113
-	}
1114
-
1115
-	// Resolve the Repository name from fqn to hostname + name
1116
-	hostname, _, err := registry.ResolveRepositoryName(remote)
1117
-	if err != nil {
1118
-		return err
1119
-	}
1120
-
1121
-	cli.LoadConfigFile()
1122
-
1123
-	// Resolve the Auth config relevant for this server
1124
-	authConfig := cli.configFile.ResolveAuthConfig(hostname)
1125
-	v := url.Values{}
1126
-	v.Set("fromImage", remote)
1127
-	v.Set("tag", *tag)
1128
-
1129
-	pull := func(authConfig registry.AuthConfig) error {
1130
-		buf, err := json.Marshal(authConfig)
1131
-		if err != nil {
1132
-			return err
1133
-		}
1134
-		registryAuthHeader := []string{
1135
-			base64.URLEncoding.EncodeToString(buf),
1136
-		}
1137
-
1138
-		return cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.out, map[string][]string{
1139
-			"X-Registry-Auth": registryAuthHeader,
1140
-		})
1141
-	}
1142
-
1143
-	if err := pull(authConfig); err != nil {
1144
-		if strings.Contains(err.Error(), "Status 401") {
1145
-			fmt.Fprintln(cli.out, "\nPlease login prior to pull:")
1146
-			if err := cli.CmdLogin(hostname); err != nil {
1147
-				return err
1148
-			}
1149
-			authConfig := cli.configFile.ResolveAuthConfig(hostname)
1150
-			return pull(authConfig)
1151
-		}
1152
-		return err
1153
-	}
1154
-
1155
-	return nil
1156
-}
1157
-
1158
-func (cli *DockerCli) CmdImages(args ...string) error {
1159
-	cmd := cli.Subcmd("images", "[OPTIONS] [NAME]", "List images")
1160
-	quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs")
1161
-	all := cmd.Bool([]string{"a", "-all"}, false, "Show all images (by default filter out the intermediate images used to build)")
1162
-	noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
1163
-	flViz := cmd.Bool([]string{"v", "#viz", "-viz"}, false, "Output graph in graphviz format")
1164
-	flTree := cmd.Bool([]string{"t", "#tree", "-tree"}, false, "Output graph in tree format")
1165
-
1166
-	if err := cmd.Parse(args); err != nil {
1167
-		return nil
1168
-	}
1169
-	if cmd.NArg() > 1 {
1170
-		cmd.Usage()
1171
-		return nil
1172
-	}
1173
-
1174
-	filter := cmd.Arg(0)
1175
-
1176
-	if *flViz || *flTree {
1177
-		body, _, err := readBody(cli.call("GET", "/images/json?all=1", nil, false))
1178
-		if err != nil {
1179
-			return err
1180
-		}
1181
-
1182
-		outs := engine.NewTable("Created", 0)
1183
-		if _, err := outs.ReadListFrom(body); err != nil {
1184
-			return err
1185
-		}
1186
-
1187
-		var (
1188
-			printNode  func(cli *DockerCli, noTrunc bool, image *engine.Env, prefix string)
1189
-			startImage *engine.Env
1190
-
1191
-			roots    = engine.NewTable("Created", outs.Len())
1192
-			byParent = make(map[string]*engine.Table)
1193
-		)
1194
-
1195
-		for _, image := range outs.Data {
1196
-			if image.Get("ParentId") == "" {
1197
-				roots.Add(image)
1198
-			} else {
1199
-				if children, exists := byParent[image.Get("ParentId")]; exists {
1200
-					children.Add(image)
1201
-				} else {
1202
-					byParent[image.Get("ParentId")] = engine.NewTable("Created", 1)
1203
-					byParent[image.Get("ParentId")].Add(image)
1204
-				}
1205
-			}
1206
-
1207
-			if filter != "" {
1208
-				if filter == image.Get("Id") || filter == utils.TruncateID(image.Get("Id")) {
1209
-					startImage = image
1210
-				}
1211
-
1212
-				for _, repotag := range image.GetList("RepoTags") {
1213
-					if repotag == filter {
1214
-						startImage = image
1215
-					}
1216
-				}
1217
-			}
1218
-		}
1219
-
1220
-		if *flViz {
1221
-			fmt.Fprintf(cli.out, "digraph docker {\n")
1222
-			printNode = (*DockerCli).printVizNode
1223
-		} else {
1224
-			printNode = (*DockerCli).printTreeNode
1225
-		}
1226
-
1227
-		if startImage != nil {
1228
-			root := engine.NewTable("Created", 1)
1229
-			root.Add(startImage)
1230
-			cli.WalkTree(*noTrunc, root, byParent, "", printNode)
1231
-		} else if filter == "" {
1232
-			cli.WalkTree(*noTrunc, roots, byParent, "", printNode)
1233
-		}
1234
-		if *flViz {
1235
-			fmt.Fprintf(cli.out, " base [style=invisible]\n}\n")
1236
-		}
1237
-	} else {
1238
-		v := url.Values{}
1239
-		if cmd.NArg() == 1 {
1240
-			v.Set("filter", filter)
1241
-		}
1242
-		if *all {
1243
-			v.Set("all", "1")
1244
-		}
1245
-
1246
-		body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, false))
1247
-
1248
-		if err != nil {
1249
-			return err
1250
-		}
1251
-
1252
-		outs := engine.NewTable("Created", 0)
1253
-		if _, err := outs.ReadListFrom(body); err != nil {
1254
-			return err
1255
-		}
1256
-
1257
-		w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
1258
-		if !*quiet {
1259
-			fmt.Fprintln(w, "REPOSITORY\tTAG\tIMAGE ID\tCREATED\tVIRTUAL SIZE")
1260
-		}
1261
-
1262
-		for _, out := range outs.Data {
1263
-			for _, repotag := range out.GetList("RepoTags") {
1264
-
1265
-				repo, tag := utils.ParseRepositoryTag(repotag)
1266
-				outID := out.Get("Id")
1267
-				if !*noTrunc {
1268
-					outID = utils.TruncateID(outID)
1269
-				}
1270
-
1271
-				if !*quiet {
1272
-					fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\n", repo, tag, outID, utils.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0))), utils.HumanSize(out.GetInt64("VirtualSize")))
1273
-				} else {
1274
-					fmt.Fprintln(w, outID)
1275
-				}
1276
-			}
1277
-		}
1278
-
1279
-		if !*quiet {
1280
-			w.Flush()
1281
-		}
1282
-	}
1283
-	return nil
1284
-}
1285
-
1286
-func (cli *DockerCli) WalkTree(noTrunc bool, images *engine.Table, byParent map[string]*engine.Table, prefix string, printNode func(cli *DockerCli, noTrunc bool, image *engine.Env, prefix string)) {
1287
-	length := images.Len()
1288
-	if length > 1 {
1289
-		for index, image := range images.Data {
1290
-			if index+1 == length {
1291
-				printNode(cli, noTrunc, image, prefix+"└─")
1292
-				if subimages, exists := byParent[image.Get("Id")]; exists {
1293
-					cli.WalkTree(noTrunc, subimages, byParent, prefix+"  ", printNode)
1294
-				}
1295
-			} else {
1296
-				printNode(cli, noTrunc, image, prefix+"\u251C─")
1297
-				if subimages, exists := byParent[image.Get("Id")]; exists {
1298
-					cli.WalkTree(noTrunc, subimages, byParent, prefix+"\u2502 ", printNode)
1299
-				}
1300
-			}
1301
-		}
1302
-	} else {
1303
-		for _, image := range images.Data {
1304
-			printNode(cli, noTrunc, image, prefix+"└─")
1305
-			if subimages, exists := byParent[image.Get("Id")]; exists {
1306
-				cli.WalkTree(noTrunc, subimages, byParent, prefix+"  ", printNode)
1307
-			}
1308
-		}
1309
-	}
1310
-}
1311
-
1312
-func (cli *DockerCli) printVizNode(noTrunc bool, image *engine.Env, prefix string) {
1313
-	var (
1314
-		imageID  string
1315
-		parentID string
1316
-	)
1317
-	if noTrunc {
1318
-		imageID = image.Get("Id")
1319
-		parentID = image.Get("ParentId")
1320
-	} else {
1321
-		imageID = utils.TruncateID(image.Get("Id"))
1322
-		parentID = utils.TruncateID(image.Get("ParentId"))
1323
-	}
1324
-	if parentID == "" {
1325
-		fmt.Fprintf(cli.out, " base -> \"%s\" [style=invis]\n", imageID)
1326
-	} else {
1327
-		fmt.Fprintf(cli.out, " \"%s\" -> \"%s\"\n", parentID, imageID)
1328
-	}
1329
-	if image.GetList("RepoTags")[0] != "<none>:<none>" {
1330
-		fmt.Fprintf(cli.out, " \"%s\" [label=\"%s\\n%s\",shape=box,fillcolor=\"paleturquoise\",style=\"filled,rounded\"];\n",
1331
-			imageID, imageID, strings.Join(image.GetList("RepoTags"), "\\n"))
1332
-	}
1333
-}
1334
-
1335
-func (cli *DockerCli) printTreeNode(noTrunc bool, image *engine.Env, prefix string) {
1336
-	var imageID string
1337
-	if noTrunc {
1338
-		imageID = image.Get("Id")
1339
-	} else {
1340
-		imageID = utils.TruncateID(image.Get("Id"))
1341
-	}
1342
-
1343
-	fmt.Fprintf(cli.out, "%s%s Virtual Size: %s", prefix, imageID, utils.HumanSize(image.GetInt64("VirtualSize")))
1344
-	if image.GetList("RepoTags")[0] != "<none>:<none>" {
1345
-		fmt.Fprintf(cli.out, " Tags: %s\n", strings.Join(image.GetList("RepoTags"), ", "))
1346
-	} else {
1347
-		fmt.Fprint(cli.out, "\n")
1348
-	}
1349
-}
1350
-
1351
-func (cli *DockerCli) CmdPs(args ...string) error {
1352
-	cmd := cli.Subcmd("ps", "[OPTIONS]", "List containers")
1353
-	quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs")
1354
-	size := cmd.Bool([]string{"s", "-size"}, false, "Display sizes")
1355
-	all := cmd.Bool([]string{"a", "-all"}, false, "Show all containers. Only running containers are shown by default.")
1356
-	noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
1357
-	nLatest := cmd.Bool([]string{"l", "-latest"}, false, "Show only the latest created container, include non-running ones.")
1358
-	since := cmd.String([]string{"#sinceId", "#-since-id", "-since"}, "", "Show only containers created since Id or Name, include non-running ones.")
1359
-	before := cmd.String([]string{"#beforeId", "#-before-id", "-before"}, "", "Show only container created before Id or Name, include non-running ones.")
1360
-	last := cmd.Int([]string{"n"}, -1, "Show n last created containers, include non-running ones.")
1361
-
1362
-	if err := cmd.Parse(args); err != nil {
1363
-		return nil
1364
-	}
1365
-	v := url.Values{}
1366
-	if *last == -1 && *nLatest {
1367
-		*last = 1
1368
-	}
1369
-	if *all {
1370
-		v.Set("all", "1")
1371
-	}
1372
-	if *last != -1 {
1373
-		v.Set("limit", strconv.Itoa(*last))
1374
-	}
1375
-	if *since != "" {
1376
-		v.Set("since", *since)
1377
-	}
1378
-	if *before != "" {
1379
-		v.Set("before", *before)
1380
-	}
1381
-	if *size {
1382
-		v.Set("size", "1")
1383
-	}
1384
-
1385
-	body, _, err := readBody(cli.call("GET", "/containers/json?"+v.Encode(), nil, false))
1386
-	if err != nil {
1387
-		return err
1388
-	}
1389
-
1390
-	outs := engine.NewTable("Created", 0)
1391
-	if _, err := outs.ReadListFrom(body); err != nil {
1392
-		return err
1393
-	}
1394
-	w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
1395
-	if !*quiet {
1396
-		fmt.Fprint(w, "CONTAINER ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES")
1397
-		if *size {
1398
-			fmt.Fprintln(w, "\tSIZE")
1399
-		} else {
1400
-			fmt.Fprint(w, "\n")
1401
-		}
1402
-	}
1403
-
1404
-	for _, out := range outs.Data {
1405
-		var (
1406
-			outID    = out.Get("Id")
1407
-			outNames = out.GetList("Names")
1408
-		)
1409
-
1410
-		if !*noTrunc {
1411
-			outID = utils.TruncateID(outID)
1412
-		}
1413
-
1414
-		// Remove the leading / from the names
1415
-		for i := 0; i < len(outNames); i++ {
1416
-			outNames[i] = outNames[i][1:]
1417
-		}
1418
-
1419
-		if !*quiet {
1420
-			var (
1421
-				outCommand = out.Get("Command")
1422
-				ports      = engine.NewTable("", 0)
1423
-			)
1424
-			if !*noTrunc {
1425
-				outCommand = utils.Trunc(outCommand, 20)
1426
-			}
1427
-			ports.ReadListFrom([]byte(out.Get("Ports")))
1428
-			fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t%s\t", outID, out.Get("Image"), outCommand, utils.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0))), out.Get("Status"), displayablePorts(ports), strings.Join(outNames, ","))
1429
-			if *size {
1430
-				if out.GetInt("SizeRootFs") > 0 {
1431
-					fmt.Fprintf(w, "%s (virtual %s)\n", utils.HumanSize(out.GetInt64("SizeRw")), utils.HumanSize(out.GetInt64("SizeRootFs")))
1432
-				} else {
1433
-					fmt.Fprintf(w, "%s\n", utils.HumanSize(out.GetInt64("SizeRw")))
1434
-				}
1435
-			} else {
1436
-				fmt.Fprint(w, "\n")
1437
-			}
1438
-		} else {
1439
-			fmt.Fprintln(w, outID)
1440
-		}
1441
-	}
1442
-
1443
-	if !*quiet {
1444
-		w.Flush()
1445
-	}
1446
-	return nil
1447
-}
1448
-
1449
-func (cli *DockerCli) CmdCommit(args ...string) error {
1450
-	cmd := cli.Subcmd("commit", "[OPTIONS] CONTAINER [REPOSITORY[:TAG]]", "Create a new image from a container's changes")
1451
-	flComment := cmd.String([]string{"m", "-message"}, "", "Commit message")
1452
-	flAuthor := cmd.String([]string{"a", "#author", "-author"}, "", "Author (eg. \"John Hannibal Smith <hannibal@a-team.com>\"")
1453
-	flConfig := cmd.String([]string{"#run", "-run"}, "", "Config automatically applied when the image is run. "+`(ex: --run='{"Cmd": ["cat", "/world"], "PortSpecs": ["22"]}')`)
1454
-	if err := cmd.Parse(args); err != nil {
1455
-		return nil
1456
-	}
1457
-
1458
-	var name, repository, tag string
1459
-
1460
-	if cmd.NArg() == 3 {
1461
-		fmt.Fprintf(cli.err, "[DEPRECATED] The format 'CONTAINER [REPOSITORY [TAG]]' as been deprecated. Please use CONTAINER [REPOSITORY[:TAG]]\n")
1462
-		name, repository, tag = cmd.Arg(0), cmd.Arg(1), cmd.Arg(2)
1463
-	} else {
1464
-		name = cmd.Arg(0)
1465
-		repository, tag = utils.ParseRepositoryTag(cmd.Arg(1))
1466
-	}
1467
-
1468
-	if name == "" {
1469
-		cmd.Usage()
1470
-		return nil
1471
-	}
1472
-
1473
-	//Check if the given image name can be resolved
1474
-	if repository != "" {
1475
-		if _, _, err := registry.ResolveRepositoryName(repository); err != nil {
1476
-			return err
1477
-		}
1478
-	}
1479
-
1480
-	v := url.Values{}
1481
-	v.Set("container", name)
1482
-	v.Set("repo", repository)
1483
-	v.Set("tag", tag)
1484
-	v.Set("comment", *flComment)
1485
-	v.Set("author", *flAuthor)
1486
-	var (
1487
-		config *runconfig.Config
1488
-		env    engine.Env
1489
-	)
1490
-	if *flConfig != "" {
1491
-		config = &runconfig.Config{}
1492
-		if err := json.Unmarshal([]byte(*flConfig), config); err != nil {
1493
-			return err
1494
-		}
1495
-	}
1496
-	stream, _, err := cli.call("POST", "/commit?"+v.Encode(), config, false)
1497
-	if err != nil {
1498
-		return err
1499
-	}
1500
-	if err := env.Decode(stream); err != nil {
1501
-		return err
1502
-	}
1503
-
1504
-	fmt.Fprintf(cli.out, "%s\n", env.Get("Id"))
1505
-	return nil
1506
-}
1507
-
1508
-func (cli *DockerCli) CmdEvents(args ...string) error {
1509
-	cmd := cli.Subcmd("events", "[OPTIONS]", "Get real time events from the server")
1510
-	since := cmd.String([]string{"#since", "-since"}, "", "Show previously created events and then stream.")
1511
-	if err := cmd.Parse(args); err != nil {
1512
-		return nil
1513
-	}
1514
-
1515
-	if cmd.NArg() != 0 {
1516
-		cmd.Usage()
1517
-		return nil
1518
-	}
1519
-
1520
-	v := url.Values{}
1521
-	if *since != "" {
1522
-		loc := time.FixedZone(time.Now().Zone())
1523
-		format := "2006-01-02 15:04:05 -0700 MST"
1524
-		if len(*since) < len(format) {
1525
-			format = format[:len(*since)]
1526
-		}
1527
-
1528
-		if t, err := time.ParseInLocation(format, *since, loc); err == nil {
1529
-			v.Set("since", strconv.FormatInt(t.Unix(), 10))
1530
-		} else {
1531
-			v.Set("since", *since)
1532
-		}
1533
-	}
1534
-
1535
-	if err := cli.stream("GET", "/events?"+v.Encode(), nil, cli.out, nil); err != nil {
1536
-		return err
1537
-	}
1538
-	return nil
1539
-}
1540
-
1541
-func (cli *DockerCli) CmdExport(args ...string) error {
1542
-	cmd := cli.Subcmd("export", "CONTAINER", "Export the contents of a filesystem as a tar archive to STDOUT")
1543
-	if err := cmd.Parse(args); err != nil {
1544
-		return nil
1545
-	}
1546
-
1547
-	if cmd.NArg() != 1 {
1548
-		cmd.Usage()
1549
-		return nil
1550
-	}
1551
-
1552
-	if err := cli.stream("GET", "/containers/"+cmd.Arg(0)+"/export", nil, cli.out, nil); err != nil {
1553
-		return err
1554
-	}
1555
-	return nil
1556
-}
1557
-
1558
-func (cli *DockerCli) CmdDiff(args ...string) error {
1559
-	cmd := cli.Subcmd("diff", "CONTAINER", "Inspect changes on a container's filesystem")
1560
-	if err := cmd.Parse(args); err != nil {
1561
-		return nil
1562
-	}
1563
-	if cmd.NArg() != 1 {
1564
-		cmd.Usage()
1565
-		return nil
1566
-	}
1567
-
1568
-	body, _, err := readBody(cli.call("GET", "/containers/"+cmd.Arg(0)+"/changes", nil, false))
1569
-
1570
-	if err != nil {
1571
-		return err
1572
-	}
1573
-
1574
-	outs := engine.NewTable("", 0)
1575
-	if _, err := outs.ReadListFrom(body); err != nil {
1576
-		return err
1577
-	}
1578
-	for _, change := range outs.Data {
1579
-		var kind string
1580
-		switch change.GetInt("Kind") {
1581
-		case archive.ChangeModify:
1582
-			kind = "C"
1583
-		case archive.ChangeAdd:
1584
-			kind = "A"
1585
-		case archive.ChangeDelete:
1586
-			kind = "D"
1587
-		}
1588
-		fmt.Fprintf(cli.out, "%s %s\n", kind, change.Get("Path"))
1589
-	}
1590
-	return nil
1591
-}
1592
-
1593
-func (cli *DockerCli) CmdLogs(args ...string) error {
1594
-	cmd := cli.Subcmd("logs", "CONTAINER", "Fetch the logs of a container")
1595
-	follow := cmd.Bool([]string{"f", "-follow"}, false, "Follow log output")
1596
-	if err := cmd.Parse(args); err != nil {
1597
-		return nil
1598
-	}
1599
-	if cmd.NArg() != 1 {
1600
-		cmd.Usage()
1601
-		return nil
1602
-	}
1603
-	name := cmd.Arg(0)
1604
-	body, _, err := readBody(cli.call("GET", "/containers/"+name+"/json", nil, false))
1605
-	if err != nil {
1606
-		return err
1607
-	}
1608
-
1609
-	container := &Container{}
1610
-	err = json.Unmarshal(body, container)
1611
-	if err != nil {
1612
-		return err
1613
-	}
1614
-
1615
-	v := url.Values{}
1616
-	v.Set("logs", "1")
1617
-	v.Set("stdout", "1")
1618
-	v.Set("stderr", "1")
1619
-	if *follow && container.State.Running {
1620
-		v.Set("stream", "1")
1621
-	}
1622
-
1623
-	if err := cli.hijack("POST", "/containers/"+name+"/attach?"+v.Encode(), container.Config.Tty, nil, cli.out, cli.err, nil); err != nil {
1624
-		return err
1625
-	}
1626
-	return nil
1627
-}
1628
-
1629
-func (cli *DockerCli) CmdAttach(args ...string) error {
1630
-	cmd := cli.Subcmd("attach", "[OPTIONS] CONTAINER", "Attach to a running container")
1631
-	noStdin := cmd.Bool([]string{"#nostdin", "-no-stdin"}, false, "Do not attach stdin")
1632
-	proxy := cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxify all received signal to the process (even in non-tty mode)")
1633
-	if err := cmd.Parse(args); err != nil {
1634
-		return nil
1635
-	}
1636
-	if cmd.NArg() != 1 {
1637
-		cmd.Usage()
1638
-		return nil
1639
-	}
1640
-	name := cmd.Arg(0)
1641
-	body, _, err := readBody(cli.call("GET", "/containers/"+name+"/json", nil, false))
1642
-	if err != nil {
1643
-		return err
1644
-	}
1645
-
1646
-	container := &Container{}
1647
-	err = json.Unmarshal(body, container)
1648
-	if err != nil {
1649
-		return err
1650
-	}
1651
-
1652
-	if !container.State.Running {
1653
-		return fmt.Errorf("You cannot attach to a stopped container, start it first")
1654
-	}
1655
-
1656
-	if container.Config.Tty && cli.isTerminal {
1657
-		if err := cli.monitorTtySize(cmd.Arg(0)); err != nil {
1658
-			utils.Debugf("Error monitoring TTY size: %s", err)
1659
-		}
1660
-	}
1661
-
1662
-	var in io.ReadCloser
1663
-
1664
-	v := url.Values{}
1665
-	v.Set("stream", "1")
1666
-	if !*noStdin && container.Config.OpenStdin {
1667
-		v.Set("stdin", "1")
1668
-		in = cli.in
1669
-	}
1670
-	v.Set("stdout", "1")
1671
-	v.Set("stderr", "1")
1672
-
1673
-	if *proxy && !container.Config.Tty {
1674
-		sigc := cli.forwardAllSignals(cmd.Arg(0))
1675
-		defer signal.StopCatch(sigc)
1676
-	}
1677
-
1678
-	if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), container.Config.Tty, in, cli.out, cli.err, nil); err != nil {
1679
-		return err
1680
-	}
1681
-
1682
-	_, status, err := getExitCode(cli, cmd.Arg(0))
1683
-	if err != nil {
1684
-		return err
1685
-	}
1686
-	if status != 0 {
1687
-		return &utils.StatusError{StatusCode: status}
1688
-	}
1689
-
1690
-	return nil
1691
-}
1692
-
1693
-func (cli *DockerCli) CmdSearch(args ...string) error {
1694
-	cmd := cli.Subcmd("search", "TERM", "Search the docker index for images")
1695
-	noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
1696
-	trusted := cmd.Bool([]string{"t", "#trusted", "-trusted"}, false, "Only show trusted builds")
1697
-	stars := cmd.Int([]string{"s", "#stars", "-stars"}, 0, "Only displays with at least xxx stars")
1698
-	if err := cmd.Parse(args); err != nil {
1699
-		return nil
1700
-	}
1701
-	if cmd.NArg() != 1 {
1702
-		cmd.Usage()
1703
-		return nil
1704
-	}
1705
-
1706
-	v := url.Values{}
1707
-	v.Set("term", cmd.Arg(0))
1708
-
1709
-	body, _, err := readBody(cli.call("GET", "/images/search?"+v.Encode(), nil, true))
1710
-
1711
-	if err != nil {
1712
-		return err
1713
-	}
1714
-	outs := engine.NewTable("star_count", 0)
1715
-	if _, err := outs.ReadListFrom(body); err != nil {
1716
-		return err
1717
-	}
1718
-	w := tabwriter.NewWriter(cli.out, 10, 1, 3, ' ', 0)
1719
-	fmt.Fprintf(w, "NAME\tDESCRIPTION\tSTARS\tOFFICIAL\tTRUSTED\n")
1720
-	for _, out := range outs.Data {
1721
-		if (*trusted && !out.GetBool("is_trusted")) || (*stars > out.GetInt("star_count")) {
1722
-			continue
1723
-		}
1724
-		desc := strings.Replace(out.Get("description"), "\n", " ", -1)
1725
-		desc = strings.Replace(desc, "\r", " ", -1)
1726
-		if !*noTrunc && len(desc) > 45 {
1727
-			desc = utils.Trunc(desc, 42) + "..."
1728
-		}
1729
-		fmt.Fprintf(w, "%s\t%s\t%d\t", out.Get("name"), desc, out.GetInt("star_count"))
1730
-		if out.GetBool("is_official") {
1731
-			fmt.Fprint(w, "[OK]")
1732
-
1733
-		}
1734
-		fmt.Fprint(w, "\t")
1735
-		if out.GetBool("is_trusted") {
1736
-			fmt.Fprint(w, "[OK]")
1737
-		}
1738
-		fmt.Fprint(w, "\n")
1739
-	}
1740
-	w.Flush()
1741
-	return nil
1742
-}
1743
-
1744
-// Ports type - Used to parse multiple -p flags
1745
-type ports []int
1746
-
1747
-func (cli *DockerCli) CmdTag(args ...string) error {
1748
-	cmd := cli.Subcmd("tag", "[OPTIONS] IMAGE [REGISTRYHOST/][USERNAME/]NAME[:TAG]", "Tag an image into a repository")
1749
-	force := cmd.Bool([]string{"f", "#force", "-force"}, false, "Force")
1750
-	if err := cmd.Parse(args); err != nil {
1751
-		return nil
1752
-	}
1753
-	if cmd.NArg() != 2 && cmd.NArg() != 3 {
1754
-		cmd.Usage()
1755
-		return nil
1756
-	}
1757
-
1758
-	var repository, tag string
1759
-
1760
-	if cmd.NArg() == 3 {
1761
-		fmt.Fprintf(cli.err, "[DEPRECATED] The format 'IMAGE [REPOSITORY [TAG]]' as been deprecated. Please use IMAGE [REGISTRYHOST/][USERNAME/]NAME[:TAG]]\n")
1762
-		repository, tag = cmd.Arg(1), cmd.Arg(2)
1763
-	} else {
1764
-		repository, tag = utils.ParseRepositoryTag(cmd.Arg(1))
1765
-	}
1766
-
1767
-	v := url.Values{}
1768
-
1769
-	//Check if the given image name can be resolved
1770
-	if _, _, err := registry.ResolveRepositoryName(repository); err != nil {
1771
-		return err
1772
-	}
1773
-	v.Set("repo", repository)
1774
-	v.Set("tag", tag)
1775
-
1776
-	if *force {
1777
-		v.Set("force", "1")
1778
-	}
1779
-
1780
-	if _, _, err := readBody(cli.call("POST", "/images/"+cmd.Arg(0)+"/tag?"+v.Encode(), nil, false)); err != nil {
1781
-		return err
1782
-	}
1783
-	return nil
1784
-}
1785
-
1786
-func (cli *DockerCli) CmdRun(args ...string) error {
1787
-	// FIXME: just use runconfig.Parse already
1788
-	config, hostConfig, cmd, err := runconfig.ParseSubcommand(cli.Subcmd("run", "[OPTIONS] IMAGE [COMMAND] [ARG...]", "Run a command in a new container"), args, nil)
1789
-	if err != nil {
1790
-		return err
1791
-	}
1792
-	if config.Image == "" {
1793
-		cmd.Usage()
1794
-		return nil
1795
-	}
1796
-
1797
-	// Retrieve relevant client-side config
1798
-	var (
1799
-		flName        = cmd.Lookup("name")
1800
-		flRm          = cmd.Lookup("rm")
1801
-		flSigProxy    = cmd.Lookup("sig-proxy")
1802
-		autoRemove, _ = strconv.ParseBool(flRm.Value.String())
1803
-		sigProxy, _   = strconv.ParseBool(flSigProxy.Value.String())
1804
-	)
1805
-
1806
-	// Disable sigProxy in case on TTY
1807
-	if config.Tty {
1808
-		sigProxy = false
1809
-	}
1810
-
1811
-	var containerIDFile io.WriteCloser
1812
-	if len(hostConfig.ContainerIDFile) > 0 {
1813
-		if _, err := os.Stat(hostConfig.ContainerIDFile); err == nil {
1814
-			return fmt.Errorf("Container ID file found, make sure the other container isn't running or delete %s", hostConfig.ContainerIDFile)
1815
-		}
1816
-		if containerIDFile, err = os.Create(hostConfig.ContainerIDFile); err != nil {
1817
-			return fmt.Errorf("Failed to create the container ID file: %s", err)
1818
-		}
1819
-		defer func() {
1820
-			containerIDFile.Close()
1821
-			var (
1822
-				cidFileInfo os.FileInfo
1823
-				err         error
1824
-			)
1825
-			if cidFileInfo, err = os.Stat(hostConfig.ContainerIDFile); err != nil {
1826
-				return
1827
-			}
1828
-			if cidFileInfo.Size() == 0 {
1829
-				if err := os.Remove(hostConfig.ContainerIDFile); err != nil {
1830
-					fmt.Printf("failed to remove CID file '%s': %s \n", hostConfig.ContainerIDFile, err)
1831
-				}
1832
-			}
1833
-		}()
1834
-	}
1835
-
1836
-	containerValues := url.Values{}
1837
-	if name := flName.Value.String(); name != "" {
1838
-		containerValues.Set("name", name)
1839
-	}
1840
-
1841
-	//create the container
1842
-	stream, statusCode, err := cli.call("POST", "/containers/create?"+containerValues.Encode(), config, false)
1843
-	//if image not found try to pull it
1844
-	if statusCode == 404 {
1845
-		fmt.Fprintf(cli.err, "Unable to find image '%s' locally\n", config.Image)
1846
-
1847
-		v := url.Values{}
1848
-		repos, tag := utils.ParseRepositoryTag(config.Image)
1849
-		v.Set("fromImage", repos)
1850
-		v.Set("tag", tag)
1851
-
1852
-		// Resolve the Repository name from fqn to hostname + name
1853
-		hostname, _, err := registry.ResolveRepositoryName(repos)
1854
-		if err != nil {
1855
-			return err
1856
-		}
1857
-
1858
-		// Load the auth config file, to be able to pull the image
1859
-		cli.LoadConfigFile()
1860
-
1861
-		// Resolve the Auth config relevant for this server
1862
-		authConfig := cli.configFile.ResolveAuthConfig(hostname)
1863
-		buf, err := json.Marshal(authConfig)
1864
-		if err != nil {
1865
-			return err
1866
-		}
1867
-
1868
-		registryAuthHeader := []string{
1869
-			base64.URLEncoding.EncodeToString(buf),
1870
-		}
1871
-		if err = cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.err, map[string][]string{"X-Registry-Auth": registryAuthHeader}); err != nil {
1872
-			return err
1873
-		}
1874
-		if stream, _, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), config, false); err != nil {
1875
-			return err
1876
-		}
1877
-	} else if err != nil {
1878
-		return err
1879
-	}
1880
-
1881
-	var runResult engine.Env
1882
-	if err := runResult.Decode(stream); err != nil {
1883
-		return err
1884
-	}
1885
-
1886
-	for _, warning := range runResult.GetList("Warnings") {
1887
-		fmt.Fprintf(cli.err, "WARNING: %s\n", warning)
1888
-	}
1889
-
1890
-	if len(hostConfig.ContainerIDFile) > 0 {
1891
-		if _, err = containerIDFile.Write([]byte(runResult.Get("Id"))); err != nil {
1892
-			return fmt.Errorf("Failed to write the container ID to the file: %s", err)
1893
-		}
1894
-	}
1895
-
1896
-	if sigProxy {
1897
-		sigc := cli.forwardAllSignals(runResult.Get("Id"))
1898
-		defer signal.StopCatch(sigc)
1899
-	}
1900
-
1901
-	var (
1902
-		waitDisplayId chan struct{}
1903
-		errCh         chan error
1904
-	)
1905
-
1906
-	if !config.AttachStdout && !config.AttachStderr {
1907
-		// Make this asynchrone in order to let the client write to stdin before having to read the ID
1908
-		waitDisplayId = make(chan struct{})
1909
-		go func() {
1910
-			defer close(waitDisplayId)
1911
-			fmt.Fprintf(cli.out, "%s\n", runResult.Get("Id"))
1912
-		}()
1913
-	}
1914
-
1915
-	// We need to instanciate the chan because the select needs it. It can
1916
-	// be closed but can't be uninitialized.
1917
-	hijacked := make(chan io.Closer)
1918
-
1919
-	// Block the return until the chan gets closed
1920
-	defer func() {
1921
-		utils.Debugf("End of CmdRun(), Waiting for hijack to finish.")
1922
-		if _, ok := <-hijacked; ok {
1923
-			utils.Errorf("Hijack did not finish (chan still open)")
1924
-		}
1925
-	}()
1926
-
1927
-	if config.AttachStdin || config.AttachStdout || config.AttachStderr {
1928
-		var (
1929
-			out, stderr io.Writer
1930
-			in          io.ReadCloser
1931
-			v           = url.Values{}
1932
-		)
1933
-		v.Set("stream", "1")
1934
-
1935
-		if config.AttachStdin {
1936
-			v.Set("stdin", "1")
1937
-			in = cli.in
1938
-		}
1939
-		if config.AttachStdout {
1940
-			v.Set("stdout", "1")
1941
-			out = cli.out
1942
-		}
1943
-		if config.AttachStderr {
1944
-			v.Set("stderr", "1")
1945
-			if config.Tty {
1946
-				stderr = cli.out
1947
-			} else {
1948
-				stderr = cli.err
1949
-			}
1950
-		}
1951
-
1952
-		errCh = utils.Go(func() error {
1953
-			return cli.hijack("POST", "/containers/"+runResult.Get("Id")+"/attach?"+v.Encode(), config.Tty, in, out, stderr, hijacked)
1954
-		})
1955
-	} else {
1956
-		close(hijacked)
1957
-	}
1958
-
1959
-	// Acknowledge the hijack before starting
1960
-	select {
1961
-	case closer := <-hijacked:
1962
-		// Make sure that hijack gets closed when returning. (result
1963
-		// in closing hijack chan and freeing server's goroutines.
1964
-		if closer != nil {
1965
-			defer closer.Close()
1966
-		}
1967
-	case err := <-errCh:
1968
-		if err != nil {
1969
-			utils.Debugf("Error hijack: %s", err)
1970
-			return err
1971
-		}
1972
-	}
1973
-
1974
-	//start the container
1975
-	if _, _, err = readBody(cli.call("POST", "/containers/"+runResult.Get("Id")+"/start", hostConfig, false)); err != nil {
1976
-		return err
1977
-	}
1978
-
1979
-	if (config.AttachStdin || config.AttachStdout || config.AttachStderr) && config.Tty && cli.isTerminal {
1980
-		if err := cli.monitorTtySize(runResult.Get("Id")); err != nil {
1981
-			utils.Errorf("Error monitoring TTY size: %s\n", err)
1982
-		}
1983
-	}
1984
-
1985
-	if errCh != nil {
1986
-		if err := <-errCh; err != nil {
1987
-			utils.Debugf("Error hijack: %s", err)
1988
-			return err
1989
-		}
1990
-	}
1991
-
1992
-	// Detached mode: wait for the id to be displayed and return.
1993
-	if !config.AttachStdout && !config.AttachStderr {
1994
-		// Detached mode
1995
-		<-waitDisplayId
1996
-		return nil
1997
-	}
1998
-
1999
-	var status int
2000
-
2001
-	// Attached mode
2002
-	if autoRemove {
2003
-		// Autoremove: wait for the container to finish, retrieve
2004
-		// the exit code and remove the container
2005
-		if _, _, err := readBody(cli.call("POST", "/containers/"+runResult.Get("Id")+"/wait", nil, false)); err != nil {
2006
-			return err
2007
-		}
2008
-		if _, status, err = getExitCode(cli, runResult.Get("Id")); err != nil {
2009
-			return err
2010
-		}
2011
-		if _, _, err := readBody(cli.call("DELETE", "/containers/"+runResult.Get("Id")+"?v=1", nil, false)); err != nil {
2012
-			return err
2013
-		}
2014
-	} else {
2015
-		if !config.Tty {
2016
-			// In non-tty mode, we can't dettach, so we know we need to wait.
2017
-			if status, err = waitForExit(cli, runResult.Get("Id")); err != nil {
2018
-				return err
2019
-			}
2020
-		} else {
2021
-			// In TTY mode, there is a race. If the process dies too slowly, the state can be update after the getExitCode call
2022
-			// and result in a wrong exit code.
2023
-			// No Autoremove: Simply retrieve the exit code
2024
-			if _, status, err = getExitCode(cli, runResult.Get("Id")); err != nil {
2025
-				return err
2026
-			}
2027
-		}
2028
-	}
2029
-	if status != 0 {
2030
-		return &utils.StatusError{StatusCode: status}
2031
-	}
2032
-	return nil
2033
-}
2034
-
2035
-func (cli *DockerCli) CmdCp(args ...string) error {
2036
-	cmd := cli.Subcmd("cp", "CONTAINER:PATH HOSTPATH", "Copy files/folders from the PATH to the HOSTPATH")
2037
-	if err := cmd.Parse(args); err != nil {
2038
-		return nil
2039
-	}
2040
-
2041
-	if cmd.NArg() != 2 {
2042
-		cmd.Usage()
2043
-		return nil
2044
-	}
2045
-
2046
-	var copyData engine.Env
2047
-	info := strings.Split(cmd.Arg(0), ":")
2048
-
2049
-	if len(info) != 2 {
2050
-		return fmt.Errorf("Error: Path not specified")
2051
-	}
2052
-
2053
-	copyData.Set("Resource", info[1])
2054
-	copyData.Set("HostPath", cmd.Arg(1))
2055
-
2056
-	stream, statusCode, err := cli.call("POST", "/containers/"+info[0]+"/copy", copyData, false)
2057
-	if stream != nil {
2058
-		defer stream.Close()
2059
-	}
2060
-	if statusCode == 404 {
2061
-		return fmt.Errorf("No such container: %v", info[0])
2062
-	}
2063
-	if err != nil {
2064
-		return err
2065
-	}
2066
-
2067
-	if statusCode == 200 {
2068
-		if err := archive.Untar(stream, copyData.Get("HostPath"), nil); err != nil {
2069
-			return err
2070
-		}
2071
-	}
2072
-	return nil
2073
-}
2074
-
2075
-func (cli *DockerCli) CmdSave(args ...string) error {
2076
-	cmd := cli.Subcmd("save", "IMAGE", "Save an image to a tar archive (streamed to stdout by default)")
2077
-	outfile := cmd.String([]string{"o", "-output"}, "", "Write to an file, instead of STDOUT")
2078
-
2079
-	if err := cmd.Parse(args); err != nil {
2080
-		return err
2081
-	}
2082
-
2083
-	if cmd.NArg() != 1 {
2084
-		cmd.Usage()
2085
-		return nil
2086
-	}
2087
-
2088
-	var (
2089
-		output io.Writer = cli.out
2090
-		err    error
2091
-	)
2092
-	if *outfile != "" {
2093
-		output, err = os.Create(*outfile)
2094
-		if err != nil {
2095
-			return err
2096
-		}
2097
-	}
2098
-	image := cmd.Arg(0)
2099
-	if err := cli.stream("GET", "/images/"+image+"/get", nil, output, nil); err != nil {
2100
-		return err
2101
-	}
2102
-	return nil
2103
-}
2104
-
2105
-func (cli *DockerCli) CmdLoad(args ...string) error {
2106
-	cmd := cli.Subcmd("load", "", "Load an image from a tar archive on STDIN")
2107
-	infile := cmd.String([]string{"i", "-input"}, "", "Read from a tar archive file, instead of STDIN")
2108
-
2109
-	if err := cmd.Parse(args); err != nil {
2110
-		return err
2111
-	}
2112
-
2113
-	if cmd.NArg() != 0 {
2114
-		cmd.Usage()
2115
-		return nil
2116
-	}
2117
-
2118
-	var (
2119
-		input io.Reader = cli.in
2120
-		err   error
2121
-	)
2122
-	if *infile != "" {
2123
-		input, err = os.Open(*infile)
2124
-		if err != nil {
2125
-			return err
2126
-		}
2127
-	}
2128
-	if err := cli.stream("POST", "/images/load", input, cli.out, nil); err != nil {
2129
-		return err
2130
-	}
2131
-	return nil
2132
-}
2133
-
2134
-func (cli *DockerCli) dial() (net.Conn, error) {
2135
-	if cli.tlsConfig != nil && cli.proto != "unix" {
2136
-		return tls.Dial(cli.proto, cli.addr, cli.tlsConfig)
2137
-	}
2138
-	return net.Dial(cli.proto, cli.addr)
2139
-}
2140
-
2141
-func (cli *DockerCli) call(method, path string, data interface{}, passAuthInfo bool) (io.ReadCloser, int, error) {
2142
-	params := bytes.NewBuffer(nil)
2143
-	if data != nil {
2144
-		if env, ok := data.(engine.Env); ok {
2145
-			if err := env.Encode(params); err != nil {
2146
-				return nil, -1, err
2147
-			}
2148
-		} else {
2149
-			buf, err := json.Marshal(data)
2150
-			if err != nil {
2151
-				return nil, -1, err
2152
-			}
2153
-			if _, err := params.Write(buf); err != nil {
2154
-				return nil, -1, err
2155
-			}
2156
-		}
2157
-	}
2158
-	// fixme: refactor client to support redirect
2159
-	re := regexp.MustCompile("/+")
2160
-	path = re.ReplaceAllString(path, "/")
2161
-
2162
-	req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", APIVERSION, path), params)
2163
-	if err != nil {
2164
-		return nil, -1, err
2165
-	}
2166
-	if passAuthInfo {
2167
-		cli.LoadConfigFile()
2168
-		// Resolve the Auth config relevant for this server
2169
-		authConfig := cli.configFile.ResolveAuthConfig(registry.IndexServerAddress())
2170
-		getHeaders := func(authConfig registry.AuthConfig) (map[string][]string, error) {
2171
-			buf, err := json.Marshal(authConfig)
2172
-			if err != nil {
2173
-				return nil, err
2174
-			}
2175
-			registryAuthHeader := []string{
2176
-				base64.URLEncoding.EncodeToString(buf),
2177
-			}
2178
-			return map[string][]string{"X-Registry-Auth": registryAuthHeader}, nil
2179
-		}
2180
-		if headers, err := getHeaders(authConfig); err == nil && headers != nil {
2181
-			for k, v := range headers {
2182
-				req.Header[k] = v
2183
-			}
2184
-		}
2185
-	}
2186
-	req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION)
2187
-	req.Host = cli.addr
2188
-	if data != nil {
2189
-		req.Header.Set("Content-Type", "application/json")
2190
-	} else if method == "POST" {
2191
-		req.Header.Set("Content-Type", "plain/text")
2192
-	}
2193
-	dial, err := cli.dial()
2194
-	if err != nil {
2195
-		if strings.Contains(err.Error(), "connection refused") {
2196
-			return nil, -1, ErrConnectionRefused
2197
-		}
2198
-		return nil, -1, err
2199
-	}
2200
-	clientconn := httputil.NewClientConn(dial, nil)
2201
-	resp, err := clientconn.Do(req)
2202
-	if err != nil {
2203
-		clientconn.Close()
2204
-		if strings.Contains(err.Error(), "connection refused") {
2205
-			return nil, -1, ErrConnectionRefused
2206
-		}
2207
-		return nil, -1, err
2208
-	}
2209
-
2210
-	if resp.StatusCode < 200 || resp.StatusCode >= 400 {
2211
-		body, err := ioutil.ReadAll(resp.Body)
2212
-		if err != nil {
2213
-			return nil, -1, err
2214
-		}
2215
-		if len(body) == 0 {
2216
-			return nil, resp.StatusCode, fmt.Errorf("Error: request returned %s for API route and version %s, check if the server supports the requested API version", http.StatusText(resp.StatusCode), req.URL)
2217
-		}
2218
-		return nil, resp.StatusCode, fmt.Errorf("Error: %s", bytes.TrimSpace(body))
2219
-	}
2220
-
2221
-	wrapper := utils.NewReadCloserWrapper(resp.Body, func() error {
2222
-		if resp != nil && resp.Body != nil {
2223
-			resp.Body.Close()
2224
-		}
2225
-		return clientconn.Close()
2226
-	})
2227
-	return wrapper, resp.StatusCode, nil
2228
-}
2229
-
2230
-func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, headers map[string][]string) error {
2231
-	if (method == "POST" || method == "PUT") && in == nil {
2232
-		in = bytes.NewReader([]byte{})
2233
-	}
2234
-
2235
-	// fixme: refactor client to support redirect
2236
-	re := regexp.MustCompile("/+")
2237
-	path = re.ReplaceAllString(path, "/")
2238
-
2239
-	req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", APIVERSION, path), in)
2240
-	if err != nil {
2241
-		return err
2242
-	}
2243
-	req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION)
2244
-	req.Host = cli.addr
2245
-	if method == "POST" {
2246
-		req.Header.Set("Content-Type", "plain/text")
2247
-	}
2248
-
2249
-	if headers != nil {
2250
-		for k, v := range headers {
2251
-			req.Header[k] = v
2252
-		}
2253
-	}
2254
-
2255
-	dial, err := cli.dial()
2256
-	if err != nil {
2257
-		if strings.Contains(err.Error(), "connection refused") {
2258
-			return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
2259
-		}
2260
-		return err
2261
-	}
2262
-	clientconn := httputil.NewClientConn(dial, nil)
2263
-	resp, err := clientconn.Do(req)
2264
-	defer clientconn.Close()
2265
-	if err != nil {
2266
-		if strings.Contains(err.Error(), "connection refused") {
2267
-			return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
2268
-		}
2269
-		return err
2270
-	}
2271
-	defer resp.Body.Close()
2272
-
2273
-	if resp.StatusCode < 200 || resp.StatusCode >= 400 {
2274
-		body, err := ioutil.ReadAll(resp.Body)
2275
-		if err != nil {
2276
-			return err
2277
-		}
2278
-		if len(body) == 0 {
2279
-			return fmt.Errorf("Error :%s", http.StatusText(resp.StatusCode))
2280
-		}
2281
-		return fmt.Errorf("Error: %s", bytes.TrimSpace(body))
2282
-	}
2283
-
2284
-	if MatchesContentType(resp.Header.Get("Content-Type"), "application/json") {
2285
-		return utils.DisplayJSONMessagesStream(resp.Body, out, cli.terminalFd, cli.isTerminal)
2286
-	}
2287
-	if _, err := io.Copy(out, resp.Body); err != nil {
2288
-		return err
2289
-	}
2290
-	return nil
2291
-}
2292
-
2293
-func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.ReadCloser, stdout, stderr io.Writer, started chan io.Closer) error {
2294
-	defer func() {
2295
-		if started != nil {
2296
-			close(started)
2297
-		}
2298
-	}()
2299
-	// fixme: refactor client to support redirect
2300
-	re := regexp.MustCompile("/+")
2301
-	path = re.ReplaceAllString(path, "/")
2302
-
2303
-	req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", APIVERSION, path), nil)
2304
-	if err != nil {
2305
-		return err
2306
-	}
2307
-	req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION)
2308
-	req.Header.Set("Content-Type", "plain/text")
2309
-	req.Host = cli.addr
2310
-
2311
-	dial, err := cli.dial()
2312
-	if err != nil {
2313
-		if strings.Contains(err.Error(), "connection refused") {
2314
-			return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
2315
-		}
2316
-		return err
2317
-	}
2318
-	clientconn := httputil.NewClientConn(dial, nil)
2319
-	defer clientconn.Close()
2320
-
2321
-	// Server hijacks the connection, error 'connection closed' expected
2322
-	clientconn.Do(req)
2323
-
2324
-	rwc, br := clientconn.Hijack()
2325
-	defer rwc.Close()
2326
-
2327
-	if started != nil {
2328
-		started <- rwc
2329
-	}
2330
-
2331
-	var receiveStdout chan error
2332
-
2333
-	var oldState *term.State
2334
-
2335
-	if in != nil && setRawTerminal && cli.isTerminal && os.Getenv("NORAW") == "" {
2336
-		oldState, err = term.SetRawTerminal(cli.terminalFd)
2337
-		if err != nil {
2338
-			return err
2339
-		}
2340
-		defer term.RestoreTerminal(cli.terminalFd, oldState)
2341
-	}
2342
-
2343
-	if stdout != nil || stderr != nil {
2344
-		receiveStdout = utils.Go(func() (err error) {
2345
-			defer func() {
2346
-				if in != nil {
2347
-					if setRawTerminal && cli.isTerminal {
2348
-						term.RestoreTerminal(cli.terminalFd, oldState)
2349
-					}
2350
-					// For some reason this Close call blocks on darwin..
2351
-					// As the client exists right after, simply discard the close
2352
-					// until we find a better solution.
2353
-					if goruntime.GOOS != "darwin" {
2354
-						in.Close()
2355
-					}
2356
-				}
2357
-			}()
2358
-
2359
-			// When TTY is ON, use regular copy
2360
-			if setRawTerminal {
2361
-				_, err = io.Copy(stdout, br)
2362
-			} else {
2363
-				_, err = utils.StdCopy(stdout, stderr, br)
2364
-			}
2365
-			utils.Debugf("[hijack] End of stdout")
2366
-			return err
2367
-		})
2368
-	}
2369
-
2370
-	sendStdin := utils.Go(func() error {
2371
-		if in != nil {
2372
-			io.Copy(rwc, in)
2373
-			utils.Debugf("[hijack] End of stdin")
2374
-		}
2375
-		if tcpc, ok := rwc.(*net.TCPConn); ok {
2376
-			if err := tcpc.CloseWrite(); err != nil {
2377
-				utils.Debugf("Couldn't send EOF: %s\n", err)
2378
-			}
2379
-		} else if unixc, ok := rwc.(*net.UnixConn); ok {
2380
-			if err := unixc.CloseWrite(); err != nil {
2381
-				utils.Debugf("Couldn't send EOF: %s\n", err)
2382
-			}
2383
-		}
2384
-		// Discard errors due to pipe interruption
2385
-		return nil
2386
-	})
2387
-
2388
-	if stdout != nil || stderr != nil {
2389
-		if err := <-receiveStdout; err != nil {
2390
-			utils.Debugf("Error receiveStdout: %s", err)
2391
-			return err
2392
-		}
2393
-	}
2394
-
2395
-	if !cli.isTerminal {
2396
-		if err := <-sendStdin; err != nil {
2397
-			utils.Debugf("Error sendStdin: %s", err)
2398
-			return err
2399
-		}
2400
-	}
2401
-	return nil
2402
-
2403
-}
2404
-
2405
-func (cli *DockerCli) getTtySize() (int, int) {
2406
-	if !cli.isTerminal {
2407
-		return 0, 0
2408
-	}
2409
-	ws, err := term.GetWinsize(cli.terminalFd)
2410
-	if err != nil {
2411
-		utils.Debugf("Error getting size: %s", err)
2412
-		if ws == nil {
2413
-			return 0, 0
2414
-		}
2415
-	}
2416
-	return int(ws.Height), int(ws.Width)
2417
-}
2418
-
2419
-func (cli *DockerCli) resizeTty(id string) {
2420
-	height, width := cli.getTtySize()
2421
-	if height == 0 && width == 0 {
2422
-		return
2423
-	}
2424
-	v := url.Values{}
2425
-	v.Set("h", strconv.Itoa(height))
2426
-	v.Set("w", strconv.Itoa(width))
2427
-	if _, _, err := readBody(cli.call("POST", "/containers/"+id+"/resize?"+v.Encode(), nil, false)); err != nil {
2428
-		utils.Debugf("Error resize: %s", err)
2429
-	}
2430
-}
2431
-
2432
-func (cli *DockerCli) monitorTtySize(id string) error {
2433
-	cli.resizeTty(id)
2434
-
2435
-	sigchan := make(chan os.Signal, 1)
2436
-	gosignal.Notify(sigchan, syscall.SIGWINCH)
2437
-	go func() {
2438
-		for _ = range sigchan {
2439
-			cli.resizeTty(id)
2440
-		}
2441
-	}()
2442
-	return nil
2443
-}
2444
-
2445
-func (cli *DockerCli) Subcmd(name, signature, description string) *flag.FlagSet {
2446
-	flags := flag.NewFlagSet(name, flag.ContinueOnError)
2447
-	flags.Usage = func() {
2448
-		fmt.Fprintf(cli.err, "\nUsage: docker %s %s\n\n%s\n\n", name, signature, description)
2449
-		flags.PrintDefaults()
2450
-		os.Exit(2)
2451
-	}
2452
-	return flags
2453
-}
2454
-
2455
-func (cli *DockerCli) LoadConfigFile() (err error) {
2456
-	cli.configFile, err = registry.LoadConfig(os.Getenv("HOME"))
2457
-	if err != nil {
2458
-		fmt.Fprintf(cli.err, "WARNING: %s\n", err)
2459
-	}
2460
-	return err
2461
-}
2462
-
2463
-func waitForExit(cli *DockerCli, containerId string) (int, error) {
2464
-	stream, _, err := cli.call("POST", "/containers/"+containerId+"/wait", nil, false)
2465
-	if err != nil {
2466
-		return -1, err
2467
-	}
2468
-
2469
-	var out engine.Env
2470
-	if err := out.Decode(stream); err != nil {
2471
-		return -1, err
2472
-	}
2473
-	return out.GetInt("StatusCode"), nil
2474
-}
2475
-
2476
-// getExitCode perform an inspect on the container. It returns
2477
-// the running state and the exit code.
2478
-func getExitCode(cli *DockerCli, containerId string) (bool, int, error) {
2479
-	body, _, err := readBody(cli.call("GET", "/containers/"+containerId+"/json", nil, false))
2480
-	if err != nil {
2481
-		// If we can't connect, then the daemon probably died.
2482
-		if err != ErrConnectionRefused {
2483
-			return false, -1, err
2484
-		}
2485
-		return false, -1, nil
2486
-	}
2487
-	c := &Container{}
2488
-	if err := json.Unmarshal(body, c); err != nil {
2489
-		return false, -1, err
2490
-	}
2491
-	return c.State.Running, c.State.ExitCode, nil
2492
-}
2493
-
2494
-func readBody(stream io.ReadCloser, statusCode int, err error) ([]byte, int, error) {
2495
-	if stream != nil {
2496
-		defer stream.Close()
2497
-	}
2498
-	if err != nil {
2499
-		return nil, statusCode, err
2500
-	}
2501
-	body, err := ioutil.ReadAll(stream)
2502
-	if err != nil {
2503
-		return nil, -1, err
2504
-	}
2505
-	return body, statusCode, nil
2506
-}
2507
-
2508
-func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string, tlsConfig *tls.Config) *DockerCli {
2509
-	var (
2510
-		isTerminal = false
2511
-		terminalFd uintptr
2512
-	)
2513
-
2514
-	if in != nil {
2515
-		if file, ok := in.(*os.File); ok {
2516
-			terminalFd = file.Fd()
2517
-			isTerminal = term.IsTerminal(terminalFd)
2518
-		}
2519
-	}
2520
-
2521
-	if err == nil {
2522
-		err = out
2523
-	}
2524
-	return &DockerCli{
2525
-		proto:      proto,
2526
-		addr:       addr,
2527
-		in:         in,
2528
-		out:        out,
2529
-		err:        err,
2530
-		isTerminal: isTerminal,
2531
-		terminalFd: terminalFd,
2532
-		tlsConfig:  tlsConfig,
2533
-	}
2534
-}
2535
-
2536
-type DockerCli struct {
2537
-	proto      string
2538
-	addr       string
2539
-	configFile *registry.ConfigFile
2540
-	in         io.ReadCloser
2541
-	out        io.Writer
2542
-	err        io.Writer
2543
-	isTerminal bool
2544
-	terminalFd uintptr
2545
-	tlsConfig  *tls.Config
2546
-}
2547 1
new file mode 100644
... ...
@@ -0,0 +1,2551 @@
0
+package client
1
+
2
+import (
3
+	"bufio"
4
+	"bytes"
5
+	"crypto/tls"
6
+	"encoding/base64"
7
+	"encoding/json"
8
+	"errors"
9
+	"fmt"
10
+	"github.com/dotcloud/docker/api"
11
+	"github.com/dotcloud/docker/archive"
12
+	"github.com/dotcloud/docker/dockerversion"
13
+	"github.com/dotcloud/docker/engine"
14
+	"github.com/dotcloud/docker/nat"
15
+	flag "github.com/dotcloud/docker/pkg/mflag"
16
+	"github.com/dotcloud/docker/pkg/signal"
17
+	"github.com/dotcloud/docker/pkg/term"
18
+	"github.com/dotcloud/docker/registry"
19
+	"github.com/dotcloud/docker/runconfig"
20
+	"github.com/dotcloud/docker/utils"
21
+	"io"
22
+	"io/ioutil"
23
+	"net"
24
+	"net/http"
25
+	"net/http/httputil"
26
+	"net/url"
27
+	"os"
28
+	"os/exec"
29
+	gosignal "os/signal"
30
+	"path"
31
+	"reflect"
32
+	"regexp"
33
+	goruntime "runtime"
34
+	"strconv"
35
+	"strings"
36
+	"syscall"
37
+	"text/tabwriter"
38
+	"text/template"
39
+	"time"
40
+)
41
+
42
+var funcMap = template.FuncMap{
43
+	"json": func(v interface{}) string {
44
+		a, _ := json.Marshal(v)
45
+		return string(a)
46
+	},
47
+}
48
+
49
+var (
50
+	ErrConnectionRefused = errors.New("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
51
+)
52
+
53
+func (cli *DockerCli) getMethod(name string) (func(...string) error, bool) {
54
+	methodName := "Cmd" + strings.ToUpper(name[:1]) + strings.ToLower(name[1:])
55
+	method := reflect.ValueOf(cli).MethodByName(methodName)
56
+	if !method.IsValid() {
57
+		return nil, false
58
+	}
59
+	return method.Interface().(func(...string) error), true
60
+}
61
+
62
+func (cli *DockerCli) ParseCommands(args ...string) error {
63
+	if len(args) > 0 {
64
+		method, exists := cli.getMethod(args[0])
65
+		if !exists {
66
+			fmt.Println("Error: Command not found:", args[0])
67
+			return cli.CmdHelp(args[1:]...)
68
+		}
69
+		return method(args[1:]...)
70
+	}
71
+	return cli.CmdHelp(args...)
72
+}
73
+
74
+func (cli *DockerCli) CmdHelp(args ...string) error {
75
+	if len(args) > 0 {
76
+		method, exists := cli.getMethod(args[0])
77
+		if !exists {
78
+			fmt.Fprintf(cli.err, "Error: Command not found: %s\n", args[0])
79
+		} else {
80
+			method("--help")
81
+			return nil
82
+		}
83
+	}
84
+	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)
85
+	for _, command := range [][]string{
86
+		{"attach", "Attach to a running container"},
87
+		{"build", "Build a container from a Dockerfile"},
88
+		{"commit", "Create a new image from a container's changes"},
89
+		{"cp", "Copy files/folders from the containers filesystem to the host path"},
90
+		{"diff", "Inspect changes on a container's filesystem"},
91
+		{"events", "Get real time events from the server"},
92
+		{"export", "Stream the contents of a container as a tar archive"},
93
+		{"history", "Show the history of an image"},
94
+		{"images", "List images"},
95
+		{"import", "Create a new filesystem image from the contents of a tarball"},
96
+		{"info", "Display system-wide information"},
97
+		{"insert", "Insert a file in an image"},
98
+		{"inspect", "Return low-level information on a container"},
99
+		{"kill", "Kill a running container"},
100
+		{"load", "Load an image from a tar archive"},
101
+		{"login", "Register or Login to the docker registry server"},
102
+		{"logs", "Fetch the logs of a container"},
103
+		{"port", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT"},
104
+		{"ps", "List containers"},
105
+		{"pull", "Pull an image or a repository from the docker registry server"},
106
+		{"push", "Push an image or a repository to the docker registry server"},
107
+		{"restart", "Restart a running container"},
108
+		{"rm", "Remove one or more containers"},
109
+		{"rmi", "Remove one or more images"},
110
+		{"run", "Run a command in a new container"},
111
+		{"save", "Save an image to a tar archive"},
112
+		{"search", "Search for an image in the docker index"},
113
+		{"start", "Start a stopped container"},
114
+		{"stop", "Stop a running container"},
115
+		{"tag", "Tag an image into a repository"},
116
+		{"top", "Lookup the running processes of a container"},
117
+		{"version", "Show the docker version information"},
118
+		{"wait", "Block until a container stops, then print its exit code"},
119
+	} {
120
+		help += fmt.Sprintf("    %-10.10s%s\n", command[0], command[1])
121
+	}
122
+	fmt.Fprintf(cli.err, "%s\n", help)
123
+	return nil
124
+}
125
+
126
+func (cli *DockerCli) CmdInsert(args ...string) error {
127
+	cmd := cli.Subcmd("insert", "IMAGE URL PATH", "Insert a file from URL in the IMAGE at PATH")
128
+	if err := cmd.Parse(args); err != nil {
129
+		return nil
130
+	}
131
+	if cmd.NArg() != 3 {
132
+		cmd.Usage()
133
+		return nil
134
+	}
135
+
136
+	v := url.Values{}
137
+	v.Set("url", cmd.Arg(1))
138
+	v.Set("path", cmd.Arg(2))
139
+
140
+	return cli.stream("POST", "/images/"+cmd.Arg(0)+"/insert?"+v.Encode(), nil, cli.out, nil)
141
+}
142
+
143
+func (cli *DockerCli) CmdBuild(args ...string) error {
144
+	cmd := cli.Subcmd("build", "[OPTIONS] PATH | URL | -", "Build a new container image from the source code at PATH")
145
+	tag := cmd.String([]string{"t", "-tag"}, "", "Repository name (and optionally a tag) to be applied to the resulting image in case of success")
146
+	suppressOutput := cmd.Bool([]string{"q", "-quiet"}, false, "Suppress the verbose output generated by the containers")
147
+	noCache := cmd.Bool([]string{"#no-cache", "-no-cache"}, false, "Do not use cache when building the image")
148
+	rm := cmd.Bool([]string{"#rm", "-rm"}, true, "Remove intermediate containers after a successful build")
149
+	if err := cmd.Parse(args); err != nil {
150
+		return nil
151
+	}
152
+	if cmd.NArg() != 1 {
153
+		cmd.Usage()
154
+		return nil
155
+	}
156
+
157
+	var (
158
+		context  archive.Archive
159
+		isRemote bool
160
+		err      error
161
+	)
162
+
163
+	_, err = exec.LookPath("git")
164
+	hasGit := err == nil
165
+	if cmd.Arg(0) == "-" {
166
+		// As a special case, 'docker build -' will build from an empty context with the
167
+		// contents of stdin as a Dockerfile
168
+		dockerfile, err := ioutil.ReadAll(cli.in)
169
+		if err != nil {
170
+			return err
171
+		}
172
+		context, err = archive.Generate("Dockerfile", string(dockerfile))
173
+	} else if utils.IsURL(cmd.Arg(0)) && (!utils.IsGIT(cmd.Arg(0)) || !hasGit) {
174
+		isRemote = true
175
+	} else {
176
+		root := cmd.Arg(0)
177
+		if utils.IsGIT(root) {
178
+			remoteURL := cmd.Arg(0)
179
+			if !strings.HasPrefix(remoteURL, "git://") && !strings.HasPrefix(remoteURL, "git@") && !utils.IsURL(remoteURL) {
180
+				remoteURL = "https://" + remoteURL
181
+			}
182
+
183
+			root, err = ioutil.TempDir("", "docker-build-git")
184
+			if err != nil {
185
+				return err
186
+			}
187
+			defer os.RemoveAll(root)
188
+
189
+			if output, err := exec.Command("git", "clone", "--recursive", remoteURL, root).CombinedOutput(); err != nil {
190
+				return fmt.Errorf("Error trying to use git: %s (%s)", err, output)
191
+			}
192
+		}
193
+		if _, err := os.Stat(root); err != nil {
194
+			return err
195
+		}
196
+		filename := path.Join(root, "Dockerfile")
197
+		if _, err = os.Stat(filename); os.IsNotExist(err) {
198
+			return fmt.Errorf("no Dockerfile found in %s", cmd.Arg(0))
199
+		}
200
+		context, err = archive.Tar(root, archive.Uncompressed)
201
+	}
202
+	var body io.Reader
203
+	// Setup an upload progress bar
204
+	// FIXME: ProgressReader shouldn't be this annoying to use
205
+	if context != nil {
206
+		sf := utils.NewStreamFormatter(false)
207
+		body = utils.ProgressReader(context, 0, cli.err, sf, true, "", "Uploading context")
208
+	}
209
+	// Upload the build context
210
+	v := &url.Values{}
211
+
212
+	//Check if the given image name can be resolved
213
+	if *tag != "" {
214
+		repository, _ := utils.ParseRepositoryTag(*tag)
215
+		if _, _, err := registry.ResolveRepositoryName(repository); err != nil {
216
+			return err
217
+		}
218
+	}
219
+
220
+	v.Set("t", *tag)
221
+
222
+	if *suppressOutput {
223
+		v.Set("q", "1")
224
+	}
225
+	if isRemote {
226
+		v.Set("remote", cmd.Arg(0))
227
+	}
228
+	if *noCache {
229
+		v.Set("nocache", "1")
230
+	}
231
+	if *rm {
232
+		v.Set("rm", "1")
233
+	}
234
+
235
+	cli.LoadConfigFile()
236
+
237
+	headers := http.Header(make(map[string][]string))
238
+	buf, err := json.Marshal(cli.configFile)
239
+	if err != nil {
240
+		return err
241
+	}
242
+	headers.Add("X-Registry-Config", base64.URLEncoding.EncodeToString(buf))
243
+
244
+	if context != nil {
245
+		headers.Set("Content-Type", "application/tar")
246
+	}
247
+	err = cli.stream("POST", fmt.Sprintf("/build?%s", v.Encode()), body, cli.out, headers)
248
+	if jerr, ok := err.(*utils.JSONError); ok {
249
+		// If no error code is set, default to 1
250
+		if jerr.Code == 0 {
251
+			jerr.Code = 1
252
+		}
253
+		return &utils.StatusError{Status: jerr.Message, StatusCode: jerr.Code}
254
+	}
255
+	return err
256
+}
257
+
258
+// 'docker login': login / register a user to registry service.
259
+func (cli *DockerCli) CmdLogin(args ...string) error {
260
+	cmd := cli.Subcmd("login", "[OPTIONS] [SERVER]", "Register or Login to a docker registry server, if no server is specified \""+registry.IndexServerAddress()+"\" is the default.")
261
+
262
+	var username, password, email string
263
+
264
+	cmd.StringVar(&username, []string{"u", "-username"}, "", "Username")
265
+	cmd.StringVar(&password, []string{"p", "-password"}, "", "Password")
266
+	cmd.StringVar(&email, []string{"e", "-email"}, "", "Email")
267
+	err := cmd.Parse(args)
268
+	if err != nil {
269
+		return nil
270
+	}
271
+	serverAddress := registry.IndexServerAddress()
272
+	if len(cmd.Args()) > 0 {
273
+		serverAddress = cmd.Arg(0)
274
+	}
275
+
276
+	promptDefault := func(prompt string, configDefault string) {
277
+		if configDefault == "" {
278
+			fmt.Fprintf(cli.out, "%s: ", prompt)
279
+		} else {
280
+			fmt.Fprintf(cli.out, "%s (%s): ", prompt, configDefault)
281
+		}
282
+	}
283
+
284
+	readInput := func(in io.Reader, out io.Writer) string {
285
+		reader := bufio.NewReader(in)
286
+		line, _, err := reader.ReadLine()
287
+		if err != nil {
288
+			fmt.Fprintln(out, err.Error())
289
+			os.Exit(1)
290
+		}
291
+		return string(line)
292
+	}
293
+
294
+	cli.LoadConfigFile()
295
+	authconfig, ok := cli.configFile.Configs[serverAddress]
296
+	if !ok {
297
+		authconfig = registry.AuthConfig{}
298
+	}
299
+
300
+	if username == "" {
301
+		promptDefault("Username", authconfig.Username)
302
+		username = readInput(cli.in, cli.out)
303
+		if username == "" {
304
+			username = authconfig.Username
305
+		}
306
+	}
307
+	if username != authconfig.Username {
308
+		if password == "" {
309
+			oldState, _ := term.SaveState(cli.terminalFd)
310
+			fmt.Fprintf(cli.out, "Password: ")
311
+			term.DisableEcho(cli.terminalFd, oldState)
312
+
313
+			password = readInput(cli.in, cli.out)
314
+			fmt.Fprint(cli.out, "\n")
315
+
316
+			term.RestoreTerminal(cli.terminalFd, oldState)
317
+			if password == "" {
318
+				return fmt.Errorf("Error : Password Required")
319
+			}
320
+		}
321
+
322
+		if email == "" {
323
+			promptDefault("Email", authconfig.Email)
324
+			email = readInput(cli.in, cli.out)
325
+			if email == "" {
326
+				email = authconfig.Email
327
+			}
328
+		}
329
+	} else {
330
+		password = authconfig.Password
331
+		email = authconfig.Email
332
+	}
333
+	authconfig.Username = username
334
+	authconfig.Password = password
335
+	authconfig.Email = email
336
+	authconfig.ServerAddress = serverAddress
337
+	cli.configFile.Configs[serverAddress] = authconfig
338
+
339
+	stream, statusCode, err := cli.call("POST", "/auth", cli.configFile.Configs[serverAddress], false)
340
+	if statusCode == 401 {
341
+		delete(cli.configFile.Configs, serverAddress)
342
+		registry.SaveConfig(cli.configFile)
343
+		return err
344
+	}
345
+	if err != nil {
346
+		return err
347
+	}
348
+	var out2 engine.Env
349
+	err = out2.Decode(stream)
350
+	if err != nil {
351
+		cli.configFile, _ = registry.LoadConfig(os.Getenv("HOME"))
352
+		return err
353
+	}
354
+	registry.SaveConfig(cli.configFile)
355
+	if out2.Get("Status") != "" {
356
+		fmt.Fprintf(cli.out, "%s\n", out2.Get("Status"))
357
+	}
358
+	return nil
359
+}
360
+
361
+// 'docker wait': block until a container stops
362
+func (cli *DockerCli) CmdWait(args ...string) error {
363
+	cmd := cli.Subcmd("wait", "CONTAINER [CONTAINER...]", "Block until a container stops, then print its exit code.")
364
+	if err := cmd.Parse(args); err != nil {
365
+		return nil
366
+	}
367
+	if cmd.NArg() < 1 {
368
+		cmd.Usage()
369
+		return nil
370
+	}
371
+	var encounteredError error
372
+	for _, name := range cmd.Args() {
373
+		status, err := waitForExit(cli, name)
374
+		if err != nil {
375
+			fmt.Fprintf(cli.err, "%s\n", err)
376
+			encounteredError = fmt.Errorf("Error: failed to wait one or more containers")
377
+		} else {
378
+			fmt.Fprintf(cli.out, "%d\n", status)
379
+		}
380
+	}
381
+	return encounteredError
382
+}
383
+
384
+// 'docker version': show version information
385
+func (cli *DockerCli) CmdVersion(args ...string) error {
386
+	cmd := cli.Subcmd("version", "", "Show the docker version information.")
387
+	if err := cmd.Parse(args); err != nil {
388
+		return nil
389
+	}
390
+
391
+	if cmd.NArg() > 0 {
392
+		cmd.Usage()
393
+		return nil
394
+	}
395
+	if dockerversion.VERSION != "" {
396
+		fmt.Fprintf(cli.out, "Client version: %s\n", dockerversion.VERSION)
397
+	}
398
+	fmt.Fprintf(cli.out, "Go version (client): %s\n", goruntime.Version())
399
+	if dockerversion.GITCOMMIT != "" {
400
+		fmt.Fprintf(cli.out, "Git commit (client): %s\n", dockerversion.GITCOMMIT)
401
+	}
402
+
403
+	body, _, err := readBody(cli.call("GET", "/version", nil, false))
404
+	if err != nil {
405
+		return err
406
+	}
407
+
408
+	out := engine.NewOutput()
409
+	remoteVersion, err := out.AddEnv()
410
+	if err != nil {
411
+		utils.Errorf("Error reading remote version: %s\n", err)
412
+		return err
413
+	}
414
+	if _, err := out.Write(body); err != nil {
415
+		utils.Errorf("Error reading remote version: %s\n", err)
416
+		return err
417
+	}
418
+	out.Close()
419
+	fmt.Fprintf(cli.out, "Server version: %s\n", remoteVersion.Get("Version"))
420
+	fmt.Fprintf(cli.out, "Git commit (server): %s\n", remoteVersion.Get("GitCommit"))
421
+	fmt.Fprintf(cli.out, "Go version (server): %s\n", remoteVersion.Get("GoVersion"))
422
+	release := utils.GetReleaseVersion()
423
+	if release != "" {
424
+		fmt.Fprintf(cli.out, "Last stable version: %s", release)
425
+		if (dockerversion.VERSION != "" || remoteVersion.Exists("Version")) && (strings.Trim(dockerversion.VERSION, "-dev") != release || strings.Trim(remoteVersion.Get("Version"), "-dev") != release) {
426
+			fmt.Fprintf(cli.out, ", please update docker")
427
+		}
428
+		fmt.Fprintf(cli.out, "\n")
429
+	}
430
+	return nil
431
+}
432
+
433
+// 'docker info': display system-wide information.
434
+func (cli *DockerCli) CmdInfo(args ...string) error {
435
+	cmd := cli.Subcmd("info", "", "Display system-wide information")
436
+	if err := cmd.Parse(args); err != nil {
437
+		return nil
438
+	}
439
+	if cmd.NArg() > 0 {
440
+		cmd.Usage()
441
+		return nil
442
+	}
443
+
444
+	body, _, err := readBody(cli.call("GET", "/info", nil, false))
445
+	if err != nil {
446
+		return err
447
+	}
448
+
449
+	out := engine.NewOutput()
450
+	remoteInfo, err := out.AddEnv()
451
+	if err != nil {
452
+		return err
453
+	}
454
+
455
+	if _, err := out.Write(body); err != nil {
456
+		utils.Errorf("Error reading remote info: %s\n", err)
457
+		return err
458
+	}
459
+	out.Close()
460
+
461
+	fmt.Fprintf(cli.out, "Containers: %d\n", remoteInfo.GetInt("Containers"))
462
+	fmt.Fprintf(cli.out, "Images: %d\n", remoteInfo.GetInt("Images"))
463
+	fmt.Fprintf(cli.out, "Storage Driver: %s\n", remoteInfo.Get("Driver"))
464
+	var driverStatus [][2]string
465
+	if err := remoteInfo.GetJson("DriverStatus", &driverStatus); err != nil {
466
+		return err
467
+	}
468
+	for _, pair := range driverStatus {
469
+		fmt.Fprintf(cli.out, " %s: %s\n", pair[0], pair[1])
470
+	}
471
+	fmt.Fprintf(cli.out, "Execution Driver: %s\n", remoteInfo.Get("ExecutionDriver"))
472
+	fmt.Fprintf(cli.out, "Kernel Version: %s\n", remoteInfo.Get("KernelVersion"))
473
+
474
+	if remoteInfo.GetBool("Debug") || os.Getenv("DEBUG") != "" {
475
+		fmt.Fprintf(cli.out, "Debug mode (server): %v\n", remoteInfo.GetBool("Debug"))
476
+		fmt.Fprintf(cli.out, "Debug mode (client): %v\n", os.Getenv("DEBUG") != "")
477
+		fmt.Fprintf(cli.out, "Fds: %d\n", remoteInfo.GetInt("NFd"))
478
+		fmt.Fprintf(cli.out, "Goroutines: %d\n", remoteInfo.GetInt("NGoroutines"))
479
+		fmt.Fprintf(cli.out, "EventsListeners: %d\n", remoteInfo.GetInt("NEventsListener"))
480
+
481
+		if initSha1 := remoteInfo.Get("InitSha1"); initSha1 != "" {
482
+			fmt.Fprintf(cli.out, "Init SHA1: %s\n", initSha1)
483
+		}
484
+		if initPath := remoteInfo.Get("InitPath"); initPath != "" {
485
+			fmt.Fprintf(cli.out, "Init Path: %s\n", initPath)
486
+		}
487
+	}
488
+
489
+	if len(remoteInfo.GetList("IndexServerAddress")) != 0 {
490
+		cli.LoadConfigFile()
491
+		u := cli.configFile.Configs[remoteInfo.Get("IndexServerAddress")].Username
492
+		if len(u) > 0 {
493
+			fmt.Fprintf(cli.out, "Username: %v\n", u)
494
+			fmt.Fprintf(cli.out, "Registry: %v\n", remoteInfo.GetList("IndexServerAddress"))
495
+		}
496
+	}
497
+	if !remoteInfo.GetBool("MemoryLimit") {
498
+		fmt.Fprintf(cli.err, "WARNING: No memory limit support\n")
499
+	}
500
+	if !remoteInfo.GetBool("SwapLimit") {
501
+		fmt.Fprintf(cli.err, "WARNING: No swap limit support\n")
502
+	}
503
+	if !remoteInfo.GetBool("IPv4Forwarding") {
504
+		fmt.Fprintf(cli.err, "WARNING: IPv4 forwarding is disabled.\n")
505
+	}
506
+	return nil
507
+}
508
+
509
+func (cli *DockerCli) CmdStop(args ...string) error {
510
+	cmd := cli.Subcmd("stop", "[OPTIONS] CONTAINER [CONTAINER...]", "Stop a running container (Send SIGTERM, and then SIGKILL after grace period)")
511
+	nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Number of seconds to wait for the container to stop before killing it.")
512
+	if err := cmd.Parse(args); err != nil {
513
+		return nil
514
+	}
515
+	if cmd.NArg() < 1 {
516
+		cmd.Usage()
517
+		return nil
518
+	}
519
+
520
+	v := url.Values{}
521
+	v.Set("t", strconv.Itoa(*nSeconds))
522
+
523
+	var encounteredError error
524
+	for _, name := range cmd.Args() {
525
+		_, _, err := readBody(cli.call("POST", "/containers/"+name+"/stop?"+v.Encode(), nil, false))
526
+		if err != nil {
527
+			fmt.Fprintf(cli.err, "%s\n", err)
528
+			encounteredError = fmt.Errorf("Error: failed to stop one or more containers")
529
+		} else {
530
+			fmt.Fprintf(cli.out, "%s\n", name)
531
+		}
532
+	}
533
+	return encounteredError
534
+}
535
+
536
+func (cli *DockerCli) CmdRestart(args ...string) error {
537
+	cmd := cli.Subcmd("restart", "[OPTIONS] CONTAINER [CONTAINER...]", "Restart a running container")
538
+	nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Number of seconds to try to stop for before killing the container. Once killed it will then be restarted. Default=10")
539
+	if err := cmd.Parse(args); err != nil {
540
+		return nil
541
+	}
542
+	if cmd.NArg() < 1 {
543
+		cmd.Usage()
544
+		return nil
545
+	}
546
+
547
+	v := url.Values{}
548
+	v.Set("t", strconv.Itoa(*nSeconds))
549
+
550
+	var encounteredError error
551
+	for _, name := range cmd.Args() {
552
+		_, _, err := readBody(cli.call("POST", "/containers/"+name+"/restart?"+v.Encode(), nil, false))
553
+		if err != nil {
554
+			fmt.Fprintf(cli.err, "%s\n", err)
555
+			encounteredError = fmt.Errorf("Error: failed to restart one or more containers")
556
+		} else {
557
+			fmt.Fprintf(cli.out, "%s\n", name)
558
+		}
559
+	}
560
+	return encounteredError
561
+}
562
+
563
+func (cli *DockerCli) forwardAllSignals(cid string) chan os.Signal {
564
+	sigc := make(chan os.Signal, 1)
565
+	signal.CatchAll(sigc)
566
+	go func() {
567
+		for s := range sigc {
568
+			if s == syscall.SIGCHLD {
569
+				continue
570
+			}
571
+			var sig string
572
+			for sigStr, sigN := range signal.SignalMap {
573
+				if sigN == s {
574
+					sig = sigStr
575
+					break
576
+				}
577
+			}
578
+			if sig == "" {
579
+				utils.Errorf("Unsupported signal: %d. Discarding.", s)
580
+			}
581
+			if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", cid, sig), nil, false)); err != nil {
582
+				utils.Debugf("Error sending signal: %s", err)
583
+			}
584
+		}
585
+	}()
586
+	return sigc
587
+}
588
+
589
+func (cli *DockerCli) CmdStart(args ...string) error {
590
+	cmd := cli.Subcmd("start", "CONTAINER [CONTAINER...]", "Restart a stopped container")
591
+	attach := cmd.Bool([]string{"a", "-attach"}, false, "Attach container's stdout/stderr and forward all signals to the process")
592
+	openStdin := cmd.Bool([]string{"i", "-interactive"}, false, "Attach container's stdin")
593
+	if err := cmd.Parse(args); err != nil {
594
+		return nil
595
+	}
596
+	if cmd.NArg() < 1 {
597
+		cmd.Usage()
598
+		return nil
599
+	}
600
+
601
+	var cErr chan error
602
+	var tty bool
603
+	if *attach || *openStdin {
604
+		if cmd.NArg() > 1 {
605
+			return fmt.Errorf("You cannot start and attach multiple containers at once.")
606
+		}
607
+
608
+		body, _, err := readBody(cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, false))
609
+		if err != nil {
610
+			return err
611
+		}
612
+
613
+		container := &api.Container{}
614
+		err = json.Unmarshal(body, container)
615
+		if err != nil {
616
+			return err
617
+		}
618
+
619
+		tty = container.Config.Tty
620
+
621
+		if !container.Config.Tty {
622
+			sigc := cli.forwardAllSignals(cmd.Arg(0))
623
+			defer signal.StopCatch(sigc)
624
+		}
625
+
626
+		var in io.ReadCloser
627
+
628
+		v := url.Values{}
629
+		v.Set("stream", "1")
630
+		if *openStdin && container.Config.OpenStdin {
631
+			v.Set("stdin", "1")
632
+			in = cli.in
633
+		}
634
+		v.Set("stdout", "1")
635
+		v.Set("stderr", "1")
636
+
637
+		cErr = utils.Go(func() error {
638
+			return cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), container.Config.Tty, in, cli.out, cli.err, nil)
639
+		})
640
+	}
641
+
642
+	var encounteredError error
643
+	for _, name := range cmd.Args() {
644
+		_, _, err := readBody(cli.call("POST", "/containers/"+name+"/start", nil, false))
645
+		if err != nil {
646
+			if !*attach || !*openStdin {
647
+				fmt.Fprintf(cli.err, "%s\n", err)
648
+				encounteredError = fmt.Errorf("Error: failed to start one or more containers")
649
+			}
650
+		} else {
651
+			if !*attach || !*openStdin {
652
+				fmt.Fprintf(cli.out, "%s\n", name)
653
+			}
654
+		}
655
+	}
656
+	if encounteredError != nil {
657
+		if *openStdin || *attach {
658
+			cli.in.Close()
659
+			<-cErr
660
+		}
661
+		return encounteredError
662
+	}
663
+
664
+	if *openStdin || *attach {
665
+		if tty && cli.isTerminal {
666
+			if err := cli.monitorTtySize(cmd.Arg(0)); err != nil {
667
+				utils.Errorf("Error monitoring TTY size: %s\n", err)
668
+			}
669
+		}
670
+		return <-cErr
671
+	}
672
+	return nil
673
+}
674
+
675
+func (cli *DockerCli) CmdInspect(args ...string) error {
676
+	cmd := cli.Subcmd("inspect", "CONTAINER|IMAGE [CONTAINER|IMAGE...]", "Return low-level information on a container/image")
677
+	tmplStr := cmd.String([]string{"f", "#format", "-format"}, "", "Format the output using the given go template.")
678
+	if err := cmd.Parse(args); err != nil {
679
+		return nil
680
+	}
681
+	if cmd.NArg() < 1 {
682
+		cmd.Usage()
683
+		return nil
684
+	}
685
+
686
+	var tmpl *template.Template
687
+	if *tmplStr != "" {
688
+		var err error
689
+		if tmpl, err = template.New("").Funcs(funcMap).Parse(*tmplStr); err != nil {
690
+			fmt.Fprintf(cli.err, "Template parsing error: %v\n", err)
691
+			return &utils.StatusError{StatusCode: 64,
692
+				Status: "Template parsing error: " + err.Error()}
693
+		}
694
+	}
695
+
696
+	indented := new(bytes.Buffer)
697
+	indented.WriteByte('[')
698
+	status := 0
699
+
700
+	for _, name := range cmd.Args() {
701
+		obj, _, err := readBody(cli.call("GET", "/containers/"+name+"/json", nil, false))
702
+		if err != nil {
703
+			obj, _, err = readBody(cli.call("GET", "/images/"+name+"/json", nil, false))
704
+			if err != nil {
705
+				if strings.Contains(err.Error(), "No such") {
706
+					fmt.Fprintf(cli.err, "Error: No such image or container: %s\n", name)
707
+				} else {
708
+					fmt.Fprintf(cli.err, "%s", err)
709
+				}
710
+				status = 1
711
+				continue
712
+			}
713
+		}
714
+
715
+		if tmpl == nil {
716
+			if err = json.Indent(indented, obj, "", "    "); err != nil {
717
+				fmt.Fprintf(cli.err, "%s\n", err)
718
+				status = 1
719
+				continue
720
+			}
721
+		} else {
722
+			// Has template, will render
723
+			var value interface{}
724
+			if err := json.Unmarshal(obj, &value); err != nil {
725
+				fmt.Fprintf(cli.err, "%s\n", err)
726
+				status = 1
727
+				continue
728
+			}
729
+			if err := tmpl.Execute(cli.out, value); err != nil {
730
+				return err
731
+			}
732
+			cli.out.Write([]byte{'\n'})
733
+		}
734
+		indented.WriteString(",")
735
+	}
736
+
737
+	if indented.Len() > 1 {
738
+		// Remove trailing ','
739
+		indented.Truncate(indented.Len() - 1)
740
+	}
741
+	indented.WriteByte(']')
742
+
743
+	if tmpl == nil {
744
+		if _, err := io.Copy(cli.out, indented); err != nil {
745
+			return err
746
+		}
747
+	}
748
+
749
+	if status != 0 {
750
+		return &utils.StatusError{StatusCode: status}
751
+	}
752
+	return nil
753
+}
754
+
755
+func (cli *DockerCli) CmdTop(args ...string) error {
756
+	cmd := cli.Subcmd("top", "CONTAINER [ps OPTIONS]", "Lookup the running processes of a container")
757
+	if err := cmd.Parse(args); err != nil {
758
+		return nil
759
+	}
760
+	if cmd.NArg() == 0 {
761
+		cmd.Usage()
762
+		return nil
763
+	}
764
+	val := url.Values{}
765
+	if cmd.NArg() > 1 {
766
+		val.Set("ps_args", strings.Join(cmd.Args()[1:], " "))
767
+	}
768
+
769
+	stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/top?"+val.Encode(), nil, false)
770
+	if err != nil {
771
+		return err
772
+	}
773
+	var procs engine.Env
774
+	if err := procs.Decode(stream); err != nil {
775
+		return err
776
+	}
777
+	w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
778
+	fmt.Fprintln(w, strings.Join(procs.GetList("Titles"), "\t"))
779
+	processes := [][]string{}
780
+	if err := procs.GetJson("Processes", &processes); err != nil {
781
+		return err
782
+	}
783
+	for _, proc := range processes {
784
+		fmt.Fprintln(w, strings.Join(proc, "\t"))
785
+	}
786
+	w.Flush()
787
+	return nil
788
+}
789
+
790
+func (cli *DockerCli) CmdPort(args ...string) error {
791
+	cmd := cli.Subcmd("port", "CONTAINER PRIVATE_PORT", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT")
792
+	if err := cmd.Parse(args); err != nil {
793
+		return nil
794
+	}
795
+	if cmd.NArg() != 2 {
796
+		cmd.Usage()
797
+		return nil
798
+	}
799
+
800
+	var (
801
+		port      = cmd.Arg(1)
802
+		proto     = "tcp"
803
+		parts     = strings.SplitN(port, "/", 2)
804
+		container api.Container
805
+	)
806
+
807
+	if len(parts) == 2 && len(parts[1]) != 0 {
808
+		port = parts[0]
809
+		proto = parts[1]
810
+	}
811
+	body, _, err := readBody(cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, false))
812
+	if err != nil {
813
+		return err
814
+	}
815
+
816
+	err = json.Unmarshal(body, &container)
817
+	if err != nil {
818
+		return err
819
+	}
820
+
821
+	if frontends, exists := container.NetworkSettings.Ports[nat.Port(port+"/"+proto)]; exists && frontends != nil {
822
+		for _, frontend := range frontends {
823
+			fmt.Fprintf(cli.out, "%s:%s\n", frontend.HostIp, frontend.HostPort)
824
+		}
825
+	} else {
826
+		return fmt.Errorf("Error: No public port '%s' published for %s", cmd.Arg(1), cmd.Arg(0))
827
+	}
828
+	return nil
829
+}
830
+
831
+// 'docker rmi IMAGE' removes all images with the name IMAGE
832
+func (cli *DockerCli) CmdRmi(args ...string) error {
833
+	var (
834
+		cmd     = cli.Subcmd("rmi", "IMAGE [IMAGE...]", "Remove one or more images")
835
+		force   = cmd.Bool([]string{"f", "-force"}, false, "Force")
836
+		noprune = cmd.Bool([]string{"-no-prune"}, false, "Do not delete untagged parents")
837
+	)
838
+	if err := cmd.Parse(args); err != nil {
839
+		return nil
840
+	}
841
+	if cmd.NArg() < 1 {
842
+		cmd.Usage()
843
+		return nil
844
+	}
845
+
846
+	v := url.Values{}
847
+	if *force {
848
+		v.Set("force", "1")
849
+	}
850
+	if *noprune {
851
+		v.Set("noprune", "1")
852
+	}
853
+
854
+	var encounteredError error
855
+	for _, name := range cmd.Args() {
856
+		body, _, err := readBody(cli.call("DELETE", "/images/"+name+"?"+v.Encode(), nil, false))
857
+		if err != nil {
858
+			fmt.Fprintf(cli.err, "%s\n", err)
859
+			encounteredError = fmt.Errorf("Error: failed to remove one or more images")
860
+		} else {
861
+			outs := engine.NewTable("Created", 0)
862
+			if _, err := outs.ReadListFrom(body); err != nil {
863
+				fmt.Fprintf(cli.err, "%s\n", err)
864
+				encounteredError = fmt.Errorf("Error: failed to remove one or more images")
865
+				continue
866
+			}
867
+			for _, out := range outs.Data {
868
+				if out.Get("Deleted") != "" {
869
+					fmt.Fprintf(cli.out, "Deleted: %s\n", out.Get("Deleted"))
870
+				} else {
871
+					fmt.Fprintf(cli.out, "Untagged: %s\n", out.Get("Untagged"))
872
+				}
873
+			}
874
+		}
875
+	}
876
+	return encounteredError
877
+}
878
+
879
+func (cli *DockerCli) CmdHistory(args ...string) error {
880
+	cmd := cli.Subcmd("history", "[OPTIONS] IMAGE", "Show the history of an image")
881
+	quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs")
882
+	noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
883
+
884
+	if err := cmd.Parse(args); err != nil {
885
+		return nil
886
+	}
887
+	if cmd.NArg() != 1 {
888
+		cmd.Usage()
889
+		return nil
890
+	}
891
+
892
+	body, _, err := readBody(cli.call("GET", "/images/"+cmd.Arg(0)+"/history", nil, false))
893
+	if err != nil {
894
+		return err
895
+	}
896
+
897
+	outs := engine.NewTable("Created", 0)
898
+	if _, err := outs.ReadListFrom(body); err != nil {
899
+		return err
900
+	}
901
+
902
+	w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
903
+	if !*quiet {
904
+		fmt.Fprintln(w, "IMAGE\tCREATED\tCREATED BY\tSIZE")
905
+	}
906
+
907
+	for _, out := range outs.Data {
908
+		outID := out.Get("Id")
909
+		if !*quiet {
910
+			if *noTrunc {
911
+				fmt.Fprintf(w, "%s\t", outID)
912
+			} else {
913
+				fmt.Fprintf(w, "%s\t", utils.TruncateID(outID))
914
+			}
915
+
916
+			fmt.Fprintf(w, "%s ago\t", utils.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0))))
917
+
918
+			if *noTrunc {
919
+				fmt.Fprintf(w, "%s\t", out.Get("CreatedBy"))
920
+			} else {
921
+				fmt.Fprintf(w, "%s\t", utils.Trunc(out.Get("CreatedBy"), 45))
922
+			}
923
+			fmt.Fprintf(w, "%s\n", utils.HumanSize(out.GetInt64("Size")))
924
+		} else {
925
+			if *noTrunc {
926
+				fmt.Fprintln(w, outID)
927
+			} else {
928
+				fmt.Fprintln(w, utils.TruncateID(outID))
929
+			}
930
+		}
931
+	}
932
+	w.Flush()
933
+	return nil
934
+}
935
+
936
+func (cli *DockerCli) CmdRm(args ...string) error {
937
+	cmd := cli.Subcmd("rm", "[OPTIONS] CONTAINER [CONTAINER...]", "Remove one or more containers")
938
+	v := cmd.Bool([]string{"v", "-volumes"}, false, "Remove the volumes associated to the container")
939
+	link := cmd.Bool([]string{"l", "#link", "-link"}, false, "Remove the specified link and not the underlying container")
940
+	force := cmd.Bool([]string{"f", "-force"}, false, "Force removal of running container")
941
+
942
+	if err := cmd.Parse(args); err != nil {
943
+		return nil
944
+	}
945
+	if cmd.NArg() < 1 {
946
+		cmd.Usage()
947
+		return nil
948
+	}
949
+	val := url.Values{}
950
+	if *v {
951
+		val.Set("v", "1")
952
+	}
953
+	if *link {
954
+		val.Set("link", "1")
955
+	}
956
+	if *force {
957
+		val.Set("force", "1")
958
+	}
959
+
960
+	var encounteredError error
961
+	for _, name := range cmd.Args() {
962
+		_, _, err := readBody(cli.call("DELETE", "/containers/"+name+"?"+val.Encode(), nil, false))
963
+		if err != nil {
964
+			fmt.Fprintf(cli.err, "%s\n", err)
965
+			encounteredError = fmt.Errorf("Error: failed to remove one or more containers")
966
+		} else {
967
+			fmt.Fprintf(cli.out, "%s\n", name)
968
+		}
969
+	}
970
+	return encounteredError
971
+}
972
+
973
+// 'docker kill NAME' kills a running container
974
+func (cli *DockerCli) CmdKill(args ...string) error {
975
+	cmd := cli.Subcmd("kill", "[OPTIONS] CONTAINER [CONTAINER...]", "Kill a running container (send SIGKILL, or specified signal)")
976
+	signal := cmd.String([]string{"s", "-signal"}, "KILL", "Signal to send to the container")
977
+
978
+	if err := cmd.Parse(args); err != nil {
979
+		return nil
980
+	}
981
+	if cmd.NArg() < 1 {
982
+		cmd.Usage()
983
+		return nil
984
+	}
985
+
986
+	var encounteredError error
987
+	for _, name := range cmd.Args() {
988
+		if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", name, *signal), nil, false)); err != nil {
989
+			fmt.Fprintf(cli.err, "%s\n", err)
990
+			encounteredError = fmt.Errorf("Error: failed to kill one or more containers")
991
+		} else {
992
+			fmt.Fprintf(cli.out, "%s\n", name)
993
+		}
994
+	}
995
+	return encounteredError
996
+}
997
+
998
+func (cli *DockerCli) CmdImport(args ...string) error {
999
+	cmd := cli.Subcmd("import", "URL|- [REPOSITORY[:TAG]]", "Create an empty filesystem image and import the contents of the tarball (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz) into it, then optionally tag it.")
1000
+
1001
+	if err := cmd.Parse(args); err != nil {
1002
+		return nil
1003
+	}
1004
+	if cmd.NArg() < 1 {
1005
+		cmd.Usage()
1006
+		return nil
1007
+	}
1008
+
1009
+	var src, repository, tag string
1010
+
1011
+	if cmd.NArg() == 3 {
1012
+		fmt.Fprintf(cli.err, "[DEPRECATED] The format 'URL|- [REPOSITORY [TAG]]' as been deprecated. Please use URL|- [REPOSITORY[:TAG]]\n")
1013
+		src, repository, tag = cmd.Arg(0), cmd.Arg(1), cmd.Arg(2)
1014
+	} else {
1015
+		src = cmd.Arg(0)
1016
+		repository, tag = utils.ParseRepositoryTag(cmd.Arg(1))
1017
+	}
1018
+	v := url.Values{}
1019
+
1020
+	if repository != "" {
1021
+		//Check if the given image name can be resolved
1022
+		if _, _, err := registry.ResolveRepositoryName(repository); err != nil {
1023
+			return err
1024
+		}
1025
+	}
1026
+
1027
+	v.Set("repo", repository)
1028
+	v.Set("tag", tag)
1029
+	v.Set("fromSrc", src)
1030
+
1031
+	var in io.Reader
1032
+
1033
+	if src == "-" {
1034
+		in = cli.in
1035
+	}
1036
+
1037
+	return cli.stream("POST", "/images/create?"+v.Encode(), in, cli.out, nil)
1038
+}
1039
+
1040
+func (cli *DockerCli) CmdPush(args ...string) error {
1041
+	cmd := cli.Subcmd("push", "NAME", "Push an image or a repository to the registry")
1042
+	if err := cmd.Parse(args); err != nil {
1043
+		return nil
1044
+	}
1045
+	name := cmd.Arg(0)
1046
+
1047
+	if name == "" {
1048
+		cmd.Usage()
1049
+		return nil
1050
+	}
1051
+
1052
+	cli.LoadConfigFile()
1053
+
1054
+	// Resolve the Repository name from fqn to hostname + name
1055
+	hostname, _, err := registry.ResolveRepositoryName(name)
1056
+	if err != nil {
1057
+		return err
1058
+	}
1059
+	// Resolve the Auth config relevant for this server
1060
+	authConfig := cli.configFile.ResolveAuthConfig(hostname)
1061
+	// If we're not using a custom registry, we know the restrictions
1062
+	// applied to repository names and can warn the user in advance.
1063
+	// Custom repositories can have different rules, and we must also
1064
+	// allow pushing by image ID.
1065
+	if len(strings.SplitN(name, "/", 2)) == 1 {
1066
+		username := cli.configFile.Configs[registry.IndexServerAddress()].Username
1067
+		if username == "" {
1068
+			username = "<user>"
1069
+		}
1070
+		return fmt.Errorf("You cannot push a \"root\" repository. Please rename your repository in <user>/<repo> (ex: %s/%s)", username, name)
1071
+	}
1072
+
1073
+	v := url.Values{}
1074
+	push := func(authConfig registry.AuthConfig) error {
1075
+		buf, err := json.Marshal(authConfig)
1076
+		if err != nil {
1077
+			return err
1078
+		}
1079
+		registryAuthHeader := []string{
1080
+			base64.URLEncoding.EncodeToString(buf),
1081
+		}
1082
+
1083
+		return cli.stream("POST", "/images/"+name+"/push?"+v.Encode(), nil, cli.out, map[string][]string{
1084
+			"X-Registry-Auth": registryAuthHeader,
1085
+		})
1086
+	}
1087
+
1088
+	if err := push(authConfig); err != nil {
1089
+		if strings.Contains(err.Error(), "Status 401") {
1090
+			fmt.Fprintln(cli.out, "\nPlease login prior to push:")
1091
+			if err := cli.CmdLogin(hostname); err != nil {
1092
+				return err
1093
+			}
1094
+			authConfig := cli.configFile.ResolveAuthConfig(hostname)
1095
+			return push(authConfig)
1096
+		}
1097
+		return err
1098
+	}
1099
+	return nil
1100
+}
1101
+
1102
+func (cli *DockerCli) CmdPull(args ...string) error {
1103
+	cmd := cli.Subcmd("pull", "NAME[:TAG]", "Pull an image or a repository from the registry")
1104
+	tag := cmd.String([]string{"#t", "#-tag"}, "", "Download tagged image in repository")
1105
+	if err := cmd.Parse(args); err != nil {
1106
+		return nil
1107
+	}
1108
+
1109
+	if cmd.NArg() != 1 {
1110
+		cmd.Usage()
1111
+		return nil
1112
+	}
1113
+
1114
+	remote, parsedTag := utils.ParseRepositoryTag(cmd.Arg(0))
1115
+	if *tag == "" {
1116
+		*tag = parsedTag
1117
+	}
1118
+
1119
+	// Resolve the Repository name from fqn to hostname + name
1120
+	hostname, _, err := registry.ResolveRepositoryName(remote)
1121
+	if err != nil {
1122
+		return err
1123
+	}
1124
+
1125
+	cli.LoadConfigFile()
1126
+
1127
+	// Resolve the Auth config relevant for this server
1128
+	authConfig := cli.configFile.ResolveAuthConfig(hostname)
1129
+	v := url.Values{}
1130
+	v.Set("fromImage", remote)
1131
+	v.Set("tag", *tag)
1132
+
1133
+	pull := func(authConfig registry.AuthConfig) error {
1134
+		buf, err := json.Marshal(authConfig)
1135
+		if err != nil {
1136
+			return err
1137
+		}
1138
+		registryAuthHeader := []string{
1139
+			base64.URLEncoding.EncodeToString(buf),
1140
+		}
1141
+
1142
+		return cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.out, map[string][]string{
1143
+			"X-Registry-Auth": registryAuthHeader,
1144
+		})
1145
+	}
1146
+
1147
+	if err := pull(authConfig); err != nil {
1148
+		if strings.Contains(err.Error(), "Status 401") {
1149
+			fmt.Fprintln(cli.out, "\nPlease login prior to pull:")
1150
+			if err := cli.CmdLogin(hostname); err != nil {
1151
+				return err
1152
+			}
1153
+			authConfig := cli.configFile.ResolveAuthConfig(hostname)
1154
+			return pull(authConfig)
1155
+		}
1156
+		return err
1157
+	}
1158
+
1159
+	return nil
1160
+}
1161
+
1162
+func (cli *DockerCli) CmdImages(args ...string) error {
1163
+	cmd := cli.Subcmd("images", "[OPTIONS] [NAME]", "List images")
1164
+	quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs")
1165
+	all := cmd.Bool([]string{"a", "-all"}, false, "Show all images (by default filter out the intermediate images used to build)")
1166
+	noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
1167
+	flViz := cmd.Bool([]string{"v", "#viz", "-viz"}, false, "Output graph in graphviz format")
1168
+	flTree := cmd.Bool([]string{"t", "#tree", "-tree"}, false, "Output graph in tree format")
1169
+
1170
+	if err := cmd.Parse(args); err != nil {
1171
+		return nil
1172
+	}
1173
+	if cmd.NArg() > 1 {
1174
+		cmd.Usage()
1175
+		return nil
1176
+	}
1177
+
1178
+	filter := cmd.Arg(0)
1179
+
1180
+	if *flViz || *flTree {
1181
+		body, _, err := readBody(cli.call("GET", "/images/json?all=1", nil, false))
1182
+		if err != nil {
1183
+			return err
1184
+		}
1185
+
1186
+		outs := engine.NewTable("Created", 0)
1187
+		if _, err := outs.ReadListFrom(body); err != nil {
1188
+			return err
1189
+		}
1190
+
1191
+		var (
1192
+			printNode  func(cli *DockerCli, noTrunc bool, image *engine.Env, prefix string)
1193
+			startImage *engine.Env
1194
+
1195
+			roots    = engine.NewTable("Created", outs.Len())
1196
+			byParent = make(map[string]*engine.Table)
1197
+		)
1198
+
1199
+		for _, image := range outs.Data {
1200
+			if image.Get("ParentId") == "" {
1201
+				roots.Add(image)
1202
+			} else {
1203
+				if children, exists := byParent[image.Get("ParentId")]; exists {
1204
+					children.Add(image)
1205
+				} else {
1206
+					byParent[image.Get("ParentId")] = engine.NewTable("Created", 1)
1207
+					byParent[image.Get("ParentId")].Add(image)
1208
+				}
1209
+			}
1210
+
1211
+			if filter != "" {
1212
+				if filter == image.Get("Id") || filter == utils.TruncateID(image.Get("Id")) {
1213
+					startImage = image
1214
+				}
1215
+
1216
+				for _, repotag := range image.GetList("RepoTags") {
1217
+					if repotag == filter {
1218
+						startImage = image
1219
+					}
1220
+				}
1221
+			}
1222
+		}
1223
+
1224
+		if *flViz {
1225
+			fmt.Fprintf(cli.out, "digraph docker {\n")
1226
+			printNode = (*DockerCli).printVizNode
1227
+		} else {
1228
+			printNode = (*DockerCli).printTreeNode
1229
+		}
1230
+
1231
+		if startImage != nil {
1232
+			root := engine.NewTable("Created", 1)
1233
+			root.Add(startImage)
1234
+			cli.WalkTree(*noTrunc, root, byParent, "", printNode)
1235
+		} else if filter == "" {
1236
+			cli.WalkTree(*noTrunc, roots, byParent, "", printNode)
1237
+		}
1238
+		if *flViz {
1239
+			fmt.Fprintf(cli.out, " base [style=invisible]\n}\n")
1240
+		}
1241
+	} else {
1242
+		v := url.Values{}
1243
+		if cmd.NArg() == 1 {
1244
+			v.Set("filter", filter)
1245
+		}
1246
+		if *all {
1247
+			v.Set("all", "1")
1248
+		}
1249
+
1250
+		body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, false))
1251
+
1252
+		if err != nil {
1253
+			return err
1254
+		}
1255
+
1256
+		outs := engine.NewTable("Created", 0)
1257
+		if _, err := outs.ReadListFrom(body); err != nil {
1258
+			return err
1259
+		}
1260
+
1261
+		w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
1262
+		if !*quiet {
1263
+			fmt.Fprintln(w, "REPOSITORY\tTAG\tIMAGE ID\tCREATED\tVIRTUAL SIZE")
1264
+		}
1265
+
1266
+		for _, out := range outs.Data {
1267
+			for _, repotag := range out.GetList("RepoTags") {
1268
+
1269
+				repo, tag := utils.ParseRepositoryTag(repotag)
1270
+				outID := out.Get("Id")
1271
+				if !*noTrunc {
1272
+					outID = utils.TruncateID(outID)
1273
+				}
1274
+
1275
+				if !*quiet {
1276
+					fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\n", repo, tag, outID, utils.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0))), utils.HumanSize(out.GetInt64("VirtualSize")))
1277
+				} else {
1278
+					fmt.Fprintln(w, outID)
1279
+				}
1280
+			}
1281
+		}
1282
+
1283
+		if !*quiet {
1284
+			w.Flush()
1285
+		}
1286
+	}
1287
+	return nil
1288
+}
1289
+
1290
+func (cli *DockerCli) WalkTree(noTrunc bool, images *engine.Table, byParent map[string]*engine.Table, prefix string, printNode func(cli *DockerCli, noTrunc bool, image *engine.Env, prefix string)) {
1291
+	length := images.Len()
1292
+	if length > 1 {
1293
+		for index, image := range images.Data {
1294
+			if index+1 == length {
1295
+				printNode(cli, noTrunc, image, prefix+"└─")
1296
+				if subimages, exists := byParent[image.Get("Id")]; exists {
1297
+					cli.WalkTree(noTrunc, subimages, byParent, prefix+"  ", printNode)
1298
+				}
1299
+			} else {
1300
+				printNode(cli, noTrunc, image, prefix+"\u251C─")
1301
+				if subimages, exists := byParent[image.Get("Id")]; exists {
1302
+					cli.WalkTree(noTrunc, subimages, byParent, prefix+"\u2502 ", printNode)
1303
+				}
1304
+			}
1305
+		}
1306
+	} else {
1307
+		for _, image := range images.Data {
1308
+			printNode(cli, noTrunc, image, prefix+"└─")
1309
+			if subimages, exists := byParent[image.Get("Id")]; exists {
1310
+				cli.WalkTree(noTrunc, subimages, byParent, prefix+"  ", printNode)
1311
+			}
1312
+		}
1313
+	}
1314
+}
1315
+
1316
+func (cli *DockerCli) printVizNode(noTrunc bool, image *engine.Env, prefix string) {
1317
+	var (
1318
+		imageID  string
1319
+		parentID string
1320
+	)
1321
+	if noTrunc {
1322
+		imageID = image.Get("Id")
1323
+		parentID = image.Get("ParentId")
1324
+	} else {
1325
+		imageID = utils.TruncateID(image.Get("Id"))
1326
+		parentID = utils.TruncateID(image.Get("ParentId"))
1327
+	}
1328
+	if parentID == "" {
1329
+		fmt.Fprintf(cli.out, " base -> \"%s\" [style=invis]\n", imageID)
1330
+	} else {
1331
+		fmt.Fprintf(cli.out, " \"%s\" -> \"%s\"\n", parentID, imageID)
1332
+	}
1333
+	if image.GetList("RepoTags")[0] != "<none>:<none>" {
1334
+		fmt.Fprintf(cli.out, " \"%s\" [label=\"%s\\n%s\",shape=box,fillcolor=\"paleturquoise\",style=\"filled,rounded\"];\n",
1335
+			imageID, imageID, strings.Join(image.GetList("RepoTags"), "\\n"))
1336
+	}
1337
+}
1338
+
1339
+func (cli *DockerCli) printTreeNode(noTrunc bool, image *engine.Env, prefix string) {
1340
+	var imageID string
1341
+	if noTrunc {
1342
+		imageID = image.Get("Id")
1343
+	} else {
1344
+		imageID = utils.TruncateID(image.Get("Id"))
1345
+	}
1346
+
1347
+	fmt.Fprintf(cli.out, "%s%s Virtual Size: %s", prefix, imageID, utils.HumanSize(image.GetInt64("VirtualSize")))
1348
+	if image.GetList("RepoTags")[0] != "<none>:<none>" {
1349
+		fmt.Fprintf(cli.out, " Tags: %s\n", strings.Join(image.GetList("RepoTags"), ", "))
1350
+	} else {
1351
+		fmt.Fprint(cli.out, "\n")
1352
+	}
1353
+}
1354
+
1355
+func (cli *DockerCli) CmdPs(args ...string) error {
1356
+	cmd := cli.Subcmd("ps", "[OPTIONS]", "List containers")
1357
+	quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs")
1358
+	size := cmd.Bool([]string{"s", "-size"}, false, "Display sizes")
1359
+	all := cmd.Bool([]string{"a", "-all"}, false, "Show all containers. Only running containers are shown by default.")
1360
+	noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
1361
+	nLatest := cmd.Bool([]string{"l", "-latest"}, false, "Show only the latest created container, include non-running ones.")
1362
+	since := cmd.String([]string{"#sinceId", "#-since-id", "-since"}, "", "Show only containers created since Id or Name, include non-running ones.")
1363
+	before := cmd.String([]string{"#beforeId", "#-before-id", "-before"}, "", "Show only container created before Id or Name, include non-running ones.")
1364
+	last := cmd.Int([]string{"n"}, -1, "Show n last created containers, include non-running ones.")
1365
+
1366
+	if err := cmd.Parse(args); err != nil {
1367
+		return nil
1368
+	}
1369
+	v := url.Values{}
1370
+	if *last == -1 && *nLatest {
1371
+		*last = 1
1372
+	}
1373
+	if *all {
1374
+		v.Set("all", "1")
1375
+	}
1376
+	if *last != -1 {
1377
+		v.Set("limit", strconv.Itoa(*last))
1378
+	}
1379
+	if *since != "" {
1380
+		v.Set("since", *since)
1381
+	}
1382
+	if *before != "" {
1383
+		v.Set("before", *before)
1384
+	}
1385
+	if *size {
1386
+		v.Set("size", "1")
1387
+	}
1388
+
1389
+	body, _, err := readBody(cli.call("GET", "/containers/json?"+v.Encode(), nil, false))
1390
+	if err != nil {
1391
+		return err
1392
+	}
1393
+
1394
+	outs := engine.NewTable("Created", 0)
1395
+	if _, err := outs.ReadListFrom(body); err != nil {
1396
+		return err
1397
+	}
1398
+	w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
1399
+	if !*quiet {
1400
+		fmt.Fprint(w, "CONTAINER ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES")
1401
+		if *size {
1402
+			fmt.Fprintln(w, "\tSIZE")
1403
+		} else {
1404
+			fmt.Fprint(w, "\n")
1405
+		}
1406
+	}
1407
+
1408
+	for _, out := range outs.Data {
1409
+		var (
1410
+			outID    = out.Get("Id")
1411
+			outNames = out.GetList("Names")
1412
+		)
1413
+
1414
+		if !*noTrunc {
1415
+			outID = utils.TruncateID(outID)
1416
+		}
1417
+
1418
+		// Remove the leading / from the names
1419
+		for i := 0; i < len(outNames); i++ {
1420
+			outNames[i] = outNames[i][1:]
1421
+		}
1422
+
1423
+		if !*quiet {
1424
+			var (
1425
+				outCommand = out.Get("Command")
1426
+				ports      = engine.NewTable("", 0)
1427
+			)
1428
+			if !*noTrunc {
1429
+				outCommand = utils.Trunc(outCommand, 20)
1430
+			}
1431
+			ports.ReadListFrom([]byte(out.Get("Ports")))
1432
+			fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t%s\t", outID, out.Get("Image"), outCommand, utils.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0))), out.Get("Status"), api.DisplayablePorts(ports), strings.Join(outNames, ","))
1433
+			if *size {
1434
+				if out.GetInt("SizeRootFs") > 0 {
1435
+					fmt.Fprintf(w, "%s (virtual %s)\n", utils.HumanSize(out.GetInt64("SizeRw")), utils.HumanSize(out.GetInt64("SizeRootFs")))
1436
+				} else {
1437
+					fmt.Fprintf(w, "%s\n", utils.HumanSize(out.GetInt64("SizeRw")))
1438
+				}
1439
+			} else {
1440
+				fmt.Fprint(w, "\n")
1441
+			}
1442
+		} else {
1443
+			fmt.Fprintln(w, outID)
1444
+		}
1445
+	}
1446
+
1447
+	if !*quiet {
1448
+		w.Flush()
1449
+	}
1450
+	return nil
1451
+}
1452
+
1453
+func (cli *DockerCli) CmdCommit(args ...string) error {
1454
+	cmd := cli.Subcmd("commit", "[OPTIONS] CONTAINER [REPOSITORY[:TAG]]", "Create a new image from a container's changes")
1455
+	flComment := cmd.String([]string{"m", "-message"}, "", "Commit message")
1456
+	flAuthor := cmd.String([]string{"a", "#author", "-author"}, "", "Author (eg. \"John Hannibal Smith <hannibal@a-team.com>\"")
1457
+	flConfig := cmd.String([]string{"#run", "-run"}, "", "Config automatically applied when the image is run. "+`(ex: --run='{"Cmd": ["cat", "/world"], "PortSpecs": ["22"]}')`)
1458
+	if err := cmd.Parse(args); err != nil {
1459
+		return nil
1460
+	}
1461
+
1462
+	var name, repository, tag string
1463
+
1464
+	if cmd.NArg() == 3 {
1465
+		fmt.Fprintf(cli.err, "[DEPRECATED] The format 'CONTAINER [REPOSITORY [TAG]]' as been deprecated. Please use CONTAINER [REPOSITORY[:TAG]]\n")
1466
+		name, repository, tag = cmd.Arg(0), cmd.Arg(1), cmd.Arg(2)
1467
+	} else {
1468
+		name = cmd.Arg(0)
1469
+		repository, tag = utils.ParseRepositoryTag(cmd.Arg(1))
1470
+	}
1471
+
1472
+	if name == "" {
1473
+		cmd.Usage()
1474
+		return nil
1475
+	}
1476
+
1477
+	//Check if the given image name can be resolved
1478
+	if repository != "" {
1479
+		if _, _, err := registry.ResolveRepositoryName(repository); err != nil {
1480
+			return err
1481
+		}
1482
+	}
1483
+
1484
+	v := url.Values{}
1485
+	v.Set("container", name)
1486
+	v.Set("repo", repository)
1487
+	v.Set("tag", tag)
1488
+	v.Set("comment", *flComment)
1489
+	v.Set("author", *flAuthor)
1490
+	var (
1491
+		config *runconfig.Config
1492
+		env    engine.Env
1493
+	)
1494
+	if *flConfig != "" {
1495
+		config = &runconfig.Config{}
1496
+		if err := json.Unmarshal([]byte(*flConfig), config); err != nil {
1497
+			return err
1498
+		}
1499
+	}
1500
+	stream, _, err := cli.call("POST", "/commit?"+v.Encode(), config, false)
1501
+	if err != nil {
1502
+		return err
1503
+	}
1504
+	if err := env.Decode(stream); err != nil {
1505
+		return err
1506
+	}
1507
+
1508
+	fmt.Fprintf(cli.out, "%s\n", env.Get("Id"))
1509
+	return nil
1510
+}
1511
+
1512
+func (cli *DockerCli) CmdEvents(args ...string) error {
1513
+	cmd := cli.Subcmd("events", "[OPTIONS]", "Get real time events from the server")
1514
+	since := cmd.String([]string{"#since", "-since"}, "", "Show previously created events and then stream.")
1515
+	if err := cmd.Parse(args); err != nil {
1516
+		return nil
1517
+	}
1518
+
1519
+	if cmd.NArg() != 0 {
1520
+		cmd.Usage()
1521
+		return nil
1522
+	}
1523
+
1524
+	v := url.Values{}
1525
+	if *since != "" {
1526
+		loc := time.FixedZone(time.Now().Zone())
1527
+		format := "2006-01-02 15:04:05 -0700 MST"
1528
+		if len(*since) < len(format) {
1529
+			format = format[:len(*since)]
1530
+		}
1531
+
1532
+		if t, err := time.ParseInLocation(format, *since, loc); err == nil {
1533
+			v.Set("since", strconv.FormatInt(t.Unix(), 10))
1534
+		} else {
1535
+			v.Set("since", *since)
1536
+		}
1537
+	}
1538
+
1539
+	if err := cli.stream("GET", "/events?"+v.Encode(), nil, cli.out, nil); err != nil {
1540
+		return err
1541
+	}
1542
+	return nil
1543
+}
1544
+
1545
+func (cli *DockerCli) CmdExport(args ...string) error {
1546
+	cmd := cli.Subcmd("export", "CONTAINER", "Export the contents of a filesystem as a tar archive to STDOUT")
1547
+	if err := cmd.Parse(args); err != nil {
1548
+		return nil
1549
+	}
1550
+
1551
+	if cmd.NArg() != 1 {
1552
+		cmd.Usage()
1553
+		return nil
1554
+	}
1555
+
1556
+	if err := cli.stream("GET", "/containers/"+cmd.Arg(0)+"/export", nil, cli.out, nil); err != nil {
1557
+		return err
1558
+	}
1559
+	return nil
1560
+}
1561
+
1562
+func (cli *DockerCli) CmdDiff(args ...string) error {
1563
+	cmd := cli.Subcmd("diff", "CONTAINER", "Inspect changes on a container's filesystem")
1564
+	if err := cmd.Parse(args); err != nil {
1565
+		return nil
1566
+	}
1567
+	if cmd.NArg() != 1 {
1568
+		cmd.Usage()
1569
+		return nil
1570
+	}
1571
+
1572
+	body, _, err := readBody(cli.call("GET", "/containers/"+cmd.Arg(0)+"/changes", nil, false))
1573
+
1574
+	if err != nil {
1575
+		return err
1576
+	}
1577
+
1578
+	outs := engine.NewTable("", 0)
1579
+	if _, err := outs.ReadListFrom(body); err != nil {
1580
+		return err
1581
+	}
1582
+	for _, change := range outs.Data {
1583
+		var kind string
1584
+		switch change.GetInt("Kind") {
1585
+		case archive.ChangeModify:
1586
+			kind = "C"
1587
+		case archive.ChangeAdd:
1588
+			kind = "A"
1589
+		case archive.ChangeDelete:
1590
+			kind = "D"
1591
+		}
1592
+		fmt.Fprintf(cli.out, "%s %s\n", kind, change.Get("Path"))
1593
+	}
1594
+	return nil
1595
+}
1596
+
1597
+func (cli *DockerCli) CmdLogs(args ...string) error {
1598
+	cmd := cli.Subcmd("logs", "CONTAINER", "Fetch the logs of a container")
1599
+	follow := cmd.Bool([]string{"f", "-follow"}, false, "Follow log output")
1600
+	if err := cmd.Parse(args); err != nil {
1601
+		return nil
1602
+	}
1603
+	if cmd.NArg() != 1 {
1604
+		cmd.Usage()
1605
+		return nil
1606
+	}
1607
+	name := cmd.Arg(0)
1608
+	body, _, err := readBody(cli.call("GET", "/containers/"+name+"/json", nil, false))
1609
+	if err != nil {
1610
+		return err
1611
+	}
1612
+
1613
+	container := &api.Container{}
1614
+	err = json.Unmarshal(body, container)
1615
+	if err != nil {
1616
+		return err
1617
+	}
1618
+
1619
+	v := url.Values{}
1620
+	v.Set("logs", "1")
1621
+	v.Set("stdout", "1")
1622
+	v.Set("stderr", "1")
1623
+	if *follow && container.State.Running {
1624
+		v.Set("stream", "1")
1625
+	}
1626
+
1627
+	if err := cli.hijack("POST", "/containers/"+name+"/attach?"+v.Encode(), container.Config.Tty, nil, cli.out, cli.err, nil); err != nil {
1628
+		return err
1629
+	}
1630
+	return nil
1631
+}
1632
+
1633
+func (cli *DockerCli) CmdAttach(args ...string) error {
1634
+	cmd := cli.Subcmd("attach", "[OPTIONS] CONTAINER", "Attach to a running container")
1635
+	noStdin := cmd.Bool([]string{"#nostdin", "-no-stdin"}, false, "Do not attach stdin")
1636
+	proxy := cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxify all received signal to the process (even in non-tty mode)")
1637
+	if err := cmd.Parse(args); err != nil {
1638
+		return nil
1639
+	}
1640
+	if cmd.NArg() != 1 {
1641
+		cmd.Usage()
1642
+		return nil
1643
+	}
1644
+	name := cmd.Arg(0)
1645
+	body, _, err := readBody(cli.call("GET", "/containers/"+name+"/json", nil, false))
1646
+	if err != nil {
1647
+		return err
1648
+	}
1649
+
1650
+	container := &api.Container{}
1651
+	err = json.Unmarshal(body, container)
1652
+	if err != nil {
1653
+		return err
1654
+	}
1655
+
1656
+	if !container.State.Running {
1657
+		return fmt.Errorf("You cannot attach to a stopped container, start it first")
1658
+	}
1659
+
1660
+	if container.Config.Tty && cli.isTerminal {
1661
+		if err := cli.monitorTtySize(cmd.Arg(0)); err != nil {
1662
+			utils.Debugf("Error monitoring TTY size: %s", err)
1663
+		}
1664
+	}
1665
+
1666
+	var in io.ReadCloser
1667
+
1668
+	v := url.Values{}
1669
+	v.Set("stream", "1")
1670
+	if !*noStdin && container.Config.OpenStdin {
1671
+		v.Set("stdin", "1")
1672
+		in = cli.in
1673
+	}
1674
+	v.Set("stdout", "1")
1675
+	v.Set("stderr", "1")
1676
+
1677
+	if *proxy && !container.Config.Tty {
1678
+		sigc := cli.forwardAllSignals(cmd.Arg(0))
1679
+		defer signal.StopCatch(sigc)
1680
+	}
1681
+
1682
+	if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), container.Config.Tty, in, cli.out, cli.err, nil); err != nil {
1683
+		return err
1684
+	}
1685
+
1686
+	_, status, err := getExitCode(cli, cmd.Arg(0))
1687
+	if err != nil {
1688
+		return err
1689
+	}
1690
+	if status != 0 {
1691
+		return &utils.StatusError{StatusCode: status}
1692
+	}
1693
+
1694
+	return nil
1695
+}
1696
+
1697
+func (cli *DockerCli) CmdSearch(args ...string) error {
1698
+	cmd := cli.Subcmd("search", "TERM", "Search the docker index for images")
1699
+	noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
1700
+	trusted := cmd.Bool([]string{"t", "#trusted", "-trusted"}, false, "Only show trusted builds")
1701
+	stars := cmd.Int([]string{"s", "#stars", "-stars"}, 0, "Only displays with at least xxx stars")
1702
+	if err := cmd.Parse(args); err != nil {
1703
+		return nil
1704
+	}
1705
+	if cmd.NArg() != 1 {
1706
+		cmd.Usage()
1707
+		return nil
1708
+	}
1709
+
1710
+	v := url.Values{}
1711
+	v.Set("term", cmd.Arg(0))
1712
+
1713
+	body, _, err := readBody(cli.call("GET", "/images/search?"+v.Encode(), nil, true))
1714
+
1715
+	if err != nil {
1716
+		return err
1717
+	}
1718
+	outs := engine.NewTable("star_count", 0)
1719
+	if _, err := outs.ReadListFrom(body); err != nil {
1720
+		return err
1721
+	}
1722
+	w := tabwriter.NewWriter(cli.out, 10, 1, 3, ' ', 0)
1723
+	fmt.Fprintf(w, "NAME\tDESCRIPTION\tSTARS\tOFFICIAL\tTRUSTED\n")
1724
+	for _, out := range outs.Data {
1725
+		if (*trusted && !out.GetBool("is_trusted")) || (*stars > out.GetInt("star_count")) {
1726
+			continue
1727
+		}
1728
+		desc := strings.Replace(out.Get("description"), "\n", " ", -1)
1729
+		desc = strings.Replace(desc, "\r", " ", -1)
1730
+		if !*noTrunc && len(desc) > 45 {
1731
+			desc = utils.Trunc(desc, 42) + "..."
1732
+		}
1733
+		fmt.Fprintf(w, "%s\t%s\t%d\t", out.Get("name"), desc, out.GetInt("star_count"))
1734
+		if out.GetBool("is_official") {
1735
+			fmt.Fprint(w, "[OK]")
1736
+
1737
+		}
1738
+		fmt.Fprint(w, "\t")
1739
+		if out.GetBool("is_trusted") {
1740
+			fmt.Fprint(w, "[OK]")
1741
+		}
1742
+		fmt.Fprint(w, "\n")
1743
+	}
1744
+	w.Flush()
1745
+	return nil
1746
+}
1747
+
1748
+// Ports type - Used to parse multiple -p flags
1749
+type ports []int
1750
+
1751
+func (cli *DockerCli) CmdTag(args ...string) error {
1752
+	cmd := cli.Subcmd("tag", "[OPTIONS] IMAGE [REGISTRYHOST/][USERNAME/]NAME[:TAG]", "Tag an image into a repository")
1753
+	force := cmd.Bool([]string{"f", "#force", "-force"}, false, "Force")
1754
+	if err := cmd.Parse(args); err != nil {
1755
+		return nil
1756
+	}
1757
+	if cmd.NArg() != 2 && cmd.NArg() != 3 {
1758
+		cmd.Usage()
1759
+		return nil
1760
+	}
1761
+
1762
+	var repository, tag string
1763
+
1764
+	if cmd.NArg() == 3 {
1765
+		fmt.Fprintf(cli.err, "[DEPRECATED] The format 'IMAGE [REPOSITORY [TAG]]' as been deprecated. Please use IMAGE [REGISTRYHOST/][USERNAME/]NAME[:TAG]]\n")
1766
+		repository, tag = cmd.Arg(1), cmd.Arg(2)
1767
+	} else {
1768
+		repository, tag = utils.ParseRepositoryTag(cmd.Arg(1))
1769
+	}
1770
+
1771
+	v := url.Values{}
1772
+
1773
+	//Check if the given image name can be resolved
1774
+	if _, _, err := registry.ResolveRepositoryName(repository); err != nil {
1775
+		return err
1776
+	}
1777
+	v.Set("repo", repository)
1778
+	v.Set("tag", tag)
1779
+
1780
+	if *force {
1781
+		v.Set("force", "1")
1782
+	}
1783
+
1784
+	if _, _, err := readBody(cli.call("POST", "/images/"+cmd.Arg(0)+"/tag?"+v.Encode(), nil, false)); err != nil {
1785
+		return err
1786
+	}
1787
+	return nil
1788
+}
1789
+
1790
+func (cli *DockerCli) CmdRun(args ...string) error {
1791
+	// FIXME: just use runconfig.Parse already
1792
+	config, hostConfig, cmd, err := runconfig.ParseSubcommand(cli.Subcmd("run", "[OPTIONS] IMAGE [COMMAND] [ARG...]", "Run a command in a new container"), args, nil)
1793
+	if err != nil {
1794
+		return err
1795
+	}
1796
+	if config.Image == "" {
1797
+		cmd.Usage()
1798
+		return nil
1799
+	}
1800
+
1801
+	// Retrieve relevant client-side config
1802
+	var (
1803
+		flName        = cmd.Lookup("name")
1804
+		flRm          = cmd.Lookup("rm")
1805
+		flSigProxy    = cmd.Lookup("sig-proxy")
1806
+		autoRemove, _ = strconv.ParseBool(flRm.Value.String())
1807
+		sigProxy, _   = strconv.ParseBool(flSigProxy.Value.String())
1808
+	)
1809
+
1810
+	// Disable sigProxy in case on TTY
1811
+	if config.Tty {
1812
+		sigProxy = false
1813
+	}
1814
+
1815
+	var containerIDFile io.WriteCloser
1816
+	if len(hostConfig.ContainerIDFile) > 0 {
1817
+		if _, err := os.Stat(hostConfig.ContainerIDFile); err == nil {
1818
+			return fmt.Errorf("Container ID file found, make sure the other container isn't running or delete %s", hostConfig.ContainerIDFile)
1819
+		}
1820
+		if containerIDFile, err = os.Create(hostConfig.ContainerIDFile); err != nil {
1821
+			return fmt.Errorf("Failed to create the container ID file: %s", err)
1822
+		}
1823
+		defer func() {
1824
+			containerIDFile.Close()
1825
+			var (
1826
+				cidFileInfo os.FileInfo
1827
+				err         error
1828
+			)
1829
+			if cidFileInfo, err = os.Stat(hostConfig.ContainerIDFile); err != nil {
1830
+				return
1831
+			}
1832
+			if cidFileInfo.Size() == 0 {
1833
+				if err := os.Remove(hostConfig.ContainerIDFile); err != nil {
1834
+					fmt.Printf("failed to remove CID file '%s': %s \n", hostConfig.ContainerIDFile, err)
1835
+				}
1836
+			}
1837
+		}()
1838
+	}
1839
+
1840
+	containerValues := url.Values{}
1841
+	if name := flName.Value.String(); name != "" {
1842
+		containerValues.Set("name", name)
1843
+	}
1844
+
1845
+	//create the container
1846
+	stream, statusCode, err := cli.call("POST", "/containers/create?"+containerValues.Encode(), config, false)
1847
+	//if image not found try to pull it
1848
+	if statusCode == 404 {
1849
+		fmt.Fprintf(cli.err, "Unable to find image '%s' locally\n", config.Image)
1850
+
1851
+		v := url.Values{}
1852
+		repos, tag := utils.ParseRepositoryTag(config.Image)
1853
+		v.Set("fromImage", repos)
1854
+		v.Set("tag", tag)
1855
+
1856
+		// Resolve the Repository name from fqn to hostname + name
1857
+		hostname, _, err := registry.ResolveRepositoryName(repos)
1858
+		if err != nil {
1859
+			return err
1860
+		}
1861
+
1862
+		// Load the auth config file, to be able to pull the image
1863
+		cli.LoadConfigFile()
1864
+
1865
+		// Resolve the Auth config relevant for this server
1866
+		authConfig := cli.configFile.ResolveAuthConfig(hostname)
1867
+		buf, err := json.Marshal(authConfig)
1868
+		if err != nil {
1869
+			return err
1870
+		}
1871
+
1872
+		registryAuthHeader := []string{
1873
+			base64.URLEncoding.EncodeToString(buf),
1874
+		}
1875
+		if err = cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.err, map[string][]string{"X-Registry-Auth": registryAuthHeader}); err != nil {
1876
+			return err
1877
+		}
1878
+		if stream, _, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), config, false); err != nil {
1879
+			return err
1880
+		}
1881
+	} else if err != nil {
1882
+		return err
1883
+	}
1884
+
1885
+	var runResult engine.Env
1886
+	if err := runResult.Decode(stream); err != nil {
1887
+		return err
1888
+	}
1889
+
1890
+	for _, warning := range runResult.GetList("Warnings") {
1891
+		fmt.Fprintf(cli.err, "WARNING: %s\n", warning)
1892
+	}
1893
+
1894
+	if len(hostConfig.ContainerIDFile) > 0 {
1895
+		if _, err = containerIDFile.Write([]byte(runResult.Get("Id"))); err != nil {
1896
+			return fmt.Errorf("Failed to write the container ID to the file: %s", err)
1897
+		}
1898
+	}
1899
+
1900
+	if sigProxy {
1901
+		sigc := cli.forwardAllSignals(runResult.Get("Id"))
1902
+		defer signal.StopCatch(sigc)
1903
+	}
1904
+
1905
+	var (
1906
+		waitDisplayId chan struct{}
1907
+		errCh         chan error
1908
+	)
1909
+
1910
+	if !config.AttachStdout && !config.AttachStderr {
1911
+		// Make this asynchrone in order to let the client write to stdin before having to read the ID
1912
+		waitDisplayId = make(chan struct{})
1913
+		go func() {
1914
+			defer close(waitDisplayId)
1915
+			fmt.Fprintf(cli.out, "%s\n", runResult.Get("Id"))
1916
+		}()
1917
+	}
1918
+
1919
+	// We need to instanciate the chan because the select needs it. It can
1920
+	// be closed but can't be uninitialized.
1921
+	hijacked := make(chan io.Closer)
1922
+
1923
+	// Block the return until the chan gets closed
1924
+	defer func() {
1925
+		utils.Debugf("End of CmdRun(), Waiting for hijack to finish.")
1926
+		if _, ok := <-hijacked; ok {
1927
+			utils.Errorf("Hijack did not finish (chan still open)")
1928
+		}
1929
+	}()
1930
+
1931
+	if config.AttachStdin || config.AttachStdout || config.AttachStderr {
1932
+		var (
1933
+			out, stderr io.Writer
1934
+			in          io.ReadCloser
1935
+			v           = url.Values{}
1936
+		)
1937
+		v.Set("stream", "1")
1938
+
1939
+		if config.AttachStdin {
1940
+			v.Set("stdin", "1")
1941
+			in = cli.in
1942
+		}
1943
+		if config.AttachStdout {
1944
+			v.Set("stdout", "1")
1945
+			out = cli.out
1946
+		}
1947
+		if config.AttachStderr {
1948
+			v.Set("stderr", "1")
1949
+			if config.Tty {
1950
+				stderr = cli.out
1951
+			} else {
1952
+				stderr = cli.err
1953
+			}
1954
+		}
1955
+
1956
+		errCh = utils.Go(func() error {
1957
+			return cli.hijack("POST", "/containers/"+runResult.Get("Id")+"/attach?"+v.Encode(), config.Tty, in, out, stderr, hijacked)
1958
+		})
1959
+	} else {
1960
+		close(hijacked)
1961
+	}
1962
+
1963
+	// Acknowledge the hijack before starting
1964
+	select {
1965
+	case closer := <-hijacked:
1966
+		// Make sure that hijack gets closed when returning. (result
1967
+		// in closing hijack chan and freeing server's goroutines.
1968
+		if closer != nil {
1969
+			defer closer.Close()
1970
+		}
1971
+	case err := <-errCh:
1972
+		if err != nil {
1973
+			utils.Debugf("Error hijack: %s", err)
1974
+			return err
1975
+		}
1976
+	}
1977
+
1978
+	//start the container
1979
+	if _, _, err = readBody(cli.call("POST", "/containers/"+runResult.Get("Id")+"/start", hostConfig, false)); err != nil {
1980
+		return err
1981
+	}
1982
+
1983
+	if (config.AttachStdin || config.AttachStdout || config.AttachStderr) && config.Tty && cli.isTerminal {
1984
+		if err := cli.monitorTtySize(runResult.Get("Id")); err != nil {
1985
+			utils.Errorf("Error monitoring TTY size: %s\n", err)
1986
+		}
1987
+	}
1988
+
1989
+	if errCh != nil {
1990
+		if err := <-errCh; err != nil {
1991
+			utils.Debugf("Error hijack: %s", err)
1992
+			return err
1993
+		}
1994
+	}
1995
+
1996
+	// Detached mode: wait for the id to be displayed and return.
1997
+	if !config.AttachStdout && !config.AttachStderr {
1998
+		// Detached mode
1999
+		<-waitDisplayId
2000
+		return nil
2001
+	}
2002
+
2003
+	var status int
2004
+
2005
+	// Attached mode
2006
+	if autoRemove {
2007
+		// Autoremove: wait for the container to finish, retrieve
2008
+		// the exit code and remove the container
2009
+		if _, _, err := readBody(cli.call("POST", "/containers/"+runResult.Get("Id")+"/wait", nil, false)); err != nil {
2010
+			return err
2011
+		}
2012
+		if _, status, err = getExitCode(cli, runResult.Get("Id")); err != nil {
2013
+			return err
2014
+		}
2015
+		if _, _, err := readBody(cli.call("DELETE", "/containers/"+runResult.Get("Id")+"?v=1", nil, false)); err != nil {
2016
+			return err
2017
+		}
2018
+	} else {
2019
+		if !config.Tty {
2020
+			// In non-tty mode, we can't dettach, so we know we need to wait.
2021
+			if status, err = waitForExit(cli, runResult.Get("Id")); err != nil {
2022
+				return err
2023
+			}
2024
+		} else {
2025
+			// In TTY mode, there is a race. If the process dies too slowly, the state can be update after the getExitCode call
2026
+			// and result in a wrong exit code.
2027
+			// No Autoremove: Simply retrieve the exit code
2028
+			if _, status, err = getExitCode(cli, runResult.Get("Id")); err != nil {
2029
+				return err
2030
+			}
2031
+		}
2032
+	}
2033
+	if status != 0 {
2034
+		return &utils.StatusError{StatusCode: status}
2035
+	}
2036
+	return nil
2037
+}
2038
+
2039
+func (cli *DockerCli) CmdCp(args ...string) error {
2040
+	cmd := cli.Subcmd("cp", "CONTAINER:PATH HOSTPATH", "Copy files/folders from the PATH to the HOSTPATH")
2041
+	if err := cmd.Parse(args); err != nil {
2042
+		return nil
2043
+	}
2044
+
2045
+	if cmd.NArg() != 2 {
2046
+		cmd.Usage()
2047
+		return nil
2048
+	}
2049
+
2050
+	var copyData engine.Env
2051
+	info := strings.Split(cmd.Arg(0), ":")
2052
+
2053
+	if len(info) != 2 {
2054
+		return fmt.Errorf("Error: Path not specified")
2055
+	}
2056
+
2057
+	copyData.Set("Resource", info[1])
2058
+	copyData.Set("HostPath", cmd.Arg(1))
2059
+
2060
+	stream, statusCode, err := cli.call("POST", "/containers/"+info[0]+"/copy", copyData, false)
2061
+	if stream != nil {
2062
+		defer stream.Close()
2063
+	}
2064
+	if statusCode == 404 {
2065
+		return fmt.Errorf("No such container: %v", info[0])
2066
+	}
2067
+	if err != nil {
2068
+		return err
2069
+	}
2070
+
2071
+	if statusCode == 200 {
2072
+		if err := archive.Untar(stream, copyData.Get("HostPath"), nil); err != nil {
2073
+			return err
2074
+		}
2075
+	}
2076
+	return nil
2077
+}
2078
+
2079
+func (cli *DockerCli) CmdSave(args ...string) error {
2080
+	cmd := cli.Subcmd("save", "IMAGE", "Save an image to a tar archive (streamed to stdout by default)")
2081
+	outfile := cmd.String([]string{"o", "-output"}, "", "Write to an file, instead of STDOUT")
2082
+
2083
+	if err := cmd.Parse(args); err != nil {
2084
+		return err
2085
+	}
2086
+
2087
+	if cmd.NArg() != 1 {
2088
+		cmd.Usage()
2089
+		return nil
2090
+	}
2091
+
2092
+	var (
2093
+		output io.Writer = cli.out
2094
+		err    error
2095
+	)
2096
+	if *outfile != "" {
2097
+		output, err = os.Create(*outfile)
2098
+		if err != nil {
2099
+			return err
2100
+		}
2101
+	}
2102
+	image := cmd.Arg(0)
2103
+	if err := cli.stream("GET", "/images/"+image+"/get", nil, output, nil); err != nil {
2104
+		return err
2105
+	}
2106
+	return nil
2107
+}
2108
+
2109
+func (cli *DockerCli) CmdLoad(args ...string) error {
2110
+	cmd := cli.Subcmd("load", "", "Load an image from a tar archive on STDIN")
2111
+	infile := cmd.String([]string{"i", "-input"}, "", "Read from a tar archive file, instead of STDIN")
2112
+
2113
+	if err := cmd.Parse(args); err != nil {
2114
+		return err
2115
+	}
2116
+
2117
+	if cmd.NArg() != 0 {
2118
+		cmd.Usage()
2119
+		return nil
2120
+	}
2121
+
2122
+	var (
2123
+		input io.Reader = cli.in
2124
+		err   error
2125
+	)
2126
+	if *infile != "" {
2127
+		input, err = os.Open(*infile)
2128
+		if err != nil {
2129
+			return err
2130
+		}
2131
+	}
2132
+	if err := cli.stream("POST", "/images/load", input, cli.out, nil); err != nil {
2133
+		return err
2134
+	}
2135
+	return nil
2136
+}
2137
+
2138
+func (cli *DockerCli) dial() (net.Conn, error) {
2139
+	if cli.tlsConfig != nil && cli.proto != "unix" {
2140
+		return tls.Dial(cli.proto, cli.addr, cli.tlsConfig)
2141
+	}
2142
+	return net.Dial(cli.proto, cli.addr)
2143
+}
2144
+
2145
+func (cli *DockerCli) call(method, path string, data interface{}, passAuthInfo bool) (io.ReadCloser, int, error) {
2146
+	params := bytes.NewBuffer(nil)
2147
+	if data != nil {
2148
+		if env, ok := data.(engine.Env); ok {
2149
+			if err := env.Encode(params); err != nil {
2150
+				return nil, -1, err
2151
+			}
2152
+		} else {
2153
+			buf, err := json.Marshal(data)
2154
+			if err != nil {
2155
+				return nil, -1, err
2156
+			}
2157
+			if _, err := params.Write(buf); err != nil {
2158
+				return nil, -1, err
2159
+			}
2160
+		}
2161
+	}
2162
+	// fixme: refactor client to support redirect
2163
+	re := regexp.MustCompile("/+")
2164
+	path = re.ReplaceAllString(path, "/")
2165
+
2166
+	req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), params)
2167
+	if err != nil {
2168
+		return nil, -1, err
2169
+	}
2170
+	if passAuthInfo {
2171
+		cli.LoadConfigFile()
2172
+		// Resolve the Auth config relevant for this server
2173
+		authConfig := cli.configFile.ResolveAuthConfig(registry.IndexServerAddress())
2174
+		getHeaders := func(authConfig registry.AuthConfig) (map[string][]string, error) {
2175
+			buf, err := json.Marshal(authConfig)
2176
+			if err != nil {
2177
+				return nil, err
2178
+			}
2179
+			registryAuthHeader := []string{
2180
+				base64.URLEncoding.EncodeToString(buf),
2181
+			}
2182
+			return map[string][]string{"X-Registry-Auth": registryAuthHeader}, nil
2183
+		}
2184
+		if headers, err := getHeaders(authConfig); err == nil && headers != nil {
2185
+			for k, v := range headers {
2186
+				req.Header[k] = v
2187
+			}
2188
+		}
2189
+	}
2190
+	req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION)
2191
+	req.Host = cli.addr
2192
+	if data != nil {
2193
+		req.Header.Set("Content-Type", "application/json")
2194
+	} else if method == "POST" {
2195
+		req.Header.Set("Content-Type", "plain/text")
2196
+	}
2197
+	dial, err := cli.dial()
2198
+	if err != nil {
2199
+		if strings.Contains(err.Error(), "connection refused") {
2200
+			return nil, -1, ErrConnectionRefused
2201
+		}
2202
+		return nil, -1, err
2203
+	}
2204
+	clientconn := httputil.NewClientConn(dial, nil)
2205
+	resp, err := clientconn.Do(req)
2206
+	if err != nil {
2207
+		clientconn.Close()
2208
+		if strings.Contains(err.Error(), "connection refused") {
2209
+			return nil, -1, ErrConnectionRefused
2210
+		}
2211
+		return nil, -1, err
2212
+	}
2213
+
2214
+	if resp.StatusCode < 200 || resp.StatusCode >= 400 {
2215
+		body, err := ioutil.ReadAll(resp.Body)
2216
+		if err != nil {
2217
+			return nil, -1, err
2218
+		}
2219
+		if len(body) == 0 {
2220
+			return nil, resp.StatusCode, fmt.Errorf("Error: request returned %s for API route and version %s, check if the server supports the requested API version", http.StatusText(resp.StatusCode), req.URL)
2221
+		}
2222
+		return nil, resp.StatusCode, fmt.Errorf("Error: %s", bytes.TrimSpace(body))
2223
+	}
2224
+
2225
+	wrapper := utils.NewReadCloserWrapper(resp.Body, func() error {
2226
+		if resp != nil && resp.Body != nil {
2227
+			resp.Body.Close()
2228
+		}
2229
+		return clientconn.Close()
2230
+	})
2231
+	return wrapper, resp.StatusCode, nil
2232
+}
2233
+
2234
+func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, headers map[string][]string) error {
2235
+	if (method == "POST" || method == "PUT") && in == nil {
2236
+		in = bytes.NewReader([]byte{})
2237
+	}
2238
+
2239
+	// fixme: refactor client to support redirect
2240
+	re := regexp.MustCompile("/+")
2241
+	path = re.ReplaceAllString(path, "/")
2242
+
2243
+	req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), in)
2244
+	if err != nil {
2245
+		return err
2246
+	}
2247
+	req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION)
2248
+	req.Host = cli.addr
2249
+	if method == "POST" {
2250
+		req.Header.Set("Content-Type", "plain/text")
2251
+	}
2252
+
2253
+	if headers != nil {
2254
+		for k, v := range headers {
2255
+			req.Header[k] = v
2256
+		}
2257
+	}
2258
+
2259
+	dial, err := cli.dial()
2260
+	if err != nil {
2261
+		if strings.Contains(err.Error(), "connection refused") {
2262
+			return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
2263
+		}
2264
+		return err
2265
+	}
2266
+	clientconn := httputil.NewClientConn(dial, nil)
2267
+	resp, err := clientconn.Do(req)
2268
+	defer clientconn.Close()
2269
+	if err != nil {
2270
+		if strings.Contains(err.Error(), "connection refused") {
2271
+			return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
2272
+		}
2273
+		return err
2274
+	}
2275
+	defer resp.Body.Close()
2276
+
2277
+	if resp.StatusCode < 200 || resp.StatusCode >= 400 {
2278
+		body, err := ioutil.ReadAll(resp.Body)
2279
+		if err != nil {
2280
+			return err
2281
+		}
2282
+		if len(body) == 0 {
2283
+			return fmt.Errorf("Error :%s", http.StatusText(resp.StatusCode))
2284
+		}
2285
+		return fmt.Errorf("Error: %s", bytes.TrimSpace(body))
2286
+	}
2287
+
2288
+	if api.MatchesContentType(resp.Header.Get("Content-Type"), "application/json") {
2289
+		return utils.DisplayJSONMessagesStream(resp.Body, out, cli.terminalFd, cli.isTerminal)
2290
+	}
2291
+	if _, err := io.Copy(out, resp.Body); err != nil {
2292
+		return err
2293
+	}
2294
+	return nil
2295
+}
2296
+
2297
+func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.ReadCloser, stdout, stderr io.Writer, started chan io.Closer) error {
2298
+	defer func() {
2299
+		if started != nil {
2300
+			close(started)
2301
+		}
2302
+	}()
2303
+	// fixme: refactor client to support redirect
2304
+	re := regexp.MustCompile("/+")
2305
+	path = re.ReplaceAllString(path, "/")
2306
+
2307
+	req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), nil)
2308
+	if err != nil {
2309
+		return err
2310
+	}
2311
+	req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION)
2312
+	req.Header.Set("Content-Type", "plain/text")
2313
+	req.Host = cli.addr
2314
+
2315
+	dial, err := cli.dial()
2316
+	if err != nil {
2317
+		if strings.Contains(err.Error(), "connection refused") {
2318
+			return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
2319
+		}
2320
+		return err
2321
+	}
2322
+	clientconn := httputil.NewClientConn(dial, nil)
2323
+	defer clientconn.Close()
2324
+
2325
+	// Server hijacks the connection, error 'connection closed' expected
2326
+	clientconn.Do(req)
2327
+
2328
+	rwc, br := clientconn.Hijack()
2329
+	defer rwc.Close()
2330
+
2331
+	if started != nil {
2332
+		started <- rwc
2333
+	}
2334
+
2335
+	var receiveStdout chan error
2336
+
2337
+	var oldState *term.State
2338
+
2339
+	if in != nil && setRawTerminal && cli.isTerminal && os.Getenv("NORAW") == "" {
2340
+		oldState, err = term.SetRawTerminal(cli.terminalFd)
2341
+		if err != nil {
2342
+			return err
2343
+		}
2344
+		defer term.RestoreTerminal(cli.terminalFd, oldState)
2345
+	}
2346
+
2347
+	if stdout != nil || stderr != nil {
2348
+		receiveStdout = utils.Go(func() (err error) {
2349
+			defer func() {
2350
+				if in != nil {
2351
+					if setRawTerminal && cli.isTerminal {
2352
+						term.RestoreTerminal(cli.terminalFd, oldState)
2353
+					}
2354
+					// For some reason this Close call blocks on darwin..
2355
+					// As the client exists right after, simply discard the close
2356
+					// until we find a better solution.
2357
+					if goruntime.GOOS != "darwin" {
2358
+						in.Close()
2359
+					}
2360
+				}
2361
+			}()
2362
+
2363
+			// When TTY is ON, use regular copy
2364
+			if setRawTerminal {
2365
+				_, err = io.Copy(stdout, br)
2366
+			} else {
2367
+				_, err = utils.StdCopy(stdout, stderr, br)
2368
+			}
2369
+			utils.Debugf("[hijack] End of stdout")
2370
+			return err
2371
+		})
2372
+	}
2373
+
2374
+	sendStdin := utils.Go(func() error {
2375
+		if in != nil {
2376
+			io.Copy(rwc, in)
2377
+			utils.Debugf("[hijack] End of stdin")
2378
+		}
2379
+		if tcpc, ok := rwc.(*net.TCPConn); ok {
2380
+			if err := tcpc.CloseWrite(); err != nil {
2381
+				utils.Debugf("Couldn't send EOF: %s\n", err)
2382
+			}
2383
+		} else if unixc, ok := rwc.(*net.UnixConn); ok {
2384
+			if err := unixc.CloseWrite(); err != nil {
2385
+				utils.Debugf("Couldn't send EOF: %s\n", err)
2386
+			}
2387
+		}
2388
+		// Discard errors due to pipe interruption
2389
+		return nil
2390
+	})
2391
+
2392
+	if stdout != nil || stderr != nil {
2393
+		if err := <-receiveStdout; err != nil {
2394
+			utils.Debugf("Error receiveStdout: %s", err)
2395
+			return err
2396
+		}
2397
+	}
2398
+
2399
+	if !cli.isTerminal {
2400
+		if err := <-sendStdin; err != nil {
2401
+			utils.Debugf("Error sendStdin: %s", err)
2402
+			return err
2403
+		}
2404
+	}
2405
+	return nil
2406
+
2407
+}
2408
+
2409
+func (cli *DockerCli) getTtySize() (int, int) {
2410
+	if !cli.isTerminal {
2411
+		return 0, 0
2412
+	}
2413
+	ws, err := term.GetWinsize(cli.terminalFd)
2414
+	if err != nil {
2415
+		utils.Debugf("Error getting size: %s", err)
2416
+		if ws == nil {
2417
+			return 0, 0
2418
+		}
2419
+	}
2420
+	return int(ws.Height), int(ws.Width)
2421
+}
2422
+
2423
+func (cli *DockerCli) resizeTty(id string) {
2424
+	height, width := cli.getTtySize()
2425
+	if height == 0 && width == 0 {
2426
+		return
2427
+	}
2428
+	v := url.Values{}
2429
+	v.Set("h", strconv.Itoa(height))
2430
+	v.Set("w", strconv.Itoa(width))
2431
+	if _, _, err := readBody(cli.call("POST", "/containers/"+id+"/resize?"+v.Encode(), nil, false)); err != nil {
2432
+		utils.Debugf("Error resize: %s", err)
2433
+	}
2434
+}
2435
+
2436
+func (cli *DockerCli) monitorTtySize(id string) error {
2437
+	cli.resizeTty(id)
2438
+
2439
+	sigchan := make(chan os.Signal, 1)
2440
+	gosignal.Notify(sigchan, syscall.SIGWINCH)
2441
+	go func() {
2442
+		for _ = range sigchan {
2443
+			cli.resizeTty(id)
2444
+		}
2445
+	}()
2446
+	return nil
2447
+}
2448
+
2449
+func (cli *DockerCli) Subcmd(name, signature, description string) *flag.FlagSet {
2450
+	flags := flag.NewFlagSet(name, flag.ContinueOnError)
2451
+	flags.Usage = func() {
2452
+		fmt.Fprintf(cli.err, "\nUsage: docker %s %s\n\n%s\n\n", name, signature, description)
2453
+		flags.PrintDefaults()
2454
+		os.Exit(2)
2455
+	}
2456
+	return flags
2457
+}
2458
+
2459
+func (cli *DockerCli) LoadConfigFile() (err error) {
2460
+	cli.configFile, err = registry.LoadConfig(os.Getenv("HOME"))
2461
+	if err != nil {
2462
+		fmt.Fprintf(cli.err, "WARNING: %s\n", err)
2463
+	}
2464
+	return err
2465
+}
2466
+
2467
+func waitForExit(cli *DockerCli, containerId string) (int, error) {
2468
+	stream, _, err := cli.call("POST", "/containers/"+containerId+"/wait", nil, false)
2469
+	if err != nil {
2470
+		return -1, err
2471
+	}
2472
+
2473
+	var out engine.Env
2474
+	if err := out.Decode(stream); err != nil {
2475
+		return -1, err
2476
+	}
2477
+	return out.GetInt("StatusCode"), nil
2478
+}
2479
+
2480
+// getExitCode perform an inspect on the container. It returns
2481
+// the running state and the exit code.
2482
+func getExitCode(cli *DockerCli, containerId string) (bool, int, error) {
2483
+	body, _, err := readBody(cli.call("GET", "/containers/"+containerId+"/json", nil, false))
2484
+	if err != nil {
2485
+		// If we can't connect, then the daemon probably died.
2486
+		if err != ErrConnectionRefused {
2487
+			return false, -1, err
2488
+		}
2489
+		return false, -1, nil
2490
+	}
2491
+	c := &api.Container{}
2492
+	if err := json.Unmarshal(body, c); err != nil {
2493
+		return false, -1, err
2494
+	}
2495
+	return c.State.Running, c.State.ExitCode, nil
2496
+}
2497
+
2498
+func readBody(stream io.ReadCloser, statusCode int, err error) ([]byte, int, error) {
2499
+	if stream != nil {
2500
+		defer stream.Close()
2501
+	}
2502
+	if err != nil {
2503
+		return nil, statusCode, err
2504
+	}
2505
+	body, err := ioutil.ReadAll(stream)
2506
+	if err != nil {
2507
+		return nil, -1, err
2508
+	}
2509
+	return body, statusCode, nil
2510
+}
2511
+
2512
+func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string, tlsConfig *tls.Config) *DockerCli {
2513
+	var (
2514
+		isTerminal = false
2515
+		terminalFd uintptr
2516
+	)
2517
+
2518
+	if in != nil {
2519
+		if file, ok := in.(*os.File); ok {
2520
+			terminalFd = file.Fd()
2521
+			isTerminal = term.IsTerminal(terminalFd)
2522
+		}
2523
+	}
2524
+
2525
+	if err == nil {
2526
+		err = out
2527
+	}
2528
+	return &DockerCli{
2529
+		proto:      proto,
2530
+		addr:       addr,
2531
+		in:         in,
2532
+		out:        out,
2533
+		err:        err,
2534
+		isTerminal: isTerminal,
2535
+		terminalFd: terminalFd,
2536
+		tlsConfig:  tlsConfig,
2537
+	}
2538
+}
2539
+
2540
+type DockerCli struct {
2541
+	proto      string
2542
+	addr       string
2543
+	configFile *registry.ConfigFile
2544
+	in         io.ReadCloser
2545
+	out        io.Writer
2546
+	err        io.Writer
2547
+	isTerminal bool
2548
+	terminalFd uintptr
2549
+	tlsConfig  *tls.Config
2550
+}
... ...
@@ -23,7 +23,7 @@ func ValidateHost(val string) (string, error) {
23 23
 }
24 24
 
25 25
 //TODO remove, used on < 1.5 in getContainersJSON
26
-func displayablePorts(ports *engine.Table) string {
26
+func DisplayablePorts(ports *engine.Table) string {
27 27
 	result := []string{}
28 28
 	ports.SetKey("PublicPort")
29 29
 	ports.Sort()
30 30
deleted file mode 100644
... ...
@@ -1,1255 +0,0 @@
1
-package api
2
-
3
-import (
4
-	"bufio"
5
-	"bytes"
6
-	"code.google.com/p/go.net/websocket"
7
-	"crypto/tls"
8
-	"crypto/x509"
9
-	"encoding/base64"
10
-	"encoding/json"
11
-	"expvar"
12
-	"fmt"
13
-	"github.com/dotcloud/docker/engine"
14
-	"github.com/dotcloud/docker/pkg/listenbuffer"
15
-	"github.com/dotcloud/docker/pkg/systemd"
16
-	"github.com/dotcloud/docker/pkg/user"
17
-	"github.com/dotcloud/docker/pkg/version"
18
-	"github.com/dotcloud/docker/registry"
19
-	"github.com/dotcloud/docker/utils"
20
-	"github.com/gorilla/mux"
21
-	"io"
22
-	"io/ioutil"
23
-	"log"
24
-	"net"
25
-	"net/http"
26
-	"net/http/pprof"
27
-	"os"
28
-	"strconv"
29
-	"strings"
30
-	"syscall"
31
-)
32
-
33
-var (
34
-	activationLock chan struct{}
35
-)
36
-
37
-type HttpApiFunc func(eng *engine.Engine, version version.Version, 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 streamJSON(job *engine.Job, w http.ResponseWriter, flush bool) {
99
-	w.Header().Set("Content-Type", "application/json")
100
-	if flush {
101
-		job.Stdout.Add(utils.NewWriteFlusher(w))
102
-	} else {
103
-		job.Stdout.Add(w)
104
-	}
105
-}
106
-
107
-func getBoolParam(value string) (bool, error) {
108
-	if value == "" {
109
-		return false, nil
110
-	}
111
-	ret, err := strconv.ParseBool(value)
112
-	if err != nil {
113
-		return false, fmt.Errorf("Bad parameter")
114
-	}
115
-	return ret, nil
116
-}
117
-
118
-func postAuth(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
119
-	var (
120
-		authConfig, err = ioutil.ReadAll(r.Body)
121
-		job             = eng.Job("auth")
122
-		status          string
123
-	)
124
-	if err != nil {
125
-		return err
126
-	}
127
-	job.Setenv("authConfig", string(authConfig))
128
-	job.Stdout.AddString(&status)
129
-	if err = job.Run(); err != nil {
130
-		return err
131
-	}
132
-	if status != "" {
133
-		var env engine.Env
134
-		env.Set("Status", status)
135
-		return writeJSON(w, http.StatusOK, env)
136
-	}
137
-	w.WriteHeader(http.StatusNoContent)
138
-	return nil
139
-}
140
-
141
-func getVersion(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
142
-	w.Header().Set("Content-Type", "application/json")
143
-	eng.ServeHTTP(w, r)
144
-	return nil
145
-}
146
-
147
-func postContainersKill(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
148
-	if vars == nil {
149
-		return fmt.Errorf("Missing parameter")
150
-	}
151
-	if err := parseForm(r); err != nil {
152
-		return err
153
-	}
154
-	job := eng.Job("kill", vars["name"])
155
-	if sig := r.Form.Get("signal"); sig != "" {
156
-		job.Args = append(job.Args, sig)
157
-	}
158
-	if err := job.Run(); err != nil {
159
-		return err
160
-	}
161
-	w.WriteHeader(http.StatusNoContent)
162
-	return nil
163
-}
164
-
165
-func getContainersExport(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
166
-	if vars == nil {
167
-		return fmt.Errorf("Missing parameter")
168
-	}
169
-	job := eng.Job("export", vars["name"])
170
-	job.Stdout.Add(w)
171
-	if err := job.Run(); err != nil {
172
-		return err
173
-	}
174
-	return nil
175
-}
176
-
177
-func getImagesJSON(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
178
-	if err := parseForm(r); err != nil {
179
-		return err
180
-	}
181
-
182
-	var (
183
-		err  error
184
-		outs *engine.Table
185
-		job  = eng.Job("images")
186
-	)
187
-
188
-	job.Setenv("filter", r.Form.Get("filter"))
189
-	job.Setenv("all", r.Form.Get("all"))
190
-
191
-	if version.GreaterThanOrEqualTo("1.7") {
192
-		streamJSON(job, w, false)
193
-	} else if outs, err = job.Stdout.AddListTable(); err != nil {
194
-		return err
195
-	}
196
-
197
-	if err := job.Run(); err != nil {
198
-		return err
199
-	}
200
-
201
-	if version.LessThan("1.7") && outs != nil { // Convert to legacy format
202
-		outsLegacy := engine.NewTable("Created", 0)
203
-		for _, out := range outs.Data {
204
-			for _, repoTag := range out.GetList("RepoTags") {
205
-				parts := strings.Split(repoTag, ":")
206
-				outLegacy := &engine.Env{}
207
-				outLegacy.Set("Repository", parts[0])
208
-				outLegacy.Set("Tag", parts[1])
209
-				outLegacy.Set("Id", out.Get("Id"))
210
-				outLegacy.SetInt64("Created", out.GetInt64("Created"))
211
-				outLegacy.SetInt64("Size", out.GetInt64("Size"))
212
-				outLegacy.SetInt64("VirtualSize", out.GetInt64("VirtualSize"))
213
-				outsLegacy.Add(outLegacy)
214
-			}
215
-		}
216
-		w.Header().Set("Content-Type", "application/json")
217
-		if _, err := outsLegacy.WriteListTo(w); err != nil {
218
-			return err
219
-		}
220
-	}
221
-	return nil
222
-}
223
-
224
-func getImagesViz(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
225
-	if version.GreaterThan("1.6") {
226
-		w.WriteHeader(http.StatusNotFound)
227
-		return fmt.Errorf("This is now implemented in the client.")
228
-	}
229
-	eng.ServeHTTP(w, r)
230
-	return nil
231
-}
232
-
233
-func getInfo(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
234
-	w.Header().Set("Content-Type", "application/json")
235
-	eng.ServeHTTP(w, r)
236
-	return nil
237
-}
238
-
239
-func getEvents(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
240
-	if err := parseForm(r); err != nil {
241
-		return err
242
-	}
243
-
244
-	var job = eng.Job("events", r.RemoteAddr)
245
-	streamJSON(job, w, true)
246
-	job.Setenv("since", r.Form.Get("since"))
247
-	return job.Run()
248
-}
249
-
250
-func getImagesHistory(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
251
-	if vars == nil {
252
-		return fmt.Errorf("Missing parameter")
253
-	}
254
-
255
-	var job = eng.Job("history", vars["name"])
256
-	streamJSON(job, w, false)
257
-
258
-	if err := job.Run(); err != nil {
259
-		return err
260
-	}
261
-	return nil
262
-}
263
-
264
-func getContainersChanges(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
265
-	if vars == nil {
266
-		return fmt.Errorf("Missing parameter")
267
-	}
268
-	var job = eng.Job("changes", vars["name"])
269
-	streamJSON(job, w, false)
270
-
271
-	return job.Run()
272
-}
273
-
274
-func getContainersTop(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
275
-	if version.LessThan("1.4") {
276
-		return fmt.Errorf("top was improved a lot since 1.3, Please upgrade your docker client.")
277
-	}
278
-	if vars == nil {
279
-		return fmt.Errorf("Missing parameter")
280
-	}
281
-	if err := parseForm(r); err != nil {
282
-		return err
283
-	}
284
-
285
-	job := eng.Job("top", vars["name"], r.Form.Get("ps_args"))
286
-	streamJSON(job, w, false)
287
-	return job.Run()
288
-}
289
-
290
-func getContainersJSON(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
291
-	if err := parseForm(r); err != nil {
292
-		return err
293
-	}
294
-	var (
295
-		err  error
296
-		outs *engine.Table
297
-		job  = eng.Job("containers")
298
-	)
299
-
300
-	job.Setenv("all", r.Form.Get("all"))
301
-	job.Setenv("size", r.Form.Get("size"))
302
-	job.Setenv("since", r.Form.Get("since"))
303
-	job.Setenv("before", r.Form.Get("before"))
304
-	job.Setenv("limit", r.Form.Get("limit"))
305
-
306
-	if version.GreaterThanOrEqualTo("1.5") {
307
-		streamJSON(job, w, false)
308
-	} else if outs, err = job.Stdout.AddTable(); err != nil {
309
-		return err
310
-	}
311
-	if err = job.Run(); err != nil {
312
-		return err
313
-	}
314
-	if version.LessThan("1.5") { // Convert to legacy format
315
-		for _, out := range outs.Data {
316
-			ports := engine.NewTable("", 0)
317
-			ports.ReadListFrom([]byte(out.Get("Ports")))
318
-			out.Set("Ports", displayablePorts(ports))
319
-		}
320
-		w.Header().Set("Content-Type", "application/json")
321
-		if _, err = outs.WriteListTo(w); err != nil {
322
-			return err
323
-		}
324
-	}
325
-	return nil
326
-}
327
-
328
-func postImagesTag(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
329
-	if err := parseForm(r); err != nil {
330
-		return err
331
-	}
332
-	if vars == nil {
333
-		return fmt.Errorf("Missing parameter")
334
-	}
335
-
336
-	job := eng.Job("tag", vars["name"], r.Form.Get("repo"), r.Form.Get("tag"))
337
-	job.Setenv("force", r.Form.Get("force"))
338
-	if err := job.Run(); err != nil {
339
-		return err
340
-	}
341
-	w.WriteHeader(http.StatusCreated)
342
-	return nil
343
-}
344
-
345
-func postCommit(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
346
-	if err := parseForm(r); err != nil {
347
-		return err
348
-	}
349
-	var (
350
-		config engine.Env
351
-		env    engine.Env
352
-		job    = eng.Job("commit", r.Form.Get("container"))
353
-	)
354
-	if err := config.Decode(r.Body); err != nil {
355
-		utils.Errorf("%s", err)
356
-	}
357
-
358
-	job.Setenv("repo", r.Form.Get("repo"))
359
-	job.Setenv("tag", r.Form.Get("tag"))
360
-	job.Setenv("author", r.Form.Get("author"))
361
-	job.Setenv("comment", r.Form.Get("comment"))
362
-	job.SetenvSubEnv("config", &config)
363
-
364
-	var id string
365
-	job.Stdout.AddString(&id)
366
-	if err := job.Run(); err != nil {
367
-		return err
368
-	}
369
-	env.Set("Id", id)
370
-	return writeJSON(w, http.StatusCreated, env)
371
-}
372
-
373
-// Creates an image from Pull or from Import
374
-func postImagesCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
375
-	if err := parseForm(r); err != nil {
376
-		return err
377
-	}
378
-
379
-	var (
380
-		image = r.Form.Get("fromImage")
381
-		tag   = r.Form.Get("tag")
382
-		job   *engine.Job
383
-	)
384
-	authEncoded := r.Header.Get("X-Registry-Auth")
385
-	authConfig := &registry.AuthConfig{}
386
-	if authEncoded != "" {
387
-		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
388
-		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
389
-			// for a pull it is not an error if no auth was given
390
-			// to increase compatibility with the existing api it is defaulting to be empty
391
-			authConfig = &registry.AuthConfig{}
392
-		}
393
-	}
394
-	if image != "" { //pull
395
-		metaHeaders := map[string][]string{}
396
-		for k, v := range r.Header {
397
-			if strings.HasPrefix(k, "X-Meta-") {
398
-				metaHeaders[k] = v
399
-			}
400
-		}
401
-		job = eng.Job("pull", r.Form.Get("fromImage"), tag)
402
-		job.SetenvBool("parallel", version.GreaterThan("1.3"))
403
-		job.SetenvJson("metaHeaders", metaHeaders)
404
-		job.SetenvJson("authConfig", authConfig)
405
-	} else { //import
406
-		job = eng.Job("import", r.Form.Get("fromSrc"), r.Form.Get("repo"), tag)
407
-		job.Stdin.Add(r.Body)
408
-	}
409
-
410
-	if version.GreaterThan("1.0") {
411
-		job.SetenvBool("json", true)
412
-		streamJSON(job, w, true)
413
-	} else {
414
-		job.Stdout.Add(utils.NewWriteFlusher(w))
415
-	}
416
-	if err := job.Run(); err != nil {
417
-		if !job.Stdout.Used() {
418
-			return err
419
-		}
420
-		sf := utils.NewStreamFormatter(version.GreaterThan("1.0"))
421
-		w.Write(sf.FormatError(err))
422
-	}
423
-
424
-	return nil
425
-}
426
-
427
-func getImagesSearch(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
428
-	if err := parseForm(r); err != nil {
429
-		return err
430
-	}
431
-	var (
432
-		authEncoded = r.Header.Get("X-Registry-Auth")
433
-		authConfig  = &registry.AuthConfig{}
434
-		metaHeaders = map[string][]string{}
435
-	)
436
-
437
-	if authEncoded != "" {
438
-		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
439
-		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
440
-			// for a search it is not an error if no auth was given
441
-			// to increase compatibility with the existing api it is defaulting to be empty
442
-			authConfig = &registry.AuthConfig{}
443
-		}
444
-	}
445
-	for k, v := range r.Header {
446
-		if strings.HasPrefix(k, "X-Meta-") {
447
-			metaHeaders[k] = v
448
-		}
449
-	}
450
-
451
-	var job = eng.Job("search", r.Form.Get("term"))
452
-	job.SetenvJson("metaHeaders", metaHeaders)
453
-	job.SetenvJson("authConfig", authConfig)
454
-	streamJSON(job, w, false)
455
-
456
-	return job.Run()
457
-}
458
-
459
-func postImagesInsert(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
460
-	if err := parseForm(r); err != nil {
461
-		return err
462
-	}
463
-	if vars == nil {
464
-		return fmt.Errorf("Missing parameter")
465
-	}
466
-	job := eng.Job("insert", vars["name"], r.Form.Get("url"), r.Form.Get("path"))
467
-	if version.GreaterThan("1.0") {
468
-		job.SetenvBool("json", true)
469
-		streamJSON(job, w, false)
470
-	} else {
471
-		job.Stdout.Add(w)
472
-	}
473
-	if err := job.Run(); err != nil {
474
-		if !job.Stdout.Used() {
475
-			return err
476
-		}
477
-		sf := utils.NewStreamFormatter(version.GreaterThan("1.0"))
478
-		w.Write(sf.FormatError(err))
479
-	}
480
-
481
-	return nil
482
-}
483
-
484
-func postImagesPush(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
485
-	if vars == nil {
486
-		return fmt.Errorf("Missing parameter")
487
-	}
488
-
489
-	metaHeaders := map[string][]string{}
490
-	for k, v := range r.Header {
491
-		if strings.HasPrefix(k, "X-Meta-") {
492
-			metaHeaders[k] = v
493
-		}
494
-	}
495
-	if err := parseForm(r); err != nil {
496
-		return err
497
-	}
498
-	authConfig := &registry.AuthConfig{}
499
-
500
-	authEncoded := r.Header.Get("X-Registry-Auth")
501
-	if authEncoded != "" {
502
-		// the new format is to handle the authConfig as a header
503
-		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
504
-		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
505
-			// to increase compatibility to existing api it is defaulting to be empty
506
-			authConfig = &registry.AuthConfig{}
507
-		}
508
-	} else {
509
-		// the old format is supported for compatibility if there was no authConfig header
510
-		if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil {
511
-			return err
512
-		}
513
-	}
514
-
515
-	job := eng.Job("push", vars["name"])
516
-	job.SetenvJson("metaHeaders", metaHeaders)
517
-	job.SetenvJson("authConfig", authConfig)
518
-	if version.GreaterThan("1.0") {
519
-		job.SetenvBool("json", true)
520
-		streamJSON(job, w, true)
521
-	} else {
522
-		job.Stdout.Add(utils.NewWriteFlusher(w))
523
-	}
524
-
525
-	if err := job.Run(); err != nil {
526
-		if !job.Stdout.Used() {
527
-			return err
528
-		}
529
-		sf := utils.NewStreamFormatter(version.GreaterThan("1.0"))
530
-		w.Write(sf.FormatError(err))
531
-	}
532
-	return nil
533
-}
534
-
535
-func getImagesGet(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
536
-	if vars == nil {
537
-		return fmt.Errorf("Missing parameter")
538
-	}
539
-	if version.GreaterThan("1.0") {
540
-		w.Header().Set("Content-Type", "application/x-tar")
541
-	}
542
-	job := eng.Job("image_export", vars["name"])
543
-	job.Stdout.Add(w)
544
-	return job.Run()
545
-}
546
-
547
-func postImagesLoad(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
548
-	job := eng.Job("load")
549
-	job.Stdin.Add(r.Body)
550
-	return job.Run()
551
-}
552
-
553
-func postContainersCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
554
-	if err := parseForm(r); err != nil {
555
-		return nil
556
-	}
557
-	var (
558
-		out         engine.Env
559
-		job         = eng.Job("create", r.Form.Get("name"))
560
-		outWarnings []string
561
-		outId       string
562
-		warnings    = bytes.NewBuffer(nil)
563
-	)
564
-	if err := job.DecodeEnv(r.Body); err != nil {
565
-		return err
566
-	}
567
-	// Read container ID from the first line of stdout
568
-	job.Stdout.AddString(&outId)
569
-	// Read warnings from stderr
570
-	job.Stderr.Add(warnings)
571
-	if err := job.Run(); err != nil {
572
-		return err
573
-	}
574
-	// Parse warnings from stderr
575
-	scanner := bufio.NewScanner(warnings)
576
-	for scanner.Scan() {
577
-		outWarnings = append(outWarnings, scanner.Text())
578
-	}
579
-	out.Set("Id", outId)
580
-	out.SetList("Warnings", outWarnings)
581
-	return writeJSON(w, http.StatusCreated, out)
582
-}
583
-
584
-func postContainersRestart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
585
-	if err := parseForm(r); err != nil {
586
-		return err
587
-	}
588
-	if vars == nil {
589
-		return fmt.Errorf("Missing parameter")
590
-	}
591
-	job := eng.Job("restart", vars["name"])
592
-	job.Setenv("t", r.Form.Get("t"))
593
-	if err := job.Run(); err != nil {
594
-		return err
595
-	}
596
-	w.WriteHeader(http.StatusNoContent)
597
-	return nil
598
-}
599
-
600
-func deleteContainers(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
601
-	if err := parseForm(r); err != nil {
602
-		return err
603
-	}
604
-	if vars == nil {
605
-		return fmt.Errorf("Missing parameter")
606
-	}
607
-	job := eng.Job("container_delete", vars["name"])
608
-	job.Setenv("removeVolume", r.Form.Get("v"))
609
-	job.Setenv("removeLink", r.Form.Get("link"))
610
-	job.Setenv("forceRemove", r.Form.Get("force"))
611
-	if err := job.Run(); err != nil {
612
-		return err
613
-	}
614
-	w.WriteHeader(http.StatusNoContent)
615
-	return nil
616
-}
617
-
618
-func deleteImages(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
619
-	if err := parseForm(r); err != nil {
620
-		return err
621
-	}
622
-	if vars == nil {
623
-		return fmt.Errorf("Missing parameter")
624
-	}
625
-	var job = eng.Job("image_delete", vars["name"])
626
-	streamJSON(job, w, false)
627
-	job.Setenv("force", r.Form.Get("force"))
628
-	job.Setenv("noprune", r.Form.Get("noprune"))
629
-
630
-	return job.Run()
631
-}
632
-
633
-func postContainersStart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
634
-	if vars == nil {
635
-		return fmt.Errorf("Missing parameter")
636
-	}
637
-	name := vars["name"]
638
-	job := eng.Job("start", name)
639
-	// allow a nil body for backwards compatibility
640
-	if r.Body != nil {
641
-		if MatchesContentType(r.Header.Get("Content-Type"), "application/json") {
642
-			if err := job.DecodeEnv(r.Body); err != nil {
643
-				return err
644
-			}
645
-		}
646
-	}
647
-	if err := job.Run(); err != nil {
648
-		return err
649
-	}
650
-	w.WriteHeader(http.StatusNoContent)
651
-	return nil
652
-}
653
-
654
-func postContainersStop(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
655
-	if err := parseForm(r); err != nil {
656
-		return err
657
-	}
658
-	if vars == nil {
659
-		return fmt.Errorf("Missing parameter")
660
-	}
661
-	job := eng.Job("stop", vars["name"])
662
-	job.Setenv("t", r.Form.Get("t"))
663
-	if err := job.Run(); err != nil {
664
-		return err
665
-	}
666
-	w.WriteHeader(http.StatusNoContent)
667
-	return nil
668
-}
669
-
670
-func postContainersWait(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
671
-	if vars == nil {
672
-		return fmt.Errorf("Missing parameter")
673
-	}
674
-	var (
675
-		env    engine.Env
676
-		status string
677
-		job    = eng.Job("wait", vars["name"])
678
-	)
679
-	job.Stdout.AddString(&status)
680
-	if err := job.Run(); err != nil {
681
-		return err
682
-	}
683
-	// Parse a 16-bit encoded integer to map typical unix exit status.
684
-	_, err := strconv.ParseInt(status, 10, 16)
685
-	if err != nil {
686
-		return err
687
-	}
688
-	env.Set("StatusCode", status)
689
-	return writeJSON(w, http.StatusOK, env)
690
-}
691
-
692
-func postContainersResize(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
693
-	if err := parseForm(r); err != nil {
694
-		return err
695
-	}
696
-	if vars == nil {
697
-		return fmt.Errorf("Missing parameter")
698
-	}
699
-	if err := eng.Job("resize", vars["name"], r.Form.Get("h"), r.Form.Get("w")).Run(); err != nil {
700
-		return err
701
-	}
702
-	return nil
703
-}
704
-
705
-func postContainersAttach(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
706
-	if err := parseForm(r); err != nil {
707
-		return err
708
-	}
709
-	if vars == nil {
710
-		return fmt.Errorf("Missing parameter")
711
-	}
712
-
713
-	var (
714
-		job    = eng.Job("inspect", vars["name"], "container")
715
-		c, err = job.Stdout.AddEnv()
716
-	)
717
-	if err != nil {
718
-		return err
719
-	}
720
-	if err = job.Run(); err != nil {
721
-		return err
722
-	}
723
-
724
-	inStream, outStream, err := hijackServer(w)
725
-	if err != nil {
726
-		return err
727
-	}
728
-	defer func() {
729
-		if tcpc, ok := inStream.(*net.TCPConn); ok {
730
-			tcpc.CloseWrite()
731
-		} else {
732
-			inStream.Close()
733
-		}
734
-	}()
735
-	defer func() {
736
-		if tcpc, ok := outStream.(*net.TCPConn); ok {
737
-			tcpc.CloseWrite()
738
-		} else if closer, ok := outStream.(io.Closer); ok {
739
-			closer.Close()
740
-		}
741
-	}()
742
-
743
-	var errStream io.Writer
744
-
745
-	fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
746
-
747
-	if c.GetSubEnv("Config") != nil && !c.GetSubEnv("Config").GetBool("Tty") && version.GreaterThanOrEqualTo("1.6") {
748
-		errStream = utils.NewStdWriter(outStream, utils.Stderr)
749
-		outStream = utils.NewStdWriter(outStream, utils.Stdout)
750
-	} else {
751
-		errStream = outStream
752
-	}
753
-
754
-	job = eng.Job("attach", vars["name"])
755
-	job.Setenv("logs", r.Form.Get("logs"))
756
-	job.Setenv("stream", r.Form.Get("stream"))
757
-	job.Setenv("stdin", r.Form.Get("stdin"))
758
-	job.Setenv("stdout", r.Form.Get("stdout"))
759
-	job.Setenv("stderr", r.Form.Get("stderr"))
760
-	job.Stdin.Add(inStream)
761
-	job.Stdout.Add(outStream)
762
-	job.Stderr.Set(errStream)
763
-	if err := job.Run(); err != nil {
764
-		fmt.Fprintf(outStream, "Error: %s\n", err)
765
-
766
-	}
767
-	return nil
768
-}
769
-
770
-func wsContainersAttach(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
771
-	if err := parseForm(r); err != nil {
772
-		return err
773
-	}
774
-	if vars == nil {
775
-		return fmt.Errorf("Missing parameter")
776
-	}
777
-
778
-	if err := eng.Job("inspect", vars["name"], "container").Run(); err != nil {
779
-		return err
780
-	}
781
-
782
-	h := websocket.Handler(func(ws *websocket.Conn) {
783
-		defer ws.Close()
784
-		job := eng.Job("attach", vars["name"])
785
-		job.Setenv("logs", r.Form.Get("logs"))
786
-		job.Setenv("stream", r.Form.Get("stream"))
787
-		job.Setenv("stdin", r.Form.Get("stdin"))
788
-		job.Setenv("stdout", r.Form.Get("stdout"))
789
-		job.Setenv("stderr", r.Form.Get("stderr"))
790
-		job.Stdin.Add(ws)
791
-		job.Stdout.Add(ws)
792
-		job.Stderr.Set(ws)
793
-		if err := job.Run(); err != nil {
794
-			utils.Errorf("Error: %s", err)
795
-		}
796
-	})
797
-	h.ServeHTTP(w, r)
798
-
799
-	return nil
800
-}
801
-
802
-func getContainersByName(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
803
-	if vars == nil {
804
-		return fmt.Errorf("Missing parameter")
805
-	}
806
-	var job = eng.Job("inspect", vars["name"], "container")
807
-	streamJSON(job, w, false)
808
-	job.SetenvBool("conflict", true) //conflict=true to detect conflict between containers and images in the job
809
-	return job.Run()
810
-}
811
-
812
-func getImagesByName(eng *engine.Engine, version version.Version, 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"], "image")
817
-	streamJSON(job, w, false)
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 postBuild(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
823
-	if version.LessThan("1.3") {
824
-		return fmt.Errorf("Multipart upload for build is no longer supported. Please upgrade your docker client.")
825
-	}
826
-	var (
827
-		authEncoded       = r.Header.Get("X-Registry-Auth")
828
-		authConfig        = &registry.AuthConfig{}
829
-		configFileEncoded = r.Header.Get("X-Registry-Config")
830
-		configFile        = &registry.ConfigFile{}
831
-		job               = eng.Job("build")
832
-	)
833
-
834
-	// This block can be removed when API versions prior to 1.9 are deprecated.
835
-	// Both headers will be parsed and sent along to the daemon, but if a non-empty
836
-	// ConfigFile is present, any value provided as an AuthConfig directly will
837
-	// be overridden. See BuildFile::CmdFrom for details.
838
-	if version.LessThan("1.9") && authEncoded != "" {
839
-		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
840
-		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
841
-			// for a pull it is not an error if no auth was given
842
-			// to increase compatibility with the existing api it is defaulting to be empty
843
-			authConfig = &registry.AuthConfig{}
844
-		}
845
-	}
846
-
847
-	if configFileEncoded != "" {
848
-		configFileJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(configFileEncoded))
849
-		if err := json.NewDecoder(configFileJson).Decode(configFile); err != nil {
850
-			// for a pull it is not an error if no auth was given
851
-			// to increase compatibility with the existing api it is defaulting to be empty
852
-			configFile = &registry.ConfigFile{}
853
-		}
854
-	}
855
-
856
-	if version.GreaterThanOrEqualTo("1.8") {
857
-		job.SetenvBool("json", true)
858
-		streamJSON(job, w, true)
859
-	} else {
860
-		job.Stdout.Add(utils.NewWriteFlusher(w))
861
-	}
862
-	job.Stdin.Add(r.Body)
863
-	job.Setenv("remote", r.FormValue("remote"))
864
-	job.Setenv("t", r.FormValue("t"))
865
-	job.Setenv("q", r.FormValue("q"))
866
-	job.Setenv("nocache", r.FormValue("nocache"))
867
-	job.Setenv("rm", r.FormValue("rm"))
868
-	job.SetenvJson("authConfig", authConfig)
869
-	job.SetenvJson("configFile", configFile)
870
-
871
-	if err := job.Run(); err != nil {
872
-		if !job.Stdout.Used() {
873
-			return err
874
-		}
875
-		sf := utils.NewStreamFormatter(version.GreaterThanOrEqualTo("1.8"))
876
-		w.Write(sf.FormatError(err))
877
-	}
878
-	return nil
879
-}
880
-
881
-func postContainersCopy(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
882
-	if vars == nil {
883
-		return fmt.Errorf("Missing parameter")
884
-	}
885
-
886
-	var copyData engine.Env
887
-
888
-	if contentType := r.Header.Get("Content-Type"); MatchesContentType(contentType, "application/json") {
889
-		if err := copyData.Decode(r.Body); err != nil {
890
-			return err
891
-		}
892
-	} else {
893
-		return fmt.Errorf("Content-Type not supported: %s", contentType)
894
-	}
895
-
896
-	if copyData.Get("Resource") == "" {
897
-		return fmt.Errorf("Path cannot be empty")
898
-	}
899
-
900
-	origResource := copyData.Get("Resource")
901
-
902
-	if copyData.Get("Resource")[0] == '/' {
903
-		copyData.Set("Resource", copyData.Get("Resource")[1:])
904
-	}
905
-
906
-	job := eng.Job("container_copy", vars["name"], copyData.Get("Resource"))
907
-	job.Stdout.Add(w)
908
-	if err := job.Run(); err != nil {
909
-		utils.Errorf("%s", err.Error())
910
-		if strings.Contains(err.Error(), "No such container") {
911
-			w.WriteHeader(http.StatusNotFound)
912
-		} else if strings.Contains(err.Error(), "no such file or directory") {
913
-			return fmt.Errorf("Could not find the file %s in container %s", origResource, vars["name"])
914
-		}
915
-	}
916
-	return nil
917
-}
918
-
919
-func optionsHandler(eng *engine.Engine, version version.Version, 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 version.Version) 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 && !dockerVersion.Equal(userAgent[1]) {
941
-				utils.Debugf("Warning: client and server don't have the same version (client: %s, server: %s)", userAgent[1], dockerVersion)
942
-			}
943
-		}
944
-		version := version.Version(mux.Vars(r)["version"])
945
-		if version == "" {
946
-			version = APIVERSION
947
-		}
948
-		if enableCors {
949
-			writeCorsHeaders(w, r)
950
-		}
951
-
952
-		if version.GreaterThan(APIVERSION) {
953
-			http.Error(w, fmt.Errorf("client and server don't have same version (client : %s, server: %s)", 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, version.Version(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 version.Version, 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%s%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
-	// We don't want to start serving on these sockets until the
1091
-	// "initserver" job has completed. Otherwise required handlers
1092
-	// won't be ready.
1093
-	<-activationLock
1094
-
1095
-	// Since ListenFD will return one or more sockets we have
1096
-	// to create a go func to spawn off multiple serves
1097
-	for i := range ls {
1098
-		listener := ls[i]
1099
-		go func() {
1100
-			httpSrv := http.Server{Handler: handle}
1101
-			chErrors <- httpSrv.Serve(listener)
1102
-		}()
1103
-	}
1104
-
1105
-	for i := 0; i < len(ls); i += 1 {
1106
-		err := <-chErrors
1107
-		if err != nil {
1108
-			return err
1109
-		}
1110
-	}
1111
-
1112
-	return nil
1113
-}
1114
-
1115
-func lookupGidByName(nameOrGid string) (int, error) {
1116
-	groups, err := user.ParseGroupFilter(func(g *user.Group) bool {
1117
-		return g.Name == nameOrGid || strconv.Itoa(g.Gid) == nameOrGid
1118
-	})
1119
-	if err != nil {
1120
-		return -1, err
1121
-	}
1122
-	if groups != nil && len(groups) > 0 {
1123
-		return groups[0].Gid, nil
1124
-	}
1125
-	return -1, fmt.Errorf("Group %s not found", nameOrGid)
1126
-}
1127
-
1128
-func changeGroup(addr string, nameOrGid string) error {
1129
-	gid, err := lookupGidByName(nameOrGid)
1130
-	if err != nil {
1131
-		return err
1132
-	}
1133
-
1134
-	utils.Debugf("%s group found. gid: %d", nameOrGid, gid)
1135
-	return os.Chown(addr, 0, gid)
1136
-}
1137
-
1138
-// ListenAndServe sets up the required http.Server and gets it listening for
1139
-// each addr passed in and does protocol specific checking.
1140
-func ListenAndServe(proto, addr string, job *engine.Job) error {
1141
-	r, err := createRouter(job.Eng, job.GetenvBool("Logging"), job.GetenvBool("EnableCors"), job.Getenv("Version"))
1142
-	if err != nil {
1143
-		return err
1144
-	}
1145
-
1146
-	if proto == "fd" {
1147
-		return ServeFd(addr, r)
1148
-	}
1149
-
1150
-	if proto == "unix" {
1151
-		if err := syscall.Unlink(addr); err != nil && !os.IsNotExist(err) {
1152
-			return err
1153
-		}
1154
-	}
1155
-
1156
-	l, err := listenbuffer.NewListenBuffer(proto, addr, activationLock)
1157
-	if err != nil {
1158
-		return err
1159
-	}
1160
-
1161
-	if proto != "unix" && (job.GetenvBool("Tls") || job.GetenvBool("TlsVerify")) {
1162
-		tlsCert := job.Getenv("TlsCert")
1163
-		tlsKey := job.Getenv("TlsKey")
1164
-		cert, err := tls.LoadX509KeyPair(tlsCert, tlsKey)
1165
-		if err != nil {
1166
-			return fmt.Errorf("Couldn't load X509 key pair (%s, %s): %s. Key encrypted?",
1167
-				tlsCert, tlsKey, err)
1168
-		}
1169
-		tlsConfig := &tls.Config{
1170
-			NextProtos:   []string{"http/1.1"},
1171
-			Certificates: []tls.Certificate{cert},
1172
-		}
1173
-		if job.GetenvBool("TlsVerify") {
1174
-			certPool := x509.NewCertPool()
1175
-			file, err := ioutil.ReadFile(job.Getenv("TlsCa"))
1176
-			if err != nil {
1177
-				return fmt.Errorf("Couldn't read CA certificate: %s", err)
1178
-			}
1179
-			certPool.AppendCertsFromPEM(file)
1180
-
1181
-			tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
1182
-			tlsConfig.ClientCAs = certPool
1183
-		}
1184
-		l = tls.NewListener(l, tlsConfig)
1185
-	}
1186
-
1187
-	// Basic error and sanity checking
1188
-	switch proto {
1189
-	case "tcp":
1190
-		if !strings.HasPrefix(addr, "127.0.0.1") && !job.GetenvBool("TlsVerify") {
1191
-			log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
1192
-		}
1193
-	case "unix":
1194
-		if err := os.Chmod(addr, 0660); err != nil {
1195
-			return err
1196
-		}
1197
-		socketGroup := job.Getenv("SocketGroup")
1198
-		if socketGroup != "" {
1199
-			if err := changeGroup(addr, socketGroup); err != nil {
1200
-				if socketGroup == "docker" {
1201
-					// if the user hasn't explicitly specified the group ownership, don't fail on errors.
1202
-					utils.Debugf("Warning: could not chgrp %s to docker: %s", addr, err.Error())
1203
-				} else {
1204
-					return err
1205
-				}
1206
-			}
1207
-		}
1208
-	default:
1209
-		return fmt.Errorf("Invalid protocol format.")
1210
-	}
1211
-
1212
-	httpSrv := http.Server{Addr: addr, Handler: r}
1213
-	return httpSrv.Serve(l)
1214
-}
1215
-
1216
-// ServeApi loops through all of the protocols sent in to docker and spawns
1217
-// off a go routine to setup a serving http.Server for each.
1218
-func ServeApi(job *engine.Job) engine.Status {
1219
-	var (
1220
-		protoAddrs = job.Args
1221
-		chErrors   = make(chan error, len(protoAddrs))
1222
-	)
1223
-	activationLock = make(chan struct{})
1224
-
1225
-	if err := job.Eng.Register("acceptconnections", AcceptConnections); err != nil {
1226
-		return job.Error(err)
1227
-	}
1228
-
1229
-	for _, protoAddr := range protoAddrs {
1230
-		protoAddrParts := strings.SplitN(protoAddr, "://", 2)
1231
-		go func() {
1232
-			log.Printf("Listening for HTTP on %s (%s)\n", protoAddrParts[0], protoAddrParts[1])
1233
-			chErrors <- ListenAndServe(protoAddrParts[0], protoAddrParts[1], job)
1234
-		}()
1235
-	}
1236
-
1237
-	for i := 0; i < len(protoAddrs); i += 1 {
1238
-		err := <-chErrors
1239
-		if err != nil {
1240
-			return job.Error(err)
1241
-		}
1242
-	}
1243
-
1244
-	return engine.StatusOK
1245
-}
1246
-
1247
-func AcceptConnections(job *engine.Job) engine.Status {
1248
-	// Tell the init daemon we are accepting requests
1249
-	go systemd.SdNotify("READY=1")
1250
-
1251
-	// close the lock so the listeners start accepting connections
1252
-	close(activationLock)
1253
-
1254
-	return engine.StatusOK
1255
-}
1256 1
new file mode 100644
... ...
@@ -0,0 +1,1257 @@
0
+package server
1
+
2
+import (
3
+	"bufio"
4
+	"bytes"
5
+	"code.google.com/p/go.net/websocket"
6
+	"crypto/tls"
7
+	"crypto/x509"
8
+	"encoding/base64"
9
+	"encoding/json"
10
+	"expvar"
11
+	"fmt"
12
+	"io"
13
+	"io/ioutil"
14
+	"log"
15
+	"net"
16
+	"net/http"
17
+	"net/http/pprof"
18
+	"os"
19
+	"strconv"
20
+	"strings"
21
+	"syscall"
22
+
23
+	"github.com/dotcloud/docker/api"
24
+	"github.com/dotcloud/docker/engine"
25
+	"github.com/dotcloud/docker/pkg/listenbuffer"
26
+	"github.com/dotcloud/docker/pkg/systemd"
27
+	"github.com/dotcloud/docker/pkg/user"
28
+	"github.com/dotcloud/docker/pkg/version"
29
+	"github.com/dotcloud/docker/registry"
30
+	"github.com/dotcloud/docker/utils"
31
+	"github.com/gorilla/mux"
32
+)
33
+
34
+var (
35
+	activationLock chan struct{}
36
+)
37
+
38
+type HttpApiFunc func(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error
39
+
40
+func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) {
41
+	conn, _, err := w.(http.Hijacker).Hijack()
42
+	if err != nil {
43
+		return nil, nil, err
44
+	}
45
+	// Flush the options to make sure the client sets the raw mode
46
+	conn.Write([]byte{})
47
+	return conn, conn, nil
48
+}
49
+
50
+//If we don't do this, POST method without Content-type (even with empty body) will fail
51
+func parseForm(r *http.Request) error {
52
+	if r == nil {
53
+		return nil
54
+	}
55
+	if err := r.ParseForm(); err != nil && !strings.HasPrefix(err.Error(), "mime:") {
56
+		return err
57
+	}
58
+	return nil
59
+}
60
+
61
+func parseMultipartForm(r *http.Request) error {
62
+	if err := r.ParseMultipartForm(4096); err != nil && !strings.HasPrefix(err.Error(), "mime:") {
63
+		return err
64
+	}
65
+	return nil
66
+}
67
+
68
+func httpError(w http.ResponseWriter, err error) {
69
+	statusCode := http.StatusInternalServerError
70
+	// FIXME: this is brittle and should not be necessary.
71
+	// If we need to differentiate between different possible error types, we should
72
+	// create appropriate error types with clearly defined meaning.
73
+	if strings.Contains(err.Error(), "No such") {
74
+		statusCode = http.StatusNotFound
75
+	} else if strings.Contains(err.Error(), "Bad parameter") {
76
+		statusCode = http.StatusBadRequest
77
+	} else if strings.Contains(err.Error(), "Conflict") {
78
+		statusCode = http.StatusConflict
79
+	} else if strings.Contains(err.Error(), "Impossible") {
80
+		statusCode = http.StatusNotAcceptable
81
+	} else if strings.Contains(err.Error(), "Wrong login/password") {
82
+		statusCode = http.StatusUnauthorized
83
+	} else if strings.Contains(err.Error(), "hasn't been activated") {
84
+		statusCode = http.StatusForbidden
85
+	}
86
+
87
+	if err != nil {
88
+		utils.Errorf("HTTP Error: statusCode=%d %s", statusCode, err.Error())
89
+		http.Error(w, err.Error(), statusCode)
90
+	}
91
+}
92
+
93
+func writeJSON(w http.ResponseWriter, code int, v engine.Env) error {
94
+	w.Header().Set("Content-Type", "application/json")
95
+	w.WriteHeader(code)
96
+	return v.Encode(w)
97
+}
98
+
99
+func streamJSON(job *engine.Job, w http.ResponseWriter, flush bool) {
100
+	w.Header().Set("Content-Type", "application/json")
101
+	if flush {
102
+		job.Stdout.Add(utils.NewWriteFlusher(w))
103
+	} else {
104
+		job.Stdout.Add(w)
105
+	}
106
+}
107
+
108
+func getBoolParam(value string) (bool, error) {
109
+	if value == "" {
110
+		return false, nil
111
+	}
112
+	ret, err := strconv.ParseBool(value)
113
+	if err != nil {
114
+		return false, fmt.Errorf("Bad parameter")
115
+	}
116
+	return ret, nil
117
+}
118
+
119
+func postAuth(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
120
+	var (
121
+		authConfig, err = ioutil.ReadAll(r.Body)
122
+		job             = eng.Job("auth")
123
+		status          string
124
+	)
125
+	if err != nil {
126
+		return err
127
+	}
128
+	job.Setenv("authConfig", string(authConfig))
129
+	job.Stdout.AddString(&status)
130
+	if err = job.Run(); err != nil {
131
+		return err
132
+	}
133
+	if status != "" {
134
+		var env engine.Env
135
+		env.Set("Status", status)
136
+		return writeJSON(w, http.StatusOK, env)
137
+	}
138
+	w.WriteHeader(http.StatusNoContent)
139
+	return nil
140
+}
141
+
142
+func getVersion(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
143
+	w.Header().Set("Content-Type", "application/json")
144
+	eng.ServeHTTP(w, r)
145
+	return nil
146
+}
147
+
148
+func postContainersKill(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
149
+	if vars == nil {
150
+		return fmt.Errorf("Missing parameter")
151
+	}
152
+	if err := parseForm(r); err != nil {
153
+		return err
154
+	}
155
+	job := eng.Job("kill", vars["name"])
156
+	if sig := r.Form.Get("signal"); sig != "" {
157
+		job.Args = append(job.Args, sig)
158
+	}
159
+	if err := job.Run(); err != nil {
160
+		return err
161
+	}
162
+	w.WriteHeader(http.StatusNoContent)
163
+	return nil
164
+}
165
+
166
+func getContainersExport(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
167
+	if vars == nil {
168
+		return fmt.Errorf("Missing parameter")
169
+	}
170
+	job := eng.Job("export", vars["name"])
171
+	job.Stdout.Add(w)
172
+	if err := job.Run(); err != nil {
173
+		return err
174
+	}
175
+	return nil
176
+}
177
+
178
+func getImagesJSON(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
179
+	if err := parseForm(r); err != nil {
180
+		return err
181
+	}
182
+
183
+	var (
184
+		err  error
185
+		outs *engine.Table
186
+		job  = eng.Job("images")
187
+	)
188
+
189
+	job.Setenv("filter", r.Form.Get("filter"))
190
+	job.Setenv("all", r.Form.Get("all"))
191
+
192
+	if version.GreaterThanOrEqualTo("1.7") {
193
+		streamJSON(job, w, false)
194
+	} else if outs, err = job.Stdout.AddListTable(); err != nil {
195
+		return err
196
+	}
197
+
198
+	if err := job.Run(); err != nil {
199
+		return err
200
+	}
201
+
202
+	if version.LessThan("1.7") && outs != nil { // Convert to legacy format
203
+		outsLegacy := engine.NewTable("Created", 0)
204
+		for _, out := range outs.Data {
205
+			for _, repoTag := range out.GetList("RepoTags") {
206
+				parts := strings.Split(repoTag, ":")
207
+				outLegacy := &engine.Env{}
208
+				outLegacy.Set("Repository", parts[0])
209
+				outLegacy.Set("Tag", parts[1])
210
+				outLegacy.Set("Id", out.Get("Id"))
211
+				outLegacy.SetInt64("Created", out.GetInt64("Created"))
212
+				outLegacy.SetInt64("Size", out.GetInt64("Size"))
213
+				outLegacy.SetInt64("VirtualSize", out.GetInt64("VirtualSize"))
214
+				outsLegacy.Add(outLegacy)
215
+			}
216
+		}
217
+		w.Header().Set("Content-Type", "application/json")
218
+		if _, err := outsLegacy.WriteListTo(w); err != nil {
219
+			return err
220
+		}
221
+	}
222
+	return nil
223
+}
224
+
225
+func getImagesViz(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
226
+	if version.GreaterThan("1.6") {
227
+		w.WriteHeader(http.StatusNotFound)
228
+		return fmt.Errorf("This is now implemented in the client.")
229
+	}
230
+	eng.ServeHTTP(w, r)
231
+	return nil
232
+}
233
+
234
+func getInfo(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
235
+	w.Header().Set("Content-Type", "application/json")
236
+	eng.ServeHTTP(w, r)
237
+	return nil
238
+}
239
+
240
+func getEvents(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
241
+	if err := parseForm(r); err != nil {
242
+		return err
243
+	}
244
+
245
+	var job = eng.Job("events", r.RemoteAddr)
246
+	streamJSON(job, w, true)
247
+	job.Setenv("since", r.Form.Get("since"))
248
+	return job.Run()
249
+}
250
+
251
+func getImagesHistory(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
252
+	if vars == nil {
253
+		return fmt.Errorf("Missing parameter")
254
+	}
255
+
256
+	var job = eng.Job("history", vars["name"])
257
+	streamJSON(job, w, false)
258
+
259
+	if err := job.Run(); err != nil {
260
+		return err
261
+	}
262
+	return nil
263
+}
264
+
265
+func getContainersChanges(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
266
+	if vars == nil {
267
+		return fmt.Errorf("Missing parameter")
268
+	}
269
+	var job = eng.Job("changes", vars["name"])
270
+	streamJSON(job, w, false)
271
+
272
+	return job.Run()
273
+}
274
+
275
+func getContainersTop(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
276
+	if version.LessThan("1.4") {
277
+		return fmt.Errorf("top was improved a lot since 1.3, Please upgrade your docker client.")
278
+	}
279
+	if vars == nil {
280
+		return fmt.Errorf("Missing parameter")
281
+	}
282
+	if err := parseForm(r); err != nil {
283
+		return err
284
+	}
285
+
286
+	job := eng.Job("top", vars["name"], r.Form.Get("ps_args"))
287
+	streamJSON(job, w, false)
288
+	return job.Run()
289
+}
290
+
291
+func getContainersJSON(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
292
+	if err := parseForm(r); err != nil {
293
+		return err
294
+	}
295
+	var (
296
+		err  error
297
+		outs *engine.Table
298
+		job  = eng.Job("containers")
299
+	)
300
+
301
+	job.Setenv("all", r.Form.Get("all"))
302
+	job.Setenv("size", r.Form.Get("size"))
303
+	job.Setenv("since", r.Form.Get("since"))
304
+	job.Setenv("before", r.Form.Get("before"))
305
+	job.Setenv("limit", r.Form.Get("limit"))
306
+
307
+	if version.GreaterThanOrEqualTo("1.5") {
308
+		streamJSON(job, w, false)
309
+	} else if outs, err = job.Stdout.AddTable(); err != nil {
310
+		return err
311
+	}
312
+	if err = job.Run(); err != nil {
313
+		return err
314
+	}
315
+	if version.LessThan("1.5") { // Convert to legacy format
316
+		for _, out := range outs.Data {
317
+			ports := engine.NewTable("", 0)
318
+			ports.ReadListFrom([]byte(out.Get("Ports")))
319
+			out.Set("Ports", api.DisplayablePorts(ports))
320
+		}
321
+		w.Header().Set("Content-Type", "application/json")
322
+		if _, err = outs.WriteListTo(w); err != nil {
323
+			return err
324
+		}
325
+	}
326
+	return nil
327
+}
328
+
329
+func postImagesTag(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
330
+	if err := parseForm(r); err != nil {
331
+		return err
332
+	}
333
+	if vars == nil {
334
+		return fmt.Errorf("Missing parameter")
335
+	}
336
+
337
+	job := eng.Job("tag", vars["name"], r.Form.Get("repo"), r.Form.Get("tag"))
338
+	job.Setenv("force", r.Form.Get("force"))
339
+	if err := job.Run(); err != nil {
340
+		return err
341
+	}
342
+	w.WriteHeader(http.StatusCreated)
343
+	return nil
344
+}
345
+
346
+func postCommit(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
347
+	if err := parseForm(r); err != nil {
348
+		return err
349
+	}
350
+	var (
351
+		config engine.Env
352
+		env    engine.Env
353
+		job    = eng.Job("commit", r.Form.Get("container"))
354
+	)
355
+	if err := config.Decode(r.Body); err != nil {
356
+		utils.Errorf("%s", err)
357
+	}
358
+
359
+	job.Setenv("repo", r.Form.Get("repo"))
360
+	job.Setenv("tag", r.Form.Get("tag"))
361
+	job.Setenv("author", r.Form.Get("author"))
362
+	job.Setenv("comment", r.Form.Get("comment"))
363
+	job.SetenvSubEnv("config", &config)
364
+
365
+	var id string
366
+	job.Stdout.AddString(&id)
367
+	if err := job.Run(); err != nil {
368
+		return err
369
+	}
370
+	env.Set("Id", id)
371
+	return writeJSON(w, http.StatusCreated, env)
372
+}
373
+
374
+// Creates an image from Pull or from Import
375
+func postImagesCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
376
+	if err := parseForm(r); err != nil {
377
+		return err
378
+	}
379
+
380
+	var (
381
+		image = r.Form.Get("fromImage")
382
+		tag   = r.Form.Get("tag")
383
+		job   *engine.Job
384
+	)
385
+	authEncoded := r.Header.Get("X-Registry-Auth")
386
+	authConfig := &registry.AuthConfig{}
387
+	if authEncoded != "" {
388
+		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
389
+		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
390
+			// for a pull it is not an error if no auth was given
391
+			// to increase compatibility with the existing api it is defaulting to be empty
392
+			authConfig = &registry.AuthConfig{}
393
+		}
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 = eng.Job("pull", r.Form.Get("fromImage"), tag)
403
+		job.SetenvBool("parallel", version.GreaterThan("1.3"))
404
+		job.SetenvJson("metaHeaders", metaHeaders)
405
+		job.SetenvJson("authConfig", authConfig)
406
+	} else { //import
407
+		job = eng.Job("import", r.Form.Get("fromSrc"), r.Form.Get("repo"), tag)
408
+		job.Stdin.Add(r.Body)
409
+	}
410
+
411
+	if version.GreaterThan("1.0") {
412
+		job.SetenvBool("json", true)
413
+		streamJSON(job, w, true)
414
+	} else {
415
+		job.Stdout.Add(utils.NewWriteFlusher(w))
416
+	}
417
+	if err := job.Run(); err != nil {
418
+		if !job.Stdout.Used() {
419
+			return err
420
+		}
421
+		sf := utils.NewStreamFormatter(version.GreaterThan("1.0"))
422
+		w.Write(sf.FormatError(err))
423
+	}
424
+
425
+	return nil
426
+}
427
+
428
+func getImagesSearch(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
429
+	if err := parseForm(r); err != nil {
430
+		return err
431
+	}
432
+	var (
433
+		authEncoded = r.Header.Get("X-Registry-Auth")
434
+		authConfig  = &registry.AuthConfig{}
435
+		metaHeaders = map[string][]string{}
436
+	)
437
+
438
+	if authEncoded != "" {
439
+		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
440
+		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
441
+			// for a search it is not an error if no auth was given
442
+			// to increase compatibility with the existing api it is defaulting to be empty
443
+			authConfig = &registry.AuthConfig{}
444
+		}
445
+	}
446
+	for k, v := range r.Header {
447
+		if strings.HasPrefix(k, "X-Meta-") {
448
+			metaHeaders[k] = v
449
+		}
450
+	}
451
+
452
+	var job = eng.Job("search", r.Form.Get("term"))
453
+	job.SetenvJson("metaHeaders", metaHeaders)
454
+	job.SetenvJson("authConfig", authConfig)
455
+	streamJSON(job, w, false)
456
+
457
+	return job.Run()
458
+}
459
+
460
+func postImagesInsert(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
461
+	if err := parseForm(r); err != nil {
462
+		return err
463
+	}
464
+	if vars == nil {
465
+		return fmt.Errorf("Missing parameter")
466
+	}
467
+	job := eng.Job("insert", vars["name"], r.Form.Get("url"), r.Form.Get("path"))
468
+	if version.GreaterThan("1.0") {
469
+		job.SetenvBool("json", true)
470
+		streamJSON(job, w, false)
471
+	} else {
472
+		job.Stdout.Add(w)
473
+	}
474
+	if err := job.Run(); err != nil {
475
+		if !job.Stdout.Used() {
476
+			return err
477
+		}
478
+		sf := utils.NewStreamFormatter(version.GreaterThan("1.0"))
479
+		w.Write(sf.FormatError(err))
480
+	}
481
+
482
+	return nil
483
+}
484
+
485
+func postImagesPush(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
486
+	if vars == nil {
487
+		return fmt.Errorf("Missing parameter")
488
+	}
489
+
490
+	metaHeaders := map[string][]string{}
491
+	for k, v := range r.Header {
492
+		if strings.HasPrefix(k, "X-Meta-") {
493
+			metaHeaders[k] = v
494
+		}
495
+	}
496
+	if err := parseForm(r); err != nil {
497
+		return err
498
+	}
499
+	authConfig := &registry.AuthConfig{}
500
+
501
+	authEncoded := r.Header.Get("X-Registry-Auth")
502
+	if authEncoded != "" {
503
+		// the new format is to handle the authConfig as a header
504
+		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
505
+		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
506
+			// to increase compatibility to existing api it is defaulting to be empty
507
+			authConfig = &registry.AuthConfig{}
508
+		}
509
+	} else {
510
+		// the old format is supported for compatibility if there was no authConfig header
511
+		if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil {
512
+			return err
513
+		}
514
+	}
515
+
516
+	job := eng.Job("push", vars["name"])
517
+	job.SetenvJson("metaHeaders", metaHeaders)
518
+	job.SetenvJson("authConfig", authConfig)
519
+	if version.GreaterThan("1.0") {
520
+		job.SetenvBool("json", true)
521
+		streamJSON(job, w, true)
522
+	} else {
523
+		job.Stdout.Add(utils.NewWriteFlusher(w))
524
+	}
525
+
526
+	if err := job.Run(); err != nil {
527
+		if !job.Stdout.Used() {
528
+			return err
529
+		}
530
+		sf := utils.NewStreamFormatter(version.GreaterThan("1.0"))
531
+		w.Write(sf.FormatError(err))
532
+	}
533
+	return nil
534
+}
535
+
536
+func getImagesGet(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
537
+	if vars == nil {
538
+		return fmt.Errorf("Missing parameter")
539
+	}
540
+	if version.GreaterThan("1.0") {
541
+		w.Header().Set("Content-Type", "application/x-tar")
542
+	}
543
+	job := eng.Job("image_export", vars["name"])
544
+	job.Stdout.Add(w)
545
+	return job.Run()
546
+}
547
+
548
+func postImagesLoad(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
549
+	job := eng.Job("load")
550
+	job.Stdin.Add(r.Body)
551
+	return job.Run()
552
+}
553
+
554
+func postContainersCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
555
+	if err := parseForm(r); err != nil {
556
+		return nil
557
+	}
558
+	var (
559
+		out         engine.Env
560
+		job         = eng.Job("create", r.Form.Get("name"))
561
+		outWarnings []string
562
+		outId       string
563
+		warnings    = bytes.NewBuffer(nil)
564
+	)
565
+	if err := job.DecodeEnv(r.Body); err != nil {
566
+		return err
567
+	}
568
+	// Read container ID from the first line of stdout
569
+	job.Stdout.AddString(&outId)
570
+	// Read warnings from stderr
571
+	job.Stderr.Add(warnings)
572
+	if err := job.Run(); err != nil {
573
+		return err
574
+	}
575
+	// Parse warnings from stderr
576
+	scanner := bufio.NewScanner(warnings)
577
+	for scanner.Scan() {
578
+		outWarnings = append(outWarnings, scanner.Text())
579
+	}
580
+	out.Set("Id", outId)
581
+	out.SetList("Warnings", outWarnings)
582
+	return writeJSON(w, http.StatusCreated, out)
583
+}
584
+
585
+func postContainersRestart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
586
+	if err := parseForm(r); err != nil {
587
+		return err
588
+	}
589
+	if vars == nil {
590
+		return fmt.Errorf("Missing parameter")
591
+	}
592
+	job := eng.Job("restart", vars["name"])
593
+	job.Setenv("t", r.Form.Get("t"))
594
+	if err := job.Run(); err != nil {
595
+		return err
596
+	}
597
+	w.WriteHeader(http.StatusNoContent)
598
+	return nil
599
+}
600
+
601
+func deleteContainers(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
602
+	if err := parseForm(r); err != nil {
603
+		return err
604
+	}
605
+	if vars == nil {
606
+		return fmt.Errorf("Missing parameter")
607
+	}
608
+	job := eng.Job("container_delete", vars["name"])
609
+	job.Setenv("removeVolume", r.Form.Get("v"))
610
+	job.Setenv("removeLink", r.Form.Get("link"))
611
+	job.Setenv("forceRemove", r.Form.Get("force"))
612
+	if err := job.Run(); err != nil {
613
+		return err
614
+	}
615
+	w.WriteHeader(http.StatusNoContent)
616
+	return nil
617
+}
618
+
619
+func deleteImages(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
620
+	if err := parseForm(r); err != nil {
621
+		return err
622
+	}
623
+	if vars == nil {
624
+		return fmt.Errorf("Missing parameter")
625
+	}
626
+	var job = eng.Job("image_delete", vars["name"])
627
+	streamJSON(job, w, false)
628
+	job.Setenv("force", r.Form.Get("force"))
629
+	job.Setenv("noprune", r.Form.Get("noprune"))
630
+
631
+	return job.Run()
632
+}
633
+
634
+func postContainersStart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
635
+	if vars == nil {
636
+		return fmt.Errorf("Missing parameter")
637
+	}
638
+	name := vars["name"]
639
+	job := eng.Job("start", name)
640
+	// allow a nil body for backwards compatibility
641
+	if r.Body != nil {
642
+		if api.MatchesContentType(r.Header.Get("Content-Type"), "application/json") {
643
+			if err := job.DecodeEnv(r.Body); err != nil {
644
+				return err
645
+			}
646
+		}
647
+	}
648
+	if err := job.Run(); err != nil {
649
+		return err
650
+	}
651
+	w.WriteHeader(http.StatusNoContent)
652
+	return nil
653
+}
654
+
655
+func postContainersStop(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
656
+	if err := parseForm(r); err != nil {
657
+		return err
658
+	}
659
+	if vars == nil {
660
+		return fmt.Errorf("Missing parameter")
661
+	}
662
+	job := eng.Job("stop", vars["name"])
663
+	job.Setenv("t", r.Form.Get("t"))
664
+	if err := job.Run(); err != nil {
665
+		return err
666
+	}
667
+	w.WriteHeader(http.StatusNoContent)
668
+	return nil
669
+}
670
+
671
+func postContainersWait(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
672
+	if vars == nil {
673
+		return fmt.Errorf("Missing parameter")
674
+	}
675
+	var (
676
+		env    engine.Env
677
+		status string
678
+		job    = eng.Job("wait", vars["name"])
679
+	)
680
+	job.Stdout.AddString(&status)
681
+	if err := job.Run(); err != nil {
682
+		return err
683
+	}
684
+	// Parse a 16-bit encoded integer to map typical unix exit status.
685
+	_, err := strconv.ParseInt(status, 10, 16)
686
+	if err != nil {
687
+		return err
688
+	}
689
+	env.Set("StatusCode", status)
690
+	return writeJSON(w, http.StatusOK, env)
691
+}
692
+
693
+func postContainersResize(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
694
+	if err := parseForm(r); err != nil {
695
+		return err
696
+	}
697
+	if vars == nil {
698
+		return fmt.Errorf("Missing parameter")
699
+	}
700
+	if err := eng.Job("resize", vars["name"], r.Form.Get("h"), r.Form.Get("w")).Run(); err != nil {
701
+		return err
702
+	}
703
+	return nil
704
+}
705
+
706
+func postContainersAttach(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
707
+	if err := parseForm(r); err != nil {
708
+		return err
709
+	}
710
+	if vars == nil {
711
+		return fmt.Errorf("Missing parameter")
712
+	}
713
+
714
+	var (
715
+		job    = eng.Job("inspect", vars["name"], "container")
716
+		c, err = job.Stdout.AddEnv()
717
+	)
718
+	if err != nil {
719
+		return err
720
+	}
721
+	if err = job.Run(); err != nil {
722
+		return err
723
+	}
724
+
725
+	inStream, outStream, err := hijackServer(w)
726
+	if err != nil {
727
+		return err
728
+	}
729
+	defer func() {
730
+		if tcpc, ok := inStream.(*net.TCPConn); ok {
731
+			tcpc.CloseWrite()
732
+		} else {
733
+			inStream.Close()
734
+		}
735
+	}()
736
+	defer func() {
737
+		if tcpc, ok := outStream.(*net.TCPConn); ok {
738
+			tcpc.CloseWrite()
739
+		} else if closer, ok := outStream.(io.Closer); ok {
740
+			closer.Close()
741
+		}
742
+	}()
743
+
744
+	var errStream io.Writer
745
+
746
+	fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
747
+
748
+	if c.GetSubEnv("Config") != nil && !c.GetSubEnv("Config").GetBool("Tty") && version.GreaterThanOrEqualTo("1.6") {
749
+		errStream = utils.NewStdWriter(outStream, utils.Stderr)
750
+		outStream = utils.NewStdWriter(outStream, utils.Stdout)
751
+	} else {
752
+		errStream = outStream
753
+	}
754
+
755
+	job = eng.Job("attach", vars["name"])
756
+	job.Setenv("logs", r.Form.Get("logs"))
757
+	job.Setenv("stream", r.Form.Get("stream"))
758
+	job.Setenv("stdin", r.Form.Get("stdin"))
759
+	job.Setenv("stdout", r.Form.Get("stdout"))
760
+	job.Setenv("stderr", r.Form.Get("stderr"))
761
+	job.Stdin.Add(inStream)
762
+	job.Stdout.Add(outStream)
763
+	job.Stderr.Set(errStream)
764
+	if err := job.Run(); err != nil {
765
+		fmt.Fprintf(outStream, "Error: %s\n", err)
766
+
767
+	}
768
+	return nil
769
+}
770
+
771
+func wsContainersAttach(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
772
+	if err := parseForm(r); err != nil {
773
+		return err
774
+	}
775
+	if vars == nil {
776
+		return fmt.Errorf("Missing parameter")
777
+	}
778
+
779
+	if err := eng.Job("inspect", vars["name"], "container").Run(); err != nil {
780
+		return err
781
+	}
782
+
783
+	h := websocket.Handler(func(ws *websocket.Conn) {
784
+		defer ws.Close()
785
+		job := eng.Job("attach", vars["name"])
786
+		job.Setenv("logs", r.Form.Get("logs"))
787
+		job.Setenv("stream", r.Form.Get("stream"))
788
+		job.Setenv("stdin", r.Form.Get("stdin"))
789
+		job.Setenv("stdout", r.Form.Get("stdout"))
790
+		job.Setenv("stderr", r.Form.Get("stderr"))
791
+		job.Stdin.Add(ws)
792
+		job.Stdout.Add(ws)
793
+		job.Stderr.Set(ws)
794
+		if err := job.Run(); err != nil {
795
+			utils.Errorf("Error: %s", err)
796
+		}
797
+	})
798
+	h.ServeHTTP(w, r)
799
+
800
+	return nil
801
+}
802
+
803
+func getContainersByName(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
804
+	if vars == nil {
805
+		return fmt.Errorf("Missing parameter")
806
+	}
807
+	var job = eng.Job("inspect", vars["name"], "container")
808
+	streamJSON(job, w, false)
809
+	job.SetenvBool("conflict", true) //conflict=true to detect conflict between containers and images in the job
810
+	return job.Run()
811
+}
812
+
813
+func getImagesByName(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
814
+	if vars == nil {
815
+		return fmt.Errorf("Missing parameter")
816
+	}
817
+	var job = eng.Job("inspect", vars["name"], "image")
818
+	streamJSON(job, w, false)
819
+	job.SetenvBool("conflict", true) //conflict=true to detect conflict between containers and images in the job
820
+	return job.Run()
821
+}
822
+
823
+func postBuild(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
824
+	if version.LessThan("1.3") {
825
+		return fmt.Errorf("Multipart upload for build is no longer supported. Please upgrade your docker client.")
826
+	}
827
+	var (
828
+		authEncoded       = r.Header.Get("X-Registry-Auth")
829
+		authConfig        = &registry.AuthConfig{}
830
+		configFileEncoded = r.Header.Get("X-Registry-Config")
831
+		configFile        = &registry.ConfigFile{}
832
+		job               = eng.Job("build")
833
+	)
834
+
835
+	// This block can be removed when API versions prior to 1.9 are deprecated.
836
+	// Both headers will be parsed and sent along to the daemon, but if a non-empty
837
+	// ConfigFile is present, any value provided as an AuthConfig directly will
838
+	// be overridden. See BuildFile::CmdFrom for details.
839
+	if version.LessThan("1.9") && authEncoded != "" {
840
+		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
841
+		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
842
+			// for a pull it is not an error if no auth was given
843
+			// to increase compatibility with the existing api it is defaulting to be empty
844
+			authConfig = &registry.AuthConfig{}
845
+		}
846
+	}
847
+
848
+	if configFileEncoded != "" {
849
+		configFileJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(configFileEncoded))
850
+		if err := json.NewDecoder(configFileJson).Decode(configFile); 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
+			configFile = &registry.ConfigFile{}
854
+		}
855
+	}
856
+
857
+	if version.GreaterThanOrEqualTo("1.8") {
858
+		job.SetenvBool("json", true)
859
+		streamJSON(job, w, true)
860
+	} else {
861
+		job.Stdout.Add(utils.NewWriteFlusher(w))
862
+	}
863
+	job.Stdin.Add(r.Body)
864
+	job.Setenv("remote", r.FormValue("remote"))
865
+	job.Setenv("t", r.FormValue("t"))
866
+	job.Setenv("q", r.FormValue("q"))
867
+	job.Setenv("nocache", r.FormValue("nocache"))
868
+	job.Setenv("rm", r.FormValue("rm"))
869
+	job.SetenvJson("authConfig", authConfig)
870
+	job.SetenvJson("configFile", configFile)
871
+
872
+	if err := job.Run(); err != nil {
873
+		if !job.Stdout.Used() {
874
+			return err
875
+		}
876
+		sf := utils.NewStreamFormatter(version.GreaterThanOrEqualTo("1.8"))
877
+		w.Write(sf.FormatError(err))
878
+	}
879
+	return nil
880
+}
881
+
882
+func postContainersCopy(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
883
+	if vars == nil {
884
+		return fmt.Errorf("Missing parameter")
885
+	}
886
+
887
+	var copyData engine.Env
888
+
889
+	if contentType := r.Header.Get("Content-Type"); api.MatchesContentType(contentType, "application/json") {
890
+		if err := copyData.Decode(r.Body); err != nil {
891
+			return err
892
+		}
893
+	} else {
894
+		return fmt.Errorf("Content-Type not supported: %s", contentType)
895
+	}
896
+
897
+	if copyData.Get("Resource") == "" {
898
+		return fmt.Errorf("Path cannot be empty")
899
+	}
900
+
901
+	origResource := copyData.Get("Resource")
902
+
903
+	if copyData.Get("Resource")[0] == '/' {
904
+		copyData.Set("Resource", copyData.Get("Resource")[1:])
905
+	}
906
+
907
+	job := eng.Job("container_copy", vars["name"], copyData.Get("Resource"))
908
+	job.Stdout.Add(w)
909
+	if err := job.Run(); err != nil {
910
+		utils.Errorf("%s", err.Error())
911
+		if strings.Contains(err.Error(), "No such container") {
912
+			w.WriteHeader(http.StatusNotFound)
913
+		} else if strings.Contains(err.Error(), "no such file or directory") {
914
+			return fmt.Errorf("Could not find the file %s in container %s", origResource, vars["name"])
915
+		}
916
+	}
917
+	return nil
918
+}
919
+
920
+func optionsHandler(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
921
+	w.WriteHeader(http.StatusOK)
922
+	return nil
923
+}
924
+func writeCorsHeaders(w http.ResponseWriter, r *http.Request) {
925
+	w.Header().Add("Access-Control-Allow-Origin", "*")
926
+	w.Header().Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
927
+	w.Header().Add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS")
928
+}
929
+
930
+func makeHttpHandler(eng *engine.Engine, logging bool, localMethod string, localRoute string, handlerFunc HttpApiFunc, enableCors bool, dockerVersion version.Version) http.HandlerFunc {
931
+	return func(w http.ResponseWriter, r *http.Request) {
932
+		// log the request
933
+		utils.Debugf("Calling %s %s", localMethod, localRoute)
934
+
935
+		if logging {
936
+			log.Println(r.Method, r.RequestURI)
937
+		}
938
+
939
+		if strings.Contains(r.Header.Get("User-Agent"), "Docker-Client/") {
940
+			userAgent := strings.Split(r.Header.Get("User-Agent"), "/")
941
+			if len(userAgent) == 2 && !dockerVersion.Equal(userAgent[1]) {
942
+				utils.Debugf("Warning: client and server don't have the same version (client: %s, server: %s)", userAgent[1], dockerVersion)
943
+			}
944
+		}
945
+		version := version.Version(mux.Vars(r)["version"])
946
+		if version == "" {
947
+			version = api.APIVERSION
948
+		}
949
+		if enableCors {
950
+			writeCorsHeaders(w, r)
951
+		}
952
+
953
+		if version.GreaterThan(api.APIVERSION) {
954
+			http.Error(w, fmt.Errorf("client and server don't have same version (client : %s, server: %s)", version, api.APIVERSION).Error(), http.StatusNotFound)
955
+			return
956
+		}
957
+
958
+		if err := handlerFunc(eng, version, w, r, mux.Vars(r)); err != nil {
959
+			utils.Errorf("Error: %s", err)
960
+			httpError(w, err)
961
+		}
962
+	}
963
+}
964
+
965
+// Replicated from expvar.go as not public.
966
+func expvarHandler(w http.ResponseWriter, r *http.Request) {
967
+	w.Header().Set("Content-Type", "application/json; charset=utf-8")
968
+	fmt.Fprintf(w, "{\n")
969
+	first := true
970
+	expvar.Do(func(kv expvar.KeyValue) {
971
+		if !first {
972
+			fmt.Fprintf(w, ",\n")
973
+		}
974
+		first = false
975
+		fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value)
976
+	})
977
+	fmt.Fprintf(w, "\n}\n")
978
+}
979
+
980
+func AttachProfiler(router *mux.Router) {
981
+	router.HandleFunc("/debug/vars", expvarHandler)
982
+	router.HandleFunc("/debug/pprof/", pprof.Index)
983
+	router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
984
+	router.HandleFunc("/debug/pprof/profile", pprof.Profile)
985
+	router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
986
+	router.HandleFunc("/debug/pprof/heap", pprof.Handler("heap").ServeHTTP)
987
+	router.HandleFunc("/debug/pprof/goroutine", pprof.Handler("goroutine").ServeHTTP)
988
+	router.HandleFunc("/debug/pprof/threadcreate", pprof.Handler("threadcreate").ServeHTTP)
989
+}
990
+
991
+func createRouter(eng *engine.Engine, logging, enableCors bool, dockerVersion string) (*mux.Router, error) {
992
+	r := mux.NewRouter()
993
+	if os.Getenv("DEBUG") != "" {
994
+		AttachProfiler(r)
995
+	}
996
+	m := map[string]map[string]HttpApiFunc{
997
+		"GET": {
998
+			"/events":                         getEvents,
999
+			"/info":                           getInfo,
1000
+			"/version":                        getVersion,
1001
+			"/images/json":                    getImagesJSON,
1002
+			"/images/viz":                     getImagesViz,
1003
+			"/images/search":                  getImagesSearch,
1004
+			"/images/{name:.*}/get":           getImagesGet,
1005
+			"/images/{name:.*}/history":       getImagesHistory,
1006
+			"/images/{name:.*}/json":          getImagesByName,
1007
+			"/containers/ps":                  getContainersJSON,
1008
+			"/containers/json":                getContainersJSON,
1009
+			"/containers/{name:.*}/export":    getContainersExport,
1010
+			"/containers/{name:.*}/changes":   getContainersChanges,
1011
+			"/containers/{name:.*}/json":      getContainersByName,
1012
+			"/containers/{name:.*}/top":       getContainersTop,
1013
+			"/containers/{name:.*}/attach/ws": wsContainersAttach,
1014
+		},
1015
+		"POST": {
1016
+			"/auth":                         postAuth,
1017
+			"/commit":                       postCommit,
1018
+			"/build":                        postBuild,
1019
+			"/images/create":                postImagesCreate,
1020
+			"/images/{name:.*}/insert":      postImagesInsert,
1021
+			"/images/load":                  postImagesLoad,
1022
+			"/images/{name:.*}/push":        postImagesPush,
1023
+			"/images/{name:.*}/tag":         postImagesTag,
1024
+			"/containers/create":            postContainersCreate,
1025
+			"/containers/{name:.*}/kill":    postContainersKill,
1026
+			"/containers/{name:.*}/restart": postContainersRestart,
1027
+			"/containers/{name:.*}/start":   postContainersStart,
1028
+			"/containers/{name:.*}/stop":    postContainersStop,
1029
+			"/containers/{name:.*}/wait":    postContainersWait,
1030
+			"/containers/{name:.*}/resize":  postContainersResize,
1031
+			"/containers/{name:.*}/attach":  postContainersAttach,
1032
+			"/containers/{name:.*}/copy":    postContainersCopy,
1033
+		},
1034
+		"DELETE": {
1035
+			"/containers/{name:.*}": deleteContainers,
1036
+			"/images/{name:.*}":     deleteImages,
1037
+		},
1038
+		"OPTIONS": {
1039
+			"": optionsHandler,
1040
+		},
1041
+	}
1042
+
1043
+	for method, routes := range m {
1044
+		for route, fct := range routes {
1045
+			utils.Debugf("Registering %s, %s", method, route)
1046
+			// NOTE: scope issue, make sure the variables are local and won't be changed
1047
+			localRoute := route
1048
+			localFct := fct
1049
+			localMethod := method
1050
+
1051
+			// build the handler function
1052
+			f := makeHttpHandler(eng, logging, localMethod, localRoute, localFct, enableCors, version.Version(dockerVersion))
1053
+
1054
+			// add the new route
1055
+			if localRoute == "" {
1056
+				r.Methods(localMethod).HandlerFunc(f)
1057
+			} else {
1058
+				r.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(f)
1059
+				r.Path(localRoute).Methods(localMethod).HandlerFunc(f)
1060
+			}
1061
+		}
1062
+	}
1063
+
1064
+	return r, nil
1065
+}
1066
+
1067
+// ServeRequest processes a single http request to the docker remote api.
1068
+// FIXME: refactor this to be part of Server and not require re-creating a new
1069
+// router each time. This requires first moving ListenAndServe into Server.
1070
+func ServeRequest(eng *engine.Engine, apiversion version.Version, w http.ResponseWriter, req *http.Request) error {
1071
+	router, err := createRouter(eng, false, true, "")
1072
+	if err != nil {
1073
+		return err
1074
+	}
1075
+	// Insert APIVERSION into the request as a convenience
1076
+	req.URL.Path = fmt.Sprintf("/v%s%s", apiversion, req.URL.Path)
1077
+	router.ServeHTTP(w, req)
1078
+	return nil
1079
+}
1080
+
1081
+// ServeFD creates an http.Server and sets it up to serve given a socket activated
1082
+// argument.
1083
+func ServeFd(addr string, handle http.Handler) error {
1084
+	ls, e := systemd.ListenFD(addr)
1085
+	if e != nil {
1086
+		return e
1087
+	}
1088
+
1089
+	chErrors := make(chan error, len(ls))
1090
+
1091
+	// We don't want to start serving on these sockets until the
1092
+	// "initserver" job has completed. Otherwise required handlers
1093
+	// won't be ready.
1094
+	<-activationLock
1095
+
1096
+	// Since ListenFD will return one or more sockets we have
1097
+	// to create a go func to spawn off multiple serves
1098
+	for i := range ls {
1099
+		listener := ls[i]
1100
+		go func() {
1101
+			httpSrv := http.Server{Handler: handle}
1102
+			chErrors <- httpSrv.Serve(listener)
1103
+		}()
1104
+	}
1105
+
1106
+	for i := 0; i < len(ls); i += 1 {
1107
+		err := <-chErrors
1108
+		if err != nil {
1109
+			return err
1110
+		}
1111
+	}
1112
+
1113
+	return nil
1114
+}
1115
+
1116
+func lookupGidByName(nameOrGid string) (int, error) {
1117
+	groups, err := user.ParseGroupFilter(func(g *user.Group) bool {
1118
+		return g.Name == nameOrGid || strconv.Itoa(g.Gid) == nameOrGid
1119
+	})
1120
+	if err != nil {
1121
+		return -1, err
1122
+	}
1123
+	if groups != nil && len(groups) > 0 {
1124
+		return groups[0].Gid, nil
1125
+	}
1126
+	return -1, fmt.Errorf("Group %s not found", nameOrGid)
1127
+}
1128
+
1129
+func changeGroup(addr string, nameOrGid string) error {
1130
+	gid, err := lookupGidByName(nameOrGid)
1131
+	if err != nil {
1132
+		return err
1133
+	}
1134
+
1135
+	utils.Debugf("%s group found. gid: %d", nameOrGid, gid)
1136
+	return os.Chown(addr, 0, gid)
1137
+}
1138
+
1139
+// ListenAndServe sets up the required http.Server and gets it listening for
1140
+// each addr passed in and does protocol specific checking.
1141
+func ListenAndServe(proto, addr string, job *engine.Job) error {
1142
+	r, err := createRouter(job.Eng, job.GetenvBool("Logging"), job.GetenvBool("EnableCors"), job.Getenv("Version"))
1143
+	if err != nil {
1144
+		return err
1145
+	}
1146
+
1147
+	if proto == "fd" {
1148
+		return ServeFd(addr, r)
1149
+	}
1150
+
1151
+	if proto == "unix" {
1152
+		if err := syscall.Unlink(addr); err != nil && !os.IsNotExist(err) {
1153
+			return err
1154
+		}
1155
+	}
1156
+
1157
+	l, err := listenbuffer.NewListenBuffer(proto, addr, activationLock)
1158
+	if err != nil {
1159
+		return err
1160
+	}
1161
+
1162
+	if proto != "unix" && (job.GetenvBool("Tls") || job.GetenvBool("TlsVerify")) {
1163
+		tlsCert := job.Getenv("TlsCert")
1164
+		tlsKey := job.Getenv("TlsKey")
1165
+		cert, err := tls.LoadX509KeyPair(tlsCert, tlsKey)
1166
+		if err != nil {
1167
+			return fmt.Errorf("Couldn't load X509 key pair (%s, %s): %s. Key encrypted?",
1168
+				tlsCert, tlsKey, err)
1169
+		}
1170
+		tlsConfig := &tls.Config{
1171
+			NextProtos:   []string{"http/1.1"},
1172
+			Certificates: []tls.Certificate{cert},
1173
+		}
1174
+		if job.GetenvBool("TlsVerify") {
1175
+			certPool := x509.NewCertPool()
1176
+			file, err := ioutil.ReadFile(job.Getenv("TlsCa"))
1177
+			if err != nil {
1178
+				return fmt.Errorf("Couldn't read CA certificate: %s", err)
1179
+			}
1180
+			certPool.AppendCertsFromPEM(file)
1181
+
1182
+			tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
1183
+			tlsConfig.ClientCAs = certPool
1184
+		}
1185
+		l = tls.NewListener(l, tlsConfig)
1186
+	}
1187
+
1188
+	// Basic error and sanity checking
1189
+	switch proto {
1190
+	case "tcp":
1191
+		if !strings.HasPrefix(addr, "127.0.0.1") && !job.GetenvBool("TlsVerify") {
1192
+			log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
1193
+		}
1194
+	case "unix":
1195
+		if err := os.Chmod(addr, 0660); err != nil {
1196
+			return err
1197
+		}
1198
+		socketGroup := job.Getenv("SocketGroup")
1199
+		if socketGroup != "" {
1200
+			if err := changeGroup(addr, socketGroup); err != nil {
1201
+				if socketGroup == "docker" {
1202
+					// if the user hasn't explicitly specified the group ownership, don't fail on errors.
1203
+					utils.Debugf("Warning: could not chgrp %s to docker: %s", addr, err.Error())
1204
+				} else {
1205
+					return err
1206
+				}
1207
+			}
1208
+		}
1209
+	default:
1210
+		return fmt.Errorf("Invalid protocol format.")
1211
+	}
1212
+
1213
+	httpSrv := http.Server{Addr: addr, Handler: r}
1214
+	return httpSrv.Serve(l)
1215
+}
1216
+
1217
+// ServeApi loops through all of the protocols sent in to docker and spawns
1218
+// off a go routine to setup a serving http.Server for each.
1219
+func ServeApi(job *engine.Job) engine.Status {
1220
+	var (
1221
+		protoAddrs = job.Args
1222
+		chErrors   = make(chan error, len(protoAddrs))
1223
+	)
1224
+	activationLock = make(chan struct{})
1225
+
1226
+	if err := job.Eng.Register("acceptconnections", AcceptConnections); err != nil {
1227
+		return job.Error(err)
1228
+	}
1229
+
1230
+	for _, protoAddr := range protoAddrs {
1231
+		protoAddrParts := strings.SplitN(protoAddr, "://", 2)
1232
+		go func() {
1233
+			log.Printf("Listening for HTTP on %s (%s)\n", protoAddrParts[0], protoAddrParts[1])
1234
+			chErrors <- ListenAndServe(protoAddrParts[0], protoAddrParts[1], job)
1235
+		}()
1236
+	}
1237
+
1238
+	for i := 0; i < len(protoAddrs); i += 1 {
1239
+		err := <-chErrors
1240
+		if err != nil {
1241
+			return job.Error(err)
1242
+		}
1243
+	}
1244
+
1245
+	return engine.StatusOK
1246
+}
1247
+
1248
+func AcceptConnections(job *engine.Job) engine.Status {
1249
+	// Tell the init daemon we are accepting requests
1250
+	go systemd.SdNotify("READY=1")
1251
+
1252
+	// close the lock so the listeners start accepting connections
1253
+	close(activationLock)
1254
+
1255
+	return engine.StatusOK
1256
+}
... ...
@@ -1,7 +1,7 @@
1 1
 package builtins
2 2
 
3 3
 import (
4
-	"github.com/dotcloud/docker/api"
4
+	api "github.com/dotcloud/docker/api/server"
5 5
 	"github.com/dotcloud/docker/engine"
6 6
 	"github.com/dotcloud/docker/runtime/networkdriver/bridge"
7 7
 	"github.com/dotcloud/docker/server"
... ...
@@ -10,6 +10,7 @@ import (
10 10
 	"strings"
11 11
 
12 12
 	"github.com/dotcloud/docker/api"
13
+	"github.com/dotcloud/docker/api/client"
13 14
 	"github.com/dotcloud/docker/builtins"
14 15
 	"github.com/dotcloud/docker/dockerversion"
15 16
 	"github.com/dotcloud/docker/engine"
... ...
@@ -178,7 +179,7 @@ func main() {
178 178
 		protoAddrParts := strings.SplitN(flHosts.GetAll()[0], "://", 2)
179 179
 
180 180
 		var (
181
-			cli       *api.DockerCli
181
+			cli       *client.DockerCli
182 182
 			tlsConfig tls.Config
183 183
 		)
184 184
 		tlsConfig.InsecureSkipVerify = true
... ...
@@ -211,9 +212,9 @@ func main() {
211 211
 		}
212 212
 
213 213
 		if *flTls || *flTlsVerify {
214
-			cli = api.NewDockerCli(os.Stdin, os.Stdout, os.Stderr, protoAddrParts[0], protoAddrParts[1], &tlsConfig)
214
+			cli = client.NewDockerCli(os.Stdin, os.Stdout, os.Stderr, protoAddrParts[0], protoAddrParts[1], &tlsConfig)
215 215
 		} else {
216
-			cli = api.NewDockerCli(os.Stdin, os.Stdout, os.Stderr, protoAddrParts[0], protoAddrParts[1], nil)
216
+			cli = client.NewDockerCli(os.Stdin, os.Stdout, os.Stderr, protoAddrParts[0], protoAddrParts[1], nil)
217 217
 		}
218 218
 
219 219
 		if err := cli.ParseCommands(flag.Args()...); err != nil {