Browse code

api/client - The code for all cli commands are in one file #11610

Signed-off-by: Joey Gibson <joey@joeygibson.com>

Joey Gibson authored on 2015/03/25 12:57:23
Showing 41 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,85 @@
0
+package client
1
+
2
+import (
3
+	"fmt"
4
+	"io"
5
+	"net/url"
6
+
7
+	log "github.com/Sirupsen/logrus"
8
+	"github.com/docker/docker/engine"
9
+	flag "github.com/docker/docker/pkg/mflag"
10
+	"github.com/docker/docker/pkg/signal"
11
+	"github.com/docker/docker/utils"
12
+)
13
+
14
+func (cli *DockerCli) CmdAttach(args ...string) error {
15
+	var (
16
+		cmd     = cli.Subcmd("attach", "CONTAINER", "Attach to a running container", true)
17
+		noStdin = cmd.Bool([]string{"#nostdin", "-no-stdin"}, false, "Do not attach STDIN")
18
+		proxy   = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxy all received signals to the process")
19
+	)
20
+	cmd.Require(flag.Exact, 1)
21
+
22
+	utils.ParseFlags(cmd, args, true)
23
+	name := cmd.Arg(0)
24
+
25
+	stream, _, err := cli.call("GET", "/containers/"+name+"/json", nil, false)
26
+	if err != nil {
27
+		return err
28
+	}
29
+
30
+	env := engine.Env{}
31
+	if err := env.Decode(stream); err != nil {
32
+		return err
33
+	}
34
+
35
+	if !env.GetSubEnv("State").GetBool("Running") {
36
+		return fmt.Errorf("You cannot attach to a stopped container, start it first")
37
+	}
38
+
39
+	var (
40
+		config = env.GetSubEnv("Config")
41
+		tty    = config.GetBool("Tty")
42
+	)
43
+
44
+	if err := cli.CheckTtyInput(!*noStdin, tty); err != nil {
45
+		return err
46
+	}
47
+
48
+	if tty && cli.isTerminalOut {
49
+		if err := cli.monitorTtySize(cmd.Arg(0), false); err != nil {
50
+			log.Debugf("Error monitoring TTY size: %s", err)
51
+		}
52
+	}
53
+
54
+	var in io.ReadCloser
55
+
56
+	v := url.Values{}
57
+	v.Set("stream", "1")
58
+	if !*noStdin && config.GetBool("OpenStdin") {
59
+		v.Set("stdin", "1")
60
+		in = cli.in
61
+	}
62
+
63
+	v.Set("stdout", "1")
64
+	v.Set("stderr", "1")
65
+
66
+	if *proxy && !tty {
67
+		sigc := cli.forwardAllSignals(cmd.Arg(0))
68
+		defer signal.StopCatch(sigc)
69
+	}
70
+
71
+	if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), tty, in, cli.out, cli.err, nil, nil); err != nil {
72
+		return err
73
+	}
74
+
75
+	_, status, err := getExitCode(cli, cmd.Arg(0))
76
+	if err != nil {
77
+		return err
78
+	}
79
+	if status != 0 {
80
+		return &utils.StatusError{StatusCode: status}
81
+	}
82
+
83
+	return nil
84
+}
0 85
new file mode 100644
... ...
@@ -0,0 +1,302 @@
0
+package client
1
+
2
+import (
3
+	"bufio"
4
+	"encoding/base64"
5
+	"encoding/json"
6
+	"fmt"
7
+	"io"
8
+	"io/ioutil"
9
+	"net/http"
10
+	"net/url"
11
+	"os"
12
+	"os/exec"
13
+	"path"
14
+	"path/filepath"
15
+	"runtime"
16
+	"strconv"
17
+	"strings"
18
+
19
+	log "github.com/Sirupsen/logrus"
20
+	"github.com/docker/docker/api"
21
+	"github.com/docker/docker/graph"
22
+	"github.com/docker/docker/pkg/archive"
23
+	"github.com/docker/docker/pkg/fileutils"
24
+	flag "github.com/docker/docker/pkg/mflag"
25
+	"github.com/docker/docker/pkg/parsers"
26
+	"github.com/docker/docker/pkg/progressreader"
27
+	"github.com/docker/docker/pkg/symlink"
28
+	"github.com/docker/docker/pkg/units"
29
+	"github.com/docker/docker/pkg/urlutil"
30
+	"github.com/docker/docker/registry"
31
+	"github.com/docker/docker/utils"
32
+)
33
+
34
+const (
35
+	tarHeaderSize = 512
36
+)
37
+
38
+func (cli *DockerCli) CmdBuild(args ...string) error {
39
+	cmd := cli.Subcmd("build", "PATH | URL | -", "Build a new image from the source code at PATH", true)
40
+	tag := cmd.String([]string{"t", "-tag"}, "", "Repository name (and optionally a tag) for the image")
41
+	suppressOutput := cmd.Bool([]string{"q", "-quiet"}, false, "Suppress the verbose output generated by the containers")
42
+	noCache := cmd.Bool([]string{"#no-cache", "-no-cache"}, false, "Do not use cache when building the image")
43
+	rm := cmd.Bool([]string{"#rm", "-rm"}, true, "Remove intermediate containers after a successful build")
44
+	forceRm := cmd.Bool([]string{"-force-rm"}, false, "Always remove intermediate containers")
45
+	pull := cmd.Bool([]string{"-pull"}, false, "Always attempt to pull a newer version of the image")
46
+	dockerfileName := cmd.String([]string{"f", "-file"}, "", "Name of the Dockerfile (Default is 'PATH/Dockerfile')")
47
+	flMemoryString := cmd.String([]string{"m", "-memory"}, "", "Memory limit")
48
+	flMemorySwap := cmd.String([]string{"-memory-swap"}, "", "Total memory (memory + swap), '-1' to disable swap")
49
+	flCpuShares := cmd.Int64([]string{"c", "-cpu-shares"}, 0, "CPU shares (relative weight)")
50
+	flCpuSetCpus := cmd.String([]string{"-cpuset-cpus"}, "", "CPUs in which to allow execution (0-3, 0,1)")
51
+
52
+	cmd.Require(flag.Exact, 1)
53
+
54
+	utils.ParseFlags(cmd, args, true)
55
+
56
+	var (
57
+		context  archive.Archive
58
+		isRemote bool
59
+		err      error
60
+	)
61
+
62
+	_, err = exec.LookPath("git")
63
+	hasGit := err == nil
64
+	if cmd.Arg(0) == "-" {
65
+		// As a special case, 'docker build -' will build from either an empty context with the
66
+		// contents of stdin as a Dockerfile, or a tar-ed context from stdin.
67
+		buf := bufio.NewReader(cli.in)
68
+		magic, err := buf.Peek(tarHeaderSize)
69
+		if err != nil && err != io.EOF {
70
+			return fmt.Errorf("failed to peek context header from STDIN: %v", err)
71
+		}
72
+		if !archive.IsArchive(magic) {
73
+			dockerfile, err := ioutil.ReadAll(buf)
74
+			if err != nil {
75
+				return fmt.Errorf("failed to read Dockerfile from STDIN: %v", err)
76
+			}
77
+
78
+			// -f option has no meaning when we're reading it from stdin,
79
+			// so just use our default Dockerfile name
80
+			*dockerfileName = api.DefaultDockerfileName
81
+			context, err = archive.Generate(*dockerfileName, string(dockerfile))
82
+		} else {
83
+			context = ioutil.NopCloser(buf)
84
+		}
85
+	} else if urlutil.IsURL(cmd.Arg(0)) && (!urlutil.IsGitURL(cmd.Arg(0)) || !hasGit) {
86
+		isRemote = true
87
+	} else {
88
+		root := cmd.Arg(0)
89
+		if urlutil.IsGitURL(root) {
90
+			remoteURL := cmd.Arg(0)
91
+			if !urlutil.IsGitTransport(remoteURL) {
92
+				remoteURL = "https://" + remoteURL
93
+			}
94
+
95
+			root, err = ioutil.TempDir("", "docker-build-git")
96
+			if err != nil {
97
+				return err
98
+			}
99
+			defer os.RemoveAll(root)
100
+
101
+			if output, err := exec.Command("git", "clone", "--recursive", remoteURL, root).CombinedOutput(); err != nil {
102
+				return fmt.Errorf("Error trying to use git: %s (%s)", err, output)
103
+			}
104
+		}
105
+		if _, err := os.Stat(root); err != nil {
106
+			return err
107
+		}
108
+
109
+		absRoot, err := filepath.Abs(root)
110
+		if err != nil {
111
+			return err
112
+		}
113
+
114
+		filename := *dockerfileName // path to Dockerfile
115
+
116
+		if *dockerfileName == "" {
117
+			// No -f/--file was specified so use the default
118
+			*dockerfileName = api.DefaultDockerfileName
119
+			filename = filepath.Join(absRoot, *dockerfileName)
120
+
121
+			// Just to be nice ;-) look for 'dockerfile' too but only
122
+			// use it if we found it, otherwise ignore this check
123
+			if _, err = os.Lstat(filename); os.IsNotExist(err) {
124
+				tmpFN := path.Join(absRoot, strings.ToLower(*dockerfileName))
125
+				if _, err = os.Lstat(tmpFN); err == nil {
126
+					*dockerfileName = strings.ToLower(*dockerfileName)
127
+					filename = tmpFN
128
+				}
129
+			}
130
+		}
131
+
132
+		origDockerfile := *dockerfileName // used for error msg
133
+		if filename, err = filepath.Abs(filename); err != nil {
134
+			return err
135
+		}
136
+
137
+		// Verify that 'filename' is within the build context
138
+		filename, err = symlink.FollowSymlinkInScope(filename, absRoot)
139
+		if err != nil {
140
+			return fmt.Errorf("The Dockerfile (%s) must be within the build context (%s)", origDockerfile, root)
141
+		}
142
+
143
+		// Now reset the dockerfileName to be relative to the build context
144
+		*dockerfileName, err = filepath.Rel(absRoot, filename)
145
+		if err != nil {
146
+			return err
147
+		}
148
+		// And canonicalize dockerfile name to a platform-independent one
149
+		*dockerfileName, err = archive.CanonicalTarNameForPath(*dockerfileName)
150
+		if err != nil {
151
+			return fmt.Errorf("Cannot canonicalize dockerfile path %s: %v", dockerfileName, err)
152
+		}
153
+
154
+		if _, err = os.Lstat(filename); os.IsNotExist(err) {
155
+			return fmt.Errorf("Cannot locate Dockerfile: %s", origDockerfile)
156
+		}
157
+		var includes = []string{"."}
158
+
159
+		excludes, err := utils.ReadDockerIgnore(path.Join(root, ".dockerignore"))
160
+		if err != nil {
161
+			return err
162
+		}
163
+
164
+		// If .dockerignore mentions .dockerignore or the Dockerfile
165
+		// then make sure we send both files over to the daemon
166
+		// because Dockerfile is, obviously, needed no matter what, and
167
+		// .dockerignore is needed to know if either one needs to be
168
+		// removed.  The deamon will remove them for us, if needed, after it
169
+		// parses the Dockerfile.
170
+		keepThem1, _ := fileutils.Matches(".dockerignore", excludes)
171
+		keepThem2, _ := fileutils.Matches(*dockerfileName, excludes)
172
+		if keepThem1 || keepThem2 {
173
+			includes = append(includes, ".dockerignore", *dockerfileName)
174
+		}
175
+
176
+		if err = utils.ValidateContextDirectory(root, excludes); err != nil {
177
+			return fmt.Errorf("Error checking context is accessible: '%s'. Please check permissions and try again.", err)
178
+		}
179
+		options := &archive.TarOptions{
180
+			Compression:     archive.Uncompressed,
181
+			ExcludePatterns: excludes,
182
+			IncludeFiles:    includes,
183
+		}
184
+		context, err = archive.TarWithOptions(root, options)
185
+		if err != nil {
186
+			return err
187
+		}
188
+	}
189
+
190
+	// windows: show error message about modified file permissions
191
+	// FIXME: this is not a valid warning when the daemon is running windows. should be removed once docker engine for windows can build.
192
+	if runtime.GOOS == "windows" {
193
+		log.Warn(`SECURITY WARNING: You are building a Docker image from Windows against a Linux Docker host. All files and directories added to build context will have '-rwxr-xr-x' permissions. It is recommended to double check and reset permissions for sensitive files and directories.`)
194
+	}
195
+
196
+	var body io.Reader
197
+	// Setup an upload progress bar
198
+	// FIXME: ProgressReader shouldn't be this annoying to use
199
+	if context != nil {
200
+		sf := utils.NewStreamFormatter(false)
201
+		body = progressreader.New(progressreader.Config{
202
+			In:        context,
203
+			Out:       cli.out,
204
+			Formatter: sf,
205
+			NewLines:  true,
206
+			ID:        "",
207
+			Action:    "Sending build context to Docker daemon",
208
+		})
209
+	}
210
+
211
+	var memory int64
212
+	if *flMemoryString != "" {
213
+		parsedMemory, err := units.RAMInBytes(*flMemoryString)
214
+		if err != nil {
215
+			return err
216
+		}
217
+		memory = parsedMemory
218
+	}
219
+
220
+	var memorySwap int64
221
+	if *flMemorySwap != "" {
222
+		if *flMemorySwap == "-1" {
223
+			memorySwap = -1
224
+		} else {
225
+			parsedMemorySwap, err := units.RAMInBytes(*flMemorySwap)
226
+			if err != nil {
227
+				return err
228
+			}
229
+			memorySwap = parsedMemorySwap
230
+		}
231
+	}
232
+	// Send the build context
233
+	v := &url.Values{}
234
+
235
+	//Check if the given image name can be resolved
236
+	if *tag != "" {
237
+		repository, tag := parsers.ParseRepositoryTag(*tag)
238
+		if err := registry.ValidateRepositoryName(repository); err != nil {
239
+			return err
240
+		}
241
+		if len(tag) > 0 {
242
+			if err := graph.ValidateTagName(tag); err != nil {
243
+				return err
244
+			}
245
+		}
246
+	}
247
+
248
+	v.Set("t", *tag)
249
+
250
+	if *suppressOutput {
251
+		v.Set("q", "1")
252
+	}
253
+	if isRemote {
254
+		v.Set("remote", cmd.Arg(0))
255
+	}
256
+	if *noCache {
257
+		v.Set("nocache", "1")
258
+	}
259
+	if *rm {
260
+		v.Set("rm", "1")
261
+	} else {
262
+		v.Set("rm", "0")
263
+	}
264
+
265
+	if *forceRm {
266
+		v.Set("forcerm", "1")
267
+	}
268
+
269
+	if *pull {
270
+		v.Set("pull", "1")
271
+	}
272
+
273
+	v.Set("cpusetcpus", *flCpuSetCpus)
274
+	v.Set("cpushares", strconv.FormatInt(*flCpuShares, 10))
275
+	v.Set("memory", strconv.FormatInt(memory, 10))
276
+	v.Set("memswap", strconv.FormatInt(memorySwap, 10))
277
+
278
+	v.Set("dockerfile", *dockerfileName)
279
+
280
+	cli.LoadConfigFile()
281
+
282
+	headers := http.Header(make(map[string][]string))
283
+	buf, err := json.Marshal(cli.configFile)
284
+	if err != nil {
285
+		return err
286
+	}
287
+	headers.Add("X-Registry-Config", base64.URLEncoding.EncodeToString(buf))
288
+
289
+	if context != nil {
290
+		headers.Set("Content-Type", "application/tar")
291
+	}
292
+	err = cli.stream("POST", fmt.Sprintf("/build?%s", v.Encode()), body, cli.out, headers)
293
+	if jerr, ok := err.(*utils.JSONError); ok {
294
+		// If no error code is set, default to 1
295
+		if jerr.Code == 0 {
296
+			jerr.Code = 1
297
+		}
298
+		return &utils.StatusError{Status: jerr.Message, StatusCode: jerr.Code}
299
+	}
300
+	return err
301
+}
0 302
deleted file mode 100644
... ...
@@ -1,2938 +0,0 @@
1
-package client
2
-
3
-import (
4
-	"bufio"
5
-	"bytes"
6
-	"encoding/base64"
7
-	"encoding/json"
8
-	"errors"
9
-	"fmt"
10
-	"io"
11
-	"io/ioutil"
12
-	"net/http"
13
-	"net/url"
14
-	"os"
15
-	"os/exec"
16
-	"path"
17
-	"path/filepath"
18
-	"runtime"
19
-	"sort"
20
-	"strconv"
21
-	"strings"
22
-	"sync"
23
-	"text/tabwriter"
24
-	"text/template"
25
-	"time"
26
-
27
-	log "github.com/Sirupsen/logrus"
28
-	"github.com/docker/docker/api"
29
-	"github.com/docker/docker/api/types"
30
-	"github.com/docker/docker/autogen/dockerversion"
31
-	"github.com/docker/docker/engine"
32
-	"github.com/docker/docker/graph"
33
-	"github.com/docker/docker/nat"
34
-	"github.com/docker/docker/opts"
35
-	"github.com/docker/docker/pkg/archive"
36
-	"github.com/docker/docker/pkg/fileutils"
37
-	"github.com/docker/docker/pkg/homedir"
38
-	flag "github.com/docker/docker/pkg/mflag"
39
-	"github.com/docker/docker/pkg/parsers"
40
-	"github.com/docker/docker/pkg/parsers/filters"
41
-	"github.com/docker/docker/pkg/progressreader"
42
-	"github.com/docker/docker/pkg/promise"
43
-	"github.com/docker/docker/pkg/resolvconf"
44
-	"github.com/docker/docker/pkg/signal"
45
-	"github.com/docker/docker/pkg/stringid"
46
-	"github.com/docker/docker/pkg/symlink"
47
-	"github.com/docker/docker/pkg/term"
48
-	"github.com/docker/docker/pkg/timeutils"
49
-	"github.com/docker/docker/pkg/units"
50
-	"github.com/docker/docker/pkg/urlutil"
51
-	"github.com/docker/docker/registry"
52
-	"github.com/docker/docker/runconfig"
53
-	"github.com/docker/docker/utils"
54
-)
55
-
56
-const (
57
-	tarHeaderSize = 512
58
-)
59
-
60
-func (cli *DockerCli) CmdHelp(args ...string) error {
61
-	if len(args) > 1 {
62
-		method, exists := cli.getMethod(args[:2]...)
63
-		if exists {
64
-			method("--help")
65
-			return nil
66
-		}
67
-	}
68
-	if len(args) > 0 {
69
-		method, exists := cli.getMethod(args[0])
70
-		if !exists {
71
-			fmt.Fprintf(cli.err, "docker: '%s' is not a docker command. See 'docker --help'.\n", args[0])
72
-			os.Exit(1)
73
-		} else {
74
-			method("--help")
75
-			return nil
76
-		}
77
-	}
78
-
79
-	flag.Usage()
80
-
81
-	return nil
82
-}
83
-
84
-func (cli *DockerCli) CmdBuild(args ...string) error {
85
-	cmd := cli.Subcmd("build", "PATH | URL | -", "Build a new image from the source code at PATH", true)
86
-	tag := cmd.String([]string{"t", "-tag"}, "", "Repository name (and optionally a tag) for the image")
87
-	suppressOutput := cmd.Bool([]string{"q", "-quiet"}, false, "Suppress the verbose output generated by the containers")
88
-	noCache := cmd.Bool([]string{"#no-cache", "-no-cache"}, false, "Do not use cache when building the image")
89
-	rm := cmd.Bool([]string{"#rm", "-rm"}, true, "Remove intermediate containers after a successful build")
90
-	forceRm := cmd.Bool([]string{"-force-rm"}, false, "Always remove intermediate containers")
91
-	pull := cmd.Bool([]string{"-pull"}, false, "Always attempt to pull a newer version of the image")
92
-	dockerfileName := cmd.String([]string{"f", "-file"}, "", "Name of the Dockerfile (Default is 'PATH/Dockerfile')")
93
-	flMemoryString := cmd.String([]string{"m", "-memory"}, "", "Memory limit")
94
-	flMemorySwap := cmd.String([]string{"-memory-swap"}, "", "Total memory (memory + swap), '-1' to disable swap")
95
-	flCpuShares := cmd.Int64([]string{"c", "-cpu-shares"}, 0, "CPU shares (relative weight)")
96
-	flCpuSetCpus := cmd.String([]string{"-cpuset-cpus"}, "", "CPUs in which to allow execution (0-3, 0,1)")
97
-
98
-	cmd.Require(flag.Exact, 1)
99
-
100
-	utils.ParseFlags(cmd, args, true)
101
-
102
-	var (
103
-		context  archive.Archive
104
-		isRemote bool
105
-		err      error
106
-	)
107
-
108
-	_, err = exec.LookPath("git")
109
-	hasGit := err == nil
110
-	if cmd.Arg(0) == "-" {
111
-		// As a special case, 'docker build -' will build from either an empty context with the
112
-		// contents of stdin as a Dockerfile, or a tar-ed context from stdin.
113
-		buf := bufio.NewReader(cli.in)
114
-		magic, err := buf.Peek(tarHeaderSize)
115
-		if err != nil && err != io.EOF {
116
-			return fmt.Errorf("failed to peek context header from STDIN: %v", err)
117
-		}
118
-		if !archive.IsArchive(magic) {
119
-			dockerfile, err := ioutil.ReadAll(buf)
120
-			if err != nil {
121
-				return fmt.Errorf("failed to read Dockerfile from STDIN: %v", err)
122
-			}
123
-
124
-			// -f option has no meaning when we're reading it from stdin,
125
-			// so just use our default Dockerfile name
126
-			*dockerfileName = api.DefaultDockerfileName
127
-			context, err = archive.Generate(*dockerfileName, string(dockerfile))
128
-		} else {
129
-			context = ioutil.NopCloser(buf)
130
-		}
131
-	} else if urlutil.IsURL(cmd.Arg(0)) && (!urlutil.IsGitURL(cmd.Arg(0)) || !hasGit) {
132
-		isRemote = true
133
-	} else {
134
-		root := cmd.Arg(0)
135
-		if urlutil.IsGitURL(root) {
136
-			remoteURL := cmd.Arg(0)
137
-			if !urlutil.IsGitTransport(remoteURL) {
138
-				remoteURL = "https://" + remoteURL
139
-			}
140
-
141
-			root, err = ioutil.TempDir("", "docker-build-git")
142
-			if err != nil {
143
-				return err
144
-			}
145
-			defer os.RemoveAll(root)
146
-
147
-			if output, err := exec.Command("git", "clone", "--recursive", remoteURL, root).CombinedOutput(); err != nil {
148
-				return fmt.Errorf("Error trying to use git: %s (%s)", err, output)
149
-			}
150
-		}
151
-		if _, err := os.Stat(root); err != nil {
152
-			return err
153
-		}
154
-
155
-		absRoot, err := filepath.Abs(root)
156
-		if err != nil {
157
-			return err
158
-		}
159
-
160
-		filename := *dockerfileName // path to Dockerfile
161
-
162
-		if *dockerfileName == "" {
163
-			// No -f/--file was specified so use the default
164
-			*dockerfileName = api.DefaultDockerfileName
165
-			filename = filepath.Join(absRoot, *dockerfileName)
166
-
167
-			// Just to be nice ;-) look for 'dockerfile' too but only
168
-			// use it if we found it, otherwise ignore this check
169
-			if _, err = os.Lstat(filename); os.IsNotExist(err) {
170
-				tmpFN := path.Join(absRoot, strings.ToLower(*dockerfileName))
171
-				if _, err = os.Lstat(tmpFN); err == nil {
172
-					*dockerfileName = strings.ToLower(*dockerfileName)
173
-					filename = tmpFN
174
-				}
175
-			}
176
-		}
177
-
178
-		origDockerfile := *dockerfileName // used for error msg
179
-		if filename, err = filepath.Abs(filename); err != nil {
180
-			return err
181
-		}
182
-
183
-		// Verify that 'filename' is within the build context
184
-		filename, err = symlink.FollowSymlinkInScope(filename, absRoot)
185
-		if err != nil {
186
-			return fmt.Errorf("The Dockerfile (%s) must be within the build context (%s)", origDockerfile, root)
187
-		}
188
-
189
-		// Now reset the dockerfileName to be relative to the build context
190
-		*dockerfileName, err = filepath.Rel(absRoot, filename)
191
-		if err != nil {
192
-			return err
193
-		}
194
-		// And canonicalize dockerfile name to a platform-independent one
195
-		*dockerfileName, err = archive.CanonicalTarNameForPath(*dockerfileName)
196
-		if err != nil {
197
-			return fmt.Errorf("Cannot canonicalize dockerfile path %s: %v", dockerfileName, err)
198
-		}
199
-
200
-		if _, err = os.Lstat(filename); os.IsNotExist(err) {
201
-			return fmt.Errorf("Cannot locate Dockerfile: %s", origDockerfile)
202
-		}
203
-		var includes = []string{"."}
204
-
205
-		excludes, err := utils.ReadDockerIgnore(path.Join(root, ".dockerignore"))
206
-		if err != nil {
207
-			return err
208
-		}
209
-
210
-		// If .dockerignore mentions .dockerignore or the Dockerfile
211
-		// then make sure we send both files over to the daemon
212
-		// because Dockerfile is, obviously, needed no matter what, and
213
-		// .dockerignore is needed to know if either one needs to be
214
-		// removed.  The deamon will remove them for us, if needed, after it
215
-		// parses the Dockerfile.
216
-		keepThem1, _ := fileutils.Matches(".dockerignore", excludes)
217
-		keepThem2, _ := fileutils.Matches(*dockerfileName, excludes)
218
-		if keepThem1 || keepThem2 {
219
-			includes = append(includes, ".dockerignore", *dockerfileName)
220
-		}
221
-
222
-		if err = utils.ValidateContextDirectory(root, excludes); err != nil {
223
-			return fmt.Errorf("Error checking context is accessible: '%s'. Please check permissions and try again.", err)
224
-		}
225
-		options := &archive.TarOptions{
226
-			Compression:     archive.Uncompressed,
227
-			ExcludePatterns: excludes,
228
-			IncludeFiles:    includes,
229
-		}
230
-		context, err = archive.TarWithOptions(root, options)
231
-		if err != nil {
232
-			return err
233
-		}
234
-	}
235
-
236
-	// windows: show error message about modified file permissions
237
-	// FIXME: this is not a valid warning when the daemon is running windows. should be removed once docker engine for windows can build.
238
-	if runtime.GOOS == "windows" {
239
-		log.Warn(`SECURITY WARNING: You are building a Docker image from Windows against a Linux Docker host. All files and directories added to build context will have '-rwxr-xr-x' permissions. It is recommended to double check and reset permissions for sensitive files and directories.`)
240
-	}
241
-
242
-	var body io.Reader
243
-	// Setup an upload progress bar
244
-	// FIXME: ProgressReader shouldn't be this annoying to use
245
-	if context != nil {
246
-		sf := utils.NewStreamFormatter(false)
247
-		body = progressreader.New(progressreader.Config{
248
-			In:        context,
249
-			Out:       cli.out,
250
-			Formatter: sf,
251
-			NewLines:  true,
252
-			ID:        "",
253
-			Action:    "Sending build context to Docker daemon",
254
-		})
255
-	}
256
-
257
-	var memory int64
258
-	if *flMemoryString != "" {
259
-		parsedMemory, err := units.RAMInBytes(*flMemoryString)
260
-		if err != nil {
261
-			return err
262
-		}
263
-		memory = parsedMemory
264
-	}
265
-
266
-	var memorySwap int64
267
-	if *flMemorySwap != "" {
268
-		if *flMemorySwap == "-1" {
269
-			memorySwap = -1
270
-		} else {
271
-			parsedMemorySwap, err := units.RAMInBytes(*flMemorySwap)
272
-			if err != nil {
273
-				return err
274
-			}
275
-			memorySwap = parsedMemorySwap
276
-		}
277
-	}
278
-	// Send the build context
279
-	v := &url.Values{}
280
-
281
-	//Check if the given image name can be resolved
282
-	if *tag != "" {
283
-		repository, tag := parsers.ParseRepositoryTag(*tag)
284
-		if err := registry.ValidateRepositoryName(repository); err != nil {
285
-			return err
286
-		}
287
-		if len(tag) > 0 {
288
-			if err := graph.ValidateTagName(tag); err != nil {
289
-				return err
290
-			}
291
-		}
292
-	}
293
-
294
-	v.Set("t", *tag)
295
-
296
-	if *suppressOutput {
297
-		v.Set("q", "1")
298
-	}
299
-	if isRemote {
300
-		v.Set("remote", cmd.Arg(0))
301
-	}
302
-	if *noCache {
303
-		v.Set("nocache", "1")
304
-	}
305
-	if *rm {
306
-		v.Set("rm", "1")
307
-	} else {
308
-		v.Set("rm", "0")
309
-	}
310
-
311
-	if *forceRm {
312
-		v.Set("forcerm", "1")
313
-	}
314
-
315
-	if *pull {
316
-		v.Set("pull", "1")
317
-	}
318
-
319
-	v.Set("cpusetcpus", *flCpuSetCpus)
320
-	v.Set("cpushares", strconv.FormatInt(*flCpuShares, 10))
321
-	v.Set("memory", strconv.FormatInt(memory, 10))
322
-	v.Set("memswap", strconv.FormatInt(memorySwap, 10))
323
-
324
-	v.Set("dockerfile", *dockerfileName)
325
-
326
-	cli.LoadConfigFile()
327
-
328
-	headers := http.Header(make(map[string][]string))
329
-	buf, err := json.Marshal(cli.configFile)
330
-	if err != nil {
331
-		return err
332
-	}
333
-	headers.Add("X-Registry-Config", base64.URLEncoding.EncodeToString(buf))
334
-
335
-	if context != nil {
336
-		headers.Set("Content-Type", "application/tar")
337
-	}
338
-	err = cli.stream("POST", fmt.Sprintf("/build?%s", v.Encode()), body, cli.out, headers)
339
-	if jerr, ok := err.(*utils.JSONError); ok {
340
-		// If no error code is set, default to 1
341
-		if jerr.Code == 0 {
342
-			jerr.Code = 1
343
-		}
344
-		return &utils.StatusError{Status: jerr.Message, StatusCode: jerr.Code}
345
-	}
346
-	return err
347
-}
348
-
349
-// 'docker login': login / register a user to registry service.
350
-func (cli *DockerCli) CmdLogin(args ...string) error {
351
-	cmd := cli.Subcmd("login", "[SERVER]", "Register or log in to a Docker registry server, if no server is\nspecified \""+registry.IndexServerAddress()+"\" is the default.", true)
352
-	cmd.Require(flag.Max, 1)
353
-
354
-	var username, password, email string
355
-
356
-	cmd.StringVar(&username, []string{"u", "-username"}, "", "Username")
357
-	cmd.StringVar(&password, []string{"p", "-password"}, "", "Password")
358
-	cmd.StringVar(&email, []string{"e", "-email"}, "", "Email")
359
-
360
-	utils.ParseFlags(cmd, args, true)
361
-
362
-	serverAddress := registry.IndexServerAddress()
363
-	if len(cmd.Args()) > 0 {
364
-		serverAddress = cmd.Arg(0)
365
-	}
366
-
367
-	promptDefault := func(prompt string, configDefault string) {
368
-		if configDefault == "" {
369
-			fmt.Fprintf(cli.out, "%s: ", prompt)
370
-		} else {
371
-			fmt.Fprintf(cli.out, "%s (%s): ", prompt, configDefault)
372
-		}
373
-	}
374
-
375
-	readInput := func(in io.Reader, out io.Writer) string {
376
-		reader := bufio.NewReader(in)
377
-		line, _, err := reader.ReadLine()
378
-		if err != nil {
379
-			fmt.Fprintln(out, err.Error())
380
-			os.Exit(1)
381
-		}
382
-		return string(line)
383
-	}
384
-
385
-	cli.LoadConfigFile()
386
-	authconfig, ok := cli.configFile.Configs[serverAddress]
387
-	if !ok {
388
-		authconfig = registry.AuthConfig{}
389
-	}
390
-
391
-	if username == "" {
392
-		promptDefault("Username", authconfig.Username)
393
-		username = readInput(cli.in, cli.out)
394
-		username = strings.Trim(username, " ")
395
-		if username == "" {
396
-			username = authconfig.Username
397
-		}
398
-	}
399
-	// Assume that a different username means they may not want to use
400
-	// the password or email from the config file, so prompt them
401
-	if username != authconfig.Username {
402
-		if password == "" {
403
-			oldState, err := term.SaveState(cli.inFd)
404
-			if err != nil {
405
-				return err
406
-			}
407
-			fmt.Fprintf(cli.out, "Password: ")
408
-			term.DisableEcho(cli.inFd, oldState)
409
-
410
-			password = readInput(cli.in, cli.out)
411
-			fmt.Fprint(cli.out, "\n")
412
-
413
-			term.RestoreTerminal(cli.inFd, oldState)
414
-			if password == "" {
415
-				return fmt.Errorf("Error : Password Required")
416
-			}
417
-		}
418
-
419
-		if email == "" {
420
-			promptDefault("Email", authconfig.Email)
421
-			email = readInput(cli.in, cli.out)
422
-			if email == "" {
423
-				email = authconfig.Email
424
-			}
425
-		}
426
-	} else {
427
-		// However, if they don't override the username use the
428
-		// password or email from the cmd line if specified. IOW, allow
429
-		// then to change/override them.  And if not specified, just
430
-		// use what's in the config file
431
-		if password == "" {
432
-			password = authconfig.Password
433
-		}
434
-		if email == "" {
435
-			email = authconfig.Email
436
-		}
437
-	}
438
-	authconfig.Username = username
439
-	authconfig.Password = password
440
-	authconfig.Email = email
441
-	authconfig.ServerAddress = serverAddress
442
-	cli.configFile.Configs[serverAddress] = authconfig
443
-
444
-	stream, statusCode, err := cli.call("POST", "/auth", cli.configFile.Configs[serverAddress], false)
445
-	if statusCode == 401 {
446
-		delete(cli.configFile.Configs, serverAddress)
447
-		registry.SaveConfig(cli.configFile)
448
-		return err
449
-	}
450
-	if err != nil {
451
-		return err
452
-	}
453
-
454
-	var response types.AuthResponse
455
-	if err := json.NewDecoder(stream).Decode(response); err != nil {
456
-		cli.configFile, _ = registry.LoadConfig(homedir.Get())
457
-		return err
458
-	}
459
-
460
-	registry.SaveConfig(cli.configFile)
461
-	fmt.Fprintf(cli.out, "WARNING: login credentials saved in %s.\n", path.Join(homedir.Get(), registry.CONFIGFILE))
462
-
463
-	if response.Status != "" {
464
-		fmt.Fprintf(cli.out, "%s\n", response.Status)
465
-	}
466
-	return nil
467
-}
468
-
469
-// log out from a Docker registry
470
-func (cli *DockerCli) CmdLogout(args ...string) error {
471
-	cmd := cli.Subcmd("logout", "[SERVER]", "Log out from a Docker registry, if no server is\nspecified \""+registry.IndexServerAddress()+"\" is the default.", true)
472
-	cmd.Require(flag.Max, 1)
473
-
474
-	utils.ParseFlags(cmd, args, false)
475
-	serverAddress := registry.IndexServerAddress()
476
-	if len(cmd.Args()) > 0 {
477
-		serverAddress = cmd.Arg(0)
478
-	}
479
-
480
-	cli.LoadConfigFile()
481
-	if _, ok := cli.configFile.Configs[serverAddress]; !ok {
482
-		fmt.Fprintf(cli.out, "Not logged in to %s\n", serverAddress)
483
-	} else {
484
-		fmt.Fprintf(cli.out, "Remove login credentials for %s\n", serverAddress)
485
-		delete(cli.configFile.Configs, serverAddress)
486
-
487
-		if err := registry.SaveConfig(cli.configFile); err != nil {
488
-			return fmt.Errorf("Failed to save docker config: %v", err)
489
-		}
490
-	}
491
-	return nil
492
-}
493
-
494
-// 'docker wait': block until a container stops
495
-func (cli *DockerCli) CmdWait(args ...string) error {
496
-	cmd := cli.Subcmd("wait", "CONTAINER [CONTAINER...]", "Block until a container stops, then print its exit code.", true)
497
-	cmd.Require(flag.Min, 1)
498
-
499
-	utils.ParseFlags(cmd, args, true)
500
-
501
-	var encounteredError error
502
-	for _, name := range cmd.Args() {
503
-		status, err := waitForExit(cli, name)
504
-		if err != nil {
505
-			fmt.Fprintf(cli.err, "%s\n", err)
506
-			encounteredError = fmt.Errorf("Error: failed to wait one or more containers")
507
-		} else {
508
-			fmt.Fprintf(cli.out, "%d\n", status)
509
-		}
510
-	}
511
-	return encounteredError
512
-}
513
-
514
-// 'docker version': show version information
515
-func (cli *DockerCli) CmdVersion(args ...string) error {
516
-	cmd := cli.Subcmd("version", "", "Show the Docker version information.", true)
517
-	cmd.Require(flag.Exact, 0)
518
-
519
-	utils.ParseFlags(cmd, args, false)
520
-
521
-	if dockerversion.VERSION != "" {
522
-		fmt.Fprintf(cli.out, "Client version: %s\n", dockerversion.VERSION)
523
-	}
524
-	fmt.Fprintf(cli.out, "Client API version: %s\n", api.APIVERSION)
525
-	fmt.Fprintf(cli.out, "Go version (client): %s\n", runtime.Version())
526
-	if dockerversion.GITCOMMIT != "" {
527
-		fmt.Fprintf(cli.out, "Git commit (client): %s\n", dockerversion.GITCOMMIT)
528
-	}
529
-	fmt.Fprintf(cli.out, "OS/Arch (client): %s/%s\n", runtime.GOOS, runtime.GOARCH)
530
-
531
-	body, _, err := readBody(cli.call("GET", "/version", nil, false))
532
-	if err != nil {
533
-		return err
534
-	}
535
-
536
-	out := engine.NewOutput()
537
-	remoteVersion, err := out.AddEnv()
538
-	if err != nil {
539
-		log.Errorf("Error reading remote version: %s", err)
540
-		return err
541
-	}
542
-	if _, err := out.Write(body); err != nil {
543
-		log.Errorf("Error reading remote version: %s", err)
544
-		return err
545
-	}
546
-	out.Close()
547
-	fmt.Fprintf(cli.out, "Server version: %s\n", remoteVersion.Get("Version"))
548
-	if apiVersion := remoteVersion.Get("ApiVersion"); apiVersion != "" {
549
-		fmt.Fprintf(cli.out, "Server API version: %s\n", apiVersion)
550
-	}
551
-	fmt.Fprintf(cli.out, "Go version (server): %s\n", remoteVersion.Get("GoVersion"))
552
-	fmt.Fprintf(cli.out, "Git commit (server): %s\n", remoteVersion.Get("GitCommit"))
553
-	fmt.Fprintf(cli.out, "OS/Arch (server): %s/%s\n", remoteVersion.Get("Os"), remoteVersion.Get("Arch"))
554
-	return nil
555
-}
556
-
557
-// 'docker info': display system-wide information.
558
-func (cli *DockerCli) CmdInfo(args ...string) error {
559
-	cmd := cli.Subcmd("info", "", "Display system-wide information", true)
560
-	cmd.Require(flag.Exact, 0)
561
-	utils.ParseFlags(cmd, args, false)
562
-
563
-	body, _, err := readBody(cli.call("GET", "/info", nil, false))
564
-	if err != nil {
565
-		return err
566
-	}
567
-
568
-	out := engine.NewOutput()
569
-	remoteInfo, err := out.AddEnv()
570
-	if err != nil {
571
-		return err
572
-	}
573
-
574
-	if _, err := out.Write(body); err != nil {
575
-		log.Errorf("Error reading remote info: %s", err)
576
-		return err
577
-	}
578
-	out.Close()
579
-
580
-	if remoteInfo.Exists("Containers") {
581
-		fmt.Fprintf(cli.out, "Containers: %d\n", remoteInfo.GetInt("Containers"))
582
-	}
583
-	if remoteInfo.Exists("Images") {
584
-		fmt.Fprintf(cli.out, "Images: %d\n", remoteInfo.GetInt("Images"))
585
-	}
586
-	if remoteInfo.Exists("Driver") {
587
-		fmt.Fprintf(cli.out, "Storage Driver: %s\n", remoteInfo.Get("Driver"))
588
-	}
589
-	if remoteInfo.Exists("DriverStatus") {
590
-		var driverStatus [][2]string
591
-		if err := remoteInfo.GetJson("DriverStatus", &driverStatus); err != nil {
592
-			return err
593
-		}
594
-		for _, pair := range driverStatus {
595
-			fmt.Fprintf(cli.out, " %s: %s\n", pair[0], pair[1])
596
-		}
597
-	}
598
-	if remoteInfo.Exists("ExecutionDriver") {
599
-		fmt.Fprintf(cli.out, "Execution Driver: %s\n", remoteInfo.Get("ExecutionDriver"))
600
-	}
601
-	if remoteInfo.Exists("KernelVersion") {
602
-		fmt.Fprintf(cli.out, "Kernel Version: %s\n", remoteInfo.Get("KernelVersion"))
603
-	}
604
-	if remoteInfo.Exists("OperatingSystem") {
605
-		fmt.Fprintf(cli.out, "Operating System: %s\n", remoteInfo.Get("OperatingSystem"))
606
-	}
607
-	if remoteInfo.Exists("NCPU") {
608
-		fmt.Fprintf(cli.out, "CPUs: %d\n", remoteInfo.GetInt("NCPU"))
609
-	}
610
-	if remoteInfo.Exists("MemTotal") {
611
-		fmt.Fprintf(cli.out, "Total Memory: %s\n", units.BytesSize(float64(remoteInfo.GetInt64("MemTotal"))))
612
-	}
613
-	if remoteInfo.Exists("Name") {
614
-		fmt.Fprintf(cli.out, "Name: %s\n", remoteInfo.Get("Name"))
615
-	}
616
-	if remoteInfo.Exists("ID") {
617
-		fmt.Fprintf(cli.out, "ID: %s\n", remoteInfo.Get("ID"))
618
-	}
619
-
620
-	if remoteInfo.GetBool("Debug") || os.Getenv("DEBUG") != "" {
621
-		if remoteInfo.Exists("Debug") {
622
-			fmt.Fprintf(cli.out, "Debug mode (server): %v\n", remoteInfo.GetBool("Debug"))
623
-		}
624
-		fmt.Fprintf(cli.out, "Debug mode (client): %v\n", os.Getenv("DEBUG") != "")
625
-		if remoteInfo.Exists("NFd") {
626
-			fmt.Fprintf(cli.out, "Fds: %d\n", remoteInfo.GetInt("NFd"))
627
-		}
628
-		if remoteInfo.Exists("NGoroutines") {
629
-			fmt.Fprintf(cli.out, "Goroutines: %d\n", remoteInfo.GetInt("NGoroutines"))
630
-		}
631
-		if remoteInfo.Exists("SystemTime") {
632
-			t, err := remoteInfo.GetTime("SystemTime")
633
-			if err != nil {
634
-				log.Errorf("Error reading system time: %v", err)
635
-			} else {
636
-				fmt.Fprintf(cli.out, "System Time: %s\n", t.Format(time.UnixDate))
637
-			}
638
-		}
639
-		if remoteInfo.Exists("NEventsListener") {
640
-			fmt.Fprintf(cli.out, "EventsListeners: %d\n", remoteInfo.GetInt("NEventsListener"))
641
-		}
642
-		if initSha1 := remoteInfo.Get("InitSha1"); initSha1 != "" {
643
-			fmt.Fprintf(cli.out, "Init SHA1: %s\n", initSha1)
644
-		}
645
-		if initPath := remoteInfo.Get("InitPath"); initPath != "" {
646
-			fmt.Fprintf(cli.out, "Init Path: %s\n", initPath)
647
-		}
648
-		if root := remoteInfo.Get("DockerRootDir"); root != "" {
649
-			fmt.Fprintf(cli.out, "Docker Root Dir: %s\n", root)
650
-		}
651
-	}
652
-	if remoteInfo.Exists("HttpProxy") {
653
-		fmt.Fprintf(cli.out, "Http Proxy: %s\n", remoteInfo.Get("HttpProxy"))
654
-	}
655
-	if remoteInfo.Exists("HttpsProxy") {
656
-		fmt.Fprintf(cli.out, "Https Proxy: %s\n", remoteInfo.Get("HttpsProxy"))
657
-	}
658
-	if remoteInfo.Exists("NoProxy") {
659
-		fmt.Fprintf(cli.out, "No Proxy: %s\n", remoteInfo.Get("NoProxy"))
660
-	}
661
-	if len(remoteInfo.GetList("IndexServerAddress")) != 0 {
662
-		cli.LoadConfigFile()
663
-		u := cli.configFile.Configs[remoteInfo.Get("IndexServerAddress")].Username
664
-		if len(u) > 0 {
665
-			fmt.Fprintf(cli.out, "Username: %v\n", u)
666
-			fmt.Fprintf(cli.out, "Registry: %v\n", remoteInfo.GetList("IndexServerAddress"))
667
-		}
668
-	}
669
-	if remoteInfo.Exists("MemoryLimit") && !remoteInfo.GetBool("MemoryLimit") {
670
-		fmt.Fprintf(cli.err, "WARNING: No memory limit support\n")
671
-	}
672
-	if remoteInfo.Exists("SwapLimit") && !remoteInfo.GetBool("SwapLimit") {
673
-		fmt.Fprintf(cli.err, "WARNING: No swap limit support\n")
674
-	}
675
-	if remoteInfo.Exists("IPv4Forwarding") && !remoteInfo.GetBool("IPv4Forwarding") {
676
-		fmt.Fprintf(cli.err, "WARNING: IPv4 forwarding is disabled.\n")
677
-	}
678
-	if remoteInfo.Exists("Labels") {
679
-		fmt.Fprintln(cli.out, "Labels:")
680
-		for _, attribute := range remoteInfo.GetList("Labels") {
681
-			fmt.Fprintf(cli.out, " %s\n", attribute)
682
-		}
683
-	}
684
-
685
-	return nil
686
-}
687
-
688
-func (cli *DockerCli) CmdStop(args ...string) error {
689
-	cmd := cli.Subcmd("stop", "CONTAINER [CONTAINER...]", "Stop a running container by sending SIGTERM and then SIGKILL after a\ngrace period", true)
690
-	nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Seconds to wait for stop before killing it")
691
-	cmd.Require(flag.Min, 1)
692
-
693
-	utils.ParseFlags(cmd, args, true)
694
-
695
-	v := url.Values{}
696
-	v.Set("t", strconv.Itoa(*nSeconds))
697
-
698
-	var encounteredError error
699
-	for _, name := range cmd.Args() {
700
-		_, _, err := readBody(cli.call("POST", "/containers/"+name+"/stop?"+v.Encode(), nil, false))
701
-		if err != nil {
702
-			fmt.Fprintf(cli.err, "%s\n", err)
703
-			encounteredError = fmt.Errorf("Error: failed to stop one or more containers")
704
-		} else {
705
-			fmt.Fprintf(cli.out, "%s\n", name)
706
-		}
707
-	}
708
-	return encounteredError
709
-}
710
-
711
-func (cli *DockerCli) CmdRestart(args ...string) error {
712
-	cmd := cli.Subcmd("restart", "CONTAINER [CONTAINER...]", "Restart a running container", true)
713
-	nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Seconds to wait for stop before killing the container")
714
-	cmd.Require(flag.Min, 1)
715
-
716
-	utils.ParseFlags(cmd, args, true)
717
-
718
-	v := url.Values{}
719
-	v.Set("t", strconv.Itoa(*nSeconds))
720
-
721
-	var encounteredError error
722
-	for _, name := range cmd.Args() {
723
-		_, _, err := readBody(cli.call("POST", "/containers/"+name+"/restart?"+v.Encode(), nil, false))
724
-		if err != nil {
725
-			fmt.Fprintf(cli.err, "%s\n", err)
726
-			encounteredError = fmt.Errorf("Error: failed to restart one or more containers")
727
-		} else {
728
-			fmt.Fprintf(cli.out, "%s\n", name)
729
-		}
730
-	}
731
-	return encounteredError
732
-}
733
-
734
-func (cli *DockerCli) forwardAllSignals(cid string) chan os.Signal {
735
-	sigc := make(chan os.Signal, 128)
736
-	signal.CatchAll(sigc)
737
-	go func() {
738
-		for s := range sigc {
739
-			if s == signal.SIGCHLD {
740
-				continue
741
-			}
742
-			var sig string
743
-			for sigStr, sigN := range signal.SignalMap {
744
-				if sigN == s {
745
-					sig = sigStr
746
-					break
747
-				}
748
-			}
749
-			if sig == "" {
750
-				log.Errorf("Unsupported signal: %v. Discarding.", s)
751
-			}
752
-			if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", cid, sig), nil, false)); err != nil {
753
-				log.Debugf("Error sending signal: %s", err)
754
-			}
755
-		}
756
-	}()
757
-	return sigc
758
-}
759
-
760
-func (cli *DockerCli) CmdStart(args ...string) error {
761
-	var (
762
-		cErr chan error
763
-		tty  bool
764
-
765
-		cmd       = cli.Subcmd("start", "CONTAINER [CONTAINER...]", "Start one or more stopped containers", true)
766
-		attach    = cmd.Bool([]string{"a", "-attach"}, false, "Attach STDOUT/STDERR and forward signals")
767
-		openStdin = cmd.Bool([]string{"i", "-interactive"}, false, "Attach container's STDIN")
768
-	)
769
-
770
-	cmd.Require(flag.Min, 1)
771
-	utils.ParseFlags(cmd, args, true)
772
-
773
-	if *attach || *openStdin {
774
-		if cmd.NArg() > 1 {
775
-			return fmt.Errorf("You cannot start and attach multiple containers at once.")
776
-		}
777
-
778
-		stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, false)
779
-		if err != nil {
780
-			return err
781
-		}
782
-
783
-		env := engine.Env{}
784
-		if err := env.Decode(stream); err != nil {
785
-			return err
786
-		}
787
-		config := env.GetSubEnv("Config")
788
-		tty = config.GetBool("Tty")
789
-
790
-		if !tty {
791
-			sigc := cli.forwardAllSignals(cmd.Arg(0))
792
-			defer signal.StopCatch(sigc)
793
-		}
794
-
795
-		var in io.ReadCloser
796
-
797
-		v := url.Values{}
798
-		v.Set("stream", "1")
799
-
800
-		if *openStdin && config.GetBool("OpenStdin") {
801
-			v.Set("stdin", "1")
802
-			in = cli.in
803
-		}
804
-
805
-		v.Set("stdout", "1")
806
-		v.Set("stderr", "1")
807
-
808
-		hijacked := make(chan io.Closer)
809
-		// Block the return until the chan gets closed
810
-		defer func() {
811
-			log.Debugf("CmdStart() returned, defer waiting for hijack to finish.")
812
-			if _, ok := <-hijacked; ok {
813
-				log.Errorf("Hijack did not finish (chan still open)")
814
-			}
815
-			cli.in.Close()
816
-		}()
817
-		cErr = promise.Go(func() error {
818
-			return cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), tty, in, cli.out, cli.err, hijacked, nil)
819
-		})
820
-
821
-		// Acknowledge the hijack before starting
822
-		select {
823
-		case closer := <-hijacked:
824
-			// Make sure that the hijack gets closed when returning (results
825
-			// in closing the hijack chan and freeing server's goroutines)
826
-			if closer != nil {
827
-				defer closer.Close()
828
-			}
829
-		case err := <-cErr:
830
-			if err != nil {
831
-				return err
832
-			}
833
-		}
834
-	}
835
-
836
-	var encounteredError error
837
-	for _, name := range cmd.Args() {
838
-		_, _, err := readBody(cli.call("POST", "/containers/"+name+"/start", nil, false))
839
-		if err != nil {
840
-			if !*attach && !*openStdin {
841
-				// attach and openStdin is false means it could be starting multiple containers
842
-				// when a container start failed, show the error message and start next
843
-				fmt.Fprintf(cli.err, "%s\n", err)
844
-				encounteredError = fmt.Errorf("Error: failed to start one or more containers")
845
-			} else {
846
-				encounteredError = err
847
-			}
848
-		} else {
849
-			if !*attach && !*openStdin {
850
-				fmt.Fprintf(cli.out, "%s\n", name)
851
-			}
852
-		}
853
-	}
854
-
855
-	if encounteredError != nil {
856
-		return encounteredError
857
-	}
858
-
859
-	if *openStdin || *attach {
860
-		if tty && cli.isTerminalOut {
861
-			if err := cli.monitorTtySize(cmd.Arg(0), false); err != nil {
862
-				log.Errorf("Error monitoring TTY size: %s", err)
863
-			}
864
-		}
865
-		if attchErr := <-cErr; attchErr != nil {
866
-			return attchErr
867
-		}
868
-		_, status, err := getExitCode(cli, cmd.Arg(0))
869
-		if err != nil {
870
-			return err
871
-		}
872
-		if status != 0 {
873
-			return &utils.StatusError{StatusCode: status}
874
-		}
875
-	}
876
-	return nil
877
-}
878
-
879
-func (cli *DockerCli) CmdUnpause(args ...string) error {
880
-	cmd := cli.Subcmd("unpause", "CONTAINER [CONTAINER...]", "Unpause all processes within a container", true)
881
-	cmd.Require(flag.Min, 1)
882
-	utils.ParseFlags(cmd, args, false)
883
-
884
-	var encounteredError error
885
-	for _, name := range cmd.Args() {
886
-		if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/unpause", name), nil, false)); err != nil {
887
-			fmt.Fprintf(cli.err, "%s\n", err)
888
-			encounteredError = fmt.Errorf("Error: failed to unpause container named %s", name)
889
-		} else {
890
-			fmt.Fprintf(cli.out, "%s\n", name)
891
-		}
892
-	}
893
-	return encounteredError
894
-}
895
-
896
-func (cli *DockerCli) CmdPause(args ...string) error {
897
-	cmd := cli.Subcmd("pause", "CONTAINER [CONTAINER...]", "Pause all processes within a container", true)
898
-	cmd.Require(flag.Min, 1)
899
-	utils.ParseFlags(cmd, args, false)
900
-
901
-	var encounteredError error
902
-	for _, name := range cmd.Args() {
903
-		if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/pause", name), nil, false)); err != nil {
904
-			fmt.Fprintf(cli.err, "%s\n", err)
905
-			encounteredError = fmt.Errorf("Error: failed to pause container named %s", name)
906
-		} else {
907
-			fmt.Fprintf(cli.out, "%s\n", name)
908
-		}
909
-	}
910
-	return encounteredError
911
-}
912
-
913
-func (cli *DockerCli) CmdRename(args ...string) error {
914
-	cmd := cli.Subcmd("rename", "OLD_NAME NEW_NAME", "Rename a container", true)
915
-	if err := cmd.Parse(args); err != nil {
916
-		return nil
917
-	}
918
-
919
-	if cmd.NArg() != 2 {
920
-		cmd.Usage()
921
-		return nil
922
-	}
923
-	old_name := cmd.Arg(0)
924
-	new_name := cmd.Arg(1)
925
-
926
-	if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/rename?name=%s", old_name, new_name), nil, false)); err != nil {
927
-		fmt.Fprintf(cli.err, "%s\n", err)
928
-		return fmt.Errorf("Error: failed to rename container named %s", old_name)
929
-	}
930
-	return nil
931
-}
932
-
933
-func (cli *DockerCli) CmdInspect(args ...string) error {
934
-	cmd := cli.Subcmd("inspect", "CONTAINER|IMAGE [CONTAINER|IMAGE...]", "Return low-level information on a container or image", true)
935
-	tmplStr := cmd.String([]string{"f", "#format", "-format"}, "", "Format the output using the given go template")
936
-	cmd.Require(flag.Min, 1)
937
-
938
-	utils.ParseFlags(cmd, args, true)
939
-
940
-	var tmpl *template.Template
941
-	if *tmplStr != "" {
942
-		var err error
943
-		if tmpl, err = template.New("").Funcs(funcMap).Parse(*tmplStr); err != nil {
944
-			fmt.Fprintf(cli.err, "Template parsing error: %v\n", err)
945
-			return &utils.StatusError{StatusCode: 64,
946
-				Status: "Template parsing error: " + err.Error()}
947
-		}
948
-	}
949
-
950
-	indented := new(bytes.Buffer)
951
-	indented.WriteByte('[')
952
-	status := 0
953
-
954
-	for _, name := range cmd.Args() {
955
-		obj, _, err := readBody(cli.call("GET", "/containers/"+name+"/json", nil, false))
956
-		if err != nil {
957
-			if strings.Contains(err.Error(), "Too many") {
958
-				fmt.Fprintf(cli.err, "Error: %v", err)
959
-				status = 1
960
-				continue
961
-			}
962
-
963
-			obj, _, err = readBody(cli.call("GET", "/images/"+name+"/json", nil, false))
964
-			if err != nil {
965
-				if strings.Contains(err.Error(), "No such") {
966
-					fmt.Fprintf(cli.err, "Error: No such image or container: %s\n", name)
967
-				} else {
968
-					fmt.Fprintf(cli.err, "%s", err)
969
-				}
970
-				status = 1
971
-				continue
972
-			}
973
-		}
974
-
975
-		if tmpl == nil {
976
-			if err = json.Indent(indented, obj, "", "    "); err != nil {
977
-				fmt.Fprintf(cli.err, "%s\n", err)
978
-				status = 1
979
-				continue
980
-			}
981
-		} else {
982
-			// Has template, will render
983
-			var value interface{}
984
-			if err := json.Unmarshal(obj, &value); err != nil {
985
-				fmt.Fprintf(cli.err, "%s\n", err)
986
-				status = 1
987
-				continue
988
-			}
989
-			if err := tmpl.Execute(cli.out, value); err != nil {
990
-				return err
991
-			}
992
-			cli.out.Write([]byte{'\n'})
993
-		}
994
-		indented.WriteString(",")
995
-	}
996
-
997
-	if indented.Len() > 1 {
998
-		// Remove trailing ','
999
-		indented.Truncate(indented.Len() - 1)
1000
-	}
1001
-	indented.WriteString("]\n")
1002
-
1003
-	if tmpl == nil {
1004
-		if _, err := io.Copy(cli.out, indented); err != nil {
1005
-			return err
1006
-		}
1007
-	}
1008
-
1009
-	if status != 0 {
1010
-		return &utils.StatusError{StatusCode: status}
1011
-	}
1012
-	return nil
1013
-}
1014
-
1015
-func (cli *DockerCli) CmdTop(args ...string) error {
1016
-	cmd := cli.Subcmd("top", "CONTAINER [ps OPTIONS]", "Display the running processes of a container", true)
1017
-	cmd.Require(flag.Min, 1)
1018
-
1019
-	utils.ParseFlags(cmd, args, true)
1020
-
1021
-	val := url.Values{}
1022
-	if cmd.NArg() > 1 {
1023
-		val.Set("ps_args", strings.Join(cmd.Args()[1:], " "))
1024
-	}
1025
-
1026
-	stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/top?"+val.Encode(), nil, false)
1027
-	if err != nil {
1028
-		return err
1029
-	}
1030
-	var procs engine.Env
1031
-	if err := procs.Decode(stream); err != nil {
1032
-		return err
1033
-	}
1034
-	w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
1035
-	fmt.Fprintln(w, strings.Join(procs.GetList("Titles"), "\t"))
1036
-	processes := [][]string{}
1037
-	if err := procs.GetJson("Processes", &processes); err != nil {
1038
-		return err
1039
-	}
1040
-	for _, proc := range processes {
1041
-		fmt.Fprintln(w, strings.Join(proc, "\t"))
1042
-	}
1043
-	w.Flush()
1044
-	return nil
1045
-}
1046
-
1047
-func (cli *DockerCli) CmdPort(args ...string) error {
1048
-	cmd := cli.Subcmd("port", "CONTAINER [PRIVATE_PORT[/PROTO]]", "List port mappings for the CONTAINER, or lookup the public-facing port that\nis NAT-ed to the PRIVATE_PORT", true)
1049
-	cmd.Require(flag.Min, 1)
1050
-	utils.ParseFlags(cmd, args, true)
1051
-
1052
-	stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, false)
1053
-	if err != nil {
1054
-		return err
1055
-	}
1056
-
1057
-	env := engine.Env{}
1058
-	if err := env.Decode(stream); err != nil {
1059
-		return err
1060
-	}
1061
-	ports := nat.PortMap{}
1062
-	if err := env.GetSubEnv("NetworkSettings").GetJson("Ports", &ports); err != nil {
1063
-		return err
1064
-	}
1065
-
1066
-	if cmd.NArg() == 2 {
1067
-		var (
1068
-			port  = cmd.Arg(1)
1069
-			proto = "tcp"
1070
-			parts = strings.SplitN(port, "/", 2)
1071
-		)
1072
-
1073
-		if len(parts) == 2 && len(parts[1]) != 0 {
1074
-			port = parts[0]
1075
-			proto = parts[1]
1076
-		}
1077
-		natPort := port + "/" + proto
1078
-		if frontends, exists := ports[nat.Port(port+"/"+proto)]; exists && frontends != nil {
1079
-			for _, frontend := range frontends {
1080
-				fmt.Fprintf(cli.out, "%s:%s\n", frontend.HostIp, frontend.HostPort)
1081
-			}
1082
-			return nil
1083
-		}
1084
-		return fmt.Errorf("Error: No public port '%s' published for %s", natPort, cmd.Arg(0))
1085
-	}
1086
-
1087
-	for from, frontends := range ports {
1088
-		for _, frontend := range frontends {
1089
-			fmt.Fprintf(cli.out, "%s -> %s:%s\n", from, frontend.HostIp, frontend.HostPort)
1090
-		}
1091
-	}
1092
-
1093
-	return nil
1094
-}
1095
-
1096
-// 'docker rmi IMAGE' removes all images with the name IMAGE
1097
-func (cli *DockerCli) CmdRmi(args ...string) error {
1098
-	var (
1099
-		cmd     = cli.Subcmd("rmi", "IMAGE [IMAGE...]", "Remove one or more images", true)
1100
-		force   = cmd.Bool([]string{"f", "-force"}, false, "Force removal of the image")
1101
-		noprune = cmd.Bool([]string{"-no-prune"}, false, "Do not delete untagged parents")
1102
-	)
1103
-	cmd.Require(flag.Min, 1)
1104
-
1105
-	utils.ParseFlags(cmd, args, true)
1106
-
1107
-	v := url.Values{}
1108
-	if *force {
1109
-		v.Set("force", "1")
1110
-	}
1111
-	if *noprune {
1112
-		v.Set("noprune", "1")
1113
-	}
1114
-
1115
-	var encounteredError error
1116
-	for _, name := range cmd.Args() {
1117
-		body, _, err := readBody(cli.call("DELETE", "/images/"+name+"?"+v.Encode(), nil, false))
1118
-		if err != nil {
1119
-			fmt.Fprintf(cli.err, "%s\n", err)
1120
-			encounteredError = fmt.Errorf("Error: failed to remove one or more images")
1121
-		} else {
1122
-			outs := engine.NewTable("Created", 0)
1123
-			if _, err := outs.ReadListFrom(body); err != nil {
1124
-				fmt.Fprintf(cli.err, "%s\n", err)
1125
-				encounteredError = fmt.Errorf("Error: failed to remove one or more images")
1126
-				continue
1127
-			}
1128
-			for _, out := range outs.Data {
1129
-				if out.Get("Deleted") != "" {
1130
-					fmt.Fprintf(cli.out, "Deleted: %s\n", out.Get("Deleted"))
1131
-				} else {
1132
-					fmt.Fprintf(cli.out, "Untagged: %s\n", out.Get("Untagged"))
1133
-				}
1134
-			}
1135
-		}
1136
-	}
1137
-	return encounteredError
1138
-}
1139
-
1140
-func (cli *DockerCli) CmdHistory(args ...string) error {
1141
-	cmd := cli.Subcmd("history", "IMAGE", "Show the history of an image", true)
1142
-	quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs")
1143
-	noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
1144
-	cmd.Require(flag.Exact, 1)
1145
-
1146
-	utils.ParseFlags(cmd, args, true)
1147
-
1148
-	body, _, err := readBody(cli.call("GET", "/images/"+cmd.Arg(0)+"/history", nil, false))
1149
-	if err != nil {
1150
-		return err
1151
-	}
1152
-
1153
-	outs := engine.NewTable("Created", 0)
1154
-	if _, err := outs.ReadListFrom(body); err != nil {
1155
-		return err
1156
-	}
1157
-
1158
-	w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
1159
-	if !*quiet {
1160
-		fmt.Fprintln(w, "IMAGE\tCREATED\tCREATED BY\tSIZE")
1161
-	}
1162
-
1163
-	for _, out := range outs.Data {
1164
-		outID := out.Get("Id")
1165
-		if !*quiet {
1166
-			if *noTrunc {
1167
-				fmt.Fprintf(w, "%s\t", outID)
1168
-			} else {
1169
-				fmt.Fprintf(w, "%s\t", stringid.TruncateID(outID))
1170
-			}
1171
-
1172
-			fmt.Fprintf(w, "%s ago\t", units.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0))))
1173
-
1174
-			if *noTrunc {
1175
-				fmt.Fprintf(w, "%s\t", out.Get("CreatedBy"))
1176
-			} else {
1177
-				fmt.Fprintf(w, "%s\t", utils.Trunc(out.Get("CreatedBy"), 45))
1178
-			}
1179
-			fmt.Fprintf(w, "%s\n", units.HumanSize(float64(out.GetInt64("Size"))))
1180
-		} else {
1181
-			if *noTrunc {
1182
-				fmt.Fprintln(w, outID)
1183
-			} else {
1184
-				fmt.Fprintln(w, stringid.TruncateID(outID))
1185
-			}
1186
-		}
1187
-	}
1188
-	w.Flush()
1189
-	return nil
1190
-}
1191
-
1192
-func (cli *DockerCli) CmdRm(args ...string) error {
1193
-	cmd := cli.Subcmd("rm", "CONTAINER [CONTAINER...]", "Remove one or more containers", true)
1194
-	v := cmd.Bool([]string{"v", "-volumes"}, false, "Remove the volumes associated with the container")
1195
-	link := cmd.Bool([]string{"l", "#link", "-link"}, false, "Remove the specified link")
1196
-	force := cmd.Bool([]string{"f", "-force"}, false, "Force the removal of a running container (uses SIGKILL)")
1197
-	cmd.Require(flag.Min, 1)
1198
-
1199
-	utils.ParseFlags(cmd, args, true)
1200
-
1201
-	val := url.Values{}
1202
-	if *v {
1203
-		val.Set("v", "1")
1204
-	}
1205
-	if *link {
1206
-		val.Set("link", "1")
1207
-	}
1208
-
1209
-	if *force {
1210
-		val.Set("force", "1")
1211
-	}
1212
-
1213
-	var encounteredError error
1214
-	for _, name := range cmd.Args() {
1215
-		_, _, err := readBody(cli.call("DELETE", "/containers/"+name+"?"+val.Encode(), nil, false))
1216
-		if err != nil {
1217
-			fmt.Fprintf(cli.err, "%s\n", err)
1218
-			encounteredError = fmt.Errorf("Error: failed to remove one or more containers")
1219
-		} else {
1220
-			fmt.Fprintf(cli.out, "%s\n", name)
1221
-		}
1222
-	}
1223
-	return encounteredError
1224
-}
1225
-
1226
-// 'docker kill NAME' kills a running container
1227
-func (cli *DockerCli) CmdKill(args ...string) error {
1228
-	cmd := cli.Subcmd("kill", "CONTAINER [CONTAINER...]", "Kill a running container using SIGKILL or a specified signal", true)
1229
-	signal := cmd.String([]string{"s", "-signal"}, "KILL", "Signal to send to the container")
1230
-	cmd.Require(flag.Min, 1)
1231
-
1232
-	utils.ParseFlags(cmd, args, true)
1233
-
1234
-	var encounteredError error
1235
-	for _, name := range cmd.Args() {
1236
-		if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", name, *signal), nil, false)); err != nil {
1237
-			fmt.Fprintf(cli.err, "%s\n", err)
1238
-			encounteredError = fmt.Errorf("Error: failed to kill one or more containers")
1239
-		} else {
1240
-			fmt.Fprintf(cli.out, "%s\n", name)
1241
-		}
1242
-	}
1243
-	return encounteredError
1244
-}
1245
-
1246
-func (cli *DockerCli) CmdImport(args ...string) error {
1247
-	cmd := cli.Subcmd("import", "URL|- [REPOSITORY[:TAG]]", "Create an empty filesystem image and import the contents of the\ntarball (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz) into it, then\noptionally tag it.", true)
1248
-	flChanges := opts.NewListOpts(nil)
1249
-	cmd.Var(&flChanges, []string{"c", "-change"}, "Apply Dockerfile instruction to the created image")
1250
-	cmd.Require(flag.Min, 1)
1251
-
1252
-	utils.ParseFlags(cmd, args, true)
1253
-
1254
-	var (
1255
-		v          = url.Values{}
1256
-		src        = cmd.Arg(0)
1257
-		repository = cmd.Arg(1)
1258
-	)
1259
-
1260
-	v.Set("fromSrc", src)
1261
-	v.Set("repo", repository)
1262
-	for _, change := range flChanges.GetAll() {
1263
-		v.Add("changes", change)
1264
-	}
1265
-	if cmd.NArg() == 3 {
1266
-		fmt.Fprintf(cli.err, "[DEPRECATED] The format 'URL|- [REPOSITORY [TAG]]' has been deprecated. Please use URL|- [REPOSITORY[:TAG]]\n")
1267
-		v.Set("tag", cmd.Arg(2))
1268
-	}
1269
-
1270
-	if repository != "" {
1271
-		//Check if the given image name can be resolved
1272
-		repo, _ := parsers.ParseRepositoryTag(repository)
1273
-		if err := registry.ValidateRepositoryName(repo); err != nil {
1274
-			return err
1275
-		}
1276
-	}
1277
-
1278
-	var in io.Reader
1279
-
1280
-	if src == "-" {
1281
-		in = cli.in
1282
-	}
1283
-
1284
-	return cli.stream("POST", "/images/create?"+v.Encode(), in, cli.out, nil)
1285
-}
1286
-
1287
-func (cli *DockerCli) CmdPush(args ...string) error {
1288
-	cmd := cli.Subcmd("push", "NAME[:TAG]", "Push an image or a repository to the registry", true)
1289
-	cmd.Require(flag.Exact, 1)
1290
-
1291
-	utils.ParseFlags(cmd, args, true)
1292
-
1293
-	name := cmd.Arg(0)
1294
-
1295
-	cli.LoadConfigFile()
1296
-
1297
-	remote, tag := parsers.ParseRepositoryTag(name)
1298
-
1299
-	// Resolve the Repository name from fqn to RepositoryInfo
1300
-	repoInfo, err := registry.ParseRepositoryInfo(remote)
1301
-	if err != nil {
1302
-		return err
1303
-	}
1304
-	// Resolve the Auth config relevant for this server
1305
-	authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index)
1306
-	// If we're not using a custom registry, we know the restrictions
1307
-	// applied to repository names and can warn the user in advance.
1308
-	// Custom repositories can have different rules, and we must also
1309
-	// allow pushing by image ID.
1310
-	if repoInfo.Official {
1311
-		username := authConfig.Username
1312
-		if username == "" {
1313
-			username = "<user>"
1314
-		}
1315
-		return fmt.Errorf("You cannot push a \"root\" repository. Please rename your repository to <user>/<repo> (ex: %s/%s)", username, repoInfo.LocalName)
1316
-	}
1317
-
1318
-	v := url.Values{}
1319
-	v.Set("tag", tag)
1320
-
1321
-	push := func(authConfig registry.AuthConfig) error {
1322
-		buf, err := json.Marshal(authConfig)
1323
-		if err != nil {
1324
-			return err
1325
-		}
1326
-		registryAuthHeader := []string{
1327
-			base64.URLEncoding.EncodeToString(buf),
1328
-		}
1329
-
1330
-		return cli.stream("POST", "/images/"+remote+"/push?"+v.Encode(), nil, cli.out, map[string][]string{
1331
-			"X-Registry-Auth": registryAuthHeader,
1332
-		})
1333
-	}
1334
-
1335
-	if err := push(authConfig); err != nil {
1336
-		if strings.Contains(err.Error(), "Status 401") {
1337
-			fmt.Fprintln(cli.out, "\nPlease login prior to push:")
1338
-			if err := cli.CmdLogin(repoInfo.Index.GetAuthConfigKey()); err != nil {
1339
-				return err
1340
-			}
1341
-			authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index)
1342
-			return push(authConfig)
1343
-		}
1344
-		return err
1345
-	}
1346
-	return nil
1347
-}
1348
-
1349
-func (cli *DockerCli) CmdPull(args ...string) error {
1350
-	cmd := cli.Subcmd("pull", "NAME[:TAG|@DIGEST]", "Pull an image or a repository from the registry", true)
1351
-	allTags := cmd.Bool([]string{"a", "-all-tags"}, false, "Download all tagged images in the repository")
1352
-	cmd.Require(flag.Exact, 1)
1353
-
1354
-	utils.ParseFlags(cmd, args, true)
1355
-
1356
-	var (
1357
-		v         = url.Values{}
1358
-		remote    = cmd.Arg(0)
1359
-		newRemote = remote
1360
-	)
1361
-	taglessRemote, tag := parsers.ParseRepositoryTag(remote)
1362
-	if tag == "" && !*allTags {
1363
-		newRemote = utils.ImageReference(taglessRemote, graph.DEFAULTTAG)
1364
-	}
1365
-	if tag != "" && *allTags {
1366
-		return fmt.Errorf("tag can't be used with --all-tags/-a")
1367
-	}
1368
-
1369
-	v.Set("fromImage", newRemote)
1370
-
1371
-	// Resolve the Repository name from fqn to RepositoryInfo
1372
-	repoInfo, err := registry.ParseRepositoryInfo(taglessRemote)
1373
-	if err != nil {
1374
-		return err
1375
-	}
1376
-
1377
-	cli.LoadConfigFile()
1378
-
1379
-	// Resolve the Auth config relevant for this server
1380
-	authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index)
1381
-
1382
-	pull := func(authConfig registry.AuthConfig) error {
1383
-		buf, err := json.Marshal(authConfig)
1384
-		if err != nil {
1385
-			return err
1386
-		}
1387
-		registryAuthHeader := []string{
1388
-			base64.URLEncoding.EncodeToString(buf),
1389
-		}
1390
-
1391
-		return cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.out, map[string][]string{
1392
-			"X-Registry-Auth": registryAuthHeader,
1393
-		})
1394
-	}
1395
-
1396
-	if err := pull(authConfig); err != nil {
1397
-		if strings.Contains(err.Error(), "Status 401") {
1398
-			fmt.Fprintln(cli.out, "\nPlease login prior to pull:")
1399
-			if err := cli.CmdLogin(repoInfo.Index.GetAuthConfigKey()); err != nil {
1400
-				return err
1401
-			}
1402
-			authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index)
1403
-			return pull(authConfig)
1404
-		}
1405
-		return err
1406
-	}
1407
-
1408
-	return nil
1409
-}
1410
-
1411
-func (cli *DockerCli) CmdImages(args ...string) error {
1412
-	cmd := cli.Subcmd("images", "[REPOSITORY]", "List images", true)
1413
-	quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs")
1414
-	all := cmd.Bool([]string{"a", "-all"}, false, "Show all images (default hides intermediate images)")
1415
-	noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
1416
-	showDigests := cmd.Bool([]string{"-digests"}, false, "Show digests")
1417
-	// FIXME: --viz and --tree are deprecated. Remove them in a future version.
1418
-	flViz := cmd.Bool([]string{"#v", "#viz", "#-viz"}, false, "Output graph in graphviz format")
1419
-	flTree := cmd.Bool([]string{"#t", "#tree", "#-tree"}, false, "Output graph in tree format")
1420
-
1421
-	flFilter := opts.NewListOpts(nil)
1422
-	cmd.Var(&flFilter, []string{"f", "-filter"}, "Filter output based on conditions provided")
1423
-	cmd.Require(flag.Max, 1)
1424
-
1425
-	utils.ParseFlags(cmd, args, true)
1426
-
1427
-	// Consolidate all filter flags, and sanity check them early.
1428
-	// They'll get process in the daemon/server.
1429
-	imageFilterArgs := filters.Args{}
1430
-	for _, f := range flFilter.GetAll() {
1431
-		var err error
1432
-		imageFilterArgs, err = filters.ParseFlag(f, imageFilterArgs)
1433
-		if err != nil {
1434
-			return err
1435
-		}
1436
-	}
1437
-
1438
-	matchName := cmd.Arg(0)
1439
-	// FIXME: --viz and --tree are deprecated. Remove them in a future version.
1440
-	if *flViz || *flTree {
1441
-		v := url.Values{
1442
-			"all": []string{"1"},
1443
-		}
1444
-		if len(imageFilterArgs) > 0 {
1445
-			filterJson, err := filters.ToParam(imageFilterArgs)
1446
-			if err != nil {
1447
-				return err
1448
-			}
1449
-			v.Set("filters", filterJson)
1450
-		}
1451
-
1452
-		body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, false))
1453
-		if err != nil {
1454
-			return err
1455
-		}
1456
-
1457
-		outs := engine.NewTable("Created", 0)
1458
-		if _, err := outs.ReadListFrom(body); err != nil {
1459
-			return err
1460
-		}
1461
-
1462
-		var (
1463
-			printNode  func(cli *DockerCli, noTrunc bool, image *engine.Env, prefix string)
1464
-			startImage *engine.Env
1465
-
1466
-			roots    = engine.NewTable("Created", outs.Len())
1467
-			byParent = make(map[string]*engine.Table)
1468
-		)
1469
-
1470
-		for _, image := range outs.Data {
1471
-			if image.Get("ParentId") == "" {
1472
-				roots.Add(image)
1473
-			} else {
1474
-				if children, exists := byParent[image.Get("ParentId")]; exists {
1475
-					children.Add(image)
1476
-				} else {
1477
-					byParent[image.Get("ParentId")] = engine.NewTable("Created", 1)
1478
-					byParent[image.Get("ParentId")].Add(image)
1479
-				}
1480
-			}
1481
-
1482
-			if matchName != "" {
1483
-				if matchName == image.Get("Id") || matchName == stringid.TruncateID(image.Get("Id")) {
1484
-					startImage = image
1485
-				}
1486
-
1487
-				for _, repotag := range image.GetList("RepoTags") {
1488
-					if repotag == matchName {
1489
-						startImage = image
1490
-					}
1491
-				}
1492
-			}
1493
-		}
1494
-
1495
-		if *flViz {
1496
-			fmt.Fprintf(cli.out, "digraph docker {\n")
1497
-			printNode = (*DockerCli).printVizNode
1498
-		} else {
1499
-			printNode = (*DockerCli).printTreeNode
1500
-		}
1501
-
1502
-		if startImage != nil {
1503
-			root := engine.NewTable("Created", 1)
1504
-			root.Add(startImage)
1505
-			cli.WalkTree(*noTrunc, root, byParent, "", printNode)
1506
-		} else if matchName == "" {
1507
-			cli.WalkTree(*noTrunc, roots, byParent, "", printNode)
1508
-		}
1509
-		if *flViz {
1510
-			fmt.Fprintf(cli.out, " base [style=invisible]\n}\n")
1511
-		}
1512
-	} else {
1513
-		v := url.Values{}
1514
-		if len(imageFilterArgs) > 0 {
1515
-			filterJson, err := filters.ToParam(imageFilterArgs)
1516
-			if err != nil {
1517
-				return err
1518
-			}
1519
-			v.Set("filters", filterJson)
1520
-		}
1521
-
1522
-		if cmd.NArg() == 1 {
1523
-			// FIXME rename this parameter, to not be confused with the filters flag
1524
-			v.Set("filter", matchName)
1525
-		}
1526
-		if *all {
1527
-			v.Set("all", "1")
1528
-		}
1529
-
1530
-		body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, false))
1531
-
1532
-		if err != nil {
1533
-			return err
1534
-		}
1535
-
1536
-		outs := engine.NewTable("Created", 0)
1537
-		if _, err := outs.ReadListFrom(body); err != nil {
1538
-			return err
1539
-		}
1540
-
1541
-		w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
1542
-		if !*quiet {
1543
-			if *showDigests {
1544
-				fmt.Fprintln(w, "REPOSITORY\tTAG\tDIGEST\tIMAGE ID\tCREATED\tVIRTUAL SIZE")
1545
-			} else {
1546
-				fmt.Fprintln(w, "REPOSITORY\tTAG\tIMAGE ID\tCREATED\tVIRTUAL SIZE")
1547
-			}
1548
-		}
1549
-
1550
-		for _, out := range outs.Data {
1551
-			outID := out.Get("Id")
1552
-			if !*noTrunc {
1553
-				outID = stringid.TruncateID(outID)
1554
-			}
1555
-
1556
-			repoTags := out.GetList("RepoTags")
1557
-			repoDigests := out.GetList("RepoDigests")
1558
-
1559
-			if len(repoTags) == 1 && repoTags[0] == "<none>:<none>" && len(repoDigests) == 1 && repoDigests[0] == "<none>@<none>" {
1560
-				// dangling image - clear out either repoTags or repoDigsts so we only show it once below
1561
-				repoDigests = []string{}
1562
-			}
1563
-
1564
-			// combine the tags and digests lists
1565
-			tagsAndDigests := append(repoTags, repoDigests...)
1566
-			for _, repoAndRef := range tagsAndDigests {
1567
-				repo, ref := parsers.ParseRepositoryTag(repoAndRef)
1568
-				// default tag and digest to none - if there's a value, it'll be set below
1569
-				tag := "<none>"
1570
-				digest := "<none>"
1571
-				if utils.DigestReference(ref) {
1572
-					digest = ref
1573
-				} else {
1574
-					tag = ref
1575
-				}
1576
-
1577
-				if !*quiet {
1578
-					if *showDigests {
1579
-						fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s ago\t%s\n", repo, tag, digest, outID, units.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0))), units.HumanSize(float64(out.GetInt64("VirtualSize"))))
1580
-					} else {
1581
-						fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\n", repo, tag, outID, units.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0))), units.HumanSize(float64(out.GetInt64("VirtualSize"))))
1582
-					}
1583
-				} else {
1584
-					fmt.Fprintln(w, outID)
1585
-				}
1586
-			}
1587
-		}
1588
-
1589
-		if !*quiet {
1590
-			w.Flush()
1591
-		}
1592
-	}
1593
-	return nil
1594
-}
1595
-
1596
-// FIXME: --viz and --tree are deprecated. Remove them in a future version.
1597
-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)) {
1598
-	length := images.Len()
1599
-	if length > 1 {
1600
-		for index, image := range images.Data {
1601
-			if index+1 == length {
1602
-				printNode(cli, noTrunc, image, prefix+"└─")
1603
-				if subimages, exists := byParent[image.Get("Id")]; exists {
1604
-					cli.WalkTree(noTrunc, subimages, byParent, prefix+"  ", printNode)
1605
-				}
1606
-			} else {
1607
-				printNode(cli, noTrunc, image, prefix+"\u251C─")
1608
-				if subimages, exists := byParent[image.Get("Id")]; exists {
1609
-					cli.WalkTree(noTrunc, subimages, byParent, prefix+"\u2502 ", printNode)
1610
-				}
1611
-			}
1612
-		}
1613
-	} else {
1614
-		for _, image := range images.Data {
1615
-			printNode(cli, noTrunc, image, prefix+"└─")
1616
-			if subimages, exists := byParent[image.Get("Id")]; exists {
1617
-				cli.WalkTree(noTrunc, subimages, byParent, prefix+"  ", printNode)
1618
-			}
1619
-		}
1620
-	}
1621
-}
1622
-
1623
-// FIXME: --viz and --tree are deprecated. Remove them in a future version.
1624
-func (cli *DockerCli) printVizNode(noTrunc bool, image *engine.Env, prefix string) {
1625
-	var (
1626
-		imageID  string
1627
-		parentID string
1628
-	)
1629
-	if noTrunc {
1630
-		imageID = image.Get("Id")
1631
-		parentID = image.Get("ParentId")
1632
-	} else {
1633
-		imageID = stringid.TruncateID(image.Get("Id"))
1634
-		parentID = stringid.TruncateID(image.Get("ParentId"))
1635
-	}
1636
-	if parentID == "" {
1637
-		fmt.Fprintf(cli.out, " base -> \"%s\" [style=invis]\n", imageID)
1638
-	} else {
1639
-		fmt.Fprintf(cli.out, " \"%s\" -> \"%s\"\n", parentID, imageID)
1640
-	}
1641
-	if image.GetList("RepoTags")[0] != "<none>:<none>" {
1642
-		fmt.Fprintf(cli.out, " \"%s\" [label=\"%s\\n%s\",shape=box,fillcolor=\"paleturquoise\",style=\"filled,rounded\"];\n",
1643
-			imageID, imageID, strings.Join(image.GetList("RepoTags"), "\\n"))
1644
-	}
1645
-}
1646
-
1647
-// FIXME: --viz and --tree are deprecated. Remove them in a future version.
1648
-func (cli *DockerCli) printTreeNode(noTrunc bool, image *engine.Env, prefix string) {
1649
-	var imageID string
1650
-	if noTrunc {
1651
-		imageID = image.Get("Id")
1652
-	} else {
1653
-		imageID = stringid.TruncateID(image.Get("Id"))
1654
-	}
1655
-
1656
-	fmt.Fprintf(cli.out, "%s%s Virtual Size: %s", prefix, imageID, units.HumanSize(float64(image.GetInt64("VirtualSize"))))
1657
-	if image.GetList("RepoTags")[0] != "<none>:<none>" {
1658
-		fmt.Fprintf(cli.out, " Tags: %s\n", strings.Join(image.GetList("RepoTags"), ", "))
1659
-	} else {
1660
-		fmt.Fprint(cli.out, "\n")
1661
-	}
1662
-}
1663
-
1664
-func (cli *DockerCli) CmdPs(args ...string) error {
1665
-	var (
1666
-		err error
1667
-
1668
-		psFilterArgs = filters.Args{}
1669
-		v            = url.Values{}
1670
-
1671
-		cmd      = cli.Subcmd("ps", "", "List containers", true)
1672
-		quiet    = cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs")
1673
-		size     = cmd.Bool([]string{"s", "-size"}, false, "Display total file sizes")
1674
-		all      = cmd.Bool([]string{"a", "-all"}, false, "Show all containers (default shows just running)")
1675
-		noTrunc  = cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
1676
-		nLatest  = cmd.Bool([]string{"l", "-latest"}, false, "Show the latest created container, include non-running")
1677
-		since    = cmd.String([]string{"#sinceId", "#-since-id", "-since"}, "", "Show created since Id or Name, include non-running")
1678
-		before   = cmd.String([]string{"#beforeId", "#-before-id", "-before"}, "", "Show only container created before Id or Name")
1679
-		last     = cmd.Int([]string{"n"}, -1, "Show n last created containers, include non-running")
1680
-		flFilter = opts.NewListOpts(nil)
1681
-	)
1682
-	cmd.Require(flag.Exact, 0)
1683
-
1684
-	cmd.Var(&flFilter, []string{"f", "-filter"}, "Filter output based on conditions provided")
1685
-
1686
-	utils.ParseFlags(cmd, args, true)
1687
-	if *last == -1 && *nLatest {
1688
-		*last = 1
1689
-	}
1690
-
1691
-	if *all {
1692
-		v.Set("all", "1")
1693
-	}
1694
-
1695
-	if *last != -1 {
1696
-		v.Set("limit", strconv.Itoa(*last))
1697
-	}
1698
-
1699
-	if *since != "" {
1700
-		v.Set("since", *since)
1701
-	}
1702
-
1703
-	if *before != "" {
1704
-		v.Set("before", *before)
1705
-	}
1706
-
1707
-	if *size {
1708
-		v.Set("size", "1")
1709
-	}
1710
-
1711
-	// Consolidate all filter flags, and sanity check them.
1712
-	// They'll get processed in the daemon/server.
1713
-	for _, f := range flFilter.GetAll() {
1714
-		if psFilterArgs, err = filters.ParseFlag(f, psFilterArgs); err != nil {
1715
-			return err
1716
-		}
1717
-	}
1718
-
1719
-	if len(psFilterArgs) > 0 {
1720
-		filterJson, err := filters.ToParam(psFilterArgs)
1721
-		if err != nil {
1722
-			return err
1723
-		}
1724
-
1725
-		v.Set("filters", filterJson)
1726
-	}
1727
-
1728
-	body, _, err := readBody(cli.call("GET", "/containers/json?"+v.Encode(), nil, false))
1729
-	if err != nil {
1730
-		return err
1731
-	}
1732
-
1733
-	outs := engine.NewTable("Created", 0)
1734
-	if _, err := outs.ReadListFrom(body); err != nil {
1735
-		return err
1736
-	}
1737
-
1738
-	w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
1739
-	if !*quiet {
1740
-		fmt.Fprint(w, "CONTAINER ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES")
1741
-
1742
-		if *size {
1743
-			fmt.Fprintln(w, "\tSIZE")
1744
-		} else {
1745
-			fmt.Fprint(w, "\n")
1746
-		}
1747
-	}
1748
-
1749
-	stripNamePrefix := func(ss []string) []string {
1750
-		for i, s := range ss {
1751
-			ss[i] = s[1:]
1752
-		}
1753
-
1754
-		return ss
1755
-	}
1756
-
1757
-	for _, out := range outs.Data {
1758
-		outID := out.Get("Id")
1759
-
1760
-		if !*noTrunc {
1761
-			outID = stringid.TruncateID(outID)
1762
-		}
1763
-
1764
-		if *quiet {
1765
-			fmt.Fprintln(w, outID)
1766
-
1767
-			continue
1768
-		}
1769
-
1770
-		var (
1771
-			outNames   = stripNamePrefix(out.GetList("Names"))
1772
-			outCommand = strconv.Quote(out.Get("Command"))
1773
-			ports      = engine.NewTable("", 0)
1774
-		)
1775
-
1776
-		if !*noTrunc {
1777
-			outCommand = utils.Trunc(outCommand, 20)
1778
-
1779
-			// only display the default name for the container with notrunc is passed
1780
-			for _, name := range outNames {
1781
-				if len(strings.Split(name, "/")) == 1 {
1782
-					outNames = []string{name}
1783
-
1784
-					break
1785
-				}
1786
-			}
1787
-		}
1788
-
1789
-		ports.ReadListFrom([]byte(out.Get("Ports")))
1790
-
1791
-		image := out.Get("Image")
1792
-		if image == "" {
1793
-			image = "<no image>"
1794
-		}
1795
-
1796
-		fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t%s\t", outID, image, outCommand,
1797
-			units.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0))),
1798
-			out.Get("Status"), api.DisplayablePorts(ports), strings.Join(outNames, ","))
1799
-
1800
-		if *size {
1801
-			if out.GetInt("SizeRootFs") > 0 {
1802
-				fmt.Fprintf(w, "%s (virtual %s)\n", units.HumanSize(float64(out.GetInt64("SizeRw"))), units.HumanSize(float64(out.GetInt64("SizeRootFs"))))
1803
-			} else {
1804
-				fmt.Fprintf(w, "%s\n", units.HumanSize(float64(out.GetInt64("SizeRw"))))
1805
-			}
1806
-
1807
-			continue
1808
-		}
1809
-
1810
-		fmt.Fprint(w, "\n")
1811
-	}
1812
-
1813
-	if !*quiet {
1814
-		w.Flush()
1815
-	}
1816
-
1817
-	return nil
1818
-}
1819
-
1820
-func (cli *DockerCli) CmdCommit(args ...string) error {
1821
-	cmd := cli.Subcmd("commit", "CONTAINER [REPOSITORY[:TAG]]", "Create a new image from a container's changes", true)
1822
-	flPause := cmd.Bool([]string{"p", "-pause"}, true, "Pause container during commit")
1823
-	flComment := cmd.String([]string{"m", "-message"}, "", "Commit message")
1824
-	flAuthor := cmd.String([]string{"a", "#author", "-author"}, "", "Author (e.g., \"John Hannibal Smith <hannibal@a-team.com>\")")
1825
-	flChanges := opts.NewListOpts(nil)
1826
-	cmd.Var(&flChanges, []string{"c", "-change"}, "Apply Dockerfile instruction to the created image")
1827
-	// FIXME: --run is deprecated, it will be replaced with inline Dockerfile commands.
1828
-	flConfig := cmd.String([]string{"#run", "#-run"}, "", "This option is deprecated and will be removed in a future version in favor of inline Dockerfile-compatible commands")
1829
-	cmd.Require(flag.Max, 2)
1830
-	cmd.Require(flag.Min, 1)
1831
-	utils.ParseFlags(cmd, args, true)
1832
-
1833
-	var (
1834
-		name            = cmd.Arg(0)
1835
-		repository, tag = parsers.ParseRepositoryTag(cmd.Arg(1))
1836
-	)
1837
-
1838
-	//Check if the given image name can be resolved
1839
-	if repository != "" {
1840
-		if err := registry.ValidateRepositoryName(repository); err != nil {
1841
-			return err
1842
-		}
1843
-	}
1844
-
1845
-	v := url.Values{}
1846
-	v.Set("container", name)
1847
-	v.Set("repo", repository)
1848
-	v.Set("tag", tag)
1849
-	v.Set("comment", *flComment)
1850
-	v.Set("author", *flAuthor)
1851
-	for _, change := range flChanges.GetAll() {
1852
-		v.Add("changes", change)
1853
-	}
1854
-
1855
-	if *flPause != true {
1856
-		v.Set("pause", "0")
1857
-	}
1858
-
1859
-	var (
1860
-		config *runconfig.Config
1861
-		env    engine.Env
1862
-	)
1863
-	if *flConfig != "" {
1864
-		config = &runconfig.Config{}
1865
-		if err := json.Unmarshal([]byte(*flConfig), config); err != nil {
1866
-			return err
1867
-		}
1868
-	}
1869
-	stream, _, err := cli.call("POST", "/commit?"+v.Encode(), config, false)
1870
-	if err != nil {
1871
-		return err
1872
-	}
1873
-	if err := env.Decode(stream); err != nil {
1874
-		return err
1875
-	}
1876
-
1877
-	fmt.Fprintf(cli.out, "%s\n", env.Get("Id"))
1878
-	return nil
1879
-}
1880
-
1881
-func (cli *DockerCli) CmdEvents(args ...string) error {
1882
-	cmd := cli.Subcmd("events", "", "Get real time events from the server", true)
1883
-	since := cmd.String([]string{"#since", "-since"}, "", "Show all events created since timestamp")
1884
-	until := cmd.String([]string{"-until"}, "", "Stream events until this timestamp")
1885
-	flFilter := opts.NewListOpts(nil)
1886
-	cmd.Var(&flFilter, []string{"f", "-filter"}, "Filter output based on conditions provided")
1887
-	cmd.Require(flag.Exact, 0)
1888
-
1889
-	utils.ParseFlags(cmd, args, true)
1890
-
1891
-	var (
1892
-		v               = url.Values{}
1893
-		loc             = time.FixedZone(time.Now().Zone())
1894
-		eventFilterArgs = filters.Args{}
1895
-	)
1896
-
1897
-	// Consolidate all filter flags, and sanity check them early.
1898
-	// They'll get process in the daemon/server.
1899
-	for _, f := range flFilter.GetAll() {
1900
-		var err error
1901
-		eventFilterArgs, err = filters.ParseFlag(f, eventFilterArgs)
1902
-		if err != nil {
1903
-			return err
1904
-		}
1905
-	}
1906
-	var setTime = func(key, value string) {
1907
-		format := timeutils.RFC3339NanoFixed
1908
-		if len(value) < len(format) {
1909
-			format = format[:len(value)]
1910
-		}
1911
-		if t, err := time.ParseInLocation(format, value, loc); err == nil {
1912
-			v.Set(key, strconv.FormatInt(t.Unix(), 10))
1913
-		} else {
1914
-			v.Set(key, value)
1915
-		}
1916
-	}
1917
-	if *since != "" {
1918
-		setTime("since", *since)
1919
-	}
1920
-	if *until != "" {
1921
-		setTime("until", *until)
1922
-	}
1923
-	if len(eventFilterArgs) > 0 {
1924
-		filterJson, err := filters.ToParam(eventFilterArgs)
1925
-		if err != nil {
1926
-			return err
1927
-		}
1928
-		v.Set("filters", filterJson)
1929
-	}
1930
-	if err := cli.stream("GET", "/events?"+v.Encode(), nil, cli.out, nil); err != nil {
1931
-		return err
1932
-	}
1933
-	return nil
1934
-}
1935
-
1936
-func (cli *DockerCli) CmdExport(args ...string) error {
1937
-	cmd := cli.Subcmd("export", "CONTAINER", "Export a filesystem as a tar archive (streamed to STDOUT by default)", true)
1938
-	outfile := cmd.String([]string{"o", "-output"}, "", "Write to a file, instead of STDOUT")
1939
-	cmd.Require(flag.Exact, 1)
1940
-
1941
-	utils.ParseFlags(cmd, args, true)
1942
-
1943
-	var (
1944
-		output io.Writer = cli.out
1945
-		err    error
1946
-	)
1947
-	if *outfile != "" {
1948
-		output, err = os.Create(*outfile)
1949
-		if err != nil {
1950
-			return err
1951
-		}
1952
-	} else if cli.isTerminalOut {
1953
-		return errors.New("Cowardly refusing to save to a terminal. Use the -o flag or redirect.")
1954
-	}
1955
-
1956
-	if len(cmd.Args()) == 1 {
1957
-		image := cmd.Arg(0)
1958
-		if err := cli.stream("GET", "/containers/"+image+"/export", nil, output, nil); err != nil {
1959
-			return err
1960
-		}
1961
-	} else {
1962
-		v := url.Values{}
1963
-		for _, arg := range cmd.Args() {
1964
-			v.Add("names", arg)
1965
-		}
1966
-		if err := cli.stream("GET", "/containers/get?"+v.Encode(), nil, output, nil); err != nil {
1967
-			return err
1968
-		}
1969
-	}
1970
-
1971
-	return nil
1972
-}
1973
-
1974
-func (cli *DockerCli) CmdDiff(args ...string) error {
1975
-	cmd := cli.Subcmd("diff", "CONTAINER", "Inspect changes on a container's filesystem", true)
1976
-	cmd.Require(flag.Exact, 1)
1977
-
1978
-	utils.ParseFlags(cmd, args, true)
1979
-
1980
-	body, _, err := readBody(cli.call("GET", "/containers/"+cmd.Arg(0)+"/changes", nil, false))
1981
-
1982
-	if err != nil {
1983
-		return err
1984
-	}
1985
-
1986
-	outs := engine.NewTable("", 0)
1987
-	if _, err := outs.ReadListFrom(body); err != nil {
1988
-		return err
1989
-	}
1990
-	for _, change := range outs.Data {
1991
-		var kind string
1992
-		switch change.GetInt("Kind") {
1993
-		case archive.ChangeModify:
1994
-			kind = "C"
1995
-		case archive.ChangeAdd:
1996
-			kind = "A"
1997
-		case archive.ChangeDelete:
1998
-			kind = "D"
1999
-		}
2000
-		fmt.Fprintf(cli.out, "%s %s\n", kind, change.Get("Path"))
2001
-	}
2002
-	return nil
2003
-}
2004
-
2005
-func (cli *DockerCli) CmdLogs(args ...string) error {
2006
-	var (
2007
-		cmd    = cli.Subcmd("logs", "CONTAINER", "Fetch the logs of a container", true)
2008
-		follow = cmd.Bool([]string{"f", "-follow"}, false, "Follow log output")
2009
-		times  = cmd.Bool([]string{"t", "-timestamps"}, false, "Show timestamps")
2010
-		tail   = cmd.String([]string{"-tail"}, "all", "Number of lines to show from the end of the logs")
2011
-	)
2012
-	cmd.Require(flag.Exact, 1)
2013
-
2014
-	utils.ParseFlags(cmd, args, true)
2015
-
2016
-	name := cmd.Arg(0)
2017
-
2018
-	stream, _, err := cli.call("GET", "/containers/"+name+"/json", nil, false)
2019
-	if err != nil {
2020
-		return err
2021
-	}
2022
-
2023
-	env := engine.Env{}
2024
-	if err := env.Decode(stream); err != nil {
2025
-		return err
2026
-	}
2027
-
2028
-	if env.GetSubEnv("HostConfig").GetSubEnv("LogConfig").Get("Type") != "json-file" {
2029
-		return fmt.Errorf("\"logs\" command is supported only for \"json-file\" logging driver")
2030
-	}
2031
-
2032
-	v := url.Values{}
2033
-	v.Set("stdout", "1")
2034
-	v.Set("stderr", "1")
2035
-
2036
-	if *times {
2037
-		v.Set("timestamps", "1")
2038
-	}
2039
-
2040
-	if *follow {
2041
-		v.Set("follow", "1")
2042
-	}
2043
-	v.Set("tail", *tail)
2044
-
2045
-	return cli.streamHelper("GET", "/containers/"+name+"/logs?"+v.Encode(), env.GetSubEnv("Config").GetBool("Tty"), nil, cli.out, cli.err, nil)
2046
-}
2047
-
2048
-func (cli *DockerCli) CmdAttach(args ...string) error {
2049
-	var (
2050
-		cmd     = cli.Subcmd("attach", "CONTAINER", "Attach to a running container", true)
2051
-		noStdin = cmd.Bool([]string{"#nostdin", "-no-stdin"}, false, "Do not attach STDIN")
2052
-		proxy   = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxy all received signals to the process")
2053
-	)
2054
-	cmd.Require(flag.Exact, 1)
2055
-
2056
-	utils.ParseFlags(cmd, args, true)
2057
-	name := cmd.Arg(0)
2058
-
2059
-	stream, _, err := cli.call("GET", "/containers/"+name+"/json", nil, false)
2060
-	if err != nil {
2061
-		return err
2062
-	}
2063
-
2064
-	env := engine.Env{}
2065
-	if err := env.Decode(stream); err != nil {
2066
-		return err
2067
-	}
2068
-
2069
-	if !env.GetSubEnv("State").GetBool("Running") {
2070
-		return fmt.Errorf("You cannot attach to a stopped container, start it first")
2071
-	}
2072
-
2073
-	var (
2074
-		config = env.GetSubEnv("Config")
2075
-		tty    = config.GetBool("Tty")
2076
-	)
2077
-
2078
-	if err := cli.CheckTtyInput(!*noStdin, tty); err != nil {
2079
-		return err
2080
-	}
2081
-
2082
-	if tty && cli.isTerminalOut {
2083
-		if err := cli.monitorTtySize(cmd.Arg(0), false); err != nil {
2084
-			log.Debugf("Error monitoring TTY size: %s", err)
2085
-		}
2086
-	}
2087
-
2088
-	var in io.ReadCloser
2089
-
2090
-	v := url.Values{}
2091
-	v.Set("stream", "1")
2092
-	if !*noStdin && config.GetBool("OpenStdin") {
2093
-		v.Set("stdin", "1")
2094
-		in = cli.in
2095
-	}
2096
-
2097
-	v.Set("stdout", "1")
2098
-	v.Set("stderr", "1")
2099
-
2100
-	if *proxy && !tty {
2101
-		sigc := cli.forwardAllSignals(cmd.Arg(0))
2102
-		defer signal.StopCatch(sigc)
2103
-	}
2104
-
2105
-	if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), tty, in, cli.out, cli.err, nil, nil); err != nil {
2106
-		return err
2107
-	}
2108
-
2109
-	_, status, err := getExitCode(cli, cmd.Arg(0))
2110
-	if err != nil {
2111
-		return err
2112
-	}
2113
-	if status != 0 {
2114
-		return &utils.StatusError{StatusCode: status}
2115
-	}
2116
-
2117
-	return nil
2118
-}
2119
-
2120
-func (cli *DockerCli) CmdSearch(args ...string) error {
2121
-	cmd := cli.Subcmd("search", "TERM", "Search the Docker Hub for images", true)
2122
-	noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
2123
-	trusted := cmd.Bool([]string{"#t", "#trusted", "#-trusted"}, false, "Only show trusted builds")
2124
-	automated := cmd.Bool([]string{"-automated"}, false, "Only show automated builds")
2125
-	stars := cmd.Int([]string{"s", "#stars", "-stars"}, 0, "Only displays with at least x stars")
2126
-	cmd.Require(flag.Exact, 1)
2127
-
2128
-	utils.ParseFlags(cmd, args, true)
2129
-
2130
-	v := url.Values{}
2131
-	v.Set("term", cmd.Arg(0))
2132
-
2133
-	body, _, err := readBody(cli.call("GET", "/images/search?"+v.Encode(), nil, true))
2134
-
2135
-	if err != nil {
2136
-		return err
2137
-	}
2138
-	outs := engine.NewTable("star_count", 0)
2139
-	if _, err := outs.ReadListFrom(body); err != nil {
2140
-		return err
2141
-	}
2142
-	w := tabwriter.NewWriter(cli.out, 10, 1, 3, ' ', 0)
2143
-	fmt.Fprintf(w, "NAME\tDESCRIPTION\tSTARS\tOFFICIAL\tAUTOMATED\n")
2144
-	for _, out := range outs.Data {
2145
-		if ((*automated || *trusted) && (!out.GetBool("is_trusted") && !out.GetBool("is_automated"))) || (*stars > out.GetInt("star_count")) {
2146
-			continue
2147
-		}
2148
-		desc := strings.Replace(out.Get("description"), "\n", " ", -1)
2149
-		desc = strings.Replace(desc, "\r", " ", -1)
2150
-		if !*noTrunc && len(desc) > 45 {
2151
-			desc = utils.Trunc(desc, 42) + "..."
2152
-		}
2153
-		fmt.Fprintf(w, "%s\t%s\t%d\t", out.Get("name"), desc, out.GetInt("star_count"))
2154
-		if out.GetBool("is_official") {
2155
-			fmt.Fprint(w, "[OK]")
2156
-
2157
-		}
2158
-		fmt.Fprint(w, "\t")
2159
-		if out.GetBool("is_automated") || out.GetBool("is_trusted") {
2160
-			fmt.Fprint(w, "[OK]")
2161
-		}
2162
-		fmt.Fprint(w, "\n")
2163
-	}
2164
-	w.Flush()
2165
-	return nil
2166
-}
2167
-
2168
-// Ports type - Used to parse multiple -p flags
2169
-type ports []int
2170
-
2171
-func (cli *DockerCli) CmdTag(args ...string) error {
2172
-	cmd := cli.Subcmd("tag", "IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]", "Tag an image into a repository", true)
2173
-	force := cmd.Bool([]string{"f", "#force", "-force"}, false, "Force")
2174
-	cmd.Require(flag.Exact, 2)
2175
-
2176
-	utils.ParseFlags(cmd, args, true)
2177
-
2178
-	var (
2179
-		repository, tag = parsers.ParseRepositoryTag(cmd.Arg(1))
2180
-		v               = url.Values{}
2181
-	)
2182
-
2183
-	//Check if the given image name can be resolved
2184
-	if err := registry.ValidateRepositoryName(repository); err != nil {
2185
-		return err
2186
-	}
2187
-	v.Set("repo", repository)
2188
-	v.Set("tag", tag)
2189
-
2190
-	if *force {
2191
-		v.Set("force", "1")
2192
-	}
2193
-
2194
-	if _, _, err := readBody(cli.call("POST", "/images/"+cmd.Arg(0)+"/tag?"+v.Encode(), nil, false)); err != nil {
2195
-		return err
2196
-	}
2197
-	return nil
2198
-}
2199
-
2200
-func (cli *DockerCli) pullImage(image string) error {
2201
-	return cli.pullImageCustomOut(image, cli.out)
2202
-}
2203
-
2204
-func (cli *DockerCli) pullImageCustomOut(image string, out io.Writer) error {
2205
-	v := url.Values{}
2206
-	repos, tag := parsers.ParseRepositoryTag(image)
2207
-	// pull only the image tagged 'latest' if no tag was specified
2208
-	if tag == "" {
2209
-		tag = graph.DEFAULTTAG
2210
-	}
2211
-	v.Set("fromImage", repos)
2212
-	v.Set("tag", tag)
2213
-
2214
-	// Resolve the Repository name from fqn to RepositoryInfo
2215
-	repoInfo, err := registry.ParseRepositoryInfo(repos)
2216
-	if err != nil {
2217
-		return err
2218
-	}
2219
-
2220
-	// Load the auth config file, to be able to pull the image
2221
-	cli.LoadConfigFile()
2222
-
2223
-	// Resolve the Auth config relevant for this server
2224
-	authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index)
2225
-	buf, err := json.Marshal(authConfig)
2226
-	if err != nil {
2227
-		return err
2228
-	}
2229
-
2230
-	registryAuthHeader := []string{
2231
-		base64.URLEncoding.EncodeToString(buf),
2232
-	}
2233
-	if err = cli.stream("POST", "/images/create?"+v.Encode(), nil, out, map[string][]string{"X-Registry-Auth": registryAuthHeader}); err != nil {
2234
-		return err
2235
-	}
2236
-	return nil
2237
-}
2238
-
2239
-type cidFile struct {
2240
-	path    string
2241
-	file    *os.File
2242
-	written bool
2243
-}
2244
-
2245
-func newCIDFile(path string) (*cidFile, error) {
2246
-	if _, err := os.Stat(path); err == nil {
2247
-		return nil, fmt.Errorf("Container ID file found, make sure the other container isn't running or delete %s", path)
2248
-	}
2249
-
2250
-	f, err := os.Create(path)
2251
-	if err != nil {
2252
-		return nil, fmt.Errorf("Failed to create the container ID file: %s", err)
2253
-	}
2254
-
2255
-	return &cidFile{path: path, file: f}, nil
2256
-}
2257
-
2258
-func (cid *cidFile) Close() error {
2259
-	cid.file.Close()
2260
-
2261
-	if !cid.written {
2262
-		if err := os.Remove(cid.path); err != nil {
2263
-			return fmt.Errorf("failed to remove the CID file '%s': %s \n", cid.path, err)
2264
-		}
2265
-	}
2266
-
2267
-	return nil
2268
-}
2269
-
2270
-func (cid *cidFile) Write(id string) error {
2271
-	if _, err := cid.file.Write([]byte(id)); err != nil {
2272
-		return fmt.Errorf("Failed to write the container ID to the file: %s", err)
2273
-	}
2274
-	cid.written = true
2275
-	return nil
2276
-}
2277
-
2278
-func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runconfig.HostConfig, cidfile, name string) (*types.ContainerCreateResponse, error) {
2279
-	containerValues := url.Values{}
2280
-	if name != "" {
2281
-		containerValues.Set("name", name)
2282
-	}
2283
-
2284
-	mergedConfig := runconfig.MergeConfigs(config, hostConfig)
2285
-
2286
-	var containerIDFile *cidFile
2287
-	if cidfile != "" {
2288
-		var err error
2289
-		if containerIDFile, err = newCIDFile(cidfile); err != nil {
2290
-			return nil, err
2291
-		}
2292
-		defer containerIDFile.Close()
2293
-	}
2294
-
2295
-	//create the container
2296
-	stream, statusCode, err := cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, false)
2297
-	//if image not found try to pull it
2298
-	if statusCode == 404 {
2299
-		repo, tag := parsers.ParseRepositoryTag(config.Image)
2300
-		if tag == "" {
2301
-			tag = graph.DEFAULTTAG
2302
-		}
2303
-		fmt.Fprintf(cli.err, "Unable to find image '%s' locally\n", utils.ImageReference(repo, tag))
2304
-
2305
-		// we don't want to write to stdout anything apart from container.ID
2306
-		if err = cli.pullImageCustomOut(config.Image, cli.err); err != nil {
2307
-			return nil, err
2308
-		}
2309
-		// Retry
2310
-		if stream, _, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, false); err != nil {
2311
-			return nil, err
2312
-		}
2313
-	} else if err != nil {
2314
-		return nil, err
2315
-	}
2316
-
2317
-	var response types.ContainerCreateResponse
2318
-	if err := json.NewDecoder(stream).Decode(&response); err != nil {
2319
-		return nil, err
2320
-	}
2321
-	for _, warning := range response.Warnings {
2322
-		fmt.Fprintf(cli.err, "WARNING: %s\n", warning)
2323
-	}
2324
-	if containerIDFile != nil {
2325
-		if err = containerIDFile.Write(response.ID); err != nil {
2326
-			return nil, err
2327
-		}
2328
-	}
2329
-	return &response, nil
2330
-}
2331
-
2332
-func (cli *DockerCli) CmdCreate(args ...string) error {
2333
-	cmd := cli.Subcmd("create", "IMAGE [COMMAND] [ARG...]", "Create a new container", true)
2334
-
2335
-	// These are flags not stored in Config/HostConfig
2336
-	var (
2337
-		flName = cmd.String([]string{"-name"}, "", "Assign a name to the container")
2338
-	)
2339
-
2340
-	config, hostConfig, cmd, err := runconfig.Parse(cmd, args)
2341
-	if err != nil {
2342
-		utils.ReportError(cmd, err.Error(), true)
2343
-	}
2344
-	if config.Image == "" {
2345
-		cmd.Usage()
2346
-		return nil
2347
-	}
2348
-	response, err := cli.createContainer(config, hostConfig, hostConfig.ContainerIDFile, *flName)
2349
-	if err != nil {
2350
-		return err
2351
-	}
2352
-	fmt.Fprintf(cli.out, "%s\n", response.ID)
2353
-	return nil
2354
-}
2355
-
2356
-func (cli *DockerCli) CmdRun(args ...string) error {
2357
-	// FIXME: just use runconfig.Parse already
2358
-	cmd := cli.Subcmd("run", "IMAGE [COMMAND] [ARG...]", "Run a command in a new container", true)
2359
-
2360
-	// These are flags not stored in Config/HostConfig
2361
-	var (
2362
-		flAutoRemove = cmd.Bool([]string{"#rm", "-rm"}, false, "Automatically remove the container when it exits")
2363
-		flDetach     = cmd.Bool([]string{"d", "-detach"}, false, "Run container in background and print container ID")
2364
-		flSigProxy   = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxy received signals to the process")
2365
-		flName       = cmd.String([]string{"#name", "-name"}, "", "Assign a name to the container")
2366
-		flAttach     *opts.ListOpts
2367
-
2368
-		ErrConflictAttachDetach               = fmt.Errorf("Conflicting options: -a and -d")
2369
-		ErrConflictRestartPolicyAndAutoRemove = fmt.Errorf("Conflicting options: --restart and --rm")
2370
-		ErrConflictDetachAutoRemove           = fmt.Errorf("Conflicting options: --rm and -d")
2371
-	)
2372
-
2373
-	config, hostConfig, cmd, err := runconfig.Parse(cmd, args)
2374
-	// just in case the Parse does not exit
2375
-	if err != nil {
2376
-		utils.ReportError(cmd, err.Error(), true)
2377
-	}
2378
-
2379
-	if len(hostConfig.Dns) > 0 {
2380
-		// check the DNS settings passed via --dns against
2381
-		// localhost regexp to warn if they are trying to
2382
-		// set a DNS to a localhost address
2383
-		for _, dnsIP := range hostConfig.Dns {
2384
-			if resolvconf.IsLocalhost(dnsIP) {
2385
-				fmt.Fprintf(cli.err, "WARNING: Localhost DNS setting (--dns=%s) may fail in containers.\n", dnsIP)
2386
-				break
2387
-			}
2388
-		}
2389
-	}
2390
-	if config.Image == "" {
2391
-		cmd.Usage()
2392
-		return nil
2393
-	}
2394
-
2395
-	if !*flDetach {
2396
-		if err := cli.CheckTtyInput(config.AttachStdin, config.Tty); err != nil {
2397
-			return err
2398
-		}
2399
-	} else {
2400
-		if fl := cmd.Lookup("-attach"); fl != nil {
2401
-			flAttach = fl.Value.(*opts.ListOpts)
2402
-			if flAttach.Len() != 0 {
2403
-				return ErrConflictAttachDetach
2404
-			}
2405
-		}
2406
-		if *flAutoRemove {
2407
-			return ErrConflictDetachAutoRemove
2408
-		}
2409
-
2410
-		config.AttachStdin = false
2411
-		config.AttachStdout = false
2412
-		config.AttachStderr = false
2413
-		config.StdinOnce = false
2414
-	}
2415
-
2416
-	// Disable flSigProxy when in TTY mode
2417
-	sigProxy := *flSigProxy
2418
-	if config.Tty {
2419
-		sigProxy = false
2420
-	}
2421
-
2422
-	createResponse, err := cli.createContainer(config, hostConfig, hostConfig.ContainerIDFile, *flName)
2423
-	if err != nil {
2424
-		return err
2425
-	}
2426
-	if sigProxy {
2427
-		sigc := cli.forwardAllSignals(createResponse.ID)
2428
-		defer signal.StopCatch(sigc)
2429
-	}
2430
-	var (
2431
-		waitDisplayId chan struct{}
2432
-		errCh         chan error
2433
-	)
2434
-	if !config.AttachStdout && !config.AttachStderr {
2435
-		// Make this asynchronous to allow the client to write to stdin before having to read the ID
2436
-		waitDisplayId = make(chan struct{})
2437
-		go func() {
2438
-			defer close(waitDisplayId)
2439
-			fmt.Fprintf(cli.out, "%s\n", createResponse.ID)
2440
-		}()
2441
-	}
2442
-	if *flAutoRemove && (hostConfig.RestartPolicy.Name == "always" || hostConfig.RestartPolicy.Name == "on-failure") {
2443
-		return ErrConflictRestartPolicyAndAutoRemove
2444
-	}
2445
-	// We need to instantiate the chan because the select needs it. It can
2446
-	// be closed but can't be uninitialized.
2447
-	hijacked := make(chan io.Closer)
2448
-	// Block the return until the chan gets closed
2449
-	defer func() {
2450
-		log.Debugf("End of CmdRun(), Waiting for hijack to finish.")
2451
-		if _, ok := <-hijacked; ok {
2452
-			log.Errorf("Hijack did not finish (chan still open)")
2453
-		}
2454
-	}()
2455
-	if config.AttachStdin || config.AttachStdout || config.AttachStderr {
2456
-		var (
2457
-			out, stderr io.Writer
2458
-			in          io.ReadCloser
2459
-			v           = url.Values{}
2460
-		)
2461
-		v.Set("stream", "1")
2462
-		if config.AttachStdin {
2463
-			v.Set("stdin", "1")
2464
-			in = cli.in
2465
-		}
2466
-		if config.AttachStdout {
2467
-			v.Set("stdout", "1")
2468
-			out = cli.out
2469
-		}
2470
-		if config.AttachStderr {
2471
-			v.Set("stderr", "1")
2472
-			if config.Tty {
2473
-				stderr = cli.out
2474
-			} else {
2475
-				stderr = cli.err
2476
-			}
2477
-		}
2478
-		errCh = promise.Go(func() error {
2479
-			return cli.hijack("POST", "/containers/"+createResponse.ID+"/attach?"+v.Encode(), config.Tty, in, out, stderr, hijacked, nil)
2480
-		})
2481
-	} else {
2482
-		close(hijacked)
2483
-	}
2484
-	// Acknowledge the hijack before starting
2485
-	select {
2486
-	case closer := <-hijacked:
2487
-		// Make sure that the hijack gets closed when returning (results
2488
-		// in closing the hijack chan and freeing server's goroutines)
2489
-		if closer != nil {
2490
-			defer closer.Close()
2491
-		}
2492
-	case err := <-errCh:
2493
-		if err != nil {
2494
-			log.Debugf("Error hijack: %s", err)
2495
-			return err
2496
-		}
2497
-	}
2498
-
2499
-	defer func() {
2500
-		if *flAutoRemove {
2501
-			if _, _, err = readBody(cli.call("DELETE", "/containers/"+createResponse.ID+"?v=1", nil, false)); err != nil {
2502
-				log.Errorf("Error deleting container: %s", err)
2503
-			}
2504
-		}
2505
-	}()
2506
-
2507
-	//start the container
2508
-	if _, _, err = readBody(cli.call("POST", "/containers/"+createResponse.ID+"/start", nil, false)); err != nil {
2509
-		return err
2510
-	}
2511
-
2512
-	if (config.AttachStdin || config.AttachStdout || config.AttachStderr) && config.Tty && cli.isTerminalOut {
2513
-		if err := cli.monitorTtySize(createResponse.ID, false); err != nil {
2514
-			log.Errorf("Error monitoring TTY size: %s", err)
2515
-		}
2516
-	}
2517
-
2518
-	if errCh != nil {
2519
-		if err := <-errCh; err != nil {
2520
-			log.Debugf("Error hijack: %s", err)
2521
-			return err
2522
-		}
2523
-	}
2524
-
2525
-	// Detached mode: wait for the id to be displayed and return.
2526
-	if !config.AttachStdout && !config.AttachStderr {
2527
-		// Detached mode
2528
-		<-waitDisplayId
2529
-		return nil
2530
-	}
2531
-
2532
-	var status int
2533
-
2534
-	// Attached mode
2535
-	if *flAutoRemove {
2536
-		// Autoremove: wait for the container to finish, retrieve
2537
-		// the exit code and remove the container
2538
-		if _, _, err := readBody(cli.call("POST", "/containers/"+createResponse.ID+"/wait", nil, false)); err != nil {
2539
-			return err
2540
-		}
2541
-		if _, status, err = getExitCode(cli, createResponse.ID); err != nil {
2542
-			return err
2543
-		}
2544
-	} else {
2545
-		// No Autoremove: Simply retrieve the exit code
2546
-		if !config.Tty {
2547
-			// In non-TTY mode, we can't detach, so we must wait for container exit
2548
-			if status, err = waitForExit(cli, createResponse.ID); err != nil {
2549
-				return err
2550
-			}
2551
-		} else {
2552
-			// In TTY mode, there is a race: if the process dies too slowly, the state could
2553
-			// be updated after the getExitCode call and result in the wrong exit code being reported
2554
-			if _, status, err = getExitCode(cli, createResponse.ID); err != nil {
2555
-				return err
2556
-			}
2557
-		}
2558
-	}
2559
-	if status != 0 {
2560
-		return &utils.StatusError{StatusCode: status}
2561
-	}
2562
-	return nil
2563
-}
2564
-
2565
-func (cli *DockerCli) CmdCp(args ...string) error {
2566
-	cmd := cli.Subcmd("cp", "CONTAINER:PATH HOSTDIR|-", "Copy files/folders from a PATH on the container to a HOSTDIR on the host\nrunning the command. Use '-' to write the data\nas a tar file to STDOUT.", true)
2567
-	cmd.Require(flag.Exact, 2)
2568
-
2569
-	utils.ParseFlags(cmd, args, true)
2570
-
2571
-	var copyData engine.Env
2572
-	info := strings.Split(cmd.Arg(0), ":")
2573
-
2574
-	if len(info) != 2 {
2575
-		return fmt.Errorf("Error: Path not specified")
2576
-	}
2577
-
2578
-	copyData.Set("Resource", info[1])
2579
-	copyData.Set("HostPath", cmd.Arg(1))
2580
-
2581
-	stream, statusCode, err := cli.call("POST", "/containers/"+info[0]+"/copy", copyData, false)
2582
-	if stream != nil {
2583
-		defer stream.Close()
2584
-	}
2585
-	if statusCode == 404 {
2586
-		return fmt.Errorf("No such container: %v", info[0])
2587
-	}
2588
-	if err != nil {
2589
-		return err
2590
-	}
2591
-
2592
-	if statusCode == 200 {
2593
-		dest := copyData.Get("HostPath")
2594
-
2595
-		if dest == "-" {
2596
-			_, err = io.Copy(cli.out, stream)
2597
-		} else {
2598
-			err = archive.Untar(stream, dest, &archive.TarOptions{NoLchown: true})
2599
-		}
2600
-		if err != nil {
2601
-			return err
2602
-		}
2603
-	}
2604
-	return nil
2605
-}
2606
-
2607
-func (cli *DockerCli) CmdSave(args ...string) error {
2608
-	cmd := cli.Subcmd("save", "IMAGE [IMAGE...]", "Save an image(s) to a tar archive (streamed to STDOUT by default)", true)
2609
-	outfile := cmd.String([]string{"o", "-output"}, "", "Write to an file, instead of STDOUT")
2610
-	cmd.Require(flag.Min, 1)
2611
-
2612
-	utils.ParseFlags(cmd, args, true)
2613
-
2614
-	var (
2615
-		output io.Writer = cli.out
2616
-		err    error
2617
-	)
2618
-	if *outfile != "" {
2619
-		output, err = os.Create(*outfile)
2620
-		if err != nil {
2621
-			return err
2622
-		}
2623
-	} else if cli.isTerminalOut {
2624
-		return errors.New("Cowardly refusing to save to a terminal. Use the -o flag or redirect.")
2625
-	}
2626
-
2627
-	if len(cmd.Args()) == 1 {
2628
-		image := cmd.Arg(0)
2629
-		if err := cli.stream("GET", "/images/"+image+"/get", nil, output, nil); err != nil {
2630
-			return err
2631
-		}
2632
-	} else {
2633
-		v := url.Values{}
2634
-		for _, arg := range cmd.Args() {
2635
-			v.Add("names", arg)
2636
-		}
2637
-		if err := cli.stream("GET", "/images/get?"+v.Encode(), nil, output, nil); err != nil {
2638
-			return err
2639
-		}
2640
-	}
2641
-	return nil
2642
-}
2643
-
2644
-func (cli *DockerCli) CmdLoad(args ...string) error {
2645
-	cmd := cli.Subcmd("load", "", "Load an image from a tar archive on STDIN", true)
2646
-	infile := cmd.String([]string{"i", "-input"}, "", "Read from a tar archive file, instead of STDIN")
2647
-	cmd.Require(flag.Exact, 0)
2648
-
2649
-	utils.ParseFlags(cmd, args, true)
2650
-
2651
-	var (
2652
-		input io.Reader = cli.in
2653
-		err   error
2654
-	)
2655
-	if *infile != "" {
2656
-		input, err = os.Open(*infile)
2657
-		if err != nil {
2658
-			return err
2659
-		}
2660
-	}
2661
-	if err := cli.stream("POST", "/images/load", input, cli.out, nil); err != nil {
2662
-		return err
2663
-	}
2664
-	return nil
2665
-}
2666
-
2667
-func (cli *DockerCli) CmdExec(args ...string) error {
2668
-	cmd := cli.Subcmd("exec", "CONTAINER COMMAND [ARG...]", "Run a command in a running container", true)
2669
-
2670
-	execConfig, err := runconfig.ParseExec(cmd, args)
2671
-	// just in case the ParseExec does not exit
2672
-	if execConfig.Container == "" || err != nil {
2673
-		return &utils.StatusError{StatusCode: 1}
2674
-	}
2675
-
2676
-	stream, _, err := cli.call("POST", "/containers/"+execConfig.Container+"/exec", execConfig, false)
2677
-	if err != nil {
2678
-		return err
2679
-	}
2680
-
2681
-	var response types.ContainerExecCreateResponse
2682
-	if err := json.NewDecoder(stream).Decode(&response); err != nil {
2683
-		return err
2684
-	}
2685
-	for _, warning := range response.Warnings {
2686
-		fmt.Fprintf(cli.err, "WARNING: %s\n", warning)
2687
-	}
2688
-
2689
-	execID := response.ID
2690
-
2691
-	if execID == "" {
2692
-		fmt.Fprintf(cli.out, "exec ID empty")
2693
-		return nil
2694
-	}
2695
-
2696
-	if !execConfig.Detach {
2697
-		if err := cli.CheckTtyInput(execConfig.AttachStdin, execConfig.Tty); err != nil {
2698
-			return err
2699
-		}
2700
-	} else {
2701
-		if _, _, err := readBody(cli.call("POST", "/exec/"+execID+"/start", execConfig, false)); err != nil {
2702
-			return err
2703
-		}
2704
-		// For now don't print this - wait for when we support exec wait()
2705
-		// fmt.Fprintf(cli.out, "%s\n", execID)
2706
-		return nil
2707
-	}
2708
-
2709
-	// Interactive exec requested.
2710
-	var (
2711
-		out, stderr io.Writer
2712
-		in          io.ReadCloser
2713
-		hijacked    = make(chan io.Closer)
2714
-		errCh       chan error
2715
-	)
2716
-
2717
-	// Block the return until the chan gets closed
2718
-	defer func() {
2719
-		log.Debugf("End of CmdExec(), Waiting for hijack to finish.")
2720
-		if _, ok := <-hijacked; ok {
2721
-			log.Errorf("Hijack did not finish (chan still open)")
2722
-		}
2723
-	}()
2724
-
2725
-	if execConfig.AttachStdin {
2726
-		in = cli.in
2727
-	}
2728
-	if execConfig.AttachStdout {
2729
-		out = cli.out
2730
-	}
2731
-	if execConfig.AttachStderr {
2732
-		if execConfig.Tty {
2733
-			stderr = cli.out
2734
-		} else {
2735
-			stderr = cli.err
2736
-		}
2737
-	}
2738
-	errCh = promise.Go(func() error {
2739
-		return cli.hijack("POST", "/exec/"+execID+"/start", execConfig.Tty, in, out, stderr, hijacked, execConfig)
2740
-	})
2741
-
2742
-	// Acknowledge the hijack before starting
2743
-	select {
2744
-	case closer := <-hijacked:
2745
-		// Make sure that hijack gets closed when returning. (result
2746
-		// in closing hijack chan and freeing server's goroutines.
2747
-		if closer != nil {
2748
-			defer closer.Close()
2749
-		}
2750
-	case err := <-errCh:
2751
-		if err != nil {
2752
-			log.Debugf("Error hijack: %s", err)
2753
-			return err
2754
-		}
2755
-	}
2756
-
2757
-	if execConfig.Tty && cli.isTerminalIn {
2758
-		if err := cli.monitorTtySize(execID, true); err != nil {
2759
-			log.Errorf("Error monitoring TTY size: %s", err)
2760
-		}
2761
-	}
2762
-
2763
-	if err := <-errCh; err != nil {
2764
-		log.Debugf("Error hijack: %s", err)
2765
-		return err
2766
-	}
2767
-
2768
-	var status int
2769
-	if _, status, err = getExecExitCode(cli, execID); err != nil {
2770
-		return err
2771
-	}
2772
-
2773
-	if status != 0 {
2774
-		return &utils.StatusError{StatusCode: status}
2775
-	}
2776
-
2777
-	return nil
2778
-}
2779
-
2780
-type containerStats struct {
2781
-	Name             string
2782
-	CpuPercentage    float64
2783
-	Memory           float64
2784
-	MemoryLimit      float64
2785
-	MemoryPercentage float64
2786
-	NetworkRx        float64
2787
-	NetworkTx        float64
2788
-	mu               sync.RWMutex
2789
-	err              error
2790
-}
2791
-
2792
-func (s *containerStats) Collect(cli *DockerCli) {
2793
-	stream, _, err := cli.call("GET", "/containers/"+s.Name+"/stats", nil, false)
2794
-	if err != nil {
2795
-		s.err = err
2796
-		return
2797
-	}
2798
-	defer stream.Close()
2799
-	var (
2800
-		previousCpu    uint64
2801
-		previousSystem uint64
2802
-		start          = true
2803
-		dec            = json.NewDecoder(stream)
2804
-		u              = make(chan error, 1)
2805
-	)
2806
-	go func() {
2807
-		for {
2808
-			var v *types.Stats
2809
-			if err := dec.Decode(&v); err != nil {
2810
-				u <- err
2811
-				return
2812
-			}
2813
-			var (
2814
-				memPercent = float64(v.MemoryStats.Usage) / float64(v.MemoryStats.Limit) * 100.0
2815
-				cpuPercent = 0.0
2816
-			)
2817
-			if !start {
2818
-				cpuPercent = calculateCpuPercent(previousCpu, previousSystem, v)
2819
-			}
2820
-			start = false
2821
-			s.mu.Lock()
2822
-			s.CpuPercentage = cpuPercent
2823
-			s.Memory = float64(v.MemoryStats.Usage)
2824
-			s.MemoryLimit = float64(v.MemoryStats.Limit)
2825
-			s.MemoryPercentage = memPercent
2826
-			s.NetworkRx = float64(v.Network.RxBytes)
2827
-			s.NetworkTx = float64(v.Network.TxBytes)
2828
-			s.mu.Unlock()
2829
-			previousCpu = v.CpuStats.CpuUsage.TotalUsage
2830
-			previousSystem = v.CpuStats.SystemUsage
2831
-			u <- nil
2832
-		}
2833
-	}()
2834
-	for {
2835
-		select {
2836
-		case <-time.After(2 * time.Second):
2837
-			// zero out the values if we have not received an update within
2838
-			// the specified duration.
2839
-			s.mu.Lock()
2840
-			s.CpuPercentage = 0
2841
-			s.Memory = 0
2842
-			s.MemoryPercentage = 0
2843
-			s.mu.Unlock()
2844
-		case err := <-u:
2845
-			if err != nil {
2846
-				s.mu.Lock()
2847
-				s.err = err
2848
-				s.mu.Unlock()
2849
-				return
2850
-			}
2851
-		}
2852
-	}
2853
-}
2854
-
2855
-func (s *containerStats) Display(w io.Writer) error {
2856
-	s.mu.RLock()
2857
-	defer s.mu.RUnlock()
2858
-	if s.err != nil {
2859
-		return s.err
2860
-	}
2861
-	fmt.Fprintf(w, "%s\t%.2f%%\t%s/%s\t%.2f%%\t%s/%s\n",
2862
-		s.Name,
2863
-		s.CpuPercentage,
2864
-		units.BytesSize(s.Memory), units.BytesSize(s.MemoryLimit),
2865
-		s.MemoryPercentage,
2866
-		units.BytesSize(s.NetworkRx), units.BytesSize(s.NetworkTx))
2867
-	return nil
2868
-}
2869
-
2870
-func (cli *DockerCli) CmdStats(args ...string) error {
2871
-	cmd := cli.Subcmd("stats", "CONTAINER [CONTAINER...]", "Display a live stream of one or more containers' resource usage statistics", true)
2872
-	cmd.Require(flag.Min, 1)
2873
-	utils.ParseFlags(cmd, args, true)
2874
-
2875
-	names := cmd.Args()
2876
-	sort.Strings(names)
2877
-	var (
2878
-		cStats []*containerStats
2879
-		w      = tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
2880
-	)
2881
-	printHeader := func() {
2882
-		fmt.Fprint(cli.out, "\033[2J")
2883
-		fmt.Fprint(cli.out, "\033[H")
2884
-		fmt.Fprintln(w, "CONTAINER\tCPU %\tMEM USAGE/LIMIT\tMEM %\tNET I/O")
2885
-	}
2886
-	for _, n := range names {
2887
-		s := &containerStats{Name: n}
2888
-		cStats = append(cStats, s)
2889
-		go s.Collect(cli)
2890
-	}
2891
-	// do a quick pause so that any failed connections for containers that do not exist are able to be
2892
-	// evicted before we display the initial or default values.
2893
-	time.Sleep(500 * time.Millisecond)
2894
-	var errs []string
2895
-	for _, c := range cStats {
2896
-		c.mu.Lock()
2897
-		if c.err != nil {
2898
-			errs = append(errs, fmt.Sprintf("%s: %v", c.Name, c.err))
2899
-		}
2900
-		c.mu.Unlock()
2901
-	}
2902
-	if len(errs) > 0 {
2903
-		return fmt.Errorf("%s", strings.Join(errs, ", "))
2904
-	}
2905
-	for _ = range time.Tick(500 * time.Millisecond) {
2906
-		printHeader()
2907
-		toRemove := []int{}
2908
-		for i, s := range cStats {
2909
-			if err := s.Display(w); err != nil {
2910
-				toRemove = append(toRemove, i)
2911
-			}
2912
-		}
2913
-		for j := len(toRemove) - 1; j >= 0; j-- {
2914
-			i := toRemove[j]
2915
-			cStats = append(cStats[:i], cStats[i+1:]...)
2916
-		}
2917
-		if len(cStats) == 0 {
2918
-			return nil
2919
-		}
2920
-		w.Flush()
2921
-	}
2922
-	return nil
2923
-}
2924
-
2925
-func calculateCpuPercent(previousCpu, previousSystem uint64, v *types.Stats) float64 {
2926
-	var (
2927
-		cpuPercent = 0.0
2928
-		// calculate the change for the cpu usage of the container in between readings
2929
-		cpuDelta = float64(v.CpuStats.CpuUsage.TotalUsage - previousCpu)
2930
-		// calculate the change for the entire system between readings
2931
-		systemDelta = float64(v.CpuStats.SystemUsage - previousSystem)
2932
-	)
2933
-
2934
-	if systemDelta > 0.0 && cpuDelta > 0.0 {
2935
-		cpuPercent = (cpuDelta / systemDelta) * float64(len(v.CpuStats.CpuUsage.PercpuUsage)) * 100.0
2936
-	}
2937
-	return cpuPercent
2938
-}
2939 1
new file mode 100644
... ...
@@ -0,0 +1,76 @@
0
+package client
1
+
2
+import (
3
+	"encoding/json"
4
+	"fmt"
5
+	"net/url"
6
+
7
+	"github.com/docker/docker/engine"
8
+	"github.com/docker/docker/opts"
9
+	flag "github.com/docker/docker/pkg/mflag"
10
+	"github.com/docker/docker/pkg/parsers"
11
+	"github.com/docker/docker/registry"
12
+	"github.com/docker/docker/runconfig"
13
+	"github.com/docker/docker/utils"
14
+)
15
+
16
+func (cli *DockerCli) CmdCommit(args ...string) error {
17
+	cmd := cli.Subcmd("commit", "CONTAINER [REPOSITORY[:TAG]]", "Create a new image from a container's changes", true)
18
+	flPause := cmd.Bool([]string{"p", "-pause"}, true, "Pause container during commit")
19
+	flComment := cmd.String([]string{"m", "-message"}, "", "Commit message")
20
+	flAuthor := cmd.String([]string{"a", "#author", "-author"}, "", "Author (e.g., \"John Hannibal Smith <hannibal@a-team.com>\")")
21
+	flChanges := opts.NewListOpts(nil)
22
+	cmd.Var(&flChanges, []string{"c", "-change"}, "Apply Dockerfile instruction to the created image")
23
+	// FIXME: --run is deprecated, it will be replaced with inline Dockerfile commands.
24
+	flConfig := cmd.String([]string{"#run", "#-run"}, "", "This option is deprecated and will be removed in a future version in favor of inline Dockerfile-compatible commands")
25
+	cmd.Require(flag.Max, 2)
26
+	cmd.Require(flag.Min, 1)
27
+	utils.ParseFlags(cmd, args, true)
28
+
29
+	var (
30
+		name            = cmd.Arg(0)
31
+		repository, tag = parsers.ParseRepositoryTag(cmd.Arg(1))
32
+	)
33
+
34
+	//Check if the given image name can be resolved
35
+	if repository != "" {
36
+		if err := registry.ValidateRepositoryName(repository); err != nil {
37
+			return err
38
+		}
39
+	}
40
+
41
+	v := url.Values{}
42
+	v.Set("container", name)
43
+	v.Set("repo", repository)
44
+	v.Set("tag", tag)
45
+	v.Set("comment", *flComment)
46
+	v.Set("author", *flAuthor)
47
+	for _, change := range flChanges.GetAll() {
48
+		v.Add("changes", change)
49
+	}
50
+
51
+	if *flPause != true {
52
+		v.Set("pause", "0")
53
+	}
54
+
55
+	var (
56
+		config *runconfig.Config
57
+		env    engine.Env
58
+	)
59
+	if *flConfig != "" {
60
+		config = &runconfig.Config{}
61
+		if err := json.Unmarshal([]byte(*flConfig), config); err != nil {
62
+			return err
63
+		}
64
+	}
65
+	stream, _, err := cli.call("POST", "/commit?"+v.Encode(), config, false)
66
+	if err != nil {
67
+		return err
68
+	}
69
+	if err := env.Decode(stream); err != nil {
70
+		return err
71
+	}
72
+
73
+	fmt.Fprintf(cli.out, "%s\n", env.Get("Id"))
74
+	return nil
75
+}
0 76
new file mode 100644
... ...
@@ -0,0 +1,54 @@
0
+package client
1
+
2
+import (
3
+	"fmt"
4
+	"io"
5
+	"strings"
6
+
7
+	"github.com/docker/docker/engine"
8
+	"github.com/docker/docker/pkg/archive"
9
+	flag "github.com/docker/docker/pkg/mflag"
10
+	"github.com/docker/docker/utils"
11
+)
12
+
13
+func (cli *DockerCli) CmdCp(args ...string) error {
14
+	cmd := cli.Subcmd("cp", "CONTAINER:PATH HOSTDIR|-", "Copy files/folders from a PATH on the container to a HOSTDIR on the host\nrunning the command. Use '-' to write the data\nas a tar file to STDOUT.", true)
15
+	cmd.Require(flag.Exact, 2)
16
+
17
+	utils.ParseFlags(cmd, args, true)
18
+
19
+	var copyData engine.Env
20
+	info := strings.Split(cmd.Arg(0), ":")
21
+
22
+	if len(info) != 2 {
23
+		return fmt.Errorf("Error: Path not specified")
24
+	}
25
+
26
+	copyData.Set("Resource", info[1])
27
+	copyData.Set("HostPath", cmd.Arg(1))
28
+
29
+	stream, statusCode, err := cli.call("POST", "/containers/"+info[0]+"/copy", copyData, false)
30
+	if stream != nil {
31
+		defer stream.Close()
32
+	}
33
+	if statusCode == 404 {
34
+		return fmt.Errorf("No such container: %v", info[0])
35
+	}
36
+	if err != nil {
37
+		return err
38
+	}
39
+
40
+	if statusCode == 200 {
41
+		dest := copyData.Get("HostPath")
42
+
43
+		if dest == "-" {
44
+			_, err = io.Copy(cli.out, stream)
45
+		} else {
46
+			err = archive.Untar(stream, dest, &archive.TarOptions{NoLchown: true})
47
+		}
48
+		if err != nil {
49
+			return err
50
+		}
51
+	}
52
+	return nil
53
+}
0 54
new file mode 100644
... ...
@@ -0,0 +1,153 @@
0
+package client
1
+
2
+import (
3
+	"encoding/base64"
4
+	"encoding/json"
5
+	"fmt"
6
+	"io"
7
+	"net/url"
8
+	"os"
9
+
10
+	"github.com/docker/docker/api/types"
11
+	"github.com/docker/docker/graph"
12
+	"github.com/docker/docker/pkg/parsers"
13
+	"github.com/docker/docker/registry"
14
+	"github.com/docker/docker/runconfig"
15
+	"github.com/docker/docker/utils"
16
+)
17
+
18
+func (cli *DockerCli) pullImage(image string) error {
19
+	return cli.pullImageCustomOut(image, cli.out)
20
+}
21
+
22
+func (cli *DockerCli) pullImageCustomOut(image string, out io.Writer) error {
23
+	v := url.Values{}
24
+	repos, tag := parsers.ParseRepositoryTag(image)
25
+	// pull only the image tagged 'latest' if no tag was specified
26
+	if tag == "" {
27
+		tag = graph.DEFAULTTAG
28
+	}
29
+	v.Set("fromImage", repos)
30
+	v.Set("tag", tag)
31
+
32
+	// Resolve the Repository name from fqn to RepositoryInfo
33
+	repoInfo, err := registry.ParseRepositoryInfo(repos)
34
+	if err != nil {
35
+		return err
36
+	}
37
+
38
+	// Load the auth config file, to be able to pull the image
39
+	cli.LoadConfigFile()
40
+
41
+	// Resolve the Auth config relevant for this server
42
+	authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index)
43
+	buf, err := json.Marshal(authConfig)
44
+	if err != nil {
45
+		return err
46
+	}
47
+
48
+	registryAuthHeader := []string{
49
+		base64.URLEncoding.EncodeToString(buf),
50
+	}
51
+	if err = cli.stream("POST", "/images/create?"+v.Encode(), nil, out, map[string][]string{"X-Registry-Auth": registryAuthHeader}); err != nil {
52
+		return err
53
+	}
54
+	return nil
55
+}
56
+
57
+type cidFile struct {
58
+	path    string
59
+	file    *os.File
60
+	written bool
61
+}
62
+
63
+func newCIDFile(path string) (*cidFile, error) {
64
+	if _, err := os.Stat(path); err == nil {
65
+		return nil, fmt.Errorf("Container ID file found, make sure the other container isn't running or delete %s", path)
66
+	}
67
+
68
+	f, err := os.Create(path)
69
+	if err != nil {
70
+		return nil, fmt.Errorf("Failed to create the container ID file: %s", err)
71
+	}
72
+
73
+	return &cidFile{path: path, file: f}, nil
74
+}
75
+
76
+func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runconfig.HostConfig, cidfile, name string) (*types.ContainerCreateResponse, error) {
77
+	containerValues := url.Values{}
78
+	if name != "" {
79
+		containerValues.Set("name", name)
80
+	}
81
+
82
+	mergedConfig := runconfig.MergeConfigs(config, hostConfig)
83
+
84
+	var containerIDFile *cidFile
85
+	if cidfile != "" {
86
+		var err error
87
+		if containerIDFile, err = newCIDFile(cidfile); err != nil {
88
+			return nil, err
89
+		}
90
+		defer containerIDFile.Close()
91
+	}
92
+
93
+	//create the container
94
+	stream, statusCode, err := cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, false)
95
+	//if image not found try to pull it
96
+	if statusCode == 404 {
97
+		repo, tag := parsers.ParseRepositoryTag(config.Image)
98
+		if tag == "" {
99
+			tag = graph.DEFAULTTAG
100
+		}
101
+		fmt.Fprintf(cli.err, "Unable to find image '%s' locally\n", utils.ImageReference(repo, tag))
102
+
103
+		// we don't want to write to stdout anything apart from container.ID
104
+		if err = cli.pullImageCustomOut(config.Image, cli.err); err != nil {
105
+			return nil, err
106
+		}
107
+		// Retry
108
+		if stream, _, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, false); err != nil {
109
+			return nil, err
110
+		}
111
+	} else if err != nil {
112
+		return nil, err
113
+	}
114
+
115
+	var response types.ContainerCreateResponse
116
+	if err := json.NewDecoder(stream).Decode(&response); err != nil {
117
+		return nil, err
118
+	}
119
+	for _, warning := range response.Warnings {
120
+		fmt.Fprintf(cli.err, "WARNING: %s\n", warning)
121
+	}
122
+	if containerIDFile != nil {
123
+		if err = containerIDFile.Write(response.ID); err != nil {
124
+			return nil, err
125
+		}
126
+	}
127
+	return &response, nil
128
+}
129
+
130
+func (cli *DockerCli) CmdCreate(args ...string) error {
131
+	cmd := cli.Subcmd("create", "IMAGE [COMMAND] [ARG...]", "Create a new container", true)
132
+
133
+	// These are flags not stored in Config/HostConfig
134
+	var (
135
+		flName = cmd.String([]string{"-name"}, "", "Assign a name to the container")
136
+	)
137
+
138
+	config, hostConfig, cmd, err := runconfig.Parse(cmd, args)
139
+	if err != nil {
140
+		utils.ReportError(cmd, err.Error(), true)
141
+	}
142
+	if config.Image == "" {
143
+		cmd.Usage()
144
+		return nil
145
+	}
146
+	response, err := cli.createContainer(config, hostConfig, hostConfig.ContainerIDFile, *flName)
147
+	if err != nil {
148
+		return err
149
+	}
150
+	fmt.Fprintf(cli.out, "%s\n", response.ID)
151
+	return nil
152
+}
0 153
new file mode 100644
... ...
@@ -0,0 +1,41 @@
0
+package client
1
+
2
+import (
3
+	"fmt"
4
+
5
+	"github.com/docker/docker/engine"
6
+	"github.com/docker/docker/pkg/archive"
7
+	flag "github.com/docker/docker/pkg/mflag"
8
+	"github.com/docker/docker/utils"
9
+)
10
+
11
+func (cli *DockerCli) CmdDiff(args ...string) error {
12
+	cmd := cli.Subcmd("diff", "CONTAINER", "Inspect changes on a container's filesystem", true)
13
+	cmd.Require(flag.Exact, 1)
14
+
15
+	utils.ParseFlags(cmd, args, true)
16
+
17
+	body, _, err := readBody(cli.call("GET", "/containers/"+cmd.Arg(0)+"/changes", nil, false))
18
+
19
+	if err != nil {
20
+		return err
21
+	}
22
+
23
+	outs := engine.NewTable("", 0)
24
+	if _, err := outs.ReadListFrom(body); err != nil {
25
+		return err
26
+	}
27
+	for _, change := range outs.Data {
28
+		var kind string
29
+		switch change.GetInt("Kind") {
30
+		case archive.ChangeModify:
31
+			kind = "C"
32
+		case archive.ChangeAdd:
33
+			kind = "A"
34
+		case archive.ChangeDelete:
35
+			kind = "D"
36
+		}
37
+		fmt.Fprintf(cli.out, "%s %s\n", kind, change.Get("Path"))
38
+	}
39
+	return nil
40
+}
0 41
new file mode 100644
... ...
@@ -0,0 +1,68 @@
0
+package client
1
+
2
+import (
3
+	"net/url"
4
+	"strconv"
5
+	"time"
6
+
7
+	"github.com/docker/docker/opts"
8
+	flag "github.com/docker/docker/pkg/mflag"
9
+	"github.com/docker/docker/pkg/parsers/filters"
10
+	"github.com/docker/docker/pkg/timeutils"
11
+	"github.com/docker/docker/utils"
12
+)
13
+
14
+func (cli *DockerCli) CmdEvents(args ...string) error {
15
+	cmd := cli.Subcmd("events", "", "Get real time events from the server", true)
16
+	since := cmd.String([]string{"#since", "-since"}, "", "Show all events created since timestamp")
17
+	until := cmd.String([]string{"-until"}, "", "Stream events until this timestamp")
18
+	flFilter := opts.NewListOpts(nil)
19
+	cmd.Var(&flFilter, []string{"f", "-filter"}, "Filter output based on conditions provided")
20
+	cmd.Require(flag.Exact, 0)
21
+
22
+	utils.ParseFlags(cmd, args, true)
23
+
24
+	var (
25
+		v               = url.Values{}
26
+		loc             = time.FixedZone(time.Now().Zone())
27
+		eventFilterArgs = filters.Args{}
28
+	)
29
+
30
+	// Consolidate all filter flags, and sanity check them early.
31
+	// They'll get process in the daemon/server.
32
+	for _, f := range flFilter.GetAll() {
33
+		var err error
34
+		eventFilterArgs, err = filters.ParseFlag(f, eventFilterArgs)
35
+		if err != nil {
36
+			return err
37
+		}
38
+	}
39
+	var setTime = func(key, value string) {
40
+		format := timeutils.RFC3339NanoFixed
41
+		if len(value) < len(format) {
42
+			format = format[:len(value)]
43
+		}
44
+		if t, err := time.ParseInLocation(format, value, loc); err == nil {
45
+			v.Set(key, strconv.FormatInt(t.Unix(), 10))
46
+		} else {
47
+			v.Set(key, value)
48
+		}
49
+	}
50
+	if *since != "" {
51
+		setTime("since", *since)
52
+	}
53
+	if *until != "" {
54
+		setTime("until", *until)
55
+	}
56
+	if len(eventFilterArgs) > 0 {
57
+		filterJson, err := filters.ToParam(eventFilterArgs)
58
+		if err != nil {
59
+			return err
60
+		}
61
+		v.Set("filters", filterJson)
62
+	}
63
+	if err := cli.stream("GET", "/events?"+v.Encode(), nil, cli.out, nil); err != nil {
64
+		return err
65
+	}
66
+	return nil
67
+}
0 68
new file mode 100644
... ...
@@ -0,0 +1,126 @@
0
+package client
1
+
2
+import (
3
+	"encoding/json"
4
+	"fmt"
5
+	"io"
6
+
7
+	log "github.com/Sirupsen/logrus"
8
+	"github.com/docker/docker/api/types"
9
+	"github.com/docker/docker/pkg/promise"
10
+	"github.com/docker/docker/runconfig"
11
+	"github.com/docker/docker/utils"
12
+)
13
+
14
+func (cli *DockerCli) CmdExec(args ...string) error {
15
+	cmd := cli.Subcmd("exec", "CONTAINER COMMAND [ARG...]", "Run a command in a running container", true)
16
+
17
+	execConfig, err := runconfig.ParseExec(cmd, args)
18
+	// just in case the ParseExec does not exit
19
+	if execConfig.Container == "" || err != nil {
20
+		return &utils.StatusError{StatusCode: 1}
21
+	}
22
+
23
+	stream, _, err := cli.call("POST", "/containers/"+execConfig.Container+"/exec", execConfig, false)
24
+	if err != nil {
25
+		return err
26
+	}
27
+
28
+	var response types.ContainerExecCreateResponse
29
+	if err := json.NewDecoder(stream).Decode(&response); err != nil {
30
+		return err
31
+	}
32
+	for _, warning := range response.Warnings {
33
+		fmt.Fprintf(cli.err, "WARNING: %s\n", warning)
34
+	}
35
+
36
+	execID := response.ID
37
+
38
+	if execID == "" {
39
+		fmt.Fprintf(cli.out, "exec ID empty")
40
+		return nil
41
+	}
42
+
43
+	if !execConfig.Detach {
44
+		if err := cli.CheckTtyInput(execConfig.AttachStdin, execConfig.Tty); err != nil {
45
+			return err
46
+		}
47
+	} else {
48
+		if _, _, err := readBody(cli.call("POST", "/exec/"+execID+"/start", execConfig, false)); err != nil {
49
+			return err
50
+		}
51
+		// For now don't print this - wait for when we support exec wait()
52
+		// fmt.Fprintf(cli.out, "%s\n", execID)
53
+		return nil
54
+	}
55
+
56
+	// Interactive exec requested.
57
+	var (
58
+		out, stderr io.Writer
59
+		in          io.ReadCloser
60
+		hijacked    = make(chan io.Closer)
61
+		errCh       chan error
62
+	)
63
+
64
+	// Block the return until the chan gets closed
65
+	defer func() {
66
+		log.Debugf("End of CmdExec(), Waiting for hijack to finish.")
67
+		if _, ok := <-hijacked; ok {
68
+			log.Errorf("Hijack did not finish (chan still open)")
69
+		}
70
+	}()
71
+
72
+	if execConfig.AttachStdin {
73
+		in = cli.in
74
+	}
75
+	if execConfig.AttachStdout {
76
+		out = cli.out
77
+	}
78
+	if execConfig.AttachStderr {
79
+		if execConfig.Tty {
80
+			stderr = cli.out
81
+		} else {
82
+			stderr = cli.err
83
+		}
84
+	}
85
+	errCh = promise.Go(func() error {
86
+		return cli.hijack("POST", "/exec/"+execID+"/start", execConfig.Tty, in, out, stderr, hijacked, execConfig)
87
+	})
88
+
89
+	// Acknowledge the hijack before starting
90
+	select {
91
+	case closer := <-hijacked:
92
+		// Make sure that hijack gets closed when returning. (result
93
+		// in closing hijack chan and freeing server's goroutines.
94
+		if closer != nil {
95
+			defer closer.Close()
96
+		}
97
+	case err := <-errCh:
98
+		if err != nil {
99
+			log.Debugf("Error hijack: %s", err)
100
+			return err
101
+		}
102
+	}
103
+
104
+	if execConfig.Tty && cli.isTerminalIn {
105
+		if err := cli.monitorTtySize(execID, true); err != nil {
106
+			log.Errorf("Error monitoring TTY size: %s", err)
107
+		}
108
+	}
109
+
110
+	if err := <-errCh; err != nil {
111
+		log.Debugf("Error hijack: %s", err)
112
+		return err
113
+	}
114
+
115
+	var status int
116
+	if _, status, err = getExecExitCode(cli, execID); err != nil {
117
+		return err
118
+	}
119
+
120
+	if status != 0 {
121
+		return &utils.StatusError{StatusCode: status}
122
+	}
123
+
124
+	return nil
125
+}
0 126
new file mode 100644
... ...
@@ -0,0 +1,49 @@
0
+package client
1
+
2
+import (
3
+	"errors"
4
+	"io"
5
+	"net/url"
6
+	"os"
7
+
8
+	flag "github.com/docker/docker/pkg/mflag"
9
+	"github.com/docker/docker/utils"
10
+)
11
+
12
+func (cli *DockerCli) CmdExport(args ...string) error {
13
+	cmd := cli.Subcmd("export", "CONTAINER", "Export a filesystem as a tar archive (streamed to STDOUT by default)", true)
14
+	outfile := cmd.String([]string{"o", "-output"}, "", "Write to a file, instead of STDOUT")
15
+	cmd.Require(flag.Exact, 1)
16
+
17
+	utils.ParseFlags(cmd, args, true)
18
+
19
+	var (
20
+		output io.Writer = cli.out
21
+		err    error
22
+	)
23
+	if *outfile != "" {
24
+		output, err = os.Create(*outfile)
25
+		if err != nil {
26
+			return err
27
+		}
28
+	} else if cli.isTerminalOut {
29
+		return errors.New("Cowardly refusing to save to a terminal. Use the -o flag or redirect.")
30
+	}
31
+
32
+	if len(cmd.Args()) == 1 {
33
+		image := cmd.Arg(0)
34
+		if err := cli.stream("GET", "/containers/"+image+"/export", nil, output, nil); err != nil {
35
+			return err
36
+		}
37
+	} else {
38
+		v := url.Values{}
39
+		for _, arg := range cmd.Args() {
40
+			v.Add("names", arg)
41
+		}
42
+		if err := cli.stream("GET", "/containers/get?"+v.Encode(), nil, output, nil); err != nil {
43
+			return err
44
+		}
45
+	}
46
+
47
+	return nil
48
+}
0 49
new file mode 100644
... ...
@@ -0,0 +1,32 @@
0
+package client
1
+
2
+import (
3
+	"fmt"
4
+	"os"
5
+
6
+	flag "github.com/docker/docker/pkg/mflag"
7
+)
8
+
9
+func (cli *DockerCli) CmdHelp(args ...string) error {
10
+	if len(args) > 1 {
11
+		method, exists := cli.getMethod(args[:2]...)
12
+		if exists {
13
+			method("--help")
14
+			return nil
15
+		}
16
+	}
17
+	if len(args) > 0 {
18
+		method, exists := cli.getMethod(args[0])
19
+		if !exists {
20
+			fmt.Fprintf(cli.err, "docker: '%s' is not a docker command. See 'docker --help'.\n", args[0])
21
+			os.Exit(1)
22
+		} else {
23
+			method("--help")
24
+			return nil
25
+		}
26
+	}
27
+
28
+	flag.Usage()
29
+
30
+	return nil
31
+}
0 32
new file mode 100644
... ...
@@ -0,0 +1,65 @@
0
+package client
1
+
2
+import (
3
+	"fmt"
4
+	"text/tabwriter"
5
+	"time"
6
+
7
+	"github.com/docker/docker/engine"
8
+	flag "github.com/docker/docker/pkg/mflag"
9
+	"github.com/docker/docker/pkg/stringid"
10
+	"github.com/docker/docker/pkg/units"
11
+	"github.com/docker/docker/utils"
12
+)
13
+
14
+func (cli *DockerCli) CmdHistory(args ...string) error {
15
+	cmd := cli.Subcmd("history", "IMAGE", "Show the history of an image", true)
16
+	quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs")
17
+	noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
18
+	cmd.Require(flag.Exact, 1)
19
+
20
+	utils.ParseFlags(cmd, args, true)
21
+
22
+	body, _, err := readBody(cli.call("GET", "/images/"+cmd.Arg(0)+"/history", nil, false))
23
+	if err != nil {
24
+		return err
25
+	}
26
+
27
+	outs := engine.NewTable("Created", 0)
28
+	if _, err := outs.ReadListFrom(body); err != nil {
29
+		return err
30
+	}
31
+
32
+	w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
33
+	if !*quiet {
34
+		fmt.Fprintln(w, "IMAGE\tCREATED\tCREATED BY\tSIZE")
35
+	}
36
+
37
+	for _, out := range outs.Data {
38
+		outID := out.Get("Id")
39
+		if !*quiet {
40
+			if *noTrunc {
41
+				fmt.Fprintf(w, "%s\t", outID)
42
+			} else {
43
+				fmt.Fprintf(w, "%s\t", stringid.TruncateID(outID))
44
+			}
45
+
46
+			fmt.Fprintf(w, "%s ago\t", units.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0))))
47
+
48
+			if *noTrunc {
49
+				fmt.Fprintf(w, "%s\t", out.Get("CreatedBy"))
50
+			} else {
51
+				fmt.Fprintf(w, "%s\t", utils.Trunc(out.Get("CreatedBy"), 45))
52
+			}
53
+			fmt.Fprintf(w, "%s\n", units.HumanSize(float64(out.GetInt64("Size"))))
54
+		} else {
55
+			if *noTrunc {
56
+				fmt.Fprintln(w, outID)
57
+			} else {
58
+				fmt.Fprintln(w, stringid.TruncateID(outID))
59
+			}
60
+		}
61
+	}
62
+	w.Flush()
63
+	return nil
64
+}
0 65
new file mode 100644
... ...
@@ -0,0 +1,271 @@
0
+package client
1
+
2
+import (
3
+	"fmt"
4
+	"net/url"
5
+	"strings"
6
+	"text/tabwriter"
7
+	"time"
8
+
9
+	"github.com/docker/docker/engine"
10
+	"github.com/docker/docker/opts"
11
+	flag "github.com/docker/docker/pkg/mflag"
12
+	"github.com/docker/docker/pkg/parsers"
13
+	"github.com/docker/docker/pkg/parsers/filters"
14
+	"github.com/docker/docker/pkg/stringid"
15
+	"github.com/docker/docker/pkg/units"
16
+	"github.com/docker/docker/utils"
17
+)
18
+
19
+// FIXME: --viz and --tree are deprecated. Remove them in a future version.
20
+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)) {
21
+	length := images.Len()
22
+	if length > 1 {
23
+		for index, image := range images.Data {
24
+			if index+1 == length {
25
+				printNode(cli, noTrunc, image, prefix+"└─")
26
+				if subimages, exists := byParent[image.Get("Id")]; exists {
27
+					cli.WalkTree(noTrunc, subimages, byParent, prefix+"  ", printNode)
28
+				}
29
+			} else {
30
+				printNode(cli, noTrunc, image, prefix+"\u251C─")
31
+				if subimages, exists := byParent[image.Get("Id")]; exists {
32
+					cli.WalkTree(noTrunc, subimages, byParent, prefix+"\u2502 ", printNode)
33
+				}
34
+			}
35
+		}
36
+	} else {
37
+		for _, image := range images.Data {
38
+			printNode(cli, noTrunc, image, prefix+"└─")
39
+			if subimages, exists := byParent[image.Get("Id")]; exists {
40
+				cli.WalkTree(noTrunc, subimages, byParent, prefix+"  ", printNode)
41
+			}
42
+		}
43
+	}
44
+}
45
+
46
+// FIXME: --viz and --tree are deprecated. Remove them in a future version.
47
+func (cli *DockerCli) printVizNode(noTrunc bool, image *engine.Env, prefix string) {
48
+	var (
49
+		imageID  string
50
+		parentID string
51
+	)
52
+	if noTrunc {
53
+		imageID = image.Get("Id")
54
+		parentID = image.Get("ParentId")
55
+	} else {
56
+		imageID = stringid.TruncateID(image.Get("Id"))
57
+		parentID = stringid.TruncateID(image.Get("ParentId"))
58
+	}
59
+	if parentID == "" {
60
+		fmt.Fprintf(cli.out, " base -> \"%s\" [style=invis]\n", imageID)
61
+	} else {
62
+		fmt.Fprintf(cli.out, " \"%s\" -> \"%s\"\n", parentID, imageID)
63
+	}
64
+	if image.GetList("RepoTags")[0] != "<none>:<none>" {
65
+		fmt.Fprintf(cli.out, " \"%s\" [label=\"%s\\n%s\",shape=box,fillcolor=\"paleturquoise\",style=\"filled,rounded\"];\n",
66
+			imageID, imageID, strings.Join(image.GetList("RepoTags"), "\\n"))
67
+	}
68
+}
69
+
70
+// FIXME: --viz and --tree are deprecated. Remove them in a future version.
71
+func (cli *DockerCli) printTreeNode(noTrunc bool, image *engine.Env, prefix string) {
72
+	var imageID string
73
+	if noTrunc {
74
+		imageID = image.Get("Id")
75
+	} else {
76
+		imageID = stringid.TruncateID(image.Get("Id"))
77
+	}
78
+
79
+	fmt.Fprintf(cli.out, "%s%s Virtual Size: %s", prefix, imageID, units.HumanSize(float64(image.GetInt64("VirtualSize"))))
80
+	if image.GetList("RepoTags")[0] != "<none>:<none>" {
81
+		fmt.Fprintf(cli.out, " Tags: %s\n", strings.Join(image.GetList("RepoTags"), ", "))
82
+	} else {
83
+		fmt.Fprint(cli.out, "\n")
84
+	}
85
+}
86
+
87
+func (cli *DockerCli) CmdImages(args ...string) error {
88
+	cmd := cli.Subcmd("images", "[REPOSITORY]", "List images", true)
89
+	quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs")
90
+	all := cmd.Bool([]string{"a", "-all"}, false, "Show all images (default hides intermediate images)")
91
+	noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
92
+	showDigests := cmd.Bool([]string{"-digests"}, false, "Show digests")
93
+	// FIXME: --viz and --tree are deprecated. Remove them in a future version.
94
+	flViz := cmd.Bool([]string{"#v", "#viz", "#-viz"}, false, "Output graph in graphviz format")
95
+	flTree := cmd.Bool([]string{"#t", "#tree", "#-tree"}, false, "Output graph in tree format")
96
+
97
+	flFilter := opts.NewListOpts(nil)
98
+	cmd.Var(&flFilter, []string{"f", "-filter"}, "Filter output based on conditions provided")
99
+	cmd.Require(flag.Max, 1)
100
+
101
+	utils.ParseFlags(cmd, args, true)
102
+
103
+	// Consolidate all filter flags, and sanity check them early.
104
+	// They'll get process in the daemon/server.
105
+	imageFilterArgs := filters.Args{}
106
+	for _, f := range flFilter.GetAll() {
107
+		var err error
108
+		imageFilterArgs, err = filters.ParseFlag(f, imageFilterArgs)
109
+		if err != nil {
110
+			return err
111
+		}
112
+	}
113
+
114
+	matchName := cmd.Arg(0)
115
+	// FIXME: --viz and --tree are deprecated. Remove them in a future version.
116
+	if *flViz || *flTree {
117
+		v := url.Values{
118
+			"all": []string{"1"},
119
+		}
120
+		if len(imageFilterArgs) > 0 {
121
+			filterJson, err := filters.ToParam(imageFilterArgs)
122
+			if err != nil {
123
+				return err
124
+			}
125
+			v.Set("filters", filterJson)
126
+		}
127
+
128
+		body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, false))
129
+		if err != nil {
130
+			return err
131
+		}
132
+
133
+		outs := engine.NewTable("Created", 0)
134
+		if _, err := outs.ReadListFrom(body); err != nil {
135
+			return err
136
+		}
137
+
138
+		var (
139
+			printNode  func(cli *DockerCli, noTrunc bool, image *engine.Env, prefix string)
140
+			startImage *engine.Env
141
+
142
+			roots    = engine.NewTable("Created", outs.Len())
143
+			byParent = make(map[string]*engine.Table)
144
+		)
145
+
146
+		for _, image := range outs.Data {
147
+			if image.Get("ParentId") == "" {
148
+				roots.Add(image)
149
+			} else {
150
+				if children, exists := byParent[image.Get("ParentId")]; exists {
151
+					children.Add(image)
152
+				} else {
153
+					byParent[image.Get("ParentId")] = engine.NewTable("Created", 1)
154
+					byParent[image.Get("ParentId")].Add(image)
155
+				}
156
+			}
157
+
158
+			if matchName != "" {
159
+				if matchName == image.Get("Id") || matchName == stringid.TruncateID(image.Get("Id")) {
160
+					startImage = image
161
+				}
162
+
163
+				for _, repotag := range image.GetList("RepoTags") {
164
+					if repotag == matchName {
165
+						startImage = image
166
+					}
167
+				}
168
+			}
169
+		}
170
+
171
+		if *flViz {
172
+			fmt.Fprintf(cli.out, "digraph docker {\n")
173
+			printNode = (*DockerCli).printVizNode
174
+		} else {
175
+			printNode = (*DockerCli).printTreeNode
176
+		}
177
+
178
+		if startImage != nil {
179
+			root := engine.NewTable("Created", 1)
180
+			root.Add(startImage)
181
+			cli.WalkTree(*noTrunc, root, byParent, "", printNode)
182
+		} else if matchName == "" {
183
+			cli.WalkTree(*noTrunc, roots, byParent, "", printNode)
184
+		}
185
+		if *flViz {
186
+			fmt.Fprintf(cli.out, " base [style=invisible]\n}\n")
187
+		}
188
+	} else {
189
+		v := url.Values{}
190
+		if len(imageFilterArgs) > 0 {
191
+			filterJson, err := filters.ToParam(imageFilterArgs)
192
+			if err != nil {
193
+				return err
194
+			}
195
+			v.Set("filters", filterJson)
196
+		}
197
+
198
+		if cmd.NArg() == 1 {
199
+			// FIXME rename this parameter, to not be confused with the filters flag
200
+			v.Set("filter", matchName)
201
+		}
202
+		if *all {
203
+			v.Set("all", "1")
204
+		}
205
+
206
+		body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, false))
207
+
208
+		if err != nil {
209
+			return err
210
+		}
211
+
212
+		outs := engine.NewTable("Created", 0)
213
+		if _, err := outs.ReadListFrom(body); err != nil {
214
+			return err
215
+		}
216
+
217
+		w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
218
+		if !*quiet {
219
+			if *showDigests {
220
+				fmt.Fprintln(w, "REPOSITORY\tTAG\tDIGEST\tIMAGE ID\tCREATED\tVIRTUAL SIZE")
221
+			} else {
222
+				fmt.Fprintln(w, "REPOSITORY\tTAG\tIMAGE ID\tCREATED\tVIRTUAL SIZE")
223
+			}
224
+		}
225
+
226
+		for _, out := range outs.Data {
227
+			outID := out.Get("Id")
228
+			if !*noTrunc {
229
+				outID = stringid.TruncateID(outID)
230
+			}
231
+
232
+			repoTags := out.GetList("RepoTags")
233
+			repoDigests := out.GetList("RepoDigests")
234
+
235
+			if len(repoTags) == 1 && repoTags[0] == "<none>:<none>" && len(repoDigests) == 1 && repoDigests[0] == "<none>@<none>" {
236
+				// dangling image - clear out either repoTags or repoDigsts so we only show it once below
237
+				repoDigests = []string{}
238
+			}
239
+
240
+			// combine the tags and digests lists
241
+			tagsAndDigests := append(repoTags, repoDigests...)
242
+			for _, repoAndRef := range tagsAndDigests {
243
+				repo, ref := parsers.ParseRepositoryTag(repoAndRef)
244
+				// default tag and digest to none - if there's a value, it'll be set below
245
+				tag := "<none>"
246
+				digest := "<none>"
247
+				if utils.DigestReference(ref) {
248
+					digest = ref
249
+				} else {
250
+					tag = ref
251
+				}
252
+
253
+				if !*quiet {
254
+					if *showDigests {
255
+						fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s ago\t%s\n", repo, tag, digest, outID, units.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0))), units.HumanSize(float64(out.GetInt64("VirtualSize"))))
256
+					} else {
257
+						fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\n", repo, tag, outID, units.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0))), units.HumanSize(float64(out.GetInt64("VirtualSize"))))
258
+					}
259
+				} else {
260
+					fmt.Fprintln(w, outID)
261
+				}
262
+			}
263
+		}
264
+
265
+		if !*quiet {
266
+			w.Flush()
267
+		}
268
+	}
269
+	return nil
270
+}
0 271
new file mode 100644
... ...
@@ -0,0 +1,54 @@
0
+package client
1
+
2
+import (
3
+	"fmt"
4
+	"io"
5
+	"net/url"
6
+
7
+	"github.com/docker/docker/opts"
8
+	flag "github.com/docker/docker/pkg/mflag"
9
+	"github.com/docker/docker/pkg/parsers"
10
+	"github.com/docker/docker/registry"
11
+	"github.com/docker/docker/utils"
12
+)
13
+
14
+func (cli *DockerCli) CmdImport(args ...string) error {
15
+	cmd := cli.Subcmd("import", "URL|- [REPOSITORY[:TAG]]", "Create an empty filesystem image and import the contents of the\ntarball (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz) into it, then\noptionally tag it.", true)
16
+	flChanges := opts.NewListOpts(nil)
17
+	cmd.Var(&flChanges, []string{"c", "-change"}, "Apply Dockerfile instruction to the created image")
18
+	cmd.Require(flag.Min, 1)
19
+
20
+	utils.ParseFlags(cmd, args, true)
21
+
22
+	var (
23
+		v          = url.Values{}
24
+		src        = cmd.Arg(0)
25
+		repository = cmd.Arg(1)
26
+	)
27
+
28
+	v.Set("fromSrc", src)
29
+	v.Set("repo", repository)
30
+	for _, change := range flChanges.GetAll() {
31
+		v.Add("changes", change)
32
+	}
33
+	if cmd.NArg() == 3 {
34
+		fmt.Fprintf(cli.err, "[DEPRECATED] The format 'URL|- [REPOSITORY [TAG]]' has been deprecated. Please use URL|- [REPOSITORY[:TAG]]\n")
35
+		v.Set("tag", cmd.Arg(2))
36
+	}
37
+
38
+	if repository != "" {
39
+		//Check if the given image name can be resolved
40
+		repo, _ := parsers.ParseRepositoryTag(repository)
41
+		if err := registry.ValidateRepositoryName(repo); err != nil {
42
+			return err
43
+		}
44
+	}
45
+
46
+	var in io.Reader
47
+
48
+	if src == "-" {
49
+		in = cli.in
50
+	}
51
+
52
+	return cli.stream("POST", "/images/create?"+v.Encode(), in, cli.out, nil)
53
+}
0 54
new file mode 100644
... ...
@@ -0,0 +1,144 @@
0
+package client
1
+
2
+import (
3
+	"fmt"
4
+	"os"
5
+	"time"
6
+
7
+	log "github.com/Sirupsen/logrus"
8
+	"github.com/docker/docker/engine"
9
+	flag "github.com/docker/docker/pkg/mflag"
10
+	"github.com/docker/docker/pkg/units"
11
+	"github.com/docker/docker/utils"
12
+)
13
+
14
+// 'docker info': display system-wide information.
15
+func (cli *DockerCli) CmdInfo(args ...string) error {
16
+	cmd := cli.Subcmd("info", "", "Display system-wide information", true)
17
+	cmd.Require(flag.Exact, 0)
18
+	utils.ParseFlags(cmd, args, false)
19
+
20
+	body, _, err := readBody(cli.call("GET", "/info", nil, false))
21
+	if err != nil {
22
+		return err
23
+	}
24
+
25
+	out := engine.NewOutput()
26
+	remoteInfo, err := out.AddEnv()
27
+	if err != nil {
28
+		return err
29
+	}
30
+
31
+	if _, err := out.Write(body); err != nil {
32
+		log.Errorf("Error reading remote info: %s", err)
33
+		return err
34
+	}
35
+	out.Close()
36
+
37
+	if remoteInfo.Exists("Containers") {
38
+		fmt.Fprintf(cli.out, "Containers: %d\n", remoteInfo.GetInt("Containers"))
39
+	}
40
+	if remoteInfo.Exists("Images") {
41
+		fmt.Fprintf(cli.out, "Images: %d\n", remoteInfo.GetInt("Images"))
42
+	}
43
+	if remoteInfo.Exists("Driver") {
44
+		fmt.Fprintf(cli.out, "Storage Driver: %s\n", remoteInfo.Get("Driver"))
45
+	}
46
+	if remoteInfo.Exists("DriverStatus") {
47
+		var driverStatus [][2]string
48
+		if err := remoteInfo.GetJson("DriverStatus", &driverStatus); err != nil {
49
+			return err
50
+		}
51
+		for _, pair := range driverStatus {
52
+			fmt.Fprintf(cli.out, " %s: %s\n", pair[0], pair[1])
53
+		}
54
+	}
55
+	if remoteInfo.Exists("ExecutionDriver") {
56
+		fmt.Fprintf(cli.out, "Execution Driver: %s\n", remoteInfo.Get("ExecutionDriver"))
57
+	}
58
+	if remoteInfo.Exists("KernelVersion") {
59
+		fmt.Fprintf(cli.out, "Kernel Version: %s\n", remoteInfo.Get("KernelVersion"))
60
+	}
61
+	if remoteInfo.Exists("OperatingSystem") {
62
+		fmt.Fprintf(cli.out, "Operating System: %s\n", remoteInfo.Get("OperatingSystem"))
63
+	}
64
+	if remoteInfo.Exists("NCPU") {
65
+		fmt.Fprintf(cli.out, "CPUs: %d\n", remoteInfo.GetInt("NCPU"))
66
+	}
67
+	if remoteInfo.Exists("MemTotal") {
68
+		fmt.Fprintf(cli.out, "Total Memory: %s\n", units.BytesSize(float64(remoteInfo.GetInt64("MemTotal"))))
69
+	}
70
+	if remoteInfo.Exists("Name") {
71
+		fmt.Fprintf(cli.out, "Name: %s\n", remoteInfo.Get("Name"))
72
+	}
73
+	if remoteInfo.Exists("ID") {
74
+		fmt.Fprintf(cli.out, "ID: %s\n", remoteInfo.Get("ID"))
75
+	}
76
+
77
+	if remoteInfo.GetBool("Debug") || os.Getenv("DEBUG") != "" {
78
+		if remoteInfo.Exists("Debug") {
79
+			fmt.Fprintf(cli.out, "Debug mode (server): %v\n", remoteInfo.GetBool("Debug"))
80
+		}
81
+		fmt.Fprintf(cli.out, "Debug mode (client): %v\n", os.Getenv("DEBUG") != "")
82
+		if remoteInfo.Exists("NFd") {
83
+			fmt.Fprintf(cli.out, "Fds: %d\n", remoteInfo.GetInt("NFd"))
84
+		}
85
+		if remoteInfo.Exists("NGoroutines") {
86
+			fmt.Fprintf(cli.out, "Goroutines: %d\n", remoteInfo.GetInt("NGoroutines"))
87
+		}
88
+		if remoteInfo.Exists("SystemTime") {
89
+			t, err := remoteInfo.GetTime("SystemTime")
90
+			if err != nil {
91
+				log.Errorf("Error reading system time: %v", err)
92
+			} else {
93
+				fmt.Fprintf(cli.out, "System Time: %s\n", t.Format(time.UnixDate))
94
+			}
95
+		}
96
+		if remoteInfo.Exists("NEventsListener") {
97
+			fmt.Fprintf(cli.out, "EventsListeners: %d\n", remoteInfo.GetInt("NEventsListener"))
98
+		}
99
+		if initSha1 := remoteInfo.Get("InitSha1"); initSha1 != "" {
100
+			fmt.Fprintf(cli.out, "Init SHA1: %s\n", initSha1)
101
+		}
102
+		if initPath := remoteInfo.Get("InitPath"); initPath != "" {
103
+			fmt.Fprintf(cli.out, "Init Path: %s\n", initPath)
104
+		}
105
+		if root := remoteInfo.Get("DockerRootDir"); root != "" {
106
+			fmt.Fprintf(cli.out, "Docker Root Dir: %s\n", root)
107
+		}
108
+	}
109
+	if remoteInfo.Exists("HttpProxy") {
110
+		fmt.Fprintf(cli.out, "Http Proxy: %s\n", remoteInfo.Get("HttpProxy"))
111
+	}
112
+	if remoteInfo.Exists("HttpsProxy") {
113
+		fmt.Fprintf(cli.out, "Https Proxy: %s\n", remoteInfo.Get("HttpsProxy"))
114
+	}
115
+	if remoteInfo.Exists("NoProxy") {
116
+		fmt.Fprintf(cli.out, "No Proxy: %s\n", remoteInfo.Get("NoProxy"))
117
+	}
118
+	if len(remoteInfo.GetList("IndexServerAddress")) != 0 {
119
+		cli.LoadConfigFile()
120
+		u := cli.configFile.Configs[remoteInfo.Get("IndexServerAddress")].Username
121
+		if len(u) > 0 {
122
+			fmt.Fprintf(cli.out, "Username: %v\n", u)
123
+			fmt.Fprintf(cli.out, "Registry: %v\n", remoteInfo.GetList("IndexServerAddress"))
124
+		}
125
+	}
126
+	if remoteInfo.Exists("MemoryLimit") && !remoteInfo.GetBool("MemoryLimit") {
127
+		fmt.Fprintf(cli.err, "WARNING: No memory limit support\n")
128
+	}
129
+	if remoteInfo.Exists("SwapLimit") && !remoteInfo.GetBool("SwapLimit") {
130
+		fmt.Fprintf(cli.err, "WARNING: No swap limit support\n")
131
+	}
132
+	if remoteInfo.Exists("IPv4Forwarding") && !remoteInfo.GetBool("IPv4Forwarding") {
133
+		fmt.Fprintf(cli.err, "WARNING: IPv4 forwarding is disabled.\n")
134
+	}
135
+	if remoteInfo.Exists("Labels") {
136
+		fmt.Fprintln(cli.out, "Labels:")
137
+		for _, attribute := range remoteInfo.GetList("Labels") {
138
+			fmt.Fprintf(cli.out, " %s\n", attribute)
139
+		}
140
+	}
141
+
142
+	return nil
143
+}
0 144
new file mode 100644
... ...
@@ -0,0 +1,95 @@
0
+package client
1
+
2
+import (
3
+	"bytes"
4
+	"encoding/json"
5
+	"fmt"
6
+	"io"
7
+	"strings"
8
+	"text/template"
9
+
10
+	flag "github.com/docker/docker/pkg/mflag"
11
+	"github.com/docker/docker/utils"
12
+)
13
+
14
+func (cli *DockerCli) CmdInspect(args ...string) error {
15
+	cmd := cli.Subcmd("inspect", "CONTAINER|IMAGE [CONTAINER|IMAGE...]", "Return low-level information on a container or image", true)
16
+	tmplStr := cmd.String([]string{"f", "#format", "-format"}, "", "Format the output using the given go template")
17
+	cmd.Require(flag.Min, 1)
18
+
19
+	utils.ParseFlags(cmd, args, true)
20
+
21
+	var tmpl *template.Template
22
+	if *tmplStr != "" {
23
+		var err error
24
+		if tmpl, err = template.New("").Funcs(funcMap).Parse(*tmplStr); err != nil {
25
+			fmt.Fprintf(cli.err, "Template parsing error: %v\n", err)
26
+			return &utils.StatusError{StatusCode: 64,
27
+				Status: "Template parsing error: " + err.Error()}
28
+		}
29
+	}
30
+
31
+	indented := new(bytes.Buffer)
32
+	indented.WriteByte('[')
33
+	status := 0
34
+
35
+	for _, name := range cmd.Args() {
36
+		obj, _, err := readBody(cli.call("GET", "/containers/"+name+"/json", nil, false))
37
+		if err != nil {
38
+			if strings.Contains(err.Error(), "Too many") {
39
+				fmt.Fprintf(cli.err, "Error: %v", err)
40
+				status = 1
41
+				continue
42
+			}
43
+
44
+			obj, _, err = readBody(cli.call("GET", "/images/"+name+"/json", nil, false))
45
+			if err != nil {
46
+				if strings.Contains(err.Error(), "No such") {
47
+					fmt.Fprintf(cli.err, "Error: No such image or container: %s\n", name)
48
+				} else {
49
+					fmt.Fprintf(cli.err, "%s", err)
50
+				}
51
+				status = 1
52
+				continue
53
+			}
54
+		}
55
+
56
+		if tmpl == nil {
57
+			if err = json.Indent(indented, obj, "", "    "); err != nil {
58
+				fmt.Fprintf(cli.err, "%s\n", err)
59
+				status = 1
60
+				continue
61
+			}
62
+		} else {
63
+			// Has template, will render
64
+			var value interface{}
65
+			if err := json.Unmarshal(obj, &value); err != nil {
66
+				fmt.Fprintf(cli.err, "%s\n", err)
67
+				status = 1
68
+				continue
69
+			}
70
+			if err := tmpl.Execute(cli.out, value); err != nil {
71
+				return err
72
+			}
73
+			cli.out.Write([]byte{'\n'})
74
+		}
75
+		indented.WriteString(",")
76
+	}
77
+
78
+	if indented.Len() > 1 {
79
+		// Remove trailing ','
80
+		indented.Truncate(indented.Len() - 1)
81
+	}
82
+	indented.WriteString("]\n")
83
+
84
+	if tmpl == nil {
85
+		if _, err := io.Copy(cli.out, indented); err != nil {
86
+			return err
87
+		}
88
+	}
89
+
90
+	if status != 0 {
91
+		return &utils.StatusError{StatusCode: status}
92
+	}
93
+	return nil
94
+}
0 95
new file mode 100644
... ...
@@ -0,0 +1,28 @@
0
+package client
1
+
2
+import (
3
+	"fmt"
4
+
5
+	flag "github.com/docker/docker/pkg/mflag"
6
+	"github.com/docker/docker/utils"
7
+)
8
+
9
+// 'docker kill NAME' kills a running container
10
+func (cli *DockerCli) CmdKill(args ...string) error {
11
+	cmd := cli.Subcmd("kill", "CONTAINER [CONTAINER...]", "Kill a running container using SIGKILL or a specified signal", true)
12
+	signal := cmd.String([]string{"s", "-signal"}, "KILL", "Signal to send to the container")
13
+	cmd.Require(flag.Min, 1)
14
+
15
+	utils.ParseFlags(cmd, args, true)
16
+
17
+	var encounteredError error
18
+	for _, name := range cmd.Args() {
19
+		if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", name, *signal), nil, false)); err != nil {
20
+			fmt.Fprintf(cli.err, "%s\n", err)
21
+			encounteredError = fmt.Errorf("Error: failed to kill one or more containers")
22
+		} else {
23
+			fmt.Fprintf(cli.out, "%s\n", name)
24
+		}
25
+	}
26
+	return encounteredError
27
+}
0 28
new file mode 100644
... ...
@@ -0,0 +1,32 @@
0
+package client
1
+
2
+import (
3
+	"io"
4
+	"os"
5
+
6
+	flag "github.com/docker/docker/pkg/mflag"
7
+	"github.com/docker/docker/utils"
8
+)
9
+
10
+func (cli *DockerCli) CmdLoad(args ...string) error {
11
+	cmd := cli.Subcmd("load", "", "Load an image from a tar archive on STDIN", true)
12
+	infile := cmd.String([]string{"i", "-input"}, "", "Read from a tar archive file, instead of STDIN")
13
+	cmd.Require(flag.Exact, 0)
14
+
15
+	utils.ParseFlags(cmd, args, true)
16
+
17
+	var (
18
+		input io.Reader = cli.in
19
+		err   error
20
+	)
21
+	if *infile != "" {
22
+		input, err = os.Open(*infile)
23
+		if err != nil {
24
+			return err
25
+		}
26
+	}
27
+	if err := cli.stream("POST", "/images/load", input, cli.out, nil); err != nil {
28
+		return err
29
+	}
30
+	return nil
31
+}
0 32
new file mode 100644
... ...
@@ -0,0 +1,138 @@
0
+package client
1
+
2
+import (
3
+	"bufio"
4
+	"encoding/json"
5
+	"fmt"
6
+	"io"
7
+	"os"
8
+	"path"
9
+	"strings"
10
+
11
+	"github.com/docker/docker/api/types"
12
+	"github.com/docker/docker/pkg/homedir"
13
+	flag "github.com/docker/docker/pkg/mflag"
14
+	"github.com/docker/docker/pkg/term"
15
+	"github.com/docker/docker/registry"
16
+	"github.com/docker/docker/utils"
17
+)
18
+
19
+// 'docker login': login / register a user to registry service.
20
+func (cli *DockerCli) CmdLogin(args ...string) error {
21
+	cmd := cli.Subcmd("login", "[SERVER]", "Register or log in to a Docker registry server, if no server is\nspecified \""+registry.IndexServerAddress()+"\" is the default.", true)
22
+	cmd.Require(flag.Max, 1)
23
+
24
+	var username, password, email string
25
+
26
+	cmd.StringVar(&username, []string{"u", "-username"}, "", "Username")
27
+	cmd.StringVar(&password, []string{"p", "-password"}, "", "Password")
28
+	cmd.StringVar(&email, []string{"e", "-email"}, "", "Email")
29
+
30
+	utils.ParseFlags(cmd, args, true)
31
+
32
+	serverAddress := registry.IndexServerAddress()
33
+	if len(cmd.Args()) > 0 {
34
+		serverAddress = cmd.Arg(0)
35
+	}
36
+
37
+	promptDefault := func(prompt string, configDefault string) {
38
+		if configDefault == "" {
39
+			fmt.Fprintf(cli.out, "%s: ", prompt)
40
+		} else {
41
+			fmt.Fprintf(cli.out, "%s (%s): ", prompt, configDefault)
42
+		}
43
+	}
44
+
45
+	readInput := func(in io.Reader, out io.Writer) string {
46
+		reader := bufio.NewReader(in)
47
+		line, _, err := reader.ReadLine()
48
+		if err != nil {
49
+			fmt.Fprintln(out, err.Error())
50
+			os.Exit(1)
51
+		}
52
+		return string(line)
53
+	}
54
+
55
+	cli.LoadConfigFile()
56
+	authconfig, ok := cli.configFile.Configs[serverAddress]
57
+	if !ok {
58
+		authconfig = registry.AuthConfig{}
59
+	}
60
+
61
+	if username == "" {
62
+		promptDefault("Username", authconfig.Username)
63
+		username = readInput(cli.in, cli.out)
64
+		username = strings.Trim(username, " ")
65
+		if username == "" {
66
+			username = authconfig.Username
67
+		}
68
+	}
69
+	// Assume that a different username means they may not want to use
70
+	// the password or email from the config file, so prompt them
71
+	if username != authconfig.Username {
72
+		if password == "" {
73
+			oldState, err := term.SaveState(cli.inFd)
74
+			if err != nil {
75
+				return err
76
+			}
77
+			fmt.Fprintf(cli.out, "Password: ")
78
+			term.DisableEcho(cli.inFd, oldState)
79
+
80
+			password = readInput(cli.in, cli.out)
81
+			fmt.Fprint(cli.out, "\n")
82
+
83
+			term.RestoreTerminal(cli.inFd, oldState)
84
+			if password == "" {
85
+				return fmt.Errorf("Error : Password Required")
86
+			}
87
+		}
88
+
89
+		if email == "" {
90
+			promptDefault("Email", authconfig.Email)
91
+			email = readInput(cli.in, cli.out)
92
+			if email == "" {
93
+				email = authconfig.Email
94
+			}
95
+		}
96
+	} else {
97
+		// However, if they don't override the username use the
98
+		// password or email from the cmd line if specified. IOW, allow
99
+		// then to change/override them.  And if not specified, just
100
+		// use what's in the config file
101
+		if password == "" {
102
+			password = authconfig.Password
103
+		}
104
+		if email == "" {
105
+			email = authconfig.Email
106
+		}
107
+	}
108
+	authconfig.Username = username
109
+	authconfig.Password = password
110
+	authconfig.Email = email
111
+	authconfig.ServerAddress = serverAddress
112
+	cli.configFile.Configs[serverAddress] = authconfig
113
+
114
+	stream, statusCode, err := cli.call("POST", "/auth", cli.configFile.Configs[serverAddress], false)
115
+	if statusCode == 401 {
116
+		delete(cli.configFile.Configs, serverAddress)
117
+		registry.SaveConfig(cli.configFile)
118
+		return err
119
+	}
120
+	if err != nil {
121
+		return err
122
+	}
123
+
124
+	var response types.AuthResponse
125
+	if err := json.NewDecoder(stream).Decode(response); err != nil {
126
+		cli.configFile, _ = registry.LoadConfig(homedir.Get())
127
+		return err
128
+	}
129
+
130
+	registry.SaveConfig(cli.configFile)
131
+	fmt.Fprintf(cli.out, "WARNING: login credentials saved in %s.\n", path.Join(homedir.Get(), registry.CONFIGFILE))
132
+
133
+	if response.Status != "" {
134
+		fmt.Fprintf(cli.out, "%s\n", response.Status)
135
+	}
136
+	return nil
137
+}
0 138
new file mode 100644
... ...
@@ -0,0 +1,34 @@
0
+package client
1
+
2
+import (
3
+	"fmt"
4
+
5
+	flag "github.com/docker/docker/pkg/mflag"
6
+	"github.com/docker/docker/registry"
7
+	"github.com/docker/docker/utils"
8
+)
9
+
10
+// 'docker logout': log out a user from a registry service.
11
+func (cli *DockerCli) CmdLogout(args ...string) error {
12
+	cmd := cli.Subcmd("logout", "[SERVER]", "Log out from a Docker registry, if no server is\nspecified \""+registry.IndexServerAddress()+"\" is the default.", true)
13
+	cmd.Require(flag.Max, 1)
14
+
15
+	utils.ParseFlags(cmd, args, false)
16
+	serverAddress := registry.IndexServerAddress()
17
+	if len(cmd.Args()) > 0 {
18
+		serverAddress = cmd.Arg(0)
19
+	}
20
+
21
+	cli.LoadConfigFile()
22
+	if _, ok := cli.configFile.Configs[serverAddress]; !ok {
23
+		fmt.Fprintf(cli.out, "Not logged in to %s\n", serverAddress)
24
+	} else {
25
+		fmt.Fprintf(cli.out, "Remove login credentials for %s\n", serverAddress)
26
+		delete(cli.configFile.Configs, serverAddress)
27
+
28
+		if err := registry.SaveConfig(cli.configFile); err != nil {
29
+			return fmt.Errorf("Failed to save docker config: %v", err)
30
+		}
31
+	}
32
+	return nil
33
+}
0 34
new file mode 100644
... ...
@@ -0,0 +1,53 @@
0
+package client
1
+
2
+import (
3
+	"fmt"
4
+	"net/url"
5
+
6
+	"github.com/docker/docker/engine"
7
+	flag "github.com/docker/docker/pkg/mflag"
8
+	"github.com/docker/docker/utils"
9
+)
10
+
11
+func (cli *DockerCli) CmdLogs(args ...string) error {
12
+	var (
13
+		cmd    = cli.Subcmd("logs", "CONTAINER", "Fetch the logs of a container", true)
14
+		follow = cmd.Bool([]string{"f", "-follow"}, false, "Follow log output")
15
+		times  = cmd.Bool([]string{"t", "-timestamps"}, false, "Show timestamps")
16
+		tail   = cmd.String([]string{"-tail"}, "all", "Number of lines to show from the end of the logs")
17
+	)
18
+	cmd.Require(flag.Exact, 1)
19
+
20
+	utils.ParseFlags(cmd, args, true)
21
+
22
+	name := cmd.Arg(0)
23
+
24
+	stream, _, err := cli.call("GET", "/containers/"+name+"/json", nil, false)
25
+	if err != nil {
26
+		return err
27
+	}
28
+
29
+	env := engine.Env{}
30
+	if err := env.Decode(stream); err != nil {
31
+		return err
32
+	}
33
+
34
+	if env.GetSubEnv("HostConfig").GetSubEnv("LogConfig").Get("Type") != "json-file" {
35
+		return fmt.Errorf("\"logs\" command is supported only for \"json-file\" logging driver")
36
+	}
37
+
38
+	v := url.Values{}
39
+	v.Set("stdout", "1")
40
+	v.Set("stderr", "1")
41
+
42
+	if *times {
43
+		v.Set("timestamps", "1")
44
+	}
45
+
46
+	if *follow {
47
+		v.Set("follow", "1")
48
+	}
49
+	v.Set("tail", *tail)
50
+
51
+	return cli.streamHelper("GET", "/containers/"+name+"/logs?"+v.Encode(), env.GetSubEnv("Config").GetBool("Tty"), nil, cli.out, cli.err, nil)
52
+}
0 53
new file mode 100644
... ...
@@ -0,0 +1,25 @@
0
+package client
1
+
2
+import (
3
+	"fmt"
4
+
5
+	flag "github.com/docker/docker/pkg/mflag"
6
+	"github.com/docker/docker/utils"
7
+)
8
+
9
+func (cli *DockerCli) CmdPause(args ...string) error {
10
+	cmd := cli.Subcmd("pause", "CONTAINER [CONTAINER...]", "Pause all processes within a container", true)
11
+	cmd.Require(flag.Min, 1)
12
+	utils.ParseFlags(cmd, args, false)
13
+
14
+	var encounteredError error
15
+	for _, name := range cmd.Args() {
16
+		if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/pause", name), nil, false)); err != nil {
17
+			fmt.Fprintf(cli.err, "%s\n", err)
18
+			encounteredError = fmt.Errorf("Error: failed to pause container named %s", name)
19
+		} else {
20
+			fmt.Fprintf(cli.out, "%s\n", name)
21
+		}
22
+	}
23
+	return encounteredError
24
+}
0 25
new file mode 100644
... ...
@@ -0,0 +1,60 @@
0
+package client
1
+
2
+import (
3
+	"fmt"
4
+	"strings"
5
+
6
+	"github.com/docker/docker/engine"
7
+	"github.com/docker/docker/nat"
8
+	flag "github.com/docker/docker/pkg/mflag"
9
+	"github.com/docker/docker/utils"
10
+)
11
+
12
+func (cli *DockerCli) CmdPort(args ...string) error {
13
+	cmd := cli.Subcmd("port", "CONTAINER [PRIVATE_PORT[/PROTO]]", "List port mappings for the CONTAINER, or lookup the public-facing port that\nis NAT-ed to the PRIVATE_PORT", true)
14
+	cmd.Require(flag.Min, 1)
15
+	utils.ParseFlags(cmd, args, true)
16
+
17
+	stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, false)
18
+	if err != nil {
19
+		return err
20
+	}
21
+
22
+	env := engine.Env{}
23
+	if err := env.Decode(stream); err != nil {
24
+		return err
25
+	}
26
+	ports := nat.PortMap{}
27
+	if err := env.GetSubEnv("NetworkSettings").GetJson("Ports", &ports); err != nil {
28
+		return err
29
+	}
30
+
31
+	if cmd.NArg() == 2 {
32
+		var (
33
+			port  = cmd.Arg(1)
34
+			proto = "tcp"
35
+			parts = strings.SplitN(port, "/", 2)
36
+		)
37
+
38
+		if len(parts) == 2 && len(parts[1]) != 0 {
39
+			port = parts[0]
40
+			proto = parts[1]
41
+		}
42
+		natPort := port + "/" + proto
43
+		if frontends, exists := ports[nat.Port(port+"/"+proto)]; exists && frontends != nil {
44
+			for _, frontend := range frontends {
45
+				fmt.Fprintf(cli.out, "%s:%s\n", frontend.HostIp, frontend.HostPort)
46
+			}
47
+			return nil
48
+		}
49
+		return fmt.Errorf("Error: No public port '%s' published for %s", natPort, cmd.Arg(0))
50
+	}
51
+
52
+	for from, frontends := range ports {
53
+		for _, frontend := range frontends {
54
+			fmt.Fprintf(cli.out, "%s -> %s:%s\n", from, frontend.HostIp, frontend.HostPort)
55
+		}
56
+	}
57
+
58
+	return nil
59
+}
0 60
new file mode 100644
... ...
@@ -0,0 +1,175 @@
0
+package client
1
+
2
+import (
3
+	"fmt"
4
+	"net/url"
5
+	"strconv"
6
+	"strings"
7
+	"text/tabwriter"
8
+	"time"
9
+
10
+	"github.com/docker/docker/api"
11
+	"github.com/docker/docker/engine"
12
+	"github.com/docker/docker/opts"
13
+	flag "github.com/docker/docker/pkg/mflag"
14
+	"github.com/docker/docker/pkg/parsers/filters"
15
+	"github.com/docker/docker/pkg/stringid"
16
+	"github.com/docker/docker/pkg/units"
17
+	"github.com/docker/docker/utils"
18
+)
19
+
20
+func (cli *DockerCli) CmdPs(args ...string) error {
21
+	var (
22
+		err error
23
+
24
+		psFilterArgs = filters.Args{}
25
+		v            = url.Values{}
26
+
27
+		cmd      = cli.Subcmd("ps", "", "List containers", true)
28
+		quiet    = cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs")
29
+		size     = cmd.Bool([]string{"s", "-size"}, false, "Display total file sizes")
30
+		all      = cmd.Bool([]string{"a", "-all"}, false, "Show all containers (default shows just running)")
31
+		noTrunc  = cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
32
+		nLatest  = cmd.Bool([]string{"l", "-latest"}, false, "Show the latest created container, include non-running")
33
+		since    = cmd.String([]string{"#sinceId", "#-since-id", "-since"}, "", "Show created since Id or Name, include non-running")
34
+		before   = cmd.String([]string{"#beforeId", "#-before-id", "-before"}, "", "Show only container created before Id or Name")
35
+		last     = cmd.Int([]string{"n"}, -1, "Show n last created containers, include non-running")
36
+		flFilter = opts.NewListOpts(nil)
37
+	)
38
+	cmd.Require(flag.Exact, 0)
39
+
40
+	cmd.Var(&flFilter, []string{"f", "-filter"}, "Filter output based on conditions provided")
41
+
42
+	utils.ParseFlags(cmd, args, true)
43
+	if *last == -1 && *nLatest {
44
+		*last = 1
45
+	}
46
+
47
+	if *all {
48
+		v.Set("all", "1")
49
+	}
50
+
51
+	if *last != -1 {
52
+		v.Set("limit", strconv.Itoa(*last))
53
+	}
54
+
55
+	if *since != "" {
56
+		v.Set("since", *since)
57
+	}
58
+
59
+	if *before != "" {
60
+		v.Set("before", *before)
61
+	}
62
+
63
+	if *size {
64
+		v.Set("size", "1")
65
+	}
66
+
67
+	// Consolidate all filter flags, and sanity check them.
68
+	// They'll get processed in the daemon/server.
69
+	for _, f := range flFilter.GetAll() {
70
+		if psFilterArgs, err = filters.ParseFlag(f, psFilterArgs); err != nil {
71
+			return err
72
+		}
73
+	}
74
+
75
+	if len(psFilterArgs) > 0 {
76
+		filterJson, err := filters.ToParam(psFilterArgs)
77
+		if err != nil {
78
+			return err
79
+		}
80
+
81
+		v.Set("filters", filterJson)
82
+	}
83
+
84
+	body, _, err := readBody(cli.call("GET", "/containers/json?"+v.Encode(), nil, false))
85
+	if err != nil {
86
+		return err
87
+	}
88
+
89
+	outs := engine.NewTable("Created", 0)
90
+	if _, err := outs.ReadListFrom(body); err != nil {
91
+		return err
92
+	}
93
+
94
+	w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
95
+	if !*quiet {
96
+		fmt.Fprint(w, "CONTAINER ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES")
97
+
98
+		if *size {
99
+			fmt.Fprintln(w, "\tSIZE")
100
+		} else {
101
+			fmt.Fprint(w, "\n")
102
+		}
103
+	}
104
+
105
+	stripNamePrefix := func(ss []string) []string {
106
+		for i, s := range ss {
107
+			ss[i] = s[1:]
108
+		}
109
+
110
+		return ss
111
+	}
112
+
113
+	for _, out := range outs.Data {
114
+		outID := out.Get("Id")
115
+
116
+		if !*noTrunc {
117
+			outID = stringid.TruncateID(outID)
118
+		}
119
+
120
+		if *quiet {
121
+			fmt.Fprintln(w, outID)
122
+
123
+			continue
124
+		}
125
+
126
+		var (
127
+			outNames   = stripNamePrefix(out.GetList("Names"))
128
+			outCommand = strconv.Quote(out.Get("Command"))
129
+			ports      = engine.NewTable("", 0)
130
+		)
131
+
132
+		if !*noTrunc {
133
+			outCommand = utils.Trunc(outCommand, 20)
134
+
135
+			// only display the default name for the container with notrunc is passed
136
+			for _, name := range outNames {
137
+				if len(strings.Split(name, "/")) == 1 {
138
+					outNames = []string{name}
139
+
140
+					break
141
+				}
142
+			}
143
+		}
144
+
145
+		ports.ReadListFrom([]byte(out.Get("Ports")))
146
+
147
+		image := out.Get("Image")
148
+		if image == "" {
149
+			image = "<no image>"
150
+		}
151
+
152
+		fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t%s\t", outID, image, outCommand,
153
+			units.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0))),
154
+			out.Get("Status"), api.DisplayablePorts(ports), strings.Join(outNames, ","))
155
+
156
+		if *size {
157
+			if out.GetInt("SizeRootFs") > 0 {
158
+				fmt.Fprintf(w, "%s (virtual %s)\n", units.HumanSize(float64(out.GetInt64("SizeRw"))), units.HumanSize(float64(out.GetInt64("SizeRootFs"))))
159
+			} else {
160
+				fmt.Fprintf(w, "%s\n", units.HumanSize(float64(out.GetInt64("SizeRw"))))
161
+			}
162
+
163
+			continue
164
+		}
165
+
166
+		fmt.Fprint(w, "\n")
167
+	}
168
+
169
+	if !*quiet {
170
+		w.Flush()
171
+	}
172
+
173
+	return nil
174
+}
0 175
new file mode 100644
... ...
@@ -0,0 +1,77 @@
0
+package client
1
+
2
+import (
3
+	"encoding/base64"
4
+	"encoding/json"
5
+	"fmt"
6
+	"net/url"
7
+	"strings"
8
+
9
+	"github.com/docker/docker/graph"
10
+	flag "github.com/docker/docker/pkg/mflag"
11
+	"github.com/docker/docker/pkg/parsers"
12
+	"github.com/docker/docker/registry"
13
+	"github.com/docker/docker/utils"
14
+)
15
+
16
+func (cli *DockerCli) CmdPull(args ...string) error {
17
+	cmd := cli.Subcmd("pull", "NAME[:TAG|@DIGEST]", "Pull an image or a repository from the registry", true)
18
+	allTags := cmd.Bool([]string{"a", "-all-tags"}, false, "Download all tagged images in the repository")
19
+	cmd.Require(flag.Exact, 1)
20
+
21
+	utils.ParseFlags(cmd, args, true)
22
+
23
+	var (
24
+		v         = url.Values{}
25
+		remote    = cmd.Arg(0)
26
+		newRemote = remote
27
+	)
28
+	taglessRemote, tag := parsers.ParseRepositoryTag(remote)
29
+	if tag == "" && !*allTags {
30
+		newRemote = utils.ImageReference(taglessRemote, graph.DEFAULTTAG)
31
+	}
32
+	if tag != "" && *allTags {
33
+		return fmt.Errorf("tag can't be used with --all-tags/-a")
34
+	}
35
+
36
+	v.Set("fromImage", newRemote)
37
+
38
+	// Resolve the Repository name from fqn to RepositoryInfo
39
+	repoInfo, err := registry.ParseRepositoryInfo(taglessRemote)
40
+	if err != nil {
41
+		return err
42
+	}
43
+
44
+	cli.LoadConfigFile()
45
+
46
+	// Resolve the Auth config relevant for this server
47
+	authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index)
48
+
49
+	pull := func(authConfig registry.AuthConfig) error {
50
+		buf, err := json.Marshal(authConfig)
51
+		if err != nil {
52
+			return err
53
+		}
54
+		registryAuthHeader := []string{
55
+			base64.URLEncoding.EncodeToString(buf),
56
+		}
57
+
58
+		return cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.out, map[string][]string{
59
+			"X-Registry-Auth": registryAuthHeader,
60
+		})
61
+	}
62
+
63
+	if err := pull(authConfig); err != nil {
64
+		if strings.Contains(err.Error(), "Status 401") {
65
+			fmt.Fprintln(cli.out, "\nPlease login prior to pull:")
66
+			if err := cli.CmdLogin(repoInfo.Index.GetAuthConfigKey()); err != nil {
67
+				return err
68
+			}
69
+			authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index)
70
+			return pull(authConfig)
71
+		}
72
+		return err
73
+	}
74
+
75
+	return nil
76
+}
0 77
new file mode 100644
... ...
@@ -0,0 +1,76 @@
0
+package client
1
+
2
+import (
3
+	"encoding/base64"
4
+	"encoding/json"
5
+	"fmt"
6
+	"net/url"
7
+	"strings"
8
+
9
+	flag "github.com/docker/docker/pkg/mflag"
10
+	"github.com/docker/docker/pkg/parsers"
11
+	"github.com/docker/docker/registry"
12
+	"github.com/docker/docker/utils"
13
+)
14
+
15
+func (cli *DockerCli) CmdPush(args ...string) error {
16
+	cmd := cli.Subcmd("push", "NAME[:TAG]", "Push an image or a repository to the registry", true)
17
+	cmd.Require(flag.Exact, 1)
18
+
19
+	utils.ParseFlags(cmd, args, true)
20
+
21
+	name := cmd.Arg(0)
22
+
23
+	cli.LoadConfigFile()
24
+
25
+	remote, tag := parsers.ParseRepositoryTag(name)
26
+
27
+	// Resolve the Repository name from fqn to RepositoryInfo
28
+	repoInfo, err := registry.ParseRepositoryInfo(remote)
29
+	if err != nil {
30
+		return err
31
+	}
32
+	// Resolve the Auth config relevant for this server
33
+	authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index)
34
+	// If we're not using a custom registry, we know the restrictions
35
+	// applied to repository names and can warn the user in advance.
36
+	// Custom repositories can have different rules, and we must also
37
+	// allow pushing by image ID.
38
+	if repoInfo.Official {
39
+		username := authConfig.Username
40
+		if username == "" {
41
+			username = "<user>"
42
+		}
43
+		return fmt.Errorf("You cannot push a \"root\" repository. Please rename your repository to <user>/<repo> (ex: %s/%s)", username, repoInfo.LocalName)
44
+	}
45
+
46
+	v := url.Values{}
47
+	v.Set("tag", tag)
48
+
49
+	push := func(authConfig registry.AuthConfig) error {
50
+		buf, err := json.Marshal(authConfig)
51
+		if err != nil {
52
+			return err
53
+		}
54
+		registryAuthHeader := []string{
55
+			base64.URLEncoding.EncodeToString(buf),
56
+		}
57
+
58
+		return cli.stream("POST", "/images/"+remote+"/push?"+v.Encode(), nil, cli.out, map[string][]string{
59
+			"X-Registry-Auth": registryAuthHeader,
60
+		})
61
+	}
62
+
63
+	if err := push(authConfig); err != nil {
64
+		if strings.Contains(err.Error(), "Status 401") {
65
+			fmt.Fprintln(cli.out, "\nPlease login prior to push:")
66
+			if err := cli.CmdLogin(repoInfo.Index.GetAuthConfigKey()); err != nil {
67
+				return err
68
+			}
69
+			authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index)
70
+			return push(authConfig)
71
+		}
72
+		return err
73
+	}
74
+	return nil
75
+}
0 76
new file mode 100644
... ...
@@ -0,0 +1,23 @@
0
+package client
1
+
2
+import "fmt"
3
+
4
+func (cli *DockerCli) CmdRename(args ...string) error {
5
+	cmd := cli.Subcmd("rename", "OLD_NAME NEW_NAME", "Rename a container", true)
6
+	if err := cmd.Parse(args); err != nil {
7
+		return nil
8
+	}
9
+
10
+	if cmd.NArg() != 2 {
11
+		cmd.Usage()
12
+		return nil
13
+	}
14
+	old_name := cmd.Arg(0)
15
+	new_name := cmd.Arg(1)
16
+
17
+	if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/rename?name=%s", old_name, new_name), nil, false)); err != nil {
18
+		fmt.Fprintf(cli.err, "%s\n", err)
19
+		return fmt.Errorf("Error: failed to rename container named %s", old_name)
20
+	}
21
+	return nil
22
+}
0 23
new file mode 100644
... ...
@@ -0,0 +1,33 @@
0
+package client
1
+
2
+import (
3
+	"fmt"
4
+	"net/url"
5
+	"strconv"
6
+
7
+	flag "github.com/docker/docker/pkg/mflag"
8
+	"github.com/docker/docker/utils"
9
+)
10
+
11
+func (cli *DockerCli) CmdRestart(args ...string) error {
12
+	cmd := cli.Subcmd("restart", "CONTAINER [CONTAINER...]", "Restart a running container", true)
13
+	nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Seconds to wait for stop before killing the container")
14
+	cmd.Require(flag.Min, 1)
15
+
16
+	utils.ParseFlags(cmd, args, true)
17
+
18
+	v := url.Values{}
19
+	v.Set("t", strconv.Itoa(*nSeconds))
20
+
21
+	var encounteredError error
22
+	for _, name := range cmd.Args() {
23
+		_, _, err := readBody(cli.call("POST", "/containers/"+name+"/restart?"+v.Encode(), nil, false))
24
+		if err != nil {
25
+			fmt.Fprintf(cli.err, "%s\n", err)
26
+			encounteredError = fmt.Errorf("Error: failed to restart one or more containers")
27
+		} else {
28
+			fmt.Fprintf(cli.out, "%s\n", name)
29
+		}
30
+	}
31
+	return encounteredError
32
+}
0 33
new file mode 100644
... ...
@@ -0,0 +1,43 @@
0
+package client
1
+
2
+import (
3
+	"fmt"
4
+	"net/url"
5
+
6
+	flag "github.com/docker/docker/pkg/mflag"
7
+	"github.com/docker/docker/utils"
8
+)
9
+
10
+func (cli *DockerCli) CmdRm(args ...string) error {
11
+	cmd := cli.Subcmd("rm", "CONTAINER [CONTAINER...]", "Remove one or more containers", true)
12
+	v := cmd.Bool([]string{"v", "-volumes"}, false, "Remove the volumes associated with the container")
13
+	link := cmd.Bool([]string{"l", "#link", "-link"}, false, "Remove the specified link")
14
+	force := cmd.Bool([]string{"f", "-force"}, false, "Force the removal of a running container (uses SIGKILL)")
15
+	cmd.Require(flag.Min, 1)
16
+
17
+	utils.ParseFlags(cmd, args, true)
18
+
19
+	val := url.Values{}
20
+	if *v {
21
+		val.Set("v", "1")
22
+	}
23
+	if *link {
24
+		val.Set("link", "1")
25
+	}
26
+
27
+	if *force {
28
+		val.Set("force", "1")
29
+	}
30
+
31
+	var encounteredError error
32
+	for _, name := range cmd.Args() {
33
+		_, _, err := readBody(cli.call("DELETE", "/containers/"+name+"?"+val.Encode(), nil, false))
34
+		if err != nil {
35
+			fmt.Fprintf(cli.err, "%s\n", err)
36
+			encounteredError = fmt.Errorf("Error: failed to remove one or more containers")
37
+		} else {
38
+			fmt.Fprintf(cli.out, "%s\n", name)
39
+		}
40
+	}
41
+	return encounteredError
42
+}
0 43
new file mode 100644
... ...
@@ -0,0 +1,54 @@
0
+package client
1
+
2
+import (
3
+	"fmt"
4
+	"net/url"
5
+
6
+	"github.com/docker/docker/engine"
7
+	flag "github.com/docker/docker/pkg/mflag"
8
+	"github.com/docker/docker/utils"
9
+)
10
+
11
+// 'docker rmi IMAGE' removes all images with the name IMAGE
12
+func (cli *DockerCli) CmdRmi(args ...string) error {
13
+	var (
14
+		cmd     = cli.Subcmd("rmi", "IMAGE [IMAGE...]", "Remove one or more images", true)
15
+		force   = cmd.Bool([]string{"f", "-force"}, false, "Force removal of the image")
16
+		noprune = cmd.Bool([]string{"-no-prune"}, false, "Do not delete untagged parents")
17
+	)
18
+	cmd.Require(flag.Min, 1)
19
+
20
+	utils.ParseFlags(cmd, args, true)
21
+
22
+	v := url.Values{}
23
+	if *force {
24
+		v.Set("force", "1")
25
+	}
26
+	if *noprune {
27
+		v.Set("noprune", "1")
28
+	}
29
+
30
+	var encounteredError error
31
+	for _, name := range cmd.Args() {
32
+		body, _, err := readBody(cli.call("DELETE", "/images/"+name+"?"+v.Encode(), nil, false))
33
+		if err != nil {
34
+			fmt.Fprintf(cli.err, "%s\n", err)
35
+			encounteredError = fmt.Errorf("Error: failed to remove one or more images")
36
+		} else {
37
+			outs := engine.NewTable("Created", 0)
38
+			if _, err := outs.ReadListFrom(body); err != nil {
39
+				fmt.Fprintf(cli.err, "%s\n", err)
40
+				encounteredError = fmt.Errorf("Error: failed to remove one or more images")
41
+				continue
42
+			}
43
+			for _, out := range outs.Data {
44
+				if out.Get("Deleted") != "" {
45
+					fmt.Fprintf(cli.out, "Deleted: %s\n", out.Get("Deleted"))
46
+				} else {
47
+					fmt.Fprintf(cli.out, "Untagged: %s\n", out.Get("Untagged"))
48
+				}
49
+			}
50
+		}
51
+	}
52
+	return encounteredError
53
+}
0 54
new file mode 100644
... ...
@@ -0,0 +1,245 @@
0
+package client
1
+
2
+import (
3
+	"fmt"
4
+	"io"
5
+	"net/url"
6
+	"os"
7
+
8
+	log "github.com/Sirupsen/logrus"
9
+	"github.com/docker/docker/opts"
10
+	"github.com/docker/docker/pkg/promise"
11
+	"github.com/docker/docker/pkg/resolvconf"
12
+	"github.com/docker/docker/pkg/signal"
13
+	"github.com/docker/docker/runconfig"
14
+	"github.com/docker/docker/utils"
15
+)
16
+
17
+func (cid *cidFile) Close() error {
18
+	cid.file.Close()
19
+
20
+	if !cid.written {
21
+		if err := os.Remove(cid.path); err != nil {
22
+			return fmt.Errorf("failed to remove the CID file '%s': %s \n", cid.path, err)
23
+		}
24
+	}
25
+
26
+	return nil
27
+}
28
+
29
+func (cid *cidFile) Write(id string) error {
30
+	if _, err := cid.file.Write([]byte(id)); err != nil {
31
+		return fmt.Errorf("Failed to write the container ID to the file: %s", err)
32
+	}
33
+	cid.written = true
34
+	return nil
35
+}
36
+
37
+func (cli *DockerCli) CmdRun(args ...string) error {
38
+	// FIXME: just use runconfig.Parse already
39
+	cmd := cli.Subcmd("run", "IMAGE [COMMAND] [ARG...]", "Run a command in a new container", true)
40
+
41
+	// These are flags not stored in Config/HostConfig
42
+	var (
43
+		flAutoRemove = cmd.Bool([]string{"#rm", "-rm"}, false, "Automatically remove the container when it exits")
44
+		flDetach     = cmd.Bool([]string{"d", "-detach"}, false, "Run container in background and print container ID")
45
+		flSigProxy   = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxy received signals to the process")
46
+		flName       = cmd.String([]string{"#name", "-name"}, "", "Assign a name to the container")
47
+		flAttach     *opts.ListOpts
48
+
49
+		ErrConflictAttachDetach               = fmt.Errorf("Conflicting options: -a and -d")
50
+		ErrConflictRestartPolicyAndAutoRemove = fmt.Errorf("Conflicting options: --restart and --rm")
51
+		ErrConflictDetachAutoRemove           = fmt.Errorf("Conflicting options: --rm and -d")
52
+	)
53
+
54
+	config, hostConfig, cmd, err := runconfig.Parse(cmd, args)
55
+	// just in case the Parse does not exit
56
+	if err != nil {
57
+		utils.ReportError(cmd, err.Error(), true)
58
+	}
59
+
60
+	if len(hostConfig.Dns) > 0 {
61
+		// check the DNS settings passed via --dns against
62
+		// localhost regexp to warn if they are trying to
63
+		// set a DNS to a localhost address
64
+		for _, dnsIP := range hostConfig.Dns {
65
+			if resolvconf.IsLocalhost(dnsIP) {
66
+				fmt.Fprintf(cli.err, "WARNING: Localhost DNS setting (--dns=%s) may fail in containers.\n", dnsIP)
67
+				break
68
+			}
69
+		}
70
+	}
71
+	if config.Image == "" {
72
+		cmd.Usage()
73
+		return nil
74
+	}
75
+
76
+	if !*flDetach {
77
+		if err := cli.CheckTtyInput(config.AttachStdin, config.Tty); err != nil {
78
+			return err
79
+		}
80
+	} else {
81
+		if fl := cmd.Lookup("-attach"); fl != nil {
82
+			flAttach = fl.Value.(*opts.ListOpts)
83
+			if flAttach.Len() != 0 {
84
+				return ErrConflictAttachDetach
85
+			}
86
+		}
87
+		if *flAutoRemove {
88
+			return ErrConflictDetachAutoRemove
89
+		}
90
+
91
+		config.AttachStdin = false
92
+		config.AttachStdout = false
93
+		config.AttachStderr = false
94
+		config.StdinOnce = false
95
+	}
96
+
97
+	// Disable flSigProxy when in TTY mode
98
+	sigProxy := *flSigProxy
99
+	if config.Tty {
100
+		sigProxy = false
101
+	}
102
+
103
+	createResponse, err := cli.createContainer(config, hostConfig, hostConfig.ContainerIDFile, *flName)
104
+	if err != nil {
105
+		return err
106
+	}
107
+	if sigProxy {
108
+		sigc := cli.forwardAllSignals(createResponse.ID)
109
+		defer signal.StopCatch(sigc)
110
+	}
111
+	var (
112
+		waitDisplayId chan struct{}
113
+		errCh         chan error
114
+	)
115
+	if !config.AttachStdout && !config.AttachStderr {
116
+		// Make this asynchronous to allow the client to write to stdin before having to read the ID
117
+		waitDisplayId = make(chan struct{})
118
+		go func() {
119
+			defer close(waitDisplayId)
120
+			fmt.Fprintf(cli.out, "%s\n", createResponse.ID)
121
+		}()
122
+	}
123
+	if *flAutoRemove && (hostConfig.RestartPolicy.Name == "always" || hostConfig.RestartPolicy.Name == "on-failure") {
124
+		return ErrConflictRestartPolicyAndAutoRemove
125
+	}
126
+	// We need to instantiate the chan because the select needs it. It can
127
+	// be closed but can't be uninitialized.
128
+	hijacked := make(chan io.Closer)
129
+	// Block the return until the chan gets closed
130
+	defer func() {
131
+		log.Debugf("End of CmdRun(), Waiting for hijack to finish.")
132
+		if _, ok := <-hijacked; ok {
133
+			log.Errorf("Hijack did not finish (chan still open)")
134
+		}
135
+	}()
136
+	if config.AttachStdin || config.AttachStdout || config.AttachStderr {
137
+		var (
138
+			out, stderr io.Writer
139
+			in          io.ReadCloser
140
+			v           = url.Values{}
141
+		)
142
+		v.Set("stream", "1")
143
+		if config.AttachStdin {
144
+			v.Set("stdin", "1")
145
+			in = cli.in
146
+		}
147
+		if config.AttachStdout {
148
+			v.Set("stdout", "1")
149
+			out = cli.out
150
+		}
151
+		if config.AttachStderr {
152
+			v.Set("stderr", "1")
153
+			if config.Tty {
154
+				stderr = cli.out
155
+			} else {
156
+				stderr = cli.err
157
+			}
158
+		}
159
+		errCh = promise.Go(func() error {
160
+			return cli.hijack("POST", "/containers/"+createResponse.ID+"/attach?"+v.Encode(), config.Tty, in, out, stderr, hijacked, nil)
161
+		})
162
+	} else {
163
+		close(hijacked)
164
+	}
165
+	// Acknowledge the hijack before starting
166
+	select {
167
+	case closer := <-hijacked:
168
+		// Make sure that the hijack gets closed when returning (results
169
+		// in closing the hijack chan and freeing server's goroutines)
170
+		if closer != nil {
171
+			defer closer.Close()
172
+		}
173
+	case err := <-errCh:
174
+		if err != nil {
175
+			log.Debugf("Error hijack: %s", err)
176
+			return err
177
+		}
178
+	}
179
+
180
+	defer func() {
181
+		if *flAutoRemove {
182
+			if _, _, err = readBody(cli.call("DELETE", "/containers/"+createResponse.ID+"?v=1", nil, false)); err != nil {
183
+				log.Errorf("Error deleting container: %s", err)
184
+			}
185
+		}
186
+	}()
187
+
188
+	//start the container
189
+	if _, _, err = readBody(cli.call("POST", "/containers/"+createResponse.ID+"/start", nil, false)); err != nil {
190
+		return err
191
+	}
192
+
193
+	if (config.AttachStdin || config.AttachStdout || config.AttachStderr) && config.Tty && cli.isTerminalOut {
194
+		if err := cli.monitorTtySize(createResponse.ID, false); err != nil {
195
+			log.Errorf("Error monitoring TTY size: %s", err)
196
+		}
197
+	}
198
+
199
+	if errCh != nil {
200
+		if err := <-errCh; err != nil {
201
+			log.Debugf("Error hijack: %s", err)
202
+			return err
203
+		}
204
+	}
205
+
206
+	// Detached mode: wait for the id to be displayed and return.
207
+	if !config.AttachStdout && !config.AttachStderr {
208
+		// Detached mode
209
+		<-waitDisplayId
210
+		return nil
211
+	}
212
+
213
+	var status int
214
+
215
+	// Attached mode
216
+	if *flAutoRemove {
217
+		// Autoremove: wait for the container to finish, retrieve
218
+		// the exit code and remove the container
219
+		if _, _, err := readBody(cli.call("POST", "/containers/"+createResponse.ID+"/wait", nil, false)); err != nil {
220
+			return err
221
+		}
222
+		if _, status, err = getExitCode(cli, createResponse.ID); err != nil {
223
+			return err
224
+		}
225
+	} else {
226
+		// No Autoremove: Simply retrieve the exit code
227
+		if !config.Tty {
228
+			// In non-TTY mode, we can't detach, so we must wait for container exit
229
+			if status, err = waitForExit(cli, createResponse.ID); err != nil {
230
+				return err
231
+			}
232
+		} else {
233
+			// In TTY mode, there is a race: if the process dies too slowly, the state could
234
+			// be updated after the getExitCode call and result in the wrong exit code being reported
235
+			if _, status, err = getExitCode(cli, createResponse.ID); err != nil {
236
+				return err
237
+			}
238
+		}
239
+	}
240
+	if status != 0 {
241
+		return &utils.StatusError{StatusCode: status}
242
+	}
243
+	return nil
244
+}
0 245
new file mode 100644
... ...
@@ -0,0 +1,48 @@
0
+package client
1
+
2
+import (
3
+	"errors"
4
+	"io"
5
+	"net/url"
6
+	"os"
7
+
8
+	flag "github.com/docker/docker/pkg/mflag"
9
+	"github.com/docker/docker/utils"
10
+)
11
+
12
+func (cli *DockerCli) CmdSave(args ...string) error {
13
+	cmd := cli.Subcmd("save", "IMAGE [IMAGE...]", "Save an image(s) to a tar archive (streamed to STDOUT by default)", true)
14
+	outfile := cmd.String([]string{"o", "-output"}, "", "Write to an file, instead of STDOUT")
15
+	cmd.Require(flag.Min, 1)
16
+
17
+	utils.ParseFlags(cmd, args, true)
18
+
19
+	var (
20
+		output io.Writer = cli.out
21
+		err    error
22
+	)
23
+	if *outfile != "" {
24
+		output, err = os.Create(*outfile)
25
+		if err != nil {
26
+			return err
27
+		}
28
+	} else if cli.isTerminalOut {
29
+		return errors.New("Cowardly refusing to save to a terminal. Use the -o flag or redirect.")
30
+	}
31
+
32
+	if len(cmd.Args()) == 1 {
33
+		image := cmd.Arg(0)
34
+		if err := cli.stream("GET", "/images/"+image+"/get", nil, output, nil); err != nil {
35
+			return err
36
+		}
37
+	} else {
38
+		v := url.Values{}
39
+		for _, arg := range cmd.Args() {
40
+			v.Add("names", arg)
41
+		}
42
+		if err := cli.stream("GET", "/images/get?"+v.Encode(), nil, output, nil); err != nil {
43
+			return err
44
+		}
45
+	}
46
+	return nil
47
+}
0 48
new file mode 100644
... ...
@@ -0,0 +1,60 @@
0
+package client
1
+
2
+import (
3
+	"fmt"
4
+	"net/url"
5
+	"strings"
6
+	"text/tabwriter"
7
+
8
+	"github.com/docker/docker/engine"
9
+	flag "github.com/docker/docker/pkg/mflag"
10
+	"github.com/docker/docker/utils"
11
+)
12
+
13
+func (cli *DockerCli) CmdSearch(args ...string) error {
14
+	cmd := cli.Subcmd("search", "TERM", "Search the Docker Hub for images", true)
15
+	noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
16
+	trusted := cmd.Bool([]string{"#t", "#trusted", "#-trusted"}, false, "Only show trusted builds")
17
+	automated := cmd.Bool([]string{"-automated"}, false, "Only show automated builds")
18
+	stars := cmd.Int([]string{"s", "#stars", "-stars"}, 0, "Only displays with at least x stars")
19
+	cmd.Require(flag.Exact, 1)
20
+
21
+	utils.ParseFlags(cmd, args, true)
22
+
23
+	v := url.Values{}
24
+	v.Set("term", cmd.Arg(0))
25
+
26
+	body, _, err := readBody(cli.call("GET", "/images/search?"+v.Encode(), nil, true))
27
+
28
+	if err != nil {
29
+		return err
30
+	}
31
+	outs := engine.NewTable("star_count", 0)
32
+	if _, err := outs.ReadListFrom(body); err != nil {
33
+		return err
34
+	}
35
+	w := tabwriter.NewWriter(cli.out, 10, 1, 3, ' ', 0)
36
+	fmt.Fprintf(w, "NAME\tDESCRIPTION\tSTARS\tOFFICIAL\tAUTOMATED\n")
37
+	for _, out := range outs.Data {
38
+		if ((*automated || *trusted) && (!out.GetBool("is_trusted") && !out.GetBool("is_automated"))) || (*stars > out.GetInt("star_count")) {
39
+			continue
40
+		}
41
+		desc := strings.Replace(out.Get("description"), "\n", " ", -1)
42
+		desc = strings.Replace(desc, "\r", " ", -1)
43
+		if !*noTrunc && len(desc) > 45 {
44
+			desc = utils.Trunc(desc, 42) + "..."
45
+		}
46
+		fmt.Fprintf(w, "%s\t%s\t%d\t", out.Get("name"), desc, out.GetInt("star_count"))
47
+		if out.GetBool("is_official") {
48
+			fmt.Fprint(w, "[OK]")
49
+
50
+		}
51
+		fmt.Fprint(w, "\t")
52
+		if out.GetBool("is_automated") || out.GetBool("is_trusted") {
53
+			fmt.Fprint(w, "[OK]")
54
+		}
55
+		fmt.Fprint(w, "\n")
56
+	}
57
+	w.Flush()
58
+	return nil
59
+}
0 60
new file mode 100644
... ...
@@ -0,0 +1,160 @@
0
+package client
1
+
2
+import (
3
+	"fmt"
4
+	"io"
5
+	"net/url"
6
+	"os"
7
+
8
+	log "github.com/Sirupsen/logrus"
9
+	"github.com/docker/docker/engine"
10
+	flag "github.com/docker/docker/pkg/mflag"
11
+	"github.com/docker/docker/pkg/promise"
12
+	"github.com/docker/docker/pkg/signal"
13
+	"github.com/docker/docker/utils"
14
+)
15
+
16
+func (cli *DockerCli) forwardAllSignals(cid string) chan os.Signal {
17
+	sigc := make(chan os.Signal, 128)
18
+	signal.CatchAll(sigc)
19
+	go func() {
20
+		for s := range sigc {
21
+			if s == signal.SIGCHLD {
22
+				continue
23
+			}
24
+			var sig string
25
+			for sigStr, sigN := range signal.SignalMap {
26
+				if sigN == s {
27
+					sig = sigStr
28
+					break
29
+				}
30
+			}
31
+			if sig == "" {
32
+				log.Errorf("Unsupported signal: %v. Discarding.", s)
33
+			}
34
+			if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", cid, sig), nil, false)); err != nil {
35
+				log.Debugf("Error sending signal: %s", err)
36
+			}
37
+		}
38
+	}()
39
+	return sigc
40
+}
41
+
42
+func (cli *DockerCli) CmdStart(args ...string) error {
43
+	var (
44
+		cErr chan error
45
+		tty  bool
46
+
47
+		cmd       = cli.Subcmd("start", "CONTAINER [CONTAINER...]", "Start one or more stopped containers", true)
48
+		attach    = cmd.Bool([]string{"a", "-attach"}, false, "Attach STDOUT/STDERR and forward signals")
49
+		openStdin = cmd.Bool([]string{"i", "-interactive"}, false, "Attach container's STDIN")
50
+	)
51
+
52
+	cmd.Require(flag.Min, 1)
53
+	utils.ParseFlags(cmd, args, true)
54
+
55
+	if *attach || *openStdin {
56
+		if cmd.NArg() > 1 {
57
+			return fmt.Errorf("You cannot start and attach multiple containers at once.")
58
+		}
59
+
60
+		stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, false)
61
+		if err != nil {
62
+			return err
63
+		}
64
+
65
+		env := engine.Env{}
66
+		if err := env.Decode(stream); err != nil {
67
+			return err
68
+		}
69
+		config := env.GetSubEnv("Config")
70
+		tty = config.GetBool("Tty")
71
+
72
+		if !tty {
73
+			sigc := cli.forwardAllSignals(cmd.Arg(0))
74
+			defer signal.StopCatch(sigc)
75
+		}
76
+
77
+		var in io.ReadCloser
78
+
79
+		v := url.Values{}
80
+		v.Set("stream", "1")
81
+
82
+		if *openStdin && config.GetBool("OpenStdin") {
83
+			v.Set("stdin", "1")
84
+			in = cli.in
85
+		}
86
+
87
+		v.Set("stdout", "1")
88
+		v.Set("stderr", "1")
89
+
90
+		hijacked := make(chan io.Closer)
91
+		// Block the return until the chan gets closed
92
+		defer func() {
93
+			log.Debugf("CmdStart() returned, defer waiting for hijack to finish.")
94
+			if _, ok := <-hijacked; ok {
95
+				log.Errorf("Hijack did not finish (chan still open)")
96
+			}
97
+			cli.in.Close()
98
+		}()
99
+		cErr = promise.Go(func() error {
100
+			return cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), tty, in, cli.out, cli.err, hijacked, nil)
101
+		})
102
+
103
+		// Acknowledge the hijack before starting
104
+		select {
105
+		case closer := <-hijacked:
106
+			// Make sure that the hijack gets closed when returning (results
107
+			// in closing the hijack chan and freeing server's goroutines)
108
+			if closer != nil {
109
+				defer closer.Close()
110
+			}
111
+		case err := <-cErr:
112
+			if err != nil {
113
+				return err
114
+			}
115
+		}
116
+	}
117
+
118
+	var encounteredError error
119
+	for _, name := range cmd.Args() {
120
+		_, _, err := readBody(cli.call("POST", "/containers/"+name+"/start", nil, false))
121
+		if err != nil {
122
+			if !*attach && !*openStdin {
123
+				// attach and openStdin is false means it could be starting multiple containers
124
+				// when a container start failed, show the error message and start next
125
+				fmt.Fprintf(cli.err, "%s\n", err)
126
+				encounteredError = fmt.Errorf("Error: failed to start one or more containers")
127
+			} else {
128
+				encounteredError = err
129
+			}
130
+		} else {
131
+			if !*attach && !*openStdin {
132
+				fmt.Fprintf(cli.out, "%s\n", name)
133
+			}
134
+		}
135
+	}
136
+
137
+	if encounteredError != nil {
138
+		return encounteredError
139
+	}
140
+
141
+	if *openStdin || *attach {
142
+		if tty && cli.isTerminalOut {
143
+			if err := cli.monitorTtySize(cmd.Arg(0), false); err != nil {
144
+				log.Errorf("Error monitoring TTY size: %s", err)
145
+			}
146
+		}
147
+		if attchErr := <-cErr; attchErr != nil {
148
+			return attchErr
149
+		}
150
+		_, status, err := getExitCode(cli, cmd.Arg(0))
151
+		if err != nil {
152
+			return err
153
+		}
154
+		if status != 0 {
155
+			return &utils.StatusError{StatusCode: status}
156
+		}
157
+	}
158
+	return nil
159
+}
0 160
new file mode 100644
... ...
@@ -0,0 +1,177 @@
0
+package client
1
+
2
+import (
3
+	"encoding/json"
4
+	"fmt"
5
+	"io"
6
+	"sort"
7
+	"strings"
8
+	"sync"
9
+	"text/tabwriter"
10
+	"time"
11
+
12
+	"github.com/docker/docker/api/types"
13
+	flag "github.com/docker/docker/pkg/mflag"
14
+	"github.com/docker/docker/pkg/units"
15
+	"github.com/docker/docker/utils"
16
+)
17
+
18
+type containerStats struct {
19
+	Name             string
20
+	CpuPercentage    float64
21
+	Memory           float64
22
+	MemoryLimit      float64
23
+	MemoryPercentage float64
24
+	NetworkRx        float64
25
+	NetworkTx        float64
26
+	mu               sync.RWMutex
27
+	err              error
28
+}
29
+
30
+func (s *containerStats) Collect(cli *DockerCli) {
31
+	stream, _, err := cli.call("GET", "/containers/"+s.Name+"/stats", nil, false)
32
+	if err != nil {
33
+		s.err = err
34
+		return
35
+	}
36
+	defer stream.Close()
37
+	var (
38
+		previousCpu    uint64
39
+		previousSystem uint64
40
+		start          = true
41
+		dec            = json.NewDecoder(stream)
42
+		u              = make(chan error, 1)
43
+	)
44
+	go func() {
45
+		for {
46
+			var v *types.Stats
47
+			if err := dec.Decode(&v); err != nil {
48
+				u <- err
49
+				return
50
+			}
51
+			var (
52
+				memPercent = float64(v.MemoryStats.Usage) / float64(v.MemoryStats.Limit) * 100.0
53
+				cpuPercent = 0.0
54
+			)
55
+			if !start {
56
+				cpuPercent = calculateCpuPercent(previousCpu, previousSystem, v)
57
+			}
58
+			start = false
59
+			s.mu.Lock()
60
+			s.CpuPercentage = cpuPercent
61
+			s.Memory = float64(v.MemoryStats.Usage)
62
+			s.MemoryLimit = float64(v.MemoryStats.Limit)
63
+			s.MemoryPercentage = memPercent
64
+			s.NetworkRx = float64(v.Network.RxBytes)
65
+			s.NetworkTx = float64(v.Network.TxBytes)
66
+			s.mu.Unlock()
67
+			previousCpu = v.CpuStats.CpuUsage.TotalUsage
68
+			previousSystem = v.CpuStats.SystemUsage
69
+			u <- nil
70
+		}
71
+	}()
72
+	for {
73
+		select {
74
+		case <-time.After(2 * time.Second):
75
+			// zero out the values if we have not received an update within
76
+			// the specified duration.
77
+			s.mu.Lock()
78
+			s.CpuPercentage = 0
79
+			s.Memory = 0
80
+			s.MemoryPercentage = 0
81
+			s.mu.Unlock()
82
+		case err := <-u:
83
+			if err != nil {
84
+				s.mu.Lock()
85
+				s.err = err
86
+				s.mu.Unlock()
87
+				return
88
+			}
89
+		}
90
+	}
91
+}
92
+
93
+func (s *containerStats) Display(w io.Writer) error {
94
+	s.mu.RLock()
95
+	defer s.mu.RUnlock()
96
+	if s.err != nil {
97
+		return s.err
98
+	}
99
+	fmt.Fprintf(w, "%s\t%.2f%%\t%s/%s\t%.2f%%\t%s/%s\n",
100
+		s.Name,
101
+		s.CpuPercentage,
102
+		units.BytesSize(s.Memory), units.BytesSize(s.MemoryLimit),
103
+		s.MemoryPercentage,
104
+		units.BytesSize(s.NetworkRx), units.BytesSize(s.NetworkTx))
105
+	return nil
106
+}
107
+
108
+func (cli *DockerCli) CmdStats(args ...string) error {
109
+	cmd := cli.Subcmd("stats", "CONTAINER [CONTAINER...]", "Display a live stream of one or more containers' resource usage statistics", true)
110
+	cmd.Require(flag.Min, 1)
111
+	utils.ParseFlags(cmd, args, true)
112
+
113
+	names := cmd.Args()
114
+	sort.Strings(names)
115
+	var (
116
+		cStats []*containerStats
117
+		w      = tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
118
+	)
119
+	printHeader := func() {
120
+		fmt.Fprint(cli.out, "\033[2J")
121
+		fmt.Fprint(cli.out, "\033[H")
122
+		fmt.Fprintln(w, "CONTAINER\tCPU %\tMEM USAGE/LIMIT\tMEM %\tNET I/O")
123
+	}
124
+	for _, n := range names {
125
+		s := &containerStats{Name: n}
126
+		cStats = append(cStats, s)
127
+		go s.Collect(cli)
128
+	}
129
+	// do a quick pause so that any failed connections for containers that do not exist are able to be
130
+	// evicted before we display the initial or default values.
131
+	time.Sleep(500 * time.Millisecond)
132
+	var errs []string
133
+	for _, c := range cStats {
134
+		c.mu.Lock()
135
+		if c.err != nil {
136
+			errs = append(errs, fmt.Sprintf("%s: %v", c.Name, c.err))
137
+		}
138
+		c.mu.Unlock()
139
+	}
140
+	if len(errs) > 0 {
141
+		return fmt.Errorf("%s", strings.Join(errs, ", "))
142
+	}
143
+	for _ = range time.Tick(500 * time.Millisecond) {
144
+		printHeader()
145
+		toRemove := []int{}
146
+		for i, s := range cStats {
147
+			if err := s.Display(w); err != nil {
148
+				toRemove = append(toRemove, i)
149
+			}
150
+		}
151
+		for j := len(toRemove) - 1; j >= 0; j-- {
152
+			i := toRemove[j]
153
+			cStats = append(cStats[:i], cStats[i+1:]...)
154
+		}
155
+		if len(cStats) == 0 {
156
+			return nil
157
+		}
158
+		w.Flush()
159
+	}
160
+	return nil
161
+}
162
+
163
+func calculateCpuPercent(previousCpu, previousSystem uint64, v *types.Stats) float64 {
164
+	var (
165
+		cpuPercent = 0.0
166
+		// calculate the change for the cpu usage of the container in between readings
167
+		cpuDelta = float64(v.CpuStats.CpuUsage.TotalUsage - previousCpu)
168
+		// calculate the change for the entire system between readings
169
+		systemDelta = float64(v.CpuStats.SystemUsage - previousSystem)
170
+	)
171
+
172
+	if systemDelta > 0.0 && cpuDelta > 0.0 {
173
+		cpuPercent = (cpuDelta / systemDelta) * float64(len(v.CpuStats.CpuUsage.PercpuUsage)) * 100.0
174
+	}
175
+	return cpuPercent
176
+}
0 177
new file mode 100644
... ...
@@ -0,0 +1,33 @@
0
+package client
1
+
2
+import (
3
+	"fmt"
4
+	"net/url"
5
+	"strconv"
6
+
7
+	flag "github.com/docker/docker/pkg/mflag"
8
+	"github.com/docker/docker/utils"
9
+)
10
+
11
+func (cli *DockerCli) CmdStop(args ...string) error {
12
+	cmd := cli.Subcmd("stop", "CONTAINER [CONTAINER...]", "Stop a running container by sending SIGTERM and then SIGKILL after a\ngrace period", true)
13
+	nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Seconds to wait for stop before killing it")
14
+	cmd.Require(flag.Min, 1)
15
+
16
+	utils.ParseFlags(cmd, args, true)
17
+
18
+	v := url.Values{}
19
+	v.Set("t", strconv.Itoa(*nSeconds))
20
+
21
+	var encounteredError error
22
+	for _, name := range cmd.Args() {
23
+		_, _, err := readBody(cli.call("POST", "/containers/"+name+"/stop?"+v.Encode(), nil, false))
24
+		if err != nil {
25
+			fmt.Fprintf(cli.err, "%s\n", err)
26
+			encounteredError = fmt.Errorf("Error: failed to stop one or more containers")
27
+		} else {
28
+			fmt.Fprintf(cli.out, "%s\n", name)
29
+		}
30
+	}
31
+	return encounteredError
32
+}
0 33
new file mode 100644
... ...
@@ -0,0 +1,39 @@
0
+package client
1
+
2
+import (
3
+	"net/url"
4
+
5
+	flag "github.com/docker/docker/pkg/mflag"
6
+	"github.com/docker/docker/pkg/parsers"
7
+	"github.com/docker/docker/registry"
8
+	"github.com/docker/docker/utils"
9
+)
10
+
11
+func (cli *DockerCli) CmdTag(args ...string) error {
12
+	cmd := cli.Subcmd("tag", "IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]", "Tag an image into a repository", true)
13
+	force := cmd.Bool([]string{"f", "#force", "-force"}, false, "Force")
14
+	cmd.Require(flag.Exact, 2)
15
+
16
+	utils.ParseFlags(cmd, args, true)
17
+
18
+	var (
19
+		repository, tag = parsers.ParseRepositoryTag(cmd.Arg(1))
20
+		v               = url.Values{}
21
+	)
22
+
23
+	//Check if the given image name can be resolved
24
+	if err := registry.ValidateRepositoryName(repository); err != nil {
25
+		return err
26
+	}
27
+	v.Set("repo", repository)
28
+	v.Set("tag", tag)
29
+
30
+	if *force {
31
+		v.Set("force", "1")
32
+	}
33
+
34
+	if _, _, err := readBody(cli.call("POST", "/images/"+cmd.Arg(0)+"/tag?"+v.Encode(), nil, false)); err != nil {
35
+		return err
36
+	}
37
+	return nil
38
+}
0 39
new file mode 100644
... ...
@@ -0,0 +1,44 @@
0
+package client
1
+
2
+import (
3
+	"fmt"
4
+	"net/url"
5
+	"strings"
6
+	"text/tabwriter"
7
+
8
+	"github.com/docker/docker/engine"
9
+	flag "github.com/docker/docker/pkg/mflag"
10
+	"github.com/docker/docker/utils"
11
+)
12
+
13
+func (cli *DockerCli) CmdTop(args ...string) error {
14
+	cmd := cli.Subcmd("top", "CONTAINER [ps OPTIONS]", "Display the running processes of a container", true)
15
+	cmd.Require(flag.Min, 1)
16
+
17
+	utils.ParseFlags(cmd, args, true)
18
+
19
+	val := url.Values{}
20
+	if cmd.NArg() > 1 {
21
+		val.Set("ps_args", strings.Join(cmd.Args()[1:], " "))
22
+	}
23
+
24
+	stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/top?"+val.Encode(), nil, false)
25
+	if err != nil {
26
+		return err
27
+	}
28
+	var procs engine.Env
29
+	if err := procs.Decode(stream); err != nil {
30
+		return err
31
+	}
32
+	w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
33
+	fmt.Fprintln(w, strings.Join(procs.GetList("Titles"), "\t"))
34
+	processes := [][]string{}
35
+	if err := procs.GetJson("Processes", &processes); err != nil {
36
+		return err
37
+	}
38
+	for _, proc := range processes {
39
+		fmt.Fprintln(w, strings.Join(proc, "\t"))
40
+	}
41
+	w.Flush()
42
+	return nil
43
+}
0 44
new file mode 100644
... ...
@@ -0,0 +1,25 @@
0
+package client
1
+
2
+import (
3
+	"fmt"
4
+
5
+	flag "github.com/docker/docker/pkg/mflag"
6
+	"github.com/docker/docker/utils"
7
+)
8
+
9
+func (cli *DockerCli) CmdUnpause(args ...string) error {
10
+	cmd := cli.Subcmd("unpause", "CONTAINER [CONTAINER...]", "Unpause all processes within a container", true)
11
+	cmd.Require(flag.Min, 1)
12
+	utils.ParseFlags(cmd, args, false)
13
+
14
+	var encounteredError error
15
+	for _, name := range cmd.Args() {
16
+		if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/unpause", name), nil, false)); err != nil {
17
+			fmt.Fprintf(cli.err, "%s\n", err)
18
+			encounteredError = fmt.Errorf("Error: failed to unpause container named %s", name)
19
+		} else {
20
+			fmt.Fprintf(cli.out, "%s\n", name)
21
+		}
22
+	}
23
+	return encounteredError
24
+}
0 25
new file mode 100644
... ...
@@ -0,0 +1,56 @@
0
+package client
1
+
2
+import (
3
+	"fmt"
4
+	"runtime"
5
+
6
+	log "github.com/Sirupsen/logrus"
7
+	"github.com/docker/docker/api"
8
+	"github.com/docker/docker/autogen/dockerversion"
9
+	"github.com/docker/docker/engine"
10
+	flag "github.com/docker/docker/pkg/mflag"
11
+	"github.com/docker/docker/utils"
12
+)
13
+
14
+// 'docker version': show version information
15
+func (cli *DockerCli) CmdVersion(args ...string) error {
16
+	cmd := cli.Subcmd("version", "", "Show the Docker version information.", true)
17
+	cmd.Require(flag.Exact, 0)
18
+
19
+	utils.ParseFlags(cmd, args, false)
20
+
21
+	if dockerversion.VERSION != "" {
22
+		fmt.Fprintf(cli.out, "Client version: %s\n", dockerversion.VERSION)
23
+	}
24
+	fmt.Fprintf(cli.out, "Client API version: %s\n", api.APIVERSION)
25
+	fmt.Fprintf(cli.out, "Go version (client): %s\n", runtime.Version())
26
+	if dockerversion.GITCOMMIT != "" {
27
+		fmt.Fprintf(cli.out, "Git commit (client): %s\n", dockerversion.GITCOMMIT)
28
+	}
29
+	fmt.Fprintf(cli.out, "OS/Arch (client): %s/%s\n", runtime.GOOS, runtime.GOARCH)
30
+
31
+	body, _, err := readBody(cli.call("GET", "/version", nil, false))
32
+	if err != nil {
33
+		return err
34
+	}
35
+
36
+	out := engine.NewOutput()
37
+	remoteVersion, err := out.AddEnv()
38
+	if err != nil {
39
+		log.Errorf("Error reading remote version: %s", err)
40
+		return err
41
+	}
42
+	if _, err := out.Write(body); err != nil {
43
+		log.Errorf("Error reading remote version: %s", err)
44
+		return err
45
+	}
46
+	out.Close()
47
+	fmt.Fprintf(cli.out, "Server version: %s\n", remoteVersion.Get("Version"))
48
+	if apiVersion := remoteVersion.Get("ApiVersion"); apiVersion != "" {
49
+		fmt.Fprintf(cli.out, "Server API version: %s\n", apiVersion)
50
+	}
51
+	fmt.Fprintf(cli.out, "Go version (server): %s\n", remoteVersion.Get("GoVersion"))
52
+	fmt.Fprintf(cli.out, "Git commit (server): %s\n", remoteVersion.Get("GitCommit"))
53
+	fmt.Fprintf(cli.out, "OS/Arch (server): %s/%s\n", remoteVersion.Get("Os"), remoteVersion.Get("Arch"))
54
+	return nil
55
+}
0 56
new file mode 100644
... ...
@@ -0,0 +1,28 @@
0
+package client
1
+
2
+import (
3
+	"fmt"
4
+
5
+	flag "github.com/docker/docker/pkg/mflag"
6
+	"github.com/docker/docker/utils"
7
+)
8
+
9
+// 'docker wait': block until a container stops
10
+func (cli *DockerCli) CmdWait(args ...string) error {
11
+	cmd := cli.Subcmd("wait", "CONTAINER [CONTAINER...]", "Block until a container stops, then print its exit code.", true)
12
+	cmd.Require(flag.Min, 1)
13
+
14
+	utils.ParseFlags(cmd, args, true)
15
+
16
+	var encounteredError error
17
+	for _, name := range cmd.Args() {
18
+		status, err := waitForExit(cli, name)
19
+		if err != nil {
20
+			fmt.Fprintf(cli.err, "%s\n", err)
21
+			encounteredError = fmt.Errorf("Error: failed to wait one or more containers")
22
+		} else {
23
+			fmt.Fprintf(cli.out, "%d\n", status)
24
+		}
25
+	}
26
+	return encounteredError
27
+}