Move container options into a struct so that tests should pass.
Remove unused FlagSet arg from Parse
Disable interspersed args on docker run
Signed-off-by: Daniel Nephin <dnephin@docker.com>
| ... | ... |
@@ -66,7 +66,7 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
|
| 66 | 66 |
} |
| 67 | 67 |
|
| 68 | 68 |
if *proxy && !c.Config.Tty {
|
| 69 |
- sigc := cli.forwardAllSignals(ctx, container) |
|
| 69 |
+ sigc := cli.ForwardAllSignals(ctx, container) |
|
| 70 | 70 |
defer signal.StopCatch(sigc) |
| 71 | 71 |
} |
| 72 | 72 |
|
| ... | ... |
@@ -80,20 +80,20 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
|
| 80 | 80 |
defer resp.Close() |
| 81 | 81 |
|
| 82 | 82 |
if c.Config.Tty && cli.isTerminalOut {
|
| 83 |
- height, width := cli.getTtySize() |
|
| 83 |
+ height, width := cli.GetTtySize() |
|
| 84 | 84 |
// To handle the case where a user repeatedly attaches/detaches without resizing their |
| 85 | 85 |
// terminal, the only way to get the shell prompt to display for attaches 2+ is to artificially |
| 86 | 86 |
// resize it, then go back to normal. Without this, every attach after the first will |
| 87 | 87 |
// require the user to manually resize or hit enter. |
| 88 | 88 |
cli.resizeTtyTo(ctx, cmd.Arg(0), height+1, width+1, false) |
| 89 | 89 |
|
| 90 |
- // After the above resizing occurs, the call to monitorTtySize below will handle resetting back |
|
| 90 |
+ // After the above resizing occurs, the call to MonitorTtySize below will handle resetting back |
|
| 91 | 91 |
// to the actual size. |
| 92 |
- if err := cli.monitorTtySize(ctx, cmd.Arg(0), false); err != nil {
|
|
| 92 |
+ if err := cli.MonitorTtySize(ctx, cmd.Arg(0), false); err != nil {
|
|
| 93 | 93 |
logrus.Debugf("Error monitoring TTY size: %s", err)
|
| 94 | 94 |
} |
| 95 | 95 |
} |
| 96 |
- if err := cli.holdHijackedConnection(ctx, c.Config.Tty, in, cli.out, cli.err, resp); err != nil {
|
|
| 96 |
+ if err := cli.HoldHijackedConnection(ctx, c.Config.Tty, in, cli.out, cli.err, resp); err != nil {
|
|
| 97 | 97 |
return err |
| 98 | 98 |
} |
| 99 | 99 |
|
| ... | ... |
@@ -101,7 +101,7 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
|
| 101 | 101 |
return errAttach |
| 102 | 102 |
} |
| 103 | 103 |
|
| 104 |
- _, status, err := cli.getExitCode(ctx, container) |
|
| 104 |
+ _, status, err := cli.GetExitCode(ctx, container) |
|
| 105 | 105 |
if err != nil {
|
| 106 | 106 |
return err |
| 107 | 107 |
} |
| ... | ... |
@@ -75,6 +75,21 @@ func (cli *DockerCli) Err() io.Writer {
|
| 75 | 75 |
return cli.err |
| 76 | 76 |
} |
| 77 | 77 |
|
| 78 |
+// In returns the reader used for stdin |
|
| 79 |
+func (cli *DockerCli) In() io.ReadCloser {
|
|
| 80 |
+ return cli.in |
|
| 81 |
+} |
|
| 82 |
+ |
|
| 83 |
+// ConfigFile returns the ConfigFile |
|
| 84 |
+func (cli *DockerCli) ConfigFile() *configfile.ConfigFile {
|
|
| 85 |
+ return cli.configFile |
|
| 86 |
+} |
|
| 87 |
+ |
|
| 88 |
+// IsTerminalOut returns true if the clients stdin is a TTY |
|
| 89 |
+func (cli *DockerCli) IsTerminalOut() bool {
|
|
| 90 |
+ return cli.isTerminalOut |
|
| 91 |
+} |
|
| 92 |
+ |
|
| 78 | 93 |
// CheckTtyInput checks if we are trying to attach to a container tty |
| 79 | 94 |
// from a non-tty client input stream, and if so, returns an error. |
| 80 | 95 |
func (cli *DockerCli) CheckTtyInput(attachStdin, ttyMode bool) error {
|
| 45 | 44 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,325 @@ |
| 0 |
+package container |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "io" |
|
| 5 |
+ "net/http/httputil" |
|
| 6 |
+ "os" |
|
| 7 |
+ "runtime" |
|
| 8 |
+ "strings" |
|
| 9 |
+ |
|
| 10 |
+ "golang.org/x/net/context" |
|
| 11 |
+ |
|
| 12 |
+ "github.com/Sirupsen/logrus" |
|
| 13 |
+ "github.com/docker/docker/api/client" |
|
| 14 |
+ "github.com/docker/docker/cli" |
|
| 15 |
+ opttypes "github.com/docker/docker/opts" |
|
| 16 |
+ "github.com/docker/docker/pkg/promise" |
|
| 17 |
+ "github.com/docker/docker/pkg/signal" |
|
| 18 |
+ runconfigopts "github.com/docker/docker/runconfig/opts" |
|
| 19 |
+ "github.com/docker/engine-api/types" |
|
| 20 |
+ "github.com/docker/libnetwork/resolvconf/dns" |
|
| 21 |
+ "github.com/spf13/cobra" |
|
| 22 |
+ "github.com/spf13/pflag" |
|
| 23 |
+) |
|
| 24 |
+ |
|
| 25 |
+const ( |
|
| 26 |
+ errCmdNotFound = "not found or does not exist" |
|
| 27 |
+ errCmdCouldNotBeInvoked = "could not be invoked" |
|
| 28 |
+) |
|
| 29 |
+ |
|
| 30 |
+type runOptions struct {
|
|
| 31 |
+ autoRemove bool |
|
| 32 |
+ detach bool |
|
| 33 |
+ sigProxy bool |
|
| 34 |
+ name string |
|
| 35 |
+ detachKeys string |
|
| 36 |
+} |
|
| 37 |
+ |
|
| 38 |
+// NewRunCommand create a new `docker run` command |
|
| 39 |
+func NewRunCommand(dockerCli *client.DockerCli) *cobra.Command {
|
|
| 40 |
+ var opts runOptions |
|
| 41 |
+ var copts *runconfigopts.ContainerOptions |
|
| 42 |
+ |
|
| 43 |
+ cmd := &cobra.Command{
|
|
| 44 |
+ Use: "run [OPTIONS] IMAGE [COMMAND] [ARG...]", |
|
| 45 |
+ Short: "Run a command in a new container", |
|
| 46 |
+ Args: cli.RequiresMinArgs(1), |
|
| 47 |
+ RunE: func(cmd *cobra.Command, args []string) error {
|
|
| 48 |
+ copts.Image = args[0] |
|
| 49 |
+ if len(args) > 1 {
|
|
| 50 |
+ copts.Args = args[1:] |
|
| 51 |
+ } |
|
| 52 |
+ return runRun(dockerCli, cmd.Flags(), &opts, copts) |
|
| 53 |
+ }, |
|
| 54 |
+ } |
|
| 55 |
+ |
|
| 56 |
+ flags := cmd.Flags() |
|
| 57 |
+ flags.SetInterspersed(false) |
|
| 58 |
+ |
|
| 59 |
+ // These are flags not stored in Config/HostConfig |
|
| 60 |
+ flags.BoolVar(&opts.autoRemove, "rm", false, "Automatically remove the container when it exits") |
|
| 61 |
+ flags.BoolVarP(&opts.detach, "detach", "d", false, "Run container in background and print container ID") |
|
| 62 |
+ flags.BoolVar(&opts.sigProxy, "sig-proxy", true, "Proxy received signals to the process") |
|
| 63 |
+ flags.StringVar(&opts.name, "name", "", "Assign a name to the container") |
|
| 64 |
+ flags.StringVar(&opts.detachKeys, "detach-keys", "", "Override the key sequence for detaching a container") |
|
| 65 |
+ |
|
| 66 |
+ // Add an explicit help that doesn't have a `-h` to prevent the conflict |
|
| 67 |
+ // with hostname |
|
| 68 |
+ flags.Bool("help", false, "Print usage")
|
|
| 69 |
+ |
|
| 70 |
+ client.AddTrustedFlags(flags, true) |
|
| 71 |
+ copts = runconfigopts.AddFlags(flags) |
|
| 72 |
+ return cmd |
|
| 73 |
+} |
|
| 74 |
+ |
|
| 75 |
+func runRun(dockerCli *client.DockerCli, flags *pflag.FlagSet, opts *runOptions, copts *runconfigopts.ContainerOptions) error {
|
|
| 76 |
+ stdout, stderr, stdin := dockerCli.Out(), dockerCli.Err(), dockerCli.In() |
|
| 77 |
+ client := dockerCli.Client() |
|
| 78 |
+ // TODO: pass this as an argument |
|
| 79 |
+ cmdPath := "run" |
|
| 80 |
+ |
|
| 81 |
+ var ( |
|
| 82 |
+ flAttach *opttypes.ListOpts |
|
| 83 |
+ ErrConflictAttachDetach = fmt.Errorf("Conflicting options: -a and -d")
|
|
| 84 |
+ ErrConflictRestartPolicyAndAutoRemove = fmt.Errorf("Conflicting options: --restart and --rm")
|
|
| 85 |
+ ErrConflictDetachAutoRemove = fmt.Errorf("Conflicting options: --rm and -d")
|
|
| 86 |
+ ) |
|
| 87 |
+ |
|
| 88 |
+ config, hostConfig, networkingConfig, err := runconfigopts.Parse(flags, copts) |
|
| 89 |
+ |
|
| 90 |
+ // just in case the Parse does not exit |
|
| 91 |
+ if err != nil {
|
|
| 92 |
+ reportError(stderr, cmdPath, err.Error(), true) |
|
| 93 |
+ os.Exit(125) |
|
| 94 |
+ } |
|
| 95 |
+ |
|
| 96 |
+ if hostConfig.OomKillDisable != nil && *hostConfig.OomKillDisable && hostConfig.Memory == 0 {
|
|
| 97 |
+ fmt.Fprintf(stderr, "WARNING: Disabling the OOM killer on containers without setting a '-m/--memory' limit may be dangerous.\n") |
|
| 98 |
+ } |
|
| 99 |
+ |
|
| 100 |
+ if len(hostConfig.DNS) > 0 {
|
|
| 101 |
+ // check the DNS settings passed via --dns against |
|
| 102 |
+ // localhost regexp to warn if they are trying to |
|
| 103 |
+ // set a DNS to a localhost address |
|
| 104 |
+ for _, dnsIP := range hostConfig.DNS {
|
|
| 105 |
+ if dns.IsLocalhost(dnsIP) {
|
|
| 106 |
+ fmt.Fprintf(stderr, "WARNING: Localhost DNS setting (--dns=%s) may fail in containers.\n", dnsIP) |
|
| 107 |
+ break |
|
| 108 |
+ } |
|
| 109 |
+ } |
|
| 110 |
+ } |
|
| 111 |
+ |
|
| 112 |
+ config.ArgsEscaped = false |
|
| 113 |
+ |
|
| 114 |
+ if !opts.detach {
|
|
| 115 |
+ if err := dockerCli.CheckTtyInput(config.AttachStdin, config.Tty); err != nil {
|
|
| 116 |
+ return err |
|
| 117 |
+ } |
|
| 118 |
+ } else {
|
|
| 119 |
+ if fl := flags.Lookup("attach"); fl != nil {
|
|
| 120 |
+ flAttach = fl.Value.(*opttypes.ListOpts) |
|
| 121 |
+ if flAttach.Len() != 0 {
|
|
| 122 |
+ return ErrConflictAttachDetach |
|
| 123 |
+ } |
|
| 124 |
+ } |
|
| 125 |
+ if opts.autoRemove {
|
|
| 126 |
+ return ErrConflictDetachAutoRemove |
|
| 127 |
+ } |
|
| 128 |
+ |
|
| 129 |
+ config.AttachStdin = false |
|
| 130 |
+ config.AttachStdout = false |
|
| 131 |
+ config.AttachStderr = false |
|
| 132 |
+ config.StdinOnce = false |
|
| 133 |
+ } |
|
| 134 |
+ |
|
| 135 |
+ // Disable sigProxy when in TTY mode |
|
| 136 |
+ if config.Tty {
|
|
| 137 |
+ opts.sigProxy = false |
|
| 138 |
+ } |
|
| 139 |
+ |
|
| 140 |
+ // Telling the Windows daemon the initial size of the tty during start makes |
|
| 141 |
+ // a far better user experience rather than relying on subsequent resizes |
|
| 142 |
+ // to cause things to catch up. |
|
| 143 |
+ if runtime.GOOS == "windows" {
|
|
| 144 |
+ hostConfig.ConsoleSize[0], hostConfig.ConsoleSize[1] = dockerCli.GetTtySize() |
|
| 145 |
+ } |
|
| 146 |
+ |
|
| 147 |
+ ctx, cancelFun := context.WithCancel(context.Background()) |
|
| 148 |
+ |
|
| 149 |
+ createResponse, err := dockerCli.CreateContainer(ctx, config, hostConfig, networkingConfig, hostConfig.ContainerIDFile, opts.name) |
|
| 150 |
+ if err != nil {
|
|
| 151 |
+ reportError(stderr, cmdPath, err.Error(), true) |
|
| 152 |
+ return runStartContainerErr(err) |
|
| 153 |
+ } |
|
| 154 |
+ if opts.sigProxy {
|
|
| 155 |
+ sigc := dockerCli.ForwardAllSignals(ctx, createResponse.ID) |
|
| 156 |
+ defer signal.StopCatch(sigc) |
|
| 157 |
+ } |
|
| 158 |
+ var ( |
|
| 159 |
+ waitDisplayID chan struct{}
|
|
| 160 |
+ errCh chan error |
|
| 161 |
+ ) |
|
| 162 |
+ if !config.AttachStdout && !config.AttachStderr {
|
|
| 163 |
+ // Make this asynchronous to allow the client to write to stdin before having to read the ID |
|
| 164 |
+ waitDisplayID = make(chan struct{})
|
|
| 165 |
+ go func() {
|
|
| 166 |
+ defer close(waitDisplayID) |
|
| 167 |
+ fmt.Fprintf(stdout, "%s\n", createResponse.ID) |
|
| 168 |
+ }() |
|
| 169 |
+ } |
|
| 170 |
+ if opts.autoRemove && (hostConfig.RestartPolicy.IsAlways() || hostConfig.RestartPolicy.IsOnFailure()) {
|
|
| 171 |
+ return ErrConflictRestartPolicyAndAutoRemove |
|
| 172 |
+ } |
|
| 173 |
+ attach := config.AttachStdin || config.AttachStdout || config.AttachStderr |
|
| 174 |
+ if attach {
|
|
| 175 |
+ var ( |
|
| 176 |
+ out, cerr io.Writer |
|
| 177 |
+ in io.ReadCloser |
|
| 178 |
+ ) |
|
| 179 |
+ if config.AttachStdin {
|
|
| 180 |
+ in = stdin |
|
| 181 |
+ } |
|
| 182 |
+ if config.AttachStdout {
|
|
| 183 |
+ out = stdout |
|
| 184 |
+ } |
|
| 185 |
+ if config.AttachStderr {
|
|
| 186 |
+ if config.Tty {
|
|
| 187 |
+ cerr = stdout |
|
| 188 |
+ } else {
|
|
| 189 |
+ cerr = stderr |
|
| 190 |
+ } |
|
| 191 |
+ } |
|
| 192 |
+ |
|
| 193 |
+ if opts.detachKeys != "" {
|
|
| 194 |
+ dockerCli.ConfigFile().DetachKeys = opts.detachKeys |
|
| 195 |
+ } |
|
| 196 |
+ |
|
| 197 |
+ options := types.ContainerAttachOptions{
|
|
| 198 |
+ Stream: true, |
|
| 199 |
+ Stdin: config.AttachStdin, |
|
| 200 |
+ Stdout: config.AttachStdout, |
|
| 201 |
+ Stderr: config.AttachStderr, |
|
| 202 |
+ DetachKeys: dockerCli.ConfigFile().DetachKeys, |
|
| 203 |
+ } |
|
| 204 |
+ |
|
| 205 |
+ resp, errAttach := client.ContainerAttach(ctx, createResponse.ID, options) |
|
| 206 |
+ if errAttach != nil && errAttach != httputil.ErrPersistEOF {
|
|
| 207 |
+ // ContainerAttach returns an ErrPersistEOF (connection closed) |
|
| 208 |
+ // means server met an error and put it in Hijacked connection |
|
| 209 |
+ // keep the error and read detailed error message from hijacked connection later |
|
| 210 |
+ return errAttach |
|
| 211 |
+ } |
|
| 212 |
+ defer resp.Close() |
|
| 213 |
+ |
|
| 214 |
+ errCh = promise.Go(func() error {
|
|
| 215 |
+ errHijack := dockerCli.HoldHijackedConnection(ctx, config.Tty, in, out, cerr, resp) |
|
| 216 |
+ if errHijack == nil {
|
|
| 217 |
+ return errAttach |
|
| 218 |
+ } |
|
| 219 |
+ return errHijack |
|
| 220 |
+ }) |
|
| 221 |
+ } |
|
| 222 |
+ |
|
| 223 |
+ if opts.autoRemove {
|
|
| 224 |
+ defer func() {
|
|
| 225 |
+ // Explicitly not sharing the context as it could be "Done" (by calling cancelFun) |
|
| 226 |
+ // and thus the container would not be removed. |
|
| 227 |
+ if err := dockerCli.RemoveContainer(context.Background(), createResponse.ID, true, false, true); err != nil {
|
|
| 228 |
+ fmt.Fprintf(stderr, "%v\n", err) |
|
| 229 |
+ } |
|
| 230 |
+ }() |
|
| 231 |
+ } |
|
| 232 |
+ |
|
| 233 |
+ //start the container |
|
| 234 |
+ if err := client.ContainerStart(ctx, createResponse.ID, types.ContainerStartOptions{}); err != nil {
|
|
| 235 |
+ // If we have holdHijackedConnection, we should notify |
|
| 236 |
+ // holdHijackedConnection we are going to exit and wait |
|
| 237 |
+ // to avoid the terminal are not restored. |
|
| 238 |
+ if attach {
|
|
| 239 |
+ cancelFun() |
|
| 240 |
+ <-errCh |
|
| 241 |
+ } |
|
| 242 |
+ |
|
| 243 |
+ reportError(stderr, cmdPath, err.Error(), false) |
|
| 244 |
+ return runStartContainerErr(err) |
|
| 245 |
+ } |
|
| 246 |
+ |
|
| 247 |
+ if (config.AttachStdin || config.AttachStdout || config.AttachStderr) && config.Tty && dockerCli.IsTerminalOut() {
|
|
| 248 |
+ if err := dockerCli.MonitorTtySize(ctx, createResponse.ID, false); err != nil {
|
|
| 249 |
+ fmt.Fprintf(stderr, "Error monitoring TTY size: %s\n", err) |
|
| 250 |
+ } |
|
| 251 |
+ } |
|
| 252 |
+ |
|
| 253 |
+ if errCh != nil {
|
|
| 254 |
+ if err := <-errCh; err != nil {
|
|
| 255 |
+ logrus.Debugf("Error hijack: %s", err)
|
|
| 256 |
+ return err |
|
| 257 |
+ } |
|
| 258 |
+ } |
|
| 259 |
+ |
|
| 260 |
+ // Detached mode: wait for the id to be displayed and return. |
|
| 261 |
+ if !config.AttachStdout && !config.AttachStderr {
|
|
| 262 |
+ // Detached mode |
|
| 263 |
+ <-waitDisplayID |
|
| 264 |
+ return nil |
|
| 265 |
+ } |
|
| 266 |
+ |
|
| 267 |
+ var status int |
|
| 268 |
+ |
|
| 269 |
+ // Attached mode |
|
| 270 |
+ if opts.autoRemove {
|
|
| 271 |
+ // Autoremove: wait for the container to finish, retrieve |
|
| 272 |
+ // the exit code and remove the container |
|
| 273 |
+ if status, err = client.ContainerWait(ctx, createResponse.ID); err != nil {
|
|
| 274 |
+ return runStartContainerErr(err) |
|
| 275 |
+ } |
|
| 276 |
+ if _, status, err = dockerCli.GetExitCode(ctx, createResponse.ID); err != nil {
|
|
| 277 |
+ return err |
|
| 278 |
+ } |
|
| 279 |
+ } else {
|
|
| 280 |
+ // No Autoremove: Simply retrieve the exit code |
|
| 281 |
+ if !config.Tty {
|
|
| 282 |
+ // In non-TTY mode, we can't detach, so we must wait for container exit |
|
| 283 |
+ if status, err = client.ContainerWait(ctx, createResponse.ID); err != nil {
|
|
| 284 |
+ return err |
|
| 285 |
+ } |
|
| 286 |
+ } else {
|
|
| 287 |
+ // In TTY mode, there is a race: if the process dies too slowly, the state could |
|
| 288 |
+ // be updated after the getExitCode call and result in the wrong exit code being reported |
|
| 289 |
+ if _, status, err = dockerCli.GetExitCode(ctx, createResponse.ID); err != nil {
|
|
| 290 |
+ return err |
|
| 291 |
+ } |
|
| 292 |
+ } |
|
| 293 |
+ } |
|
| 294 |
+ if status != 0 {
|
|
| 295 |
+ return cli.StatusError{StatusCode: status}
|
|
| 296 |
+ } |
|
| 297 |
+ return nil |
|
| 298 |
+} |
|
| 299 |
+ |
|
| 300 |
+// reportError is a utility method that prints a user-friendly message |
|
| 301 |
+// containing the error that occurred during parsing and a suggestion to get help |
|
| 302 |
+func reportError(stderr io.Writer, name string, str string, withHelp bool) {
|
|
| 303 |
+ if withHelp {
|
|
| 304 |
+ str += ".\nSee '" + os.Args[0] + " " + name + " --help'" |
|
| 305 |
+ } |
|
| 306 |
+ fmt.Fprintf(stderr, "%s: %s.\n", os.Args[0], str) |
|
| 307 |
+} |
|
| 308 |
+ |
|
| 309 |
+// if container start fails with 'command not found' error, return 127 |
|
| 310 |
+// if container start fails with 'command cannot be invoked' error, return 126 |
|
| 311 |
+// return 125 for generic docker daemon failures |
|
| 312 |
+func runStartContainerErr(err error) error {
|
|
| 313 |
+ trimmedErr := strings.TrimPrefix(err.Error(), "Error response from daemon: ") |
|
| 314 |
+ statusError := cli.StatusError{StatusCode: 125}
|
|
| 315 |
+ if strings.HasPrefix(trimmedErr, "Container command") {
|
|
| 316 |
+ if strings.Contains(trimmedErr, errCmdNotFound) {
|
|
| 317 |
+ statusError = cli.StatusError{StatusCode: 127}
|
|
| 318 |
+ } else if strings.Contains(trimmedErr, errCmdCouldNotBeInvoked) {
|
|
| 319 |
+ statusError = cli.StatusError{StatusCode: 126}
|
|
| 320 |
+ } |
|
| 321 |
+ } |
|
| 322 |
+ |
|
| 323 |
+ return statusError |
|
| 324 |
+} |
| ... | ... |
@@ -12,7 +12,7 @@ import ( |
| 12 | 12 |
// FIXME migrate to docker/distribution/reference |
| 13 | 13 |
"github.com/docker/docker/reference" |
| 14 | 14 |
"github.com/docker/docker/registry" |
| 15 |
- runconfigopts "github.com/docker/docker/runconfig/opts" |
|
| 15 |
+ //runconfigopts "github.com/docker/docker/runconfig/opts" |
|
| 16 | 16 |
"github.com/docker/engine-api/client" |
| 17 | 17 |
"github.com/docker/engine-api/types" |
| 18 | 18 |
"github.com/docker/engine-api/types/container" |
| ... | ... |
@@ -56,6 +56,26 @@ type cidFile struct {
|
| 56 | 56 |
written bool |
| 57 | 57 |
} |
| 58 | 58 |
|
| 59 |
+func (cid *cidFile) Close() error {
|
|
| 60 |
+ cid.file.Close() |
|
| 61 |
+ |
|
| 62 |
+ if !cid.written {
|
|
| 63 |
+ if err := os.Remove(cid.path); err != nil {
|
|
| 64 |
+ return fmt.Errorf("failed to remove the CID file '%s': %s \n", cid.path, err)
|
|
| 65 |
+ } |
|
| 66 |
+ } |
|
| 67 |
+ |
|
| 68 |
+ return nil |
|
| 69 |
+} |
|
| 70 |
+ |
|
| 71 |
+func (cid *cidFile) Write(id string) error {
|
|
| 72 |
+ if _, err := cid.file.Write([]byte(id)); err != nil {
|
|
| 73 |
+ return fmt.Errorf("Failed to write the container ID to the file: %s", err)
|
|
| 74 |
+ } |
|
| 75 |
+ cid.written = true |
|
| 76 |
+ return nil |
|
| 77 |
+} |
|
| 78 |
+ |
|
| 59 | 79 |
func newCIDFile(path string) (*cidFile, error) {
|
| 60 | 80 |
if _, err := os.Stat(path); err == nil {
|
| 61 | 81 |
return nil, fmt.Errorf("Container ID file found, make sure the other container isn't running or delete %s", path)
|
| ... | ... |
@@ -69,7 +89,10 @@ func newCIDFile(path string) (*cidFile, error) {
|
| 69 | 69 |
return &cidFile{path: path, file: f}, nil
|
| 70 | 70 |
} |
| 71 | 71 |
|
| 72 |
-func (cli *DockerCli) createContainer(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *networktypes.NetworkingConfig, cidfile, name string) (*types.ContainerCreateResponse, error) {
|
|
| 72 |
+// CreateContainer creates a container from a config |
|
| 73 |
+// TODO: this can be unexported again once all container commands are under |
|
| 74 |
+// api/client/container |
|
| 75 |
+func (cli *DockerCli) CreateContainer(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *networktypes.NetworkingConfig, cidfile, name string) (*types.ContainerCreateResponse, error) {
|
|
| 73 | 76 |
var containerIDFile *cidFile |
| 74 | 77 |
if cidfile != "" {
|
| 75 | 78 |
var err error |
| ... | ... |
@@ -143,25 +166,26 @@ func (cli *DockerCli) CmdCreate(args ...string) error {
|
| 143 | 143 |
cmd := Cli.Subcmd("create", []string{"IMAGE [COMMAND] [ARG...]"}, Cli.DockerCommands["create"].Description, true)
|
| 144 | 144 |
addTrustedFlags(cmd, true) |
| 145 | 145 |
|
| 146 |
+ // TODO: tmp disable for PoC, convert to cobra and pflag later |
|
| 146 | 147 |
// These are flags not stored in Config/HostConfig |
| 147 |
- var ( |
|
| 148 |
- flName = cmd.String([]string{"-name"}, "", "Assign a name to the container")
|
|
| 149 |
- ) |
|
| 150 |
- |
|
| 151 |
- config, hostConfig, networkingConfig, cmd, err := runconfigopts.Parse(cmd, args) |
|
| 152 |
- |
|
| 153 |
- if err != nil {
|
|
| 154 |
- cmd.ReportError(err.Error(), true) |
|
| 155 |
- os.Exit(1) |
|
| 156 |
- } |
|
| 157 |
- if config.Image == "" {
|
|
| 158 |
- cmd.Usage() |
|
| 159 |
- return nil |
|
| 160 |
- } |
|
| 161 |
- response, err := cli.createContainer(context.Background(), config, hostConfig, networkingConfig, hostConfig.ContainerIDFile, *flName) |
|
| 162 |
- if err != nil {
|
|
| 163 |
- return err |
|
| 164 |
- } |
|
| 165 |
- fmt.Fprintf(cli.out, "%s\n", response.ID) |
|
| 148 |
+ // var ( |
|
| 149 |
+ // flName = cmd.String([]string{"-name"}, "", "Assign a name to the container")
|
|
| 150 |
+ // ) |
|
| 151 |
+ |
|
| 152 |
+ // config, hostConfig, networkingConfig, cmd, err := runconfigopts.Parse(cmd, args) |
|
| 153 |
+ // |
|
| 154 |
+ // if err != nil {
|
|
| 155 |
+ // cmd.ReportError(err.Error(), true) |
|
| 156 |
+ // os.Exit(1) |
|
| 157 |
+ // } |
|
| 158 |
+ // if config.Image == "" {
|
|
| 159 |
+ // cmd.Usage() |
|
| 160 |
+ // return nil |
|
| 161 |
+ // } |
|
| 162 |
+ // response, err := cli.CreateContainer(context.Background(), config, hostConfig, networkingConfig, hostConfig.ContainerIDFile, *flName) |
|
| 163 |
+ // if err != nil {
|
|
| 164 |
+ // return err |
|
| 165 |
+ // } |
|
| 166 |
+ // fmt.Fprintf(cli.out, "%s\n", response.ID) |
|
| 166 | 167 |
return nil |
| 167 | 168 |
} |
| ... | ... |
@@ -93,11 +93,11 @@ func (cli *DockerCli) CmdExec(args ...string) error {
|
| 93 | 93 |
} |
| 94 | 94 |
defer resp.Close() |
| 95 | 95 |
errCh = promise.Go(func() error {
|
| 96 |
- return cli.holdHijackedConnection(ctx, execConfig.Tty, in, out, stderr, resp) |
|
| 96 |
+ return cli.HoldHijackedConnection(ctx, execConfig.Tty, in, out, stderr, resp) |
|
| 97 | 97 |
}) |
| 98 | 98 |
|
| 99 | 99 |
if execConfig.Tty && cli.isTerminalIn {
|
| 100 |
- if err := cli.monitorTtySize(ctx, execID, true); err != nil {
|
|
| 100 |
+ if err := cli.MonitorTtySize(ctx, execID, true); err != nil {
|
|
| 101 | 101 |
fmt.Fprintf(cli.err, "Error monitoring TTY size: %s\n", err) |
| 102 | 102 |
} |
| 103 | 103 |
} |
| ... | ... |
@@ -11,7 +11,8 @@ import ( |
| 11 | 11 |
"github.com/docker/engine-api/types" |
| 12 | 12 |
) |
| 13 | 13 |
|
| 14 |
-func (cli *DockerCli) holdHijackedConnection(ctx context.Context, tty bool, inputStream io.ReadCloser, outputStream, errorStream io.Writer, resp types.HijackedResponse) error {
|
|
| 14 |
+// HoldHijackedConnection ... TODO docstring |
|
| 15 |
+func (cli *DockerCli) HoldHijackedConnection(ctx context.Context, tty bool, inputStream io.ReadCloser, outputStream, errorStream io.Writer, resp types.HijackedResponse) error {
|
|
| 15 | 16 |
var ( |
| 16 | 17 |
err error |
| 17 | 18 |
restoreOnce sync.Once |
| ... | ... |
@@ -32,7 +32,7 @@ func (cli *DockerCli) CmdRm(args ...string) error {
|
| 32 | 32 |
} |
| 33 | 33 |
name = strings.Trim(name, "/") |
| 34 | 34 |
|
| 35 |
- if err := cli.removeContainer(ctx, name, *v, *link, *force); err != nil {
|
|
| 35 |
+ if err := cli.RemoveContainer(ctx, name, *v, *link, *force); err != nil {
|
|
| 36 | 36 |
errs = append(errs, err.Error()) |
| 37 | 37 |
} else {
|
| 38 | 38 |
fmt.Fprintf(cli.out, "%s\n", name) |
| ... | ... |
@@ -44,7 +44,10 @@ func (cli *DockerCli) CmdRm(args ...string) error {
|
| 44 | 44 |
return nil |
| 45 | 45 |
} |
| 46 | 46 |
|
| 47 |
-func (cli *DockerCli) removeContainer(ctx context.Context, container string, removeVolumes, removeLinks, force bool) error {
|
|
| 47 |
+// RemoveContainer removes a container |
|
| 48 |
+// TODO: this can be unexported again once all container commands are under |
|
| 49 |
+// api/client/container |
|
| 50 |
+func (cli *DockerCli) RemoveContainer(ctx context.Context, container string, removeVolumes, removeLinks, force bool) error {
|
|
| 48 | 51 |
options := types.ContainerRemoveOptions{
|
| 49 | 52 |
RemoveVolumes: removeVolumes, |
| 50 | 53 |
RemoveLinks: removeLinks, |
| 51 | 54 |
deleted file mode 100644 |
| ... | ... |
@@ -1,301 +0,0 @@ |
| 1 |
-package client |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "fmt" |
|
| 5 |
- "io" |
|
| 6 |
- "net/http/httputil" |
|
| 7 |
- "os" |
|
| 8 |
- "runtime" |
|
| 9 |
- "strings" |
|
| 10 |
- |
|
| 11 |
- "golang.org/x/net/context" |
|
| 12 |
- |
|
| 13 |
- "github.com/Sirupsen/logrus" |
|
| 14 |
- Cli "github.com/docker/docker/cli" |
|
| 15 |
- "github.com/docker/docker/opts" |
|
| 16 |
- "github.com/docker/docker/pkg/promise" |
|
| 17 |
- "github.com/docker/docker/pkg/signal" |
|
| 18 |
- runconfigopts "github.com/docker/docker/runconfig/opts" |
|
| 19 |
- "github.com/docker/engine-api/types" |
|
| 20 |
- "github.com/docker/libnetwork/resolvconf/dns" |
|
| 21 |
-) |
|
| 22 |
- |
|
| 23 |
-const ( |
|
| 24 |
- errCmdNotFound = "not found or does not exist" |
|
| 25 |
- errCmdCouldNotBeInvoked = "could not be invoked" |
|
| 26 |
-) |
|
| 27 |
- |
|
| 28 |
-func (cid *cidFile) Close() error {
|
|
| 29 |
- cid.file.Close() |
|
| 30 |
- |
|
| 31 |
- if !cid.written {
|
|
| 32 |
- if err := os.Remove(cid.path); err != nil {
|
|
| 33 |
- return fmt.Errorf("failed to remove the CID file '%s': %s \n", cid.path, err)
|
|
| 34 |
- } |
|
| 35 |
- } |
|
| 36 |
- |
|
| 37 |
- return nil |
|
| 38 |
-} |
|
| 39 |
- |
|
| 40 |
-func (cid *cidFile) Write(id string) error {
|
|
| 41 |
- if _, err := cid.file.Write([]byte(id)); err != nil {
|
|
| 42 |
- return fmt.Errorf("Failed to write the container ID to the file: %s", err)
|
|
| 43 |
- } |
|
| 44 |
- cid.written = true |
|
| 45 |
- return nil |
|
| 46 |
-} |
|
| 47 |
- |
|
| 48 |
-// if container start fails with 'command not found' error, return 127 |
|
| 49 |
-// if container start fails with 'command cannot be invoked' error, return 126 |
|
| 50 |
-// return 125 for generic docker daemon failures |
|
| 51 |
-func runStartContainerErr(err error) error {
|
|
| 52 |
- trimmedErr := strings.TrimPrefix(err.Error(), "Error response from daemon: ") |
|
| 53 |
- statusError := Cli.StatusError{StatusCode: 125}
|
|
| 54 |
- if strings.HasPrefix(trimmedErr, "Container command") {
|
|
| 55 |
- if strings.Contains(trimmedErr, errCmdNotFound) {
|
|
| 56 |
- statusError = Cli.StatusError{StatusCode: 127}
|
|
| 57 |
- } else if strings.Contains(trimmedErr, errCmdCouldNotBeInvoked) {
|
|
| 58 |
- statusError = Cli.StatusError{StatusCode: 126}
|
|
| 59 |
- } |
|
| 60 |
- } |
|
| 61 |
- |
|
| 62 |
- return statusError |
|
| 63 |
-} |
|
| 64 |
- |
|
| 65 |
-// CmdRun runs a command in a new container. |
|
| 66 |
-// |
|
| 67 |
-// Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...] |
|
| 68 |
-func (cli *DockerCli) CmdRun(args ...string) error {
|
|
| 69 |
- cmd := Cli.Subcmd("run", []string{"IMAGE [COMMAND] [ARG...]"}, Cli.DockerCommands["run"].Description, true)
|
|
| 70 |
- addTrustedFlags(cmd, true) |
|
| 71 |
- |
|
| 72 |
- // These are flags not stored in Config/HostConfig |
|
| 73 |
- var ( |
|
| 74 |
- flAutoRemove = cmd.Bool([]string{"-rm"}, false, "Automatically remove the container when it exits")
|
|
| 75 |
- flDetach = cmd.Bool([]string{"d", "-detach"}, false, "Run container in background and print container ID")
|
|
| 76 |
- flSigProxy = cmd.Bool([]string{"-sig-proxy"}, true, "Proxy received signals to the process")
|
|
| 77 |
- flName = cmd.String([]string{"-name"}, "", "Assign a name to the container")
|
|
| 78 |
- flDetachKeys = cmd.String([]string{"-detach-keys"}, "", "Override the key sequence for detaching a container")
|
|
| 79 |
- flAttach *opts.ListOpts |
|
| 80 |
- |
|
| 81 |
- ErrConflictAttachDetach = fmt.Errorf("Conflicting options: -a and -d")
|
|
| 82 |
- ErrConflictRestartPolicyAndAutoRemove = fmt.Errorf("Conflicting options: --restart and --rm")
|
|
| 83 |
- ErrConflictDetachAutoRemove = fmt.Errorf("Conflicting options: --rm and -d")
|
|
| 84 |
- ) |
|
| 85 |
- |
|
| 86 |
- config, hostConfig, networkingConfig, cmd, err := runconfigopts.Parse(cmd, args) |
|
| 87 |
- |
|
| 88 |
- // just in case the Parse does not exit |
|
| 89 |
- if err != nil {
|
|
| 90 |
- cmd.ReportError(err.Error(), true) |
|
| 91 |
- os.Exit(125) |
|
| 92 |
- } |
|
| 93 |
- |
|
| 94 |
- if hostConfig.OomKillDisable != nil && *hostConfig.OomKillDisable && hostConfig.Memory == 0 {
|
|
| 95 |
- fmt.Fprintf(cli.err, "WARNING: Disabling the OOM killer on containers without setting a '-m/--memory' limit may be dangerous.\n") |
|
| 96 |
- } |
|
| 97 |
- |
|
| 98 |
- if len(hostConfig.DNS) > 0 {
|
|
| 99 |
- // check the DNS settings passed via --dns against |
|
| 100 |
- // localhost regexp to warn if they are trying to |
|
| 101 |
- // set a DNS to a localhost address |
|
| 102 |
- for _, dnsIP := range hostConfig.DNS {
|
|
| 103 |
- if dns.IsLocalhost(dnsIP) {
|
|
| 104 |
- fmt.Fprintf(cli.err, "WARNING: Localhost DNS setting (--dns=%s) may fail in containers.\n", dnsIP) |
|
| 105 |
- break |
|
| 106 |
- } |
|
| 107 |
- } |
|
| 108 |
- } |
|
| 109 |
- if config.Image == "" {
|
|
| 110 |
- cmd.Usage() |
|
| 111 |
- return nil |
|
| 112 |
- } |
|
| 113 |
- |
|
| 114 |
- config.ArgsEscaped = false |
|
| 115 |
- |
|
| 116 |
- if !*flDetach {
|
|
| 117 |
- if err := cli.CheckTtyInput(config.AttachStdin, config.Tty); err != nil {
|
|
| 118 |
- return err |
|
| 119 |
- } |
|
| 120 |
- } else {
|
|
| 121 |
- if fl := cmd.Lookup("-attach"); fl != nil {
|
|
| 122 |
- flAttach = fl.Value.(*opts.ListOpts) |
|
| 123 |
- if flAttach.Len() != 0 {
|
|
| 124 |
- return ErrConflictAttachDetach |
|
| 125 |
- } |
|
| 126 |
- } |
|
| 127 |
- if *flAutoRemove {
|
|
| 128 |
- return ErrConflictDetachAutoRemove |
|
| 129 |
- } |
|
| 130 |
- |
|
| 131 |
- config.AttachStdin = false |
|
| 132 |
- config.AttachStdout = false |
|
| 133 |
- config.AttachStderr = false |
|
| 134 |
- config.StdinOnce = false |
|
| 135 |
- } |
|
| 136 |
- |
|
| 137 |
- // Disable flSigProxy when in TTY mode |
|
| 138 |
- sigProxy := *flSigProxy |
|
| 139 |
- if config.Tty {
|
|
| 140 |
- sigProxy = false |
|
| 141 |
- } |
|
| 142 |
- |
|
| 143 |
- // Telling the Windows daemon the initial size of the tty during start makes |
|
| 144 |
- // a far better user experience rather than relying on subsequent resizes |
|
| 145 |
- // to cause things to catch up. |
|
| 146 |
- if runtime.GOOS == "windows" {
|
|
| 147 |
- hostConfig.ConsoleSize[0], hostConfig.ConsoleSize[1] = cli.getTtySize() |
|
| 148 |
- } |
|
| 149 |
- |
|
| 150 |
- ctx, cancelFun := context.WithCancel(context.Background()) |
|
| 151 |
- |
|
| 152 |
- createResponse, err := cli.createContainer(ctx, config, hostConfig, networkingConfig, hostConfig.ContainerIDFile, *flName) |
|
| 153 |
- if err != nil {
|
|
| 154 |
- cmd.ReportError(err.Error(), true) |
|
| 155 |
- return runStartContainerErr(err) |
|
| 156 |
- } |
|
| 157 |
- if sigProxy {
|
|
| 158 |
- sigc := cli.forwardAllSignals(ctx, createResponse.ID) |
|
| 159 |
- defer signal.StopCatch(sigc) |
|
| 160 |
- } |
|
| 161 |
- var ( |
|
| 162 |
- waitDisplayID chan struct{}
|
|
| 163 |
- errCh chan error |
|
| 164 |
- ) |
|
| 165 |
- if !config.AttachStdout && !config.AttachStderr {
|
|
| 166 |
- // Make this asynchronous to allow the client to write to stdin before having to read the ID |
|
| 167 |
- waitDisplayID = make(chan struct{})
|
|
| 168 |
- go func() {
|
|
| 169 |
- defer close(waitDisplayID) |
|
| 170 |
- fmt.Fprintf(cli.out, "%s\n", createResponse.ID) |
|
| 171 |
- }() |
|
| 172 |
- } |
|
| 173 |
- if *flAutoRemove && (hostConfig.RestartPolicy.IsAlways() || hostConfig.RestartPolicy.IsOnFailure()) {
|
|
| 174 |
- return ErrConflictRestartPolicyAndAutoRemove |
|
| 175 |
- } |
|
| 176 |
- attach := config.AttachStdin || config.AttachStdout || config.AttachStderr |
|
| 177 |
- if attach {
|
|
| 178 |
- var ( |
|
| 179 |
- out, stderr io.Writer |
|
| 180 |
- in io.ReadCloser |
|
| 181 |
- ) |
|
| 182 |
- if config.AttachStdin {
|
|
| 183 |
- in = cli.in |
|
| 184 |
- } |
|
| 185 |
- if config.AttachStdout {
|
|
| 186 |
- out = cli.out |
|
| 187 |
- } |
|
| 188 |
- if config.AttachStderr {
|
|
| 189 |
- if config.Tty {
|
|
| 190 |
- stderr = cli.out |
|
| 191 |
- } else {
|
|
| 192 |
- stderr = cli.err |
|
| 193 |
- } |
|
| 194 |
- } |
|
| 195 |
- |
|
| 196 |
- if *flDetachKeys != "" {
|
|
| 197 |
- cli.configFile.DetachKeys = *flDetachKeys |
|
| 198 |
- } |
|
| 199 |
- |
|
| 200 |
- options := types.ContainerAttachOptions{
|
|
| 201 |
- Stream: true, |
|
| 202 |
- Stdin: config.AttachStdin, |
|
| 203 |
- Stdout: config.AttachStdout, |
|
| 204 |
- Stderr: config.AttachStderr, |
|
| 205 |
- DetachKeys: cli.configFile.DetachKeys, |
|
| 206 |
- } |
|
| 207 |
- |
|
| 208 |
- resp, errAttach := cli.client.ContainerAttach(ctx, createResponse.ID, options) |
|
| 209 |
- if errAttach != nil && errAttach != httputil.ErrPersistEOF {
|
|
| 210 |
- // ContainerAttach returns an ErrPersistEOF (connection closed) |
|
| 211 |
- // means server met an error and put it in Hijacked connection |
|
| 212 |
- // keep the error and read detailed error message from hijacked connection later |
|
| 213 |
- return errAttach |
|
| 214 |
- } |
|
| 215 |
- defer resp.Close() |
|
| 216 |
- |
|
| 217 |
- errCh = promise.Go(func() error {
|
|
| 218 |
- errHijack := cli.holdHijackedConnection(ctx, config.Tty, in, out, stderr, resp) |
|
| 219 |
- if errHijack == nil {
|
|
| 220 |
- return errAttach |
|
| 221 |
- } |
|
| 222 |
- return errHijack |
|
| 223 |
- }) |
|
| 224 |
- } |
|
| 225 |
- |
|
| 226 |
- if *flAutoRemove {
|
|
| 227 |
- defer func() {
|
|
| 228 |
- // Explicitly not sharing the context as it could be "Done" (by calling cancelFun) |
|
| 229 |
- // and thus the container would not be removed. |
|
| 230 |
- if err := cli.removeContainer(context.Background(), createResponse.ID, true, false, true); err != nil {
|
|
| 231 |
- fmt.Fprintf(cli.err, "%v\n", err) |
|
| 232 |
- } |
|
| 233 |
- }() |
|
| 234 |
- } |
|
| 235 |
- |
|
| 236 |
- //start the container |
|
| 237 |
- if err := cli.client.ContainerStart(ctx, createResponse.ID, types.ContainerStartOptions{}); err != nil {
|
|
| 238 |
- // If we have holdHijackedConnection, we should notify |
|
| 239 |
- // holdHijackedConnection we are going to exit and wait |
|
| 240 |
- // to avoid the terminal are not restored. |
|
| 241 |
- if attach {
|
|
| 242 |
- cancelFun() |
|
| 243 |
- <-errCh |
|
| 244 |
- } |
|
| 245 |
- |
|
| 246 |
- cmd.ReportError(err.Error(), false) |
|
| 247 |
- return runStartContainerErr(err) |
|
| 248 |
- } |
|
| 249 |
- |
|
| 250 |
- if (config.AttachStdin || config.AttachStdout || config.AttachStderr) && config.Tty && cli.isTerminalOut {
|
|
| 251 |
- if err := cli.monitorTtySize(ctx, createResponse.ID, false); err != nil {
|
|
| 252 |
- fmt.Fprintf(cli.err, "Error monitoring TTY size: %s\n", err) |
|
| 253 |
- } |
|
| 254 |
- } |
|
| 255 |
- |
|
| 256 |
- if errCh != nil {
|
|
| 257 |
- if err := <-errCh; err != nil {
|
|
| 258 |
- logrus.Debugf("Error hijack: %s", err)
|
|
| 259 |
- return err |
|
| 260 |
- } |
|
| 261 |
- } |
|
| 262 |
- |
|
| 263 |
- // Detached mode: wait for the id to be displayed and return. |
|
| 264 |
- if !config.AttachStdout && !config.AttachStderr {
|
|
| 265 |
- // Detached mode |
|
| 266 |
- <-waitDisplayID |
|
| 267 |
- return nil |
|
| 268 |
- } |
|
| 269 |
- |
|
| 270 |
- var status int |
|
| 271 |
- |
|
| 272 |
- // Attached mode |
|
| 273 |
- if *flAutoRemove {
|
|
| 274 |
- // Autoremove: wait for the container to finish, retrieve |
|
| 275 |
- // the exit code and remove the container |
|
| 276 |
- if status, err = cli.client.ContainerWait(ctx, createResponse.ID); err != nil {
|
|
| 277 |
- return runStartContainerErr(err) |
|
| 278 |
- } |
|
| 279 |
- if _, status, err = cli.getExitCode(ctx, createResponse.ID); err != nil {
|
|
| 280 |
- return err |
|
| 281 |
- } |
|
| 282 |
- } else {
|
|
| 283 |
- // No Autoremove: Simply retrieve the exit code |
|
| 284 |
- if !config.Tty {
|
|
| 285 |
- // In non-TTY mode, we can't detach, so we must wait for container exit |
|
| 286 |
- if status, err = cli.client.ContainerWait(ctx, createResponse.ID); err != nil {
|
|
| 287 |
- return err |
|
| 288 |
- } |
|
| 289 |
- } else {
|
|
| 290 |
- // In TTY mode, there is a race: if the process dies too slowly, the state could |
|
| 291 |
- // be updated after the getExitCode call and result in the wrong exit code being reported |
|
| 292 |
- if _, status, err = cli.getExitCode(ctx, createResponse.ID); err != nil {
|
|
| 293 |
- return err |
|
| 294 |
- } |
|
| 295 |
- } |
|
| 296 |
- } |
|
| 297 |
- if status != 0 {
|
|
| 298 |
- return Cli.StatusError{StatusCode: status}
|
|
| 299 |
- } |
|
| 300 |
- return nil |
|
| 301 |
-} |
| ... | ... |
@@ -17,7 +17,10 @@ import ( |
| 17 | 17 |
"github.com/docker/engine-api/types" |
| 18 | 18 |
) |
| 19 | 19 |
|
| 20 |
-func (cli *DockerCli) forwardAllSignals(ctx context.Context, cid string) chan os.Signal {
|
|
| 20 |
+// ForwardAllSignals forwards signals to the contianer |
|
| 21 |
+// TODO: this can be unexported again once all container commands are under |
|
| 22 |
+// api/client/container |
|
| 23 |
+func (cli *DockerCli) ForwardAllSignals(ctx context.Context, cid string) chan os.Signal {
|
|
| 21 | 24 |
sigc := make(chan os.Signal, 128) |
| 22 | 25 |
signal.CatchAll(sigc) |
| 23 | 26 |
go func() {
|
| ... | ... |
@@ -74,7 +77,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
|
| 74 | 74 |
} |
| 75 | 75 |
|
| 76 | 76 |
if !c.Config.Tty {
|
| 77 |
- sigc := cli.forwardAllSignals(ctx, container) |
|
| 77 |
+ sigc := cli.ForwardAllSignals(ctx, container) |
|
| 78 | 78 |
defer signal.StopCatch(sigc) |
| 79 | 79 |
} |
| 80 | 80 |
|
| ... | ... |
@@ -105,7 +108,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
|
| 105 | 105 |
} |
| 106 | 106 |
defer resp.Close() |
| 107 | 107 |
cErr := promise.Go(func() error {
|
| 108 |
- errHijack := cli.holdHijackedConnection(ctx, c.Config.Tty, in, cli.out, cli.err, resp) |
|
| 108 |
+ errHijack := cli.HoldHijackedConnection(ctx, c.Config.Tty, in, cli.out, cli.err, resp) |
|
| 109 | 109 |
if errHijack == nil {
|
| 110 | 110 |
return errAttach |
| 111 | 111 |
} |
| ... | ... |
@@ -121,14 +124,14 @@ func (cli *DockerCli) CmdStart(args ...string) error {
|
| 121 | 121 |
|
| 122 | 122 |
// 4. Wait for attachment to break. |
| 123 | 123 |
if c.Config.Tty && cli.isTerminalOut {
|
| 124 |
- if err := cli.monitorTtySize(ctx, container, false); err != nil {
|
|
| 124 |
+ if err := cli.MonitorTtySize(ctx, container, false); err != nil {
|
|
| 125 | 125 |
fmt.Fprintf(cli.err, "Error monitoring TTY size: %s\n", err) |
| 126 | 126 |
} |
| 127 | 127 |
} |
| 128 | 128 |
if attchErr := <-cErr; attchErr != nil {
|
| 129 | 129 |
return attchErr |
| 130 | 130 |
} |
| 131 |
- _, status, err := cli.getExitCode(ctx, container) |
|
| 131 |
+ _, status, err := cli.GetExitCode(ctx, container) |
|
| 132 | 132 |
if err != nil {
|
| 133 | 133 |
return err |
| 134 | 134 |
} |
| ... | ... |
@@ -37,6 +37,7 @@ import ( |
| 37 | 37 |
"github.com/docker/notary/tuf/data" |
| 38 | 38 |
"github.com/docker/notary/tuf/signed" |
| 39 | 39 |
"github.com/docker/notary/tuf/store" |
| 40 |
+ "github.com/spf13/pflag" |
|
| 40 | 41 |
) |
| 41 | 42 |
|
| 42 | 43 |
var ( |
| ... | ... |
@@ -44,7 +45,20 @@ var ( |
| 44 | 44 |
untrusted bool |
| 45 | 45 |
) |
| 46 | 46 |
|
| 47 |
+// TODO: tmp workaround to get this PoC working, change everything to use |
|
| 48 |
+// exported version |
|
| 47 | 49 |
func addTrustedFlags(fs *flag.FlagSet, verify bool) {
|
| 50 |
+ trusted, message := setupTrustedFlag(verify) |
|
| 51 |
+ fs.BoolVar(&untrusted, []string{"-disable-content-trust"}, !trusted, message)
|
|
| 52 |
+} |
|
| 53 |
+ |
|
| 54 |
+// AddTrustedFlags adds the trust flags to a FlagSet |
|
| 55 |
+func AddTrustedFlags(fs *pflag.FlagSet, verify bool) {
|
|
| 56 |
+ trusted, message := setupTrustedFlag(verify) |
|
| 57 |
+ fs.BoolVar(&untrusted, "disable-content-trust", !trusted, message) |
|
| 58 |
+} |
|
| 59 |
+ |
|
| 60 |
+func setupTrustedFlag(verify bool) (bool, string) {
|
|
| 48 | 61 |
var trusted bool |
| 49 | 62 |
if e := os.Getenv("DOCKER_CONTENT_TRUST"); e != "" {
|
| 50 | 63 |
if t, err := strconv.ParseBool(e); t || err != nil {
|
| ... | ... |
@@ -56,7 +70,7 @@ func addTrustedFlags(fs *flag.FlagSet, verify bool) {
|
| 56 | 56 |
if verify {
|
| 57 | 57 |
message = "Skip image verification" |
| 58 | 58 |
} |
| 59 |
- fs.BoolVar(&untrusted, []string{"-disable-content-trust"}, !trusted, message)
|
|
| 59 |
+ return trusted, message |
|
| 60 | 60 |
} |
| 61 | 61 |
|
| 62 | 62 |
func isTrusted() bool {
|
| ... | ... |
@@ -61,7 +61,7 @@ func (cli *DockerCli) RegistryAuthenticationPrivilegedFunc(index *registrytypes. |
| 61 | 61 |
} |
| 62 | 62 |
|
| 63 | 63 |
func (cli *DockerCli) resizeTty(ctx context.Context, id string, isExec bool) {
|
| 64 |
- height, width := cli.getTtySize() |
|
| 64 |
+ height, width := cli.GetTtySize() |
|
| 65 | 65 |
cli.resizeTtyTo(ctx, id, height, width, isExec) |
| 66 | 66 |
} |
| 67 | 67 |
|
| ... | ... |
@@ -87,9 +87,9 @@ func (cli *DockerCli) resizeTtyTo(ctx context.Context, id string, height, width |
| 87 | 87 |
} |
| 88 | 88 |
} |
| 89 | 89 |
|
| 90 |
-// getExitCode perform an inspect on the container. It returns |
|
| 90 |
+// GetExitCode perform an inspect on the container. It returns |
|
| 91 | 91 |
// the running state and the exit code. |
| 92 |
-func (cli *DockerCli) getExitCode(ctx context.Context, containerID string) (bool, int, error) {
|
|
| 92 |
+func (cli *DockerCli) GetExitCode(ctx context.Context, containerID string) (bool, int, error) {
|
|
| 93 | 93 |
c, err := cli.client.ContainerInspect(ctx, containerID) |
| 94 | 94 |
if err != nil {
|
| 95 | 95 |
// If we can't connect, then the daemon probably died. |
| ... | ... |
@@ -117,15 +117,16 @@ func (cli *DockerCli) getExecExitCode(ctx context.Context, execID string) (bool, |
| 117 | 117 |
return resp.Running, resp.ExitCode, nil |
| 118 | 118 |
} |
| 119 | 119 |
|
| 120 |
-func (cli *DockerCli) monitorTtySize(ctx context.Context, id string, isExec bool) error {
|
|
| 120 |
+// MonitorTtySize updates the container tty size when the terminal tty changes size |
|
| 121 |
+func (cli *DockerCli) MonitorTtySize(ctx context.Context, id string, isExec bool) error {
|
|
| 121 | 122 |
cli.resizeTty(ctx, id, isExec) |
| 122 | 123 |
|
| 123 | 124 |
if runtime.GOOS == "windows" {
|
| 124 | 125 |
go func() {
|
| 125 |
- prevH, prevW := cli.getTtySize() |
|
| 126 |
+ prevH, prevW := cli.GetTtySize() |
|
| 126 | 127 |
for {
|
| 127 | 128 |
time.Sleep(time.Millisecond * 250) |
| 128 |
- h, w := cli.getTtySize() |
|
| 129 |
+ h, w := cli.GetTtySize() |
|
| 129 | 130 |
|
| 130 | 131 |
if prevW != w || prevH != h {
|
| 131 | 132 |
cli.resizeTty(ctx, id, isExec) |
| ... | ... |
@@ -146,7 +147,8 @@ func (cli *DockerCli) monitorTtySize(ctx context.Context, id string, isExec bool |
| 146 | 146 |
return nil |
| 147 | 147 |
} |
| 148 | 148 |
|
| 149 |
-func (cli *DockerCli) getTtySize() (int, int) {
|
|
| 149 |
+// GetTtySize returns the height and width in characters of the tty |
|
| 150 |
+func (cli *DockerCli) GetTtySize() (int, int) {
|
|
| 150 | 151 |
if !cli.isTerminalOut {
|
| 151 | 152 |
return 0, 0 |
| 152 | 153 |
} |
| ... | ... |
@@ -4,6 +4,7 @@ import ( |
| 4 | 4 |
"fmt" |
| 5 | 5 |
|
| 6 | 6 |
"github.com/docker/docker/api/client" |
| 7 |
+ "github.com/docker/docker/api/client/container" |
|
| 7 | 8 |
"github.com/docker/docker/api/client/image" |
| 8 | 9 |
"github.com/docker/docker/api/client/volume" |
| 9 | 10 |
"github.com/docker/docker/cli" |
| ... | ... |
@@ -34,8 +35,9 @@ func NewCobraAdaptor(clientFlags *cliflags.ClientFlags) CobraAdaptor {
|
| 34 | 34 |
rootCmd.SetFlagErrorFunc(flagErrorFunc) |
| 35 | 35 |
rootCmd.SetOutput(stdout) |
| 36 | 36 |
rootCmd.AddCommand( |
| 37 |
- volume.NewVolumeCommand(dockerCli), |
|
| 37 |
+ container.NewRunCommand(dockerCli), |
|
| 38 | 38 |
image.NewSearchCommand(dockerCli), |
| 39 |
+ volume.NewVolumeCommand(dockerCli), |
|
| 39 | 40 |
) |
| 40 | 41 |
|
| 41 | 42 |
rootCmd.PersistentFlags().BoolP("help", "h", false, "Print usage")
|
| ... | ... |
@@ -52,7 +54,7 @@ func NewCobraAdaptor(clientFlags *cliflags.ClientFlags) CobraAdaptor {
|
| 52 | 52 |
func (c CobraAdaptor) Usage() []cli.Command {
|
| 53 | 53 |
cmds := []cli.Command{}
|
| 54 | 54 |
for _, cmd := range c.rootCmd.Commands() {
|
| 55 |
- cmds = append(cmds, cli.Command{Name: cmd.Use, Description: cmd.Short})
|
|
| 55 |
+ cmds = append(cmds, cli.Command{Name: cmd.Name(), Description: cmd.Short})
|
|
| 56 | 56 |
} |
| 57 | 57 |
return cmds |
| 58 | 58 |
} |
| ... | ... |
@@ -37,7 +37,6 @@ var DockerCommandUsage = []Command{
|
| 37 | 37 |
{"restart", "Restart a container"},
|
| 38 | 38 |
{"rm", "Remove one or more containers"},
|
| 39 | 39 |
{"rmi", "Remove one or more images"},
|
| 40 |
- {"run", "Run a command in a new container"},
|
|
| 41 | 40 |
{"save", "Save one or more images to a tar archive"},
|
| 42 | 41 |
{"start", "Start one or more stopped containers"},
|
| 43 | 42 |
{"stats", "Display a live stream of container(s) resource usage statistics"},
|
| ... | ... |
@@ -8,160 +8,245 @@ import ( |
| 8 | 8 |
"path" |
| 9 | 9 |
"strconv" |
| 10 | 10 |
"strings" |
| 11 |
+ "time" |
|
| 11 | 12 |
|
| 12 | 13 |
"github.com/docker/docker/opts" |
| 13 |
- flag "github.com/docker/docker/pkg/mflag" |
|
| 14 | 14 |
"github.com/docker/docker/pkg/mount" |
| 15 | 15 |
"github.com/docker/docker/pkg/signal" |
| 16 | 16 |
"github.com/docker/engine-api/types/container" |
| 17 | 17 |
networktypes "github.com/docker/engine-api/types/network" |
| 18 | 18 |
"github.com/docker/engine-api/types/strslice" |
| 19 | 19 |
"github.com/docker/go-connections/nat" |
| 20 |
- "github.com/docker/go-units" |
|
| 20 |
+ units "github.com/docker/go-units" |
|
| 21 |
+ "github.com/spf13/pflag" |
|
| 21 | 22 |
) |
| 22 | 23 |
|
| 23 |
-// Parse parses the specified args for the specified command and generates a Config, |
|
| 24 |
+// ContainerOptions is a data object with all the options for creating a container |
|
| 25 |
+// TODO: remove fl prefix |
|
| 26 |
+type ContainerOptions struct {
|
|
| 27 |
+ flAttach opts.ListOpts |
|
| 28 |
+ flVolumes opts.ListOpts |
|
| 29 |
+ flTmpfs opts.ListOpts |
|
| 30 |
+ flBlkioWeightDevice WeightdeviceOpt |
|
| 31 |
+ flDeviceReadBps ThrottledeviceOpt |
|
| 32 |
+ flDeviceWriteBps ThrottledeviceOpt |
|
| 33 |
+ flLinks opts.ListOpts |
|
| 34 |
+ flAliases opts.ListOpts |
|
| 35 |
+ flDeviceReadIOps ThrottledeviceOpt |
|
| 36 |
+ flDeviceWriteIOps ThrottledeviceOpt |
|
| 37 |
+ flEnv opts.ListOpts |
|
| 38 |
+ flLabels opts.ListOpts |
|
| 39 |
+ flDevices opts.ListOpts |
|
| 40 |
+ flUlimits *UlimitOpt |
|
| 41 |
+ flSysctls *opts.MapOpts |
|
| 42 |
+ flPublish opts.ListOpts |
|
| 43 |
+ flExpose opts.ListOpts |
|
| 44 |
+ flDNS opts.ListOpts |
|
| 45 |
+ flDNSSearch opts.ListOpts |
|
| 46 |
+ flDNSOptions opts.ListOpts |
|
| 47 |
+ flExtraHosts opts.ListOpts |
|
| 48 |
+ flVolumesFrom opts.ListOpts |
|
| 49 |
+ flEnvFile opts.ListOpts |
|
| 50 |
+ flCapAdd opts.ListOpts |
|
| 51 |
+ flCapDrop opts.ListOpts |
|
| 52 |
+ flGroupAdd opts.ListOpts |
|
| 53 |
+ flSecurityOpt opts.ListOpts |
|
| 54 |
+ flStorageOpt opts.ListOpts |
|
| 55 |
+ flLabelsFile opts.ListOpts |
|
| 56 |
+ flLoggingOpts opts.ListOpts |
|
| 57 |
+ flPrivileged *bool |
|
| 58 |
+ flPidMode *string |
|
| 59 |
+ flUTSMode *string |
|
| 60 |
+ flUsernsMode *string |
|
| 61 |
+ flPublishAll *bool |
|
| 62 |
+ flStdin *bool |
|
| 63 |
+ flTty *bool |
|
| 64 |
+ flOomKillDisable *bool |
|
| 65 |
+ flOomScoreAdj *int |
|
| 66 |
+ flContainerIDFile *string |
|
| 67 |
+ flEntrypoint *string |
|
| 68 |
+ flHostname *string |
|
| 69 |
+ flMemoryString *string |
|
| 70 |
+ flMemoryReservation *string |
|
| 71 |
+ flMemorySwap *string |
|
| 72 |
+ flKernelMemory *string |
|
| 73 |
+ flUser *string |
|
| 74 |
+ flWorkingDir *string |
|
| 75 |
+ flCPUShares *int64 |
|
| 76 |
+ flCPUPercent *int64 |
|
| 77 |
+ flCPUPeriod *int64 |
|
| 78 |
+ flCPUQuota *int64 |
|
| 79 |
+ flCpusetCpus *string |
|
| 80 |
+ flCpusetMems *string |
|
| 81 |
+ flBlkioWeight *uint16 |
|
| 82 |
+ flIOMaxBandwidth *string |
|
| 83 |
+ flIOMaxIOps *uint64 |
|
| 84 |
+ flSwappiness *int64 |
|
| 85 |
+ flNetMode *string |
|
| 86 |
+ flMacAddress *string |
|
| 87 |
+ flIPv4Address *string |
|
| 88 |
+ flIPv6Address *string |
|
| 89 |
+ flIpcMode *string |
|
| 90 |
+ flPidsLimit *int64 |
|
| 91 |
+ flRestartPolicy *string |
|
| 92 |
+ flReadonlyRootfs *bool |
|
| 93 |
+ flLoggingDriver *string |
|
| 94 |
+ flCgroupParent *string |
|
| 95 |
+ flVolumeDriver *string |
|
| 96 |
+ flStopSignal *string |
|
| 97 |
+ flIsolation *string |
|
| 98 |
+ flShmSize *string |
|
| 99 |
+ flNoHealthcheck *bool |
|
| 100 |
+ flHealthCmd *string |
|
| 101 |
+ flHealthInterval *time.Duration |
|
| 102 |
+ flHealthTimeout *time.Duration |
|
| 103 |
+ flHealthRetries *int |
|
| 104 |
+ |
|
| 105 |
+ Image string |
|
| 106 |
+ Args []string |
|
| 107 |
+} |
|
| 108 |
+ |
|
| 109 |
+// AddFlags adds all command line flags that will be used by Parse to the FlagSet |
|
| 110 |
+func AddFlags(flags *pflag.FlagSet) *ContainerOptions {
|
|
| 111 |
+ copts := &ContainerOptions{
|
|
| 112 |
+ flAttach: opts.NewListOpts(ValidateAttach), |
|
| 113 |
+ flVolumes: opts.NewListOpts(nil), |
|
| 114 |
+ flTmpfs: opts.NewListOpts(nil), |
|
| 115 |
+ flBlkioWeightDevice: NewWeightdeviceOpt(ValidateWeightDevice), |
|
| 116 |
+ flDeviceReadBps: NewThrottledeviceOpt(ValidateThrottleBpsDevice), |
|
| 117 |
+ flDeviceWriteBps: NewThrottledeviceOpt(ValidateThrottleBpsDevice), |
|
| 118 |
+ flLinks: opts.NewListOpts(ValidateLink), |
|
| 119 |
+ flAliases: opts.NewListOpts(nil), |
|
| 120 |
+ flDeviceReadIOps: NewThrottledeviceOpt(ValidateThrottleIOpsDevice), |
|
| 121 |
+ flDeviceWriteIOps: NewThrottledeviceOpt(ValidateThrottleIOpsDevice), |
|
| 122 |
+ flEnv: opts.NewListOpts(ValidateEnv), |
|
| 123 |
+ flLabels: opts.NewListOpts(ValidateEnv), |
|
| 124 |
+ flDevices: opts.NewListOpts(ValidateDevice), |
|
| 125 |
+ |
|
| 126 |
+ flUlimits: NewUlimitOpt(nil), |
|
| 127 |
+ flSysctls: opts.NewMapOpts(nil, opts.ValidateSysctl), |
|
| 128 |
+ |
|
| 129 |
+ flPublish: opts.NewListOpts(nil), |
|
| 130 |
+ flExpose: opts.NewListOpts(nil), |
|
| 131 |
+ flDNS: opts.NewListOpts(opts.ValidateIPAddress), |
|
| 132 |
+ flDNSSearch: opts.NewListOpts(opts.ValidateDNSSearch), |
|
| 133 |
+ flDNSOptions: opts.NewListOpts(nil), |
|
| 134 |
+ flExtraHosts: opts.NewListOpts(ValidateExtraHost), |
|
| 135 |
+ flVolumesFrom: opts.NewListOpts(nil), |
|
| 136 |
+ flEnvFile: opts.NewListOpts(nil), |
|
| 137 |
+ flCapAdd: opts.NewListOpts(nil), |
|
| 138 |
+ flCapDrop: opts.NewListOpts(nil), |
|
| 139 |
+ flGroupAdd: opts.NewListOpts(nil), |
|
| 140 |
+ flSecurityOpt: opts.NewListOpts(nil), |
|
| 141 |
+ flStorageOpt: opts.NewListOpts(nil), |
|
| 142 |
+ flLabelsFile: opts.NewListOpts(nil), |
|
| 143 |
+ flLoggingOpts: opts.NewListOpts(nil), |
|
| 144 |
+ |
|
| 145 |
+ flPrivileged: flags.Bool("privileged", false, "Give extended privileges to this container"),
|
|
| 146 |
+ flPidMode: flags.String("pid", "", "PID namespace to use"),
|
|
| 147 |
+ flUTSMode: flags.String("uts", "", "UTS namespace to use"),
|
|
| 148 |
+ flUsernsMode: flags.String("userns", "", "User namespace to use"),
|
|
| 149 |
+ flPublishAll: flags.BoolP("publish-all", "P", false, "Publish all exposed ports to random ports"),
|
|
| 150 |
+ flStdin: flags.BoolP("interactive", "i", false, "Keep STDIN open even if not attached"),
|
|
| 151 |
+ flTty: flags.BoolP("tty", "t", false, "Allocate a pseudo-TTY"),
|
|
| 152 |
+ flOomKillDisable: flags.Bool("oom-kill-disable", false, "Disable OOM Killer"),
|
|
| 153 |
+ flOomScoreAdj: flags.Int("oom-score-adj", 0, "Tune host's OOM preferences (-1000 to 1000)"),
|
|
| 154 |
+ flContainerIDFile: flags.String("cidfile", "", "Write the container ID to the file"),
|
|
| 155 |
+ flEntrypoint: flags.String("entrypoint", "", "Overwrite the default ENTRYPOINT of the image"),
|
|
| 156 |
+ flHostname: flags.StringP("hostname", "h", "", "Container host name"),
|
|
| 157 |
+ flMemoryString: flags.StringP("memory", "m", "", "Memory limit"),
|
|
| 158 |
+ flMemoryReservation: flags.String("memory-reservation", "", "Memory soft limit"),
|
|
| 159 |
+ flMemorySwap: flags.String("memory-swap", "", "Swap limit equal to memory plus swap: '-1' to enable unlimited swap"),
|
|
| 160 |
+ flKernelMemory: flags.String("kernel-memory", "", "Kernel memory limit"),
|
|
| 161 |
+ flUser: flags.StringP("user", "u", "", "Username or UID (format: <name|uid>[:<group|gid>])"),
|
|
| 162 |
+ flWorkingDir: flags.StringP("workdir", "w", "", "Working directory inside the container"),
|
|
| 163 |
+ flCPUShares: flags.Int64P("cpu-shares", "c", 0, "CPU shares (relative weight)"),
|
|
| 164 |
+ flCPUPercent: flags.Int64("cpu-percent", 0, "CPU percent (Windows only)"),
|
|
| 165 |
+ flCPUPeriod: flags.Int64("cpu-period", 0, "Limit CPU CFS (Completely Fair Scheduler) period"),
|
|
| 166 |
+ flCPUQuota: flags.Int64("cpu-quota", 0, "Limit CPU CFS (Completely Fair Scheduler) quota"),
|
|
| 167 |
+ flCpusetCpus: flags.String("cpuset-cpus", "", "CPUs in which to allow execution (0-3, 0,1)"),
|
|
| 168 |
+ flCpusetMems: flags.String("cpuset-mems", "", "MEMs in which to allow execution (0-3, 0,1)"),
|
|
| 169 |
+ flBlkioWeight: flags.Uint16("blkio-weight", 0, "Block IO (relative weight), between 10 and 1000"),
|
|
| 170 |
+ flIOMaxBandwidth: flags.String("io-maxbandwidth", "", "Maximum IO bandwidth limit for the system drive (Windows only)"),
|
|
| 171 |
+ flIOMaxIOps: flags.Uint64("io-maxiops", 0, "Maximum IOps limit for the system drive (Windows only)"),
|
|
| 172 |
+ flSwappiness: flags.Int64("memory-swappiness", -1, "Tune container memory swappiness (0 to 100)"),
|
|
| 173 |
+ flNetMode: flags.String("net", "default", "Connect a container to a network"),
|
|
| 174 |
+ flMacAddress: flags.String("mac-address", "", "Container MAC address (e.g. 92:d0:c6:0a:29:33)"),
|
|
| 175 |
+ flIPv4Address: flags.String("ip", "", "Container IPv4 address (e.g. 172.30.100.104)"),
|
|
| 176 |
+ flIPv6Address: flags.String("ip6", "", "Container IPv6 address (e.g. 2001:db8::33)"),
|
|
| 177 |
+ flIpcMode: flags.String("ipc", "", "IPC namespace to use"),
|
|
| 178 |
+ flPidsLimit: flags.Int64("pids-limit", 0, "Tune container pids limit (set -1 for unlimited)"),
|
|
| 179 |
+ flRestartPolicy: flags.String("restart", "no", "Restart policy to apply when a container exits"),
|
|
| 180 |
+ flReadonlyRootfs: flags.Bool("read-only", false, "Mount the container's root filesystem as read only"),
|
|
| 181 |
+ flLoggingDriver: flags.String("log-driver", "", "Logging driver for container"),
|
|
| 182 |
+ flCgroupParent: flags.String("cgroup-parent", "", "Optional parent cgroup for the container"),
|
|
| 183 |
+ flVolumeDriver: flags.String("volume-driver", "", "Optional volume driver for the container"),
|
|
| 184 |
+ flStopSignal: flags.String("stop-signal", signal.DefaultStopSignal, fmt.Sprintf("Signal to stop a container, %v by default", signal.DefaultStopSignal)),
|
|
| 185 |
+ flIsolation: flags.String("isolation", "", "Container isolation technology"),
|
|
| 186 |
+ flShmSize: flags.String("shm-size", "", "Size of /dev/shm, default value is 64MB"),
|
|
| 187 |
+ flNoHealthcheck: cmd.Bool([]string{"-no-healthcheck"}, false, "Disable any container-specified HEALTHCHECK"),
|
|
| 188 |
+ flHealthCmd: cmd.String([]string{"-health-cmd"}, "", "Command to run to check health"),
|
|
| 189 |
+ flHealthInterval: cmd.Duration([]string{"-health-interval"}, 0, "Time between running the check"),
|
|
| 190 |
+ flHealthTimeout: cmd.Duration([]string{"-health-timeout"}, 0, "Maximum time to allow one check to run"),
|
|
| 191 |
+ flHealthRetries: cmd.Int([]string{"-health-retries"}, 0, "Consecutive failures needed to report unhealthy"),
|
|
| 192 |
+ } |
|
| 193 |
+ |
|
| 194 |
+ flags.VarP(&copts.flAttach, "attach", "a", "Attach to STDIN, STDOUT or STDERR") |
|
| 195 |
+ flags.Var(&copts.flBlkioWeightDevice, "blkio-weight-device", "Block IO weight (relative device weight)") |
|
| 196 |
+ flags.Var(&copts.flDeviceReadBps, "device-read-bps", "Limit read rate (bytes per second) from a device") |
|
| 197 |
+ flags.Var(&copts.flDeviceWriteBps, "device-write-bps", "Limit write rate (bytes per second) to a device") |
|
| 198 |
+ flags.Var(&copts.flDeviceReadIOps, "device-read-iops", "Limit read rate (IO per second) from a device") |
|
| 199 |
+ flags.Var(&copts.flDeviceWriteIOps, "device-write-iops", "Limit write rate (IO per second) to a device") |
|
| 200 |
+ flags.VarP(&copts.flVolumes, "volume", "v", "Bind mount a volume") |
|
| 201 |
+ flags.Var(&copts.flTmpfs, "tmpfs", "Mount a tmpfs directory") |
|
| 202 |
+ flags.Var(&copts.flLinks, "link", "Add link to another container") |
|
| 203 |
+ flags.Var(&copts.flAliases, "net-alias", "Add network-scoped alias for the container") |
|
| 204 |
+ flags.Var(&copts.flDevices, "device", "Add a host device to the container") |
|
| 205 |
+ flags.VarP(&copts.flLabels, "label", "l", "Set meta data on a container") |
|
| 206 |
+ flags.Var(&copts.flLabelsFile, "label-file", "Read in a line delimited file of labels") |
|
| 207 |
+ flags.VarP(&copts.flEnv, "env", "e", "Set environment variables") |
|
| 208 |
+ flags.Var(&copts.flEnvFile, "env-file", "Read in a file of environment variables") |
|
| 209 |
+ flags.VarP(&copts.flPublish, "publish", "p", "Publish a container's port(s) to the host") |
|
| 210 |
+ flags.Var(&copts.flExpose, "expose", "Expose a port or a range of ports") |
|
| 211 |
+ flags.Var(&copts.flDNS, "dns", "Set custom DNS servers") |
|
| 212 |
+ flags.Var(&copts.flDNSSearch, "dns-search", "Set custom DNS search domains") |
|
| 213 |
+ flags.Var(&copts.flDNSOptions, "dns-opt", "Set DNS options") |
|
| 214 |
+ flags.Var(&copts.flExtraHosts, "add-host", "Add a custom host-to-IP mapping (host:ip)") |
|
| 215 |
+ flags.Var(&copts.flVolumesFrom, "volumes-from", "Mount volumes from the specified container(s)") |
|
| 216 |
+ flags.Var(&copts.flCapAdd, "cap-add", "Add Linux capabilities") |
|
| 217 |
+ flags.Var(&copts.flCapDrop, "cap-drop", "Drop Linux capabilities") |
|
| 218 |
+ flags.Var(&copts.flGroupAdd, "group-add", "Add additional groups to join") |
|
| 219 |
+ flags.Var(&copts.flSecurityOpt, "security-opt", "Security Options") |
|
| 220 |
+ flags.Var(&copts.flStorageOpt, "storage-opt", "Set storage driver options per container") |
|
| 221 |
+ flags.Var(copts.flUlimits, "ulimit", "Ulimit options") |
|
| 222 |
+ flags.Var(copts.flSysctls, "sysctl", "Sysctl options") |
|
| 223 |
+ flags.Var(&copts.flLoggingOpts, "log-opt", "Log driver options") |
|
| 224 |
+ |
|
| 225 |
+ return copts |
|
| 226 |
+} |
|
| 227 |
+ |
|
| 228 |
+// Parse parses the args for the specified command and generates a Config, |
|
| 24 | 229 |
// a HostConfig and returns them with the specified command. |
| 25 | 230 |
// If the specified args are not valid, it will return an error. |
| 26 |
-func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig, *flag.FlagSet, error) {
|
|
| 27 |
- var ( |
|
| 28 |
- // FIXME: use utils.ListOpts for attach and volumes? |
|
| 29 |
- flAttach = opts.NewListOpts(ValidateAttach) |
|
| 30 |
- flVolumes = opts.NewListOpts(nil) |
|
| 31 |
- flTmpfs = opts.NewListOpts(nil) |
|
| 32 |
- flBlkioWeightDevice = NewWeightdeviceOpt(ValidateWeightDevice) |
|
| 33 |
- flDeviceReadBps = NewThrottledeviceOpt(ValidateThrottleBpsDevice) |
|
| 34 |
- flDeviceWriteBps = NewThrottledeviceOpt(ValidateThrottleBpsDevice) |
|
| 35 |
- flLinks = opts.NewListOpts(ValidateLink) |
|
| 36 |
- flAliases = opts.NewListOpts(nil) |
|
| 37 |
- flDeviceReadIOps = NewThrottledeviceOpt(ValidateThrottleIOpsDevice) |
|
| 38 |
- flDeviceWriteIOps = NewThrottledeviceOpt(ValidateThrottleIOpsDevice) |
|
| 39 |
- flEnv = opts.NewListOpts(ValidateEnv) |
|
| 40 |
- flLabels = opts.NewListOpts(ValidateEnv) |
|
| 41 |
- flDevices = opts.NewListOpts(ValidateDevice) |
|
| 42 |
- |
|
| 43 |
- flUlimits = NewUlimitOpt(nil) |
|
| 44 |
- flSysctls = opts.NewMapOpts(nil, opts.ValidateSysctl) |
|
| 45 |
- |
|
| 46 |
- flPublish = opts.NewListOpts(nil) |
|
| 47 |
- flExpose = opts.NewListOpts(nil) |
|
| 48 |
- flDNS = opts.NewListOpts(opts.ValidateIPAddress) |
|
| 49 |
- flDNSSearch = opts.NewListOpts(opts.ValidateDNSSearch) |
|
| 50 |
- flDNSOptions = opts.NewListOpts(nil) |
|
| 51 |
- flExtraHosts = opts.NewListOpts(ValidateExtraHost) |
|
| 52 |
- flVolumesFrom = opts.NewListOpts(nil) |
|
| 53 |
- flEnvFile = opts.NewListOpts(nil) |
|
| 54 |
- flCapAdd = opts.NewListOpts(nil) |
|
| 55 |
- flCapDrop = opts.NewListOpts(nil) |
|
| 56 |
- flGroupAdd = opts.NewListOpts(nil) |
|
| 57 |
- flSecurityOpt = opts.NewListOpts(nil) |
|
| 58 |
- flStorageOpt = opts.NewListOpts(nil) |
|
| 59 |
- flLabelsFile = opts.NewListOpts(nil) |
|
| 60 |
- flLoggingOpts = opts.NewListOpts(nil) |
|
| 61 |
- flPrivileged = cmd.Bool([]string{"-privileged"}, false, "Give extended privileges to this container")
|
|
| 62 |
- flPidMode = cmd.String([]string{"-pid"}, "", "PID namespace to use")
|
|
| 63 |
- flUTSMode = cmd.String([]string{"-uts"}, "", "UTS namespace to use")
|
|
| 64 |
- flUsernsMode = cmd.String([]string{"-userns"}, "", "User namespace to use")
|
|
| 65 |
- flPublishAll = cmd.Bool([]string{"P", "-publish-all"}, false, "Publish all exposed ports to random ports")
|
|
| 66 |
- flStdin = cmd.Bool([]string{"i", "-interactive"}, false, "Keep STDIN open even if not attached")
|
|
| 67 |
- flTty = cmd.Bool([]string{"t", "-tty"}, false, "Allocate a pseudo-TTY")
|
|
| 68 |
- flOomKillDisable = cmd.Bool([]string{"-oom-kill-disable"}, false, "Disable OOM Killer")
|
|
| 69 |
- flOomScoreAdj = cmd.Int([]string{"-oom-score-adj"}, 0, "Tune host's OOM preferences (-1000 to 1000)")
|
|
| 70 |
- flContainerIDFile = cmd.String([]string{"-cidfile"}, "", "Write the container ID to the file")
|
|
| 71 |
- flEntrypoint = cmd.String([]string{"-entrypoint"}, "", "Overwrite the default ENTRYPOINT of the image")
|
|
| 72 |
- flHostname = cmd.String([]string{"h", "-hostname"}, "", "Container host name")
|
|
| 73 |
- flMemoryString = cmd.String([]string{"m", "-memory"}, "", "Memory limit")
|
|
| 74 |
- flMemoryReservation = cmd.String([]string{"-memory-reservation"}, "", "Memory soft limit")
|
|
| 75 |
- flMemorySwap = cmd.String([]string{"-memory-swap"}, "", "Swap limit equal to memory plus swap: '-1' to enable unlimited swap")
|
|
| 76 |
- flKernelMemory = cmd.String([]string{"-kernel-memory"}, "", "Kernel memory limit")
|
|
| 77 |
- flUser = cmd.String([]string{"u", "-user"}, "", "Username or UID (format: <name|uid>[:<group|gid>])")
|
|
| 78 |
- flWorkingDir = cmd.String([]string{"w", "-workdir"}, "", "Working directory inside the container")
|
|
| 79 |
- flCPUShares = cmd.Int64([]string{"c", "-cpu-shares"}, 0, "CPU shares (relative weight)")
|
|
| 80 |
- flCPUPercent = cmd.Int64([]string{"-cpu-percent"}, 0, "CPU percent (Windows only)")
|
|
| 81 |
- flCPUPeriod = cmd.Int64([]string{"-cpu-period"}, 0, "Limit CPU CFS (Completely Fair Scheduler) period")
|
|
| 82 |
- flCPUQuota = cmd.Int64([]string{"-cpu-quota"}, 0, "Limit CPU CFS (Completely Fair Scheduler) quota")
|
|
| 83 |
- flCpusetCpus = cmd.String([]string{"-cpuset-cpus"}, "", "CPUs in which to allow execution (0-3, 0,1)")
|
|
| 84 |
- flCpusetMems = cmd.String([]string{"-cpuset-mems"}, "", "MEMs in which to allow execution (0-3, 0,1)")
|
|
| 85 |
- flBlkioWeight = cmd.Uint16([]string{"-blkio-weight"}, 0, "Block IO (relative weight), between 10 and 1000")
|
|
| 86 |
- flIOMaxBandwidth = cmd.String([]string{"-io-maxbandwidth"}, "", "Maximum IO bandwidth limit for the system drive (Windows only)")
|
|
| 87 |
- flIOMaxIOps = cmd.Uint64([]string{"-io-maxiops"}, 0, "Maximum IOps limit for the system drive (Windows only)")
|
|
| 88 |
- flSwappiness = cmd.Int64([]string{"-memory-swappiness"}, -1, "Tune container memory swappiness (0 to 100)")
|
|
| 89 |
- flNetMode = cmd.String([]string{"-net"}, "default", "Connect a container to a network")
|
|
| 90 |
- flMacAddress = cmd.String([]string{"-mac-address"}, "", "Container MAC address (e.g. 92:d0:c6:0a:29:33)")
|
|
| 91 |
- flIPv4Address = cmd.String([]string{"-ip"}, "", "Container IPv4 address (e.g. 172.30.100.104)")
|
|
| 92 |
- flIPv6Address = cmd.String([]string{"-ip6"}, "", "Container IPv6 address (e.g. 2001:db8::33)")
|
|
| 93 |
- flIpcMode = cmd.String([]string{"-ipc"}, "", "IPC namespace to use")
|
|
| 94 |
- flPidsLimit = cmd.Int64([]string{"-pids-limit"}, 0, "Tune container pids limit (set -1 for unlimited)")
|
|
| 95 |
- flRestartPolicy = cmd.String([]string{"-restart"}, "no", "Restart policy to apply when a container exits")
|
|
| 96 |
- flReadonlyRootfs = cmd.Bool([]string{"-read-only"}, false, "Mount the container's root filesystem as read only")
|
|
| 97 |
- flLoggingDriver = cmd.String([]string{"-log-driver"}, "", "Logging driver for container")
|
|
| 98 |
- flCgroupParent = cmd.String([]string{"-cgroup-parent"}, "", "Optional parent cgroup for the container")
|
|
| 99 |
- flVolumeDriver = cmd.String([]string{"-volume-driver"}, "", "Optional volume driver for the container")
|
|
| 100 |
- flStopSignal = cmd.String([]string{"-stop-signal"}, signal.DefaultStopSignal, fmt.Sprintf("Signal to stop a container, %v by default", signal.DefaultStopSignal))
|
|
| 101 |
- flIsolation = cmd.String([]string{"-isolation"}, "", "Container isolation technology")
|
|
| 102 |
- flShmSize = cmd.String([]string{"-shm-size"}, "", "Size of /dev/shm, default value is 64MB")
|
|
| 103 |
- // Healthcheck |
|
| 104 |
- flNoHealthcheck = cmd.Bool([]string{"-no-healthcheck"}, false, "Disable any container-specified HEALTHCHECK")
|
|
| 105 |
- flHealthCmd = cmd.String([]string{"-health-cmd"}, "", "Command to run to check health")
|
|
| 106 |
- flHealthInterval = cmd.Duration([]string{"-health-interval"}, 0, "Time between running the check")
|
|
| 107 |
- flHealthTimeout = cmd.Duration([]string{"-health-timeout"}, 0, "Maximum time to allow one check to run")
|
|
| 108 |
- flHealthRetries = cmd.Int([]string{"-health-retries"}, 0, "Consecutive failures needed to report unhealthy")
|
|
| 109 |
- ) |
|
| 110 |
- |
|
| 111 |
- cmd.Var(&flAttach, []string{"a", "-attach"}, "Attach to STDIN, STDOUT or STDERR")
|
|
| 112 |
- cmd.Var(&flBlkioWeightDevice, []string{"-blkio-weight-device"}, "Block IO weight (relative device weight)")
|
|
| 113 |
- cmd.Var(&flDeviceReadBps, []string{"-device-read-bps"}, "Limit read rate (bytes per second) from a device")
|
|
| 114 |
- cmd.Var(&flDeviceWriteBps, []string{"-device-write-bps"}, "Limit write rate (bytes per second) to a device")
|
|
| 115 |
- cmd.Var(&flDeviceReadIOps, []string{"-device-read-iops"}, "Limit read rate (IO per second) from a device")
|
|
| 116 |
- cmd.Var(&flDeviceWriteIOps, []string{"-device-write-iops"}, "Limit write rate (IO per second) to a device")
|
|
| 117 |
- cmd.Var(&flVolumes, []string{"v", "-volume"}, "Bind mount a volume")
|
|
| 118 |
- cmd.Var(&flTmpfs, []string{"-tmpfs"}, "Mount a tmpfs directory")
|
|
| 119 |
- cmd.Var(&flLinks, []string{"-link"}, "Add link to another container")
|
|
| 120 |
- cmd.Var(&flAliases, []string{"-net-alias"}, "Add network-scoped alias for the container")
|
|
| 121 |
- cmd.Var(&flDevices, []string{"-device"}, "Add a host device to the container")
|
|
| 122 |
- cmd.Var(&flLabels, []string{"l", "-label"}, "Set meta data on a container")
|
|
| 123 |
- cmd.Var(&flLabelsFile, []string{"-label-file"}, "Read in a line delimited file of labels")
|
|
| 124 |
- cmd.Var(&flEnv, []string{"e", "-env"}, "Set environment variables")
|
|
| 125 |
- cmd.Var(&flEnvFile, []string{"-env-file"}, "Read in a file of environment variables")
|
|
| 126 |
- cmd.Var(&flPublish, []string{"p", "-publish"}, "Publish a container's port(s) to the host")
|
|
| 127 |
- cmd.Var(&flExpose, []string{"-expose"}, "Expose a port or a range of ports")
|
|
| 128 |
- cmd.Var(&flDNS, []string{"-dns"}, "Set custom DNS servers")
|
|
| 129 |
- cmd.Var(&flDNSSearch, []string{"-dns-search"}, "Set custom DNS search domains")
|
|
| 130 |
- cmd.Var(&flDNSOptions, []string{"-dns-opt"}, "Set DNS options")
|
|
| 131 |
- cmd.Var(&flExtraHosts, []string{"-add-host"}, "Add a custom host-to-IP mapping (host:ip)")
|
|
| 132 |
- cmd.Var(&flVolumesFrom, []string{"-volumes-from"}, "Mount volumes from the specified container(s)")
|
|
| 133 |
- cmd.Var(&flCapAdd, []string{"-cap-add"}, "Add Linux capabilities")
|
|
| 134 |
- cmd.Var(&flCapDrop, []string{"-cap-drop"}, "Drop Linux capabilities")
|
|
| 135 |
- cmd.Var(&flGroupAdd, []string{"-group-add"}, "Add additional groups to join")
|
|
| 136 |
- cmd.Var(&flSecurityOpt, []string{"-security-opt"}, "Security Options")
|
|
| 137 |
- cmd.Var(&flStorageOpt, []string{"-storage-opt"}, "Set storage driver options per container")
|
|
| 138 |
- cmd.Var(flUlimits, []string{"-ulimit"}, "Ulimit options")
|
|
| 139 |
- cmd.Var(flSysctls, []string{"-sysctl"}, "Sysctl options")
|
|
| 140 |
- cmd.Var(&flLoggingOpts, []string{"-log-opt"}, "Log driver options")
|
|
| 141 |
- |
|
| 142 |
- cmd.Require(flag.Min, 1) |
|
| 143 |
- |
|
| 144 |
- if err := cmd.ParseFlags(args, true); err != nil {
|
|
| 145 |
- return nil, nil, nil, cmd, err |
|
| 146 |
- } |
|
| 231 |
+func Parse(flags *pflag.FlagSet, copts *ContainerOptions) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig, error) {
|
|
| 147 | 232 |
|
| 148 | 233 |
var ( |
| 149 |
- attachStdin = flAttach.Get("stdin")
|
|
| 150 |
- attachStdout = flAttach.Get("stdout")
|
|
| 151 |
- attachStderr = flAttach.Get("stderr")
|
|
| 234 |
+ attachStdin = copts.flAttach.Get("stdin")
|
|
| 235 |
+ attachStdout = copts.flAttach.Get("stdout")
|
|
| 236 |
+ attachStderr = copts.flAttach.Get("stderr")
|
|
| 152 | 237 |
) |
| 153 | 238 |
|
| 154 | 239 |
// Validate the input mac address |
| 155 |
- if *flMacAddress != "" {
|
|
| 156 |
- if _, err := ValidateMACAddress(*flMacAddress); err != nil {
|
|
| 157 |
- return nil, nil, nil, cmd, fmt.Errorf("%s is not a valid mac address", *flMacAddress)
|
|
| 240 |
+ if *copts.flMacAddress != "" {
|
|
| 241 |
+ if _, err := ValidateMACAddress(*copts.flMacAddress); err != nil {
|
|
| 242 |
+ return nil, nil, nil, fmt.Errorf("%s is not a valid mac address", *copts.flMacAddress)
|
|
| 158 | 243 |
} |
| 159 | 244 |
} |
| 160 |
- if *flStdin {
|
|
| 245 |
+ if *copts.flStdin {
|
|
| 161 | 246 |
attachStdin = true |
| 162 | 247 |
} |
| 163 | 248 |
// If -a is not set, attach to stdout and stderr |
| 164 |
- if flAttach.Len() == 0 {
|
|
| 249 |
+ if copts.flAttach.Len() == 0 {
|
|
| 165 | 250 |
attachStdout = true |
| 166 | 251 |
attachStderr = true |
| 167 | 252 |
} |
| ... | ... |
@@ -169,83 +254,83 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host |
| 169 | 169 |
var err error |
| 170 | 170 |
|
| 171 | 171 |
var flMemory int64 |
| 172 |
- if *flMemoryString != "" {
|
|
| 173 |
- flMemory, err = units.RAMInBytes(*flMemoryString) |
|
| 172 |
+ if *copts.flMemoryString != "" {
|
|
| 173 |
+ flMemory, err = units.RAMInBytes(*copts.flMemoryString) |
|
| 174 | 174 |
if err != nil {
|
| 175 |
- return nil, nil, nil, cmd, err |
|
| 175 |
+ return nil, nil, nil, err |
|
| 176 | 176 |
} |
| 177 | 177 |
} |
| 178 | 178 |
|
| 179 | 179 |
var MemoryReservation int64 |
| 180 |
- if *flMemoryReservation != "" {
|
|
| 181 |
- MemoryReservation, err = units.RAMInBytes(*flMemoryReservation) |
|
| 180 |
+ if *copts.flMemoryReservation != "" {
|
|
| 181 |
+ MemoryReservation, err = units.RAMInBytes(*copts.flMemoryReservation) |
|
| 182 | 182 |
if err != nil {
|
| 183 |
- return nil, nil, nil, cmd, err |
|
| 183 |
+ return nil, nil, nil, err |
|
| 184 | 184 |
} |
| 185 | 185 |
} |
| 186 | 186 |
|
| 187 | 187 |
var memorySwap int64 |
| 188 |
- if *flMemorySwap != "" {
|
|
| 189 |
- if *flMemorySwap == "-1" {
|
|
| 188 |
+ if *copts.flMemorySwap != "" {
|
|
| 189 |
+ if *copts.flMemorySwap == "-1" {
|
|
| 190 | 190 |
memorySwap = -1 |
| 191 | 191 |
} else {
|
| 192 |
- memorySwap, err = units.RAMInBytes(*flMemorySwap) |
|
| 192 |
+ memorySwap, err = units.RAMInBytes(*copts.flMemorySwap) |
|
| 193 | 193 |
if err != nil {
|
| 194 |
- return nil, nil, nil, cmd, err |
|
| 194 |
+ return nil, nil, nil, err |
|
| 195 | 195 |
} |
| 196 | 196 |
} |
| 197 | 197 |
} |
| 198 | 198 |
|
| 199 | 199 |
var KernelMemory int64 |
| 200 |
- if *flKernelMemory != "" {
|
|
| 201 |
- KernelMemory, err = units.RAMInBytes(*flKernelMemory) |
|
| 200 |
+ if *copts.flKernelMemory != "" {
|
|
| 201 |
+ KernelMemory, err = units.RAMInBytes(*copts.flKernelMemory) |
|
| 202 | 202 |
if err != nil {
|
| 203 |
- return nil, nil, nil, cmd, err |
|
| 203 |
+ return nil, nil, nil, err |
|
| 204 | 204 |
} |
| 205 | 205 |
} |
| 206 | 206 |
|
| 207 |
- swappiness := *flSwappiness |
|
| 207 |
+ swappiness := *copts.flSwappiness |
|
| 208 | 208 |
if swappiness != -1 && (swappiness < 0 || swappiness > 100) {
|
| 209 |
- return nil, nil, nil, cmd, fmt.Errorf("invalid value: %d. Valid memory swappiness range is 0-100", swappiness)
|
|
| 209 |
+ return nil, nil, nil, fmt.Errorf("invalid value: %d. Valid memory swappiness range is 0-100", swappiness)
|
|
| 210 | 210 |
} |
| 211 | 211 |
|
| 212 | 212 |
var shmSize int64 |
| 213 |
- if *flShmSize != "" {
|
|
| 214 |
- shmSize, err = units.RAMInBytes(*flShmSize) |
|
| 213 |
+ if *copts.flShmSize != "" {
|
|
| 214 |
+ shmSize, err = units.RAMInBytes(*copts.flShmSize) |
|
| 215 | 215 |
if err != nil {
|
| 216 |
- return nil, nil, nil, cmd, err |
|
| 216 |
+ return nil, nil, nil, err |
|
| 217 | 217 |
} |
| 218 | 218 |
} |
| 219 | 219 |
|
| 220 | 220 |
// TODO FIXME units.RAMInBytes should have a uint64 version |
| 221 | 221 |
var maxIOBandwidth int64 |
| 222 |
- if *flIOMaxBandwidth != "" {
|
|
| 223 |
- maxIOBandwidth, err = units.RAMInBytes(*flIOMaxBandwidth) |
|
| 222 |
+ if *copts.flIOMaxBandwidth != "" {
|
|
| 223 |
+ maxIOBandwidth, err = units.RAMInBytes(*copts.flIOMaxBandwidth) |
|
| 224 | 224 |
if err != nil {
|
| 225 |
- return nil, nil, nil, cmd, err |
|
| 225 |
+ return nil, nil, nil, err |
|
| 226 | 226 |
} |
| 227 | 227 |
if maxIOBandwidth < 0 {
|
| 228 |
- return nil, nil, nil, cmd, fmt.Errorf("invalid value: %s. Maximum IO Bandwidth must be positive", *flIOMaxBandwidth)
|
|
| 228 |
+ return nil, nil, nil, fmt.Errorf("invalid value: %s. Maximum IO Bandwidth must be positive", *copts.flIOMaxBandwidth)
|
|
| 229 | 229 |
} |
| 230 | 230 |
} |
| 231 | 231 |
|
| 232 | 232 |
var binds []string |
| 233 | 233 |
// add any bind targets to the list of container volumes |
| 234 |
- for bind := range flVolumes.GetMap() {
|
|
| 234 |
+ for bind := range copts.flVolumes.GetMap() {
|
|
| 235 | 235 |
if arr := volumeSplitN(bind, 2); len(arr) > 1 {
|
| 236 |
- // after creating the bind mount we want to delete it from the flVolumes values because |
|
| 236 |
+ // after creating the bind mount we want to delete it from the copts.flVolumes values because |
|
| 237 | 237 |
// we do not want bind mounts being committed to image configs |
| 238 | 238 |
binds = append(binds, bind) |
| 239 |
- flVolumes.Delete(bind) |
|
| 239 |
+ copts.flVolumes.Delete(bind) |
|
| 240 | 240 |
} |
| 241 | 241 |
} |
| 242 | 242 |
|
| 243 | 243 |
// Can't evaluate options passed into --tmpfs until we actually mount |
| 244 | 244 |
tmpfs := make(map[string]string) |
| 245 |
- for _, t := range flTmpfs.GetAll() {
|
|
| 245 |
+ for _, t := range copts.flTmpfs.GetAll() {
|
|
| 246 | 246 |
if arr := strings.SplitN(t, ":", 2); len(arr) > 1 {
|
| 247 | 247 |
if _, _, err := mount.ParseTmpfsOptions(arr[1]); err != nil {
|
| 248 |
- return nil, nil, nil, cmd, err |
|
| 248 |
+ return nil, nil, nil, err |
|
| 249 | 249 |
} |
| 250 | 250 |
tmpfs[arr[0]] = arr[1] |
| 251 | 251 |
} else {
|
| ... | ... |
@@ -254,27 +339,25 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host |
| 254 | 254 |
} |
| 255 | 255 |
|
| 256 | 256 |
var ( |
| 257 |
- parsedArgs = cmd.Args() |
|
| 258 | 257 |
runCmd strslice.StrSlice |
| 259 | 258 |
entrypoint strslice.StrSlice |
| 260 |
- image = cmd.Arg(0) |
|
| 261 | 259 |
) |
| 262 |
- if len(parsedArgs) > 1 {
|
|
| 263 |
- runCmd = strslice.StrSlice(parsedArgs[1:]) |
|
| 260 |
+ if len(copts.Args) > 0 {
|
|
| 261 |
+ runCmd = strslice.StrSlice(copts.Args) |
|
| 264 | 262 |
} |
| 265 |
- if *flEntrypoint != "" {
|
|
| 266 |
- entrypoint = strslice.StrSlice{*flEntrypoint}
|
|
| 263 |
+ if *copts.flEntrypoint != "" {
|
|
| 264 |
+ entrypoint = strslice.StrSlice{*copts.flEntrypoint}
|
|
| 267 | 265 |
} |
| 268 | 266 |
|
| 269 |
- ports, portBindings, err := nat.ParsePortSpecs(flPublish.GetAll()) |
|
| 267 |
+ ports, portBindings, err := nat.ParsePortSpecs(copts.flPublish.GetAll()) |
|
| 270 | 268 |
if err != nil {
|
| 271 |
- return nil, nil, nil, cmd, err |
|
| 269 |
+ return nil, nil, nil, err |
|
| 272 | 270 |
} |
| 273 | 271 |
|
| 274 | 272 |
// Merge in exposed ports to the map of published ports |
| 275 |
- for _, e := range flExpose.GetAll() {
|
|
| 273 |
+ for _, e := range copts.flExpose.GetAll() {
|
|
| 276 | 274 |
if strings.Contains(e, ":") {
|
| 277 |
- return nil, nil, nil, cmd, fmt.Errorf("invalid port format for --expose: %s", e)
|
|
| 275 |
+ return nil, nil, nil, fmt.Errorf("invalid port format for --expose: %s", e)
|
|
| 278 | 276 |
} |
| 279 | 277 |
//support two formats for expose, original format <portnum>/[<proto>] or <startport-endport>/[<proto>] |
| 280 | 278 |
proto, port := nat.SplitProtoPort(e) |
| ... | ... |
@@ -282,12 +365,12 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host |
| 282 | 282 |
//if expose a port, the start and end port are the same |
| 283 | 283 |
start, end, err := nat.ParsePortRange(port) |
| 284 | 284 |
if err != nil {
|
| 285 |
- return nil, nil, nil, cmd, fmt.Errorf("invalid range format for --expose: %s, error: %s", e, err)
|
|
| 285 |
+ return nil, nil, nil, fmt.Errorf("invalid range format for --expose: %s, error: %s", e, err)
|
|
| 286 | 286 |
} |
| 287 | 287 |
for i := start; i <= end; i++ {
|
| 288 | 288 |
p, err := nat.NewPort(proto, strconv.FormatUint(i, 10)) |
| 289 | 289 |
if err != nil {
|
| 290 |
- return nil, nil, nil, cmd, err |
|
| 290 |
+ return nil, nil, nil, err |
|
| 291 | 291 |
} |
| 292 | 292 |
if _, exists := ports[p]; !exists {
|
| 293 | 293 |
ports[p] = struct{}{}
|
| ... | ... |
@@ -297,64 +380,64 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host |
| 297 | 297 |
|
| 298 | 298 |
// parse device mappings |
| 299 | 299 |
deviceMappings := []container.DeviceMapping{}
|
| 300 |
- for _, device := range flDevices.GetAll() {
|
|
| 300 |
+ for _, device := range copts.flDevices.GetAll() {
|
|
| 301 | 301 |
deviceMapping, err := ParseDevice(device) |
| 302 | 302 |
if err != nil {
|
| 303 |
- return nil, nil, nil, cmd, err |
|
| 303 |
+ return nil, nil, nil, err |
|
| 304 | 304 |
} |
| 305 | 305 |
deviceMappings = append(deviceMappings, deviceMapping) |
| 306 | 306 |
} |
| 307 | 307 |
|
| 308 | 308 |
// collect all the environment variables for the container |
| 309 |
- envVariables, err := readKVStrings(flEnvFile.GetAll(), flEnv.GetAll()) |
|
| 309 |
+ envVariables, err := readKVStrings(copts.flEnvFile.GetAll(), copts.flEnv.GetAll()) |
|
| 310 | 310 |
if err != nil {
|
| 311 |
- return nil, nil, nil, cmd, err |
|
| 311 |
+ return nil, nil, nil, err |
|
| 312 | 312 |
} |
| 313 | 313 |
|
| 314 | 314 |
// collect all the labels for the container |
| 315 |
- labels, err := readKVStrings(flLabelsFile.GetAll(), flLabels.GetAll()) |
|
| 315 |
+ labels, err := readKVStrings(copts.flLabelsFile.GetAll(), copts.flLabels.GetAll()) |
|
| 316 | 316 |
if err != nil {
|
| 317 |
- return nil, nil, nil, cmd, err |
|
| 317 |
+ return nil, nil, nil, err |
|
| 318 | 318 |
} |
| 319 | 319 |
|
| 320 |
- ipcMode := container.IpcMode(*flIpcMode) |
|
| 320 |
+ ipcMode := container.IpcMode(*copts.flIpcMode) |
|
| 321 | 321 |
if !ipcMode.Valid() {
|
| 322 |
- return nil, nil, nil, cmd, fmt.Errorf("--ipc: invalid IPC mode")
|
|
| 322 |
+ return nil, nil, nil, fmt.Errorf("--ipc: invalid IPC mode")
|
|
| 323 | 323 |
} |
| 324 | 324 |
|
| 325 |
- pidMode := container.PidMode(*flPidMode) |
|
| 325 |
+ pidMode := container.PidMode(*copts.flPidMode) |
|
| 326 | 326 |
if !pidMode.Valid() {
|
| 327 |
- return nil, nil, nil, cmd, fmt.Errorf("--pid: invalid PID mode")
|
|
| 327 |
+ return nil, nil, nil, fmt.Errorf("--pid: invalid PID mode")
|
|
| 328 | 328 |
} |
| 329 | 329 |
|
| 330 |
- utsMode := container.UTSMode(*flUTSMode) |
|
| 330 |
+ utsMode := container.UTSMode(*copts.flUTSMode) |
|
| 331 | 331 |
if !utsMode.Valid() {
|
| 332 |
- return nil, nil, nil, cmd, fmt.Errorf("--uts: invalid UTS mode")
|
|
| 332 |
+ return nil, nil, nil, fmt.Errorf("--uts: invalid UTS mode")
|
|
| 333 | 333 |
} |
| 334 | 334 |
|
| 335 |
- usernsMode := container.UsernsMode(*flUsernsMode) |
|
| 335 |
+ usernsMode := container.UsernsMode(*copts.flUsernsMode) |
|
| 336 | 336 |
if !usernsMode.Valid() {
|
| 337 |
- return nil, nil, nil, cmd, fmt.Errorf("--userns: invalid USER mode")
|
|
| 337 |
+ return nil, nil, nil, fmt.Errorf("--userns: invalid USER mode")
|
|
| 338 | 338 |
} |
| 339 | 339 |
|
| 340 |
- restartPolicy, err := ParseRestartPolicy(*flRestartPolicy) |
|
| 340 |
+ restartPolicy, err := ParseRestartPolicy(*copts.flRestartPolicy) |
|
| 341 | 341 |
if err != nil {
|
| 342 |
- return nil, nil, nil, cmd, err |
|
| 342 |
+ return nil, nil, nil, err |
|
| 343 | 343 |
} |
| 344 | 344 |
|
| 345 |
- loggingOpts, err := parseLoggingOpts(*flLoggingDriver, flLoggingOpts.GetAll()) |
|
| 345 |
+ loggingOpts, err := parseLoggingOpts(*copts.flLoggingDriver, copts.flLoggingOpts.GetAll()) |
|
| 346 | 346 |
if err != nil {
|
| 347 |
- return nil, nil, nil, cmd, err |
|
| 347 |
+ return nil, nil, nil, err |
|
| 348 | 348 |
} |
| 349 | 349 |
|
| 350 |
- securityOpts, err := parseSecurityOpts(flSecurityOpt.GetAll()) |
|
| 350 |
+ securityOpts, err := parseSecurityOpts(copts.flSecurityOpt.GetAll()) |
|
| 351 | 351 |
if err != nil {
|
| 352 |
- return nil, nil, nil, cmd, err |
|
| 352 |
+ return nil, nil, nil, err |
|
| 353 | 353 |
} |
| 354 | 354 |
|
| 355 |
- storageOpts, err := parseStorageOpts(flStorageOpt.GetAll()) |
|
| 355 |
+ storageOpts, err := parseStorageOpts(copts.flStorageOpt.GetAll()) |
|
| 356 | 356 |
if err != nil {
|
| 357 |
- return nil, nil, nil, cmd, err |
|
| 357 |
+ return nil, nil, nil, err |
|
| 358 | 358 |
} |
| 359 | 359 |
|
| 360 | 360 |
// Healthcheck |
| ... | ... |
@@ -391,96 +474,96 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host |
| 391 | 391 |
} |
| 392 | 392 |
|
| 393 | 393 |
resources := container.Resources{
|
| 394 |
- CgroupParent: *flCgroupParent, |
|
| 394 |
+ CgroupParent: *copts.flCgroupParent, |
|
| 395 | 395 |
Memory: flMemory, |
| 396 | 396 |
MemoryReservation: MemoryReservation, |
| 397 | 397 |
MemorySwap: memorySwap, |
| 398 |
- MemorySwappiness: flSwappiness, |
|
| 398 |
+ MemorySwappiness: copts.flSwappiness, |
|
| 399 | 399 |
KernelMemory: KernelMemory, |
| 400 |
- OomKillDisable: flOomKillDisable, |
|
| 401 |
- CPUPercent: *flCPUPercent, |
|
| 402 |
- CPUShares: *flCPUShares, |
|
| 403 |
- CPUPeriod: *flCPUPeriod, |
|
| 404 |
- CpusetCpus: *flCpusetCpus, |
|
| 405 |
- CpusetMems: *flCpusetMems, |
|
| 406 |
- CPUQuota: *flCPUQuota, |
|
| 407 |
- PidsLimit: *flPidsLimit, |
|
| 408 |
- BlkioWeight: *flBlkioWeight, |
|
| 409 |
- BlkioWeightDevice: flBlkioWeightDevice.GetList(), |
|
| 410 |
- BlkioDeviceReadBps: flDeviceReadBps.GetList(), |
|
| 411 |
- BlkioDeviceWriteBps: flDeviceWriteBps.GetList(), |
|
| 412 |
- BlkioDeviceReadIOps: flDeviceReadIOps.GetList(), |
|
| 413 |
- BlkioDeviceWriteIOps: flDeviceWriteIOps.GetList(), |
|
| 414 |
- IOMaximumIOps: *flIOMaxIOps, |
|
| 400 |
+ OomKillDisable: copts.flOomKillDisable, |
|
| 401 |
+ CPUPercent: *copts.flCPUPercent, |
|
| 402 |
+ CPUShares: *copts.flCPUShares, |
|
| 403 |
+ CPUPeriod: *copts.flCPUPeriod, |
|
| 404 |
+ CpusetCpus: *copts.flCpusetCpus, |
|
| 405 |
+ CpusetMems: *copts.flCpusetMems, |
|
| 406 |
+ CPUQuota: *copts.flCPUQuota, |
|
| 407 |
+ PidsLimit: *copts.flPidsLimit, |
|
| 408 |
+ BlkioWeight: *copts.flBlkioWeight, |
|
| 409 |
+ BlkioWeightDevice: copts.flBlkioWeightDevice.GetList(), |
|
| 410 |
+ BlkioDeviceReadBps: copts.flDeviceReadBps.GetList(), |
|
| 411 |
+ BlkioDeviceWriteBps: copts.flDeviceWriteBps.GetList(), |
|
| 412 |
+ BlkioDeviceReadIOps: copts.flDeviceReadIOps.GetList(), |
|
| 413 |
+ BlkioDeviceWriteIOps: copts.flDeviceWriteIOps.GetList(), |
|
| 414 |
+ IOMaximumIOps: *copts.flIOMaxIOps, |
|
| 415 | 415 |
IOMaximumBandwidth: uint64(maxIOBandwidth), |
| 416 |
- Ulimits: flUlimits.GetList(), |
|
| 416 |
+ Ulimits: copts.flUlimits.GetList(), |
|
| 417 | 417 |
Devices: deviceMappings, |
| 418 | 418 |
} |
| 419 | 419 |
|
| 420 | 420 |
config := &container.Config{
|
| 421 |
- Hostname: *flHostname, |
|
| 421 |
+ Hostname: *copts.flHostname, |
|
| 422 | 422 |
ExposedPorts: ports, |
| 423 |
- User: *flUser, |
|
| 424 |
- Tty: *flTty, |
|
| 423 |
+ User: *copts.flUser, |
|
| 424 |
+ Tty: *copts.flTty, |
|
| 425 | 425 |
// TODO: deprecated, it comes from -n, --networking |
| 426 | 426 |
// it's still needed internally to set the network to disabled |
| 427 | 427 |
// if e.g. bridge is none in daemon opts, and in inspect |
| 428 | 428 |
NetworkDisabled: false, |
| 429 |
- OpenStdin: *flStdin, |
|
| 429 |
+ OpenStdin: *copts.flStdin, |
|
| 430 | 430 |
AttachStdin: attachStdin, |
| 431 | 431 |
AttachStdout: attachStdout, |
| 432 | 432 |
AttachStderr: attachStderr, |
| 433 | 433 |
Env: envVariables, |
| 434 | 434 |
Cmd: runCmd, |
| 435 |
- Image: image, |
|
| 436 |
- Volumes: flVolumes.GetMap(), |
|
| 437 |
- MacAddress: *flMacAddress, |
|
| 435 |
+ Image: copts.Image, |
|
| 436 |
+ Volumes: copts.flVolumes.GetMap(), |
|
| 437 |
+ MacAddress: *copts.flMacAddress, |
|
| 438 | 438 |
Entrypoint: entrypoint, |
| 439 |
- WorkingDir: *flWorkingDir, |
|
| 439 |
+ WorkingDir: *copts.flWorkingDir, |
|
| 440 | 440 |
Labels: ConvertKVStringsToMap(labels), |
| 441 | 441 |
Healthcheck: healthConfig, |
| 442 | 442 |
} |
| 443 |
- if cmd.IsSet("-stop-signal") {
|
|
| 444 |
- config.StopSignal = *flStopSignal |
|
| 443 |
+ if flags.Changed("stop-signal") {
|
|
| 444 |
+ config.StopSignal = *copts.flStopSignal |
|
| 445 | 445 |
} |
| 446 | 446 |
|
| 447 | 447 |
hostConfig := &container.HostConfig{
|
| 448 | 448 |
Binds: binds, |
| 449 |
- ContainerIDFile: *flContainerIDFile, |
|
| 450 |
- OomScoreAdj: *flOomScoreAdj, |
|
| 451 |
- Privileged: *flPrivileged, |
|
| 449 |
+ ContainerIDFile: *copts.flContainerIDFile, |
|
| 450 |
+ OomScoreAdj: *copts.flOomScoreAdj, |
|
| 451 |
+ Privileged: *copts.flPrivileged, |
|
| 452 | 452 |
PortBindings: portBindings, |
| 453 |
- Links: flLinks.GetAll(), |
|
| 454 |
- PublishAllPorts: *flPublishAll, |
|
| 453 |
+ Links: copts.flLinks.GetAll(), |
|
| 454 |
+ PublishAllPorts: *copts.flPublishAll, |
|
| 455 | 455 |
// Make sure the dns fields are never nil. |
| 456 | 456 |
// New containers don't ever have those fields nil, |
| 457 | 457 |
// but pre created containers can still have those nil values. |
| 458 | 458 |
// See https://github.com/docker/docker/pull/17779 |
| 459 | 459 |
// for a more detailed explanation on why we don't want that. |
| 460 |
- DNS: flDNS.GetAllOrEmpty(), |
|
| 461 |
- DNSSearch: flDNSSearch.GetAllOrEmpty(), |
|
| 462 |
- DNSOptions: flDNSOptions.GetAllOrEmpty(), |
|
| 463 |
- ExtraHosts: flExtraHosts.GetAll(), |
|
| 464 |
- VolumesFrom: flVolumesFrom.GetAll(), |
|
| 465 |
- NetworkMode: container.NetworkMode(*flNetMode), |
|
| 460 |
+ DNS: copts.flDNS.GetAllOrEmpty(), |
|
| 461 |
+ DNSSearch: copts.flDNSSearch.GetAllOrEmpty(), |
|
| 462 |
+ DNSOptions: copts.flDNSOptions.GetAllOrEmpty(), |
|
| 463 |
+ ExtraHosts: copts.flExtraHosts.GetAll(), |
|
| 464 |
+ VolumesFrom: copts.flVolumesFrom.GetAll(), |
|
| 465 |
+ NetworkMode: container.NetworkMode(*copts.flNetMode), |
|
| 466 | 466 |
IpcMode: ipcMode, |
| 467 | 467 |
PidMode: pidMode, |
| 468 | 468 |
UTSMode: utsMode, |
| 469 | 469 |
UsernsMode: usernsMode, |
| 470 |
- CapAdd: strslice.StrSlice(flCapAdd.GetAll()), |
|
| 471 |
- CapDrop: strslice.StrSlice(flCapDrop.GetAll()), |
|
| 472 |
- GroupAdd: flGroupAdd.GetAll(), |
|
| 470 |
+ CapAdd: strslice.StrSlice(copts.flCapAdd.GetAll()), |
|
| 471 |
+ CapDrop: strslice.StrSlice(copts.flCapDrop.GetAll()), |
|
| 472 |
+ GroupAdd: copts.flGroupAdd.GetAll(), |
|
| 473 | 473 |
RestartPolicy: restartPolicy, |
| 474 | 474 |
SecurityOpt: securityOpts, |
| 475 | 475 |
StorageOpt: storageOpts, |
| 476 |
- ReadonlyRootfs: *flReadonlyRootfs, |
|
| 477 |
- LogConfig: container.LogConfig{Type: *flLoggingDriver, Config: loggingOpts},
|
|
| 478 |
- VolumeDriver: *flVolumeDriver, |
|
| 479 |
- Isolation: container.Isolation(*flIsolation), |
|
| 476 |
+ ReadonlyRootfs: *copts.flReadonlyRootfs, |
|
| 477 |
+ LogConfig: container.LogConfig{Type: *copts.flLoggingDriver, Config: loggingOpts},
|
|
| 478 |
+ VolumeDriver: *copts.flVolumeDriver, |
|
| 479 |
+ Isolation: container.Isolation(*copts.flIsolation), |
|
| 480 | 480 |
ShmSize: shmSize, |
| 481 | 481 |
Resources: resources, |
| 482 | 482 |
Tmpfs: tmpfs, |
| 483 |
- Sysctls: flSysctls.GetAll(), |
|
| 483 |
+ Sysctls: copts.flSysctls.GetAll(), |
|
| 484 | 484 |
} |
| 485 | 485 |
|
| 486 | 486 |
// When allocating stdin in attached mode, close stdin at client disconnect |
| ... | ... |
@@ -492,11 +575,11 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host |
| 492 | 492 |
EndpointsConfig: make(map[string]*networktypes.EndpointSettings), |
| 493 | 493 |
} |
| 494 | 494 |
|
| 495 |
- if *flIPv4Address != "" || *flIPv6Address != "" {
|
|
| 495 |
+ if *copts.flIPv4Address != "" || *copts.flIPv6Address != "" {
|
|
| 496 | 496 |
networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = &networktypes.EndpointSettings{
|
| 497 | 497 |
IPAMConfig: &networktypes.EndpointIPAMConfig{
|
| 498 |
- IPv4Address: *flIPv4Address, |
|
| 499 |
- IPv6Address: *flIPv6Address, |
|
| 498 |
+ IPv4Address: *copts.flIPv4Address, |
|
| 499 |
+ IPv6Address: *copts.flIPv6Address, |
|
| 500 | 500 |
}, |
| 501 | 501 |
} |
| 502 | 502 |
} |
| ... | ... |
@@ -511,17 +594,17 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host |
| 511 | 511 |
networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = epConfig |
| 512 | 512 |
} |
| 513 | 513 |
|
| 514 |
- if flAliases.Len() > 0 {
|
|
| 514 |
+ if copts.flAliases.Len() > 0 {
|
|
| 515 | 515 |
epConfig := networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] |
| 516 | 516 |
if epConfig == nil {
|
| 517 | 517 |
epConfig = &networktypes.EndpointSettings{}
|
| 518 | 518 |
} |
| 519 |
- epConfig.Aliases = make([]string, flAliases.Len()) |
|
| 520 |
- copy(epConfig.Aliases, flAliases.GetAll()) |
|
| 519 |
+ epConfig.Aliases = make([]string, copts.flAliases.Len()) |
|
| 520 |
+ copy(epConfig.Aliases, copts.flAliases.GetAll()) |
|
| 521 | 521 |
networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = epConfig |
| 522 | 522 |
} |
| 523 | 523 |
|
| 524 |
- return config, hostConfig, networkingConfig, cmd, nil |
|
| 524 |
+ return config, hostConfig, networkingConfig, nil |
|
| 525 | 525 |
} |
| 526 | 526 |
|
| 527 | 527 |
// reads a file of line terminated key=value pairs, and overrides any keys |
| ... | ... |
@@ -11,22 +11,27 @@ import ( |
| 11 | 11 |
"testing" |
| 12 | 12 |
"time" |
| 13 | 13 |
|
| 14 |
- flag "github.com/docker/docker/pkg/mflag" |
|
| 15 | 14 |
"github.com/docker/docker/runconfig" |
| 16 | 15 |
"github.com/docker/engine-api/types/container" |
| 17 | 16 |
networktypes "github.com/docker/engine-api/types/network" |
| 18 | 17 |
"github.com/docker/go-connections/nat" |
| 18 |
+ "github.com/spf13/pflag" |
|
| 19 | 19 |
) |
| 20 | 20 |
|
| 21 |
-func parseRun(args []string) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig, *flag.FlagSet, error) {
|
|
| 22 |
- cmd := flag.NewFlagSet("run", flag.ContinueOnError)
|
|
| 23 |
- cmd.SetOutput(ioutil.Discard) |
|
| 24 |
- cmd.Usage = nil |
|
| 25 |
- return Parse(cmd, args) |
|
| 21 |
+// TODO: drop FlagSet from return value |
|
| 22 |
+func parseRun(args []string) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig, error) {
|
|
| 23 |
+ flags := pflag.NewFlagSet("run", pflag.ContinueOnError)
|
|
| 24 |
+ flags.SetOutput(ioutil.Discard) |
|
| 25 |
+ flags.Usage = nil |
|
| 26 |
+ copts := AddFlags(flags) |
|
| 27 |
+ if err := flags.Parse(args); err != nil {
|
|
| 28 |
+ return nil, nil, nil, err |
|
| 29 |
+ } |
|
| 30 |
+ return Parse(flags, copts) |
|
| 26 | 31 |
} |
| 27 | 32 |
|
| 28 | 33 |
func parse(t *testing.T, args string) (*container.Config, *container.HostConfig, error) {
|
| 29 |
- config, hostConfig, _, _, err := parseRun(strings.Split(args+" ubuntu bash", " ")) |
|
| 34 |
+ config, hostConfig, _, err := parseRun(strings.Split(args+" ubuntu bash", " ")) |
|
| 30 | 35 |
return config, hostConfig, err |
| 31 | 36 |
} |
| 32 | 37 |
|
| ... | ... |
@@ -351,7 +356,7 @@ func setupPlatformVolume(u []string, w []string) ([]string, string) {
|
| 351 | 351 |
func TestParseWithMacAddress(t *testing.T) {
|
| 352 | 352 |
invalidMacAddress := "--mac-address=invalidMacAddress" |
| 353 | 353 |
validMacAddress := "--mac-address=92:d0:c6:0a:29:33" |
| 354 |
- if _, _, _, _, err := parseRun([]string{invalidMacAddress, "img", "cmd"}); err != nil && err.Error() != "invalidMacAddress is not a valid mac address" {
|
|
| 354 |
+ if _, _, _, err := parseRun([]string{invalidMacAddress, "img", "cmd"}); err != nil && err.Error() != "invalidMacAddress is not a valid mac address" {
|
|
| 355 | 355 |
t.Fatalf("Expected an error with %v mac-address, got %v", invalidMacAddress, err)
|
| 356 | 356 |
} |
| 357 | 357 |
if config, _ := mustParse(t, validMacAddress); config.MacAddress != "92:d0:c6:0a:29:33" {
|
| ... | ... |
@@ -362,7 +367,7 @@ func TestParseWithMacAddress(t *testing.T) {
|
| 362 | 362 |
func TestParseWithMemory(t *testing.T) {
|
| 363 | 363 |
invalidMemory := "--memory=invalid" |
| 364 | 364 |
validMemory := "--memory=1G" |
| 365 |
- if _, _, _, _, err := parseRun([]string{invalidMemory, "img", "cmd"}); err != nil && err.Error() != "invalid size: 'invalid'" {
|
|
| 365 |
+ if _, _, _, err := parseRun([]string{invalidMemory, "img", "cmd"}); err != nil && err.Error() != "invalid size: 'invalid'" {
|
|
| 366 | 366 |
t.Fatalf("Expected an error with '%v' Memory, got '%v'", invalidMemory, err)
|
| 367 | 367 |
} |
| 368 | 368 |
if _, hostconfig := mustParse(t, validMemory); hostconfig.Memory != 1073741824 {
|
| ... | ... |
@@ -374,7 +379,7 @@ func TestParseWithMemorySwap(t *testing.T) {
|
| 374 | 374 |
invalidMemory := "--memory-swap=invalid" |
| 375 | 375 |
validMemory := "--memory-swap=1G" |
| 376 | 376 |
anotherValidMemory := "--memory-swap=-1" |
| 377 |
- if _, _, _, _, err := parseRun([]string{invalidMemory, "img", "cmd"}); err == nil || err.Error() != "invalid size: 'invalid'" {
|
|
| 377 |
+ if _, _, _, err := parseRun([]string{invalidMemory, "img", "cmd"}); err == nil || err.Error() != "invalid size: 'invalid'" {
|
|
| 378 | 378 |
t.Fatalf("Expected an error with '%v' MemorySwap, got '%v'", invalidMemory, err)
|
| 379 | 379 |
} |
| 380 | 380 |
if _, hostconfig := mustParse(t, validMemory); hostconfig.MemorySwap != 1073741824 {
|
| ... | ... |
@@ -427,12 +432,12 @@ func TestParseWithExpose(t *testing.T) {
|
| 427 | 427 |
"8080-8082/tcp": {"8080/tcp", "8081/tcp", "8082/tcp"},
|
| 428 | 428 |
} |
| 429 | 429 |
for expose, expectedError := range invalids {
|
| 430 |
- if _, _, _, _, err := parseRun([]string{fmt.Sprintf("--expose=%v", expose), "img", "cmd"}); err == nil || err.Error() != expectedError {
|
|
| 430 |
+ if _, _, _, err := parseRun([]string{fmt.Sprintf("--expose=%v", expose), "img", "cmd"}); err == nil || err.Error() != expectedError {
|
|
| 431 | 431 |
t.Fatalf("Expected error '%v' with '--expose=%v', got '%v'", expectedError, expose, err)
|
| 432 | 432 |
} |
| 433 | 433 |
} |
| 434 | 434 |
for expose, exposedPorts := range valids {
|
| 435 |
- config, _, _, _, err := parseRun([]string{fmt.Sprintf("--expose=%v", expose), "img", "cmd"})
|
|
| 435 |
+ config, _, _, err := parseRun([]string{fmt.Sprintf("--expose=%v", expose), "img", "cmd"})
|
|
| 436 | 436 |
if err != nil {
|
| 437 | 437 |
t.Fatal(err) |
| 438 | 438 |
} |
| ... | ... |
@@ -446,7 +451,7 @@ func TestParseWithExpose(t *testing.T) {
|
| 446 | 446 |
} |
| 447 | 447 |
} |
| 448 | 448 |
// Merge with actual published port |
| 449 |
- config, _, _, _, err := parseRun([]string{"--publish=80", "--expose=80-81/tcp", "img", "cmd"})
|
|
| 449 |
+ config, _, _, err := parseRun([]string{"--publish=80", "--expose=80-81/tcp", "img", "cmd"})
|
|
| 450 | 450 |
if err != nil {
|
| 451 | 451 |
t.Fatal(err) |
| 452 | 452 |
} |
| ... | ... |
@@ -485,7 +490,7 @@ func TestParseDevice(t *testing.T) {
|
| 485 | 485 |
}, |
| 486 | 486 |
} |
| 487 | 487 |
for device, deviceMapping := range valids {
|
| 488 |
- _, hostconfig, _, _, err := parseRun([]string{fmt.Sprintf("--device=%v", device), "img", "cmd"})
|
|
| 488 |
+ _, hostconfig, _, err := parseRun([]string{fmt.Sprintf("--device=%v", device), "img", "cmd"})
|
|
| 489 | 489 |
if err != nil {
|
| 490 | 490 |
t.Fatal(err) |
| 491 | 491 |
} |
| ... | ... |
@@ -501,11 +506,11 @@ func TestParseDevice(t *testing.T) {
|
| 501 | 501 |
|
| 502 | 502 |
func TestParseModes(t *testing.T) {
|
| 503 | 503 |
// ipc ko |
| 504 |
- if _, _, _, _, err := parseRun([]string{"--ipc=container:", "img", "cmd"}); err == nil || err.Error() != "--ipc: invalid IPC mode" {
|
|
| 504 |
+ if _, _, _, err := parseRun([]string{"--ipc=container:", "img", "cmd"}); err == nil || err.Error() != "--ipc: invalid IPC mode" {
|
|
| 505 | 505 |
t.Fatalf("Expected an error with message '--ipc: invalid IPC mode', got %v", err)
|
| 506 | 506 |
} |
| 507 | 507 |
// ipc ok |
| 508 |
- _, hostconfig, _, _, err := parseRun([]string{"--ipc=host", "img", "cmd"})
|
|
| 508 |
+ _, hostconfig, _, err := parseRun([]string{"--ipc=host", "img", "cmd"})
|
|
| 509 | 509 |
if err != nil {
|
| 510 | 510 |
t.Fatal(err) |
| 511 | 511 |
} |
| ... | ... |
@@ -513,11 +518,11 @@ func TestParseModes(t *testing.T) {
|
| 513 | 513 |
t.Fatalf("Expected a valid IpcMode, got %v", hostconfig.IpcMode)
|
| 514 | 514 |
} |
| 515 | 515 |
// pid ko |
| 516 |
- if _, _, _, _, err := parseRun([]string{"--pid=container:", "img", "cmd"}); err == nil || err.Error() != "--pid: invalid PID mode" {
|
|
| 516 |
+ if _, _, _, err := parseRun([]string{"--pid=container:", "img", "cmd"}); err == nil || err.Error() != "--pid: invalid PID mode" {
|
|
| 517 | 517 |
t.Fatalf("Expected an error with message '--pid: invalid PID mode', got %v", err)
|
| 518 | 518 |
} |
| 519 | 519 |
// pid ok |
| 520 |
- _, hostconfig, _, _, err = parseRun([]string{"--pid=host", "img", "cmd"})
|
|
| 520 |
+ _, hostconfig, _, err = parseRun([]string{"--pid=host", "img", "cmd"})
|
|
| 521 | 521 |
if err != nil {
|
| 522 | 522 |
t.Fatal(err) |
| 523 | 523 |
} |
| ... | ... |
@@ -525,11 +530,11 @@ func TestParseModes(t *testing.T) {
|
| 525 | 525 |
t.Fatalf("Expected a valid PidMode, got %v", hostconfig.PidMode)
|
| 526 | 526 |
} |
| 527 | 527 |
// uts ko |
| 528 |
- if _, _, _, _, err := parseRun([]string{"--uts=container:", "img", "cmd"}); err == nil || err.Error() != "--uts: invalid UTS mode" {
|
|
| 528 |
+ if _, _, _, err := parseRun([]string{"--uts=container:", "img", "cmd"}); err == nil || err.Error() != "--uts: invalid UTS mode" {
|
|
| 529 | 529 |
t.Fatalf("Expected an error with message '--uts: invalid UTS mode', got %v", err)
|
| 530 | 530 |
} |
| 531 | 531 |
// uts ok |
| 532 |
- _, hostconfig, _, _, err = parseRun([]string{"--uts=host", "img", "cmd"})
|
|
| 532 |
+ _, hostconfig, _, err = parseRun([]string{"--uts=host", "img", "cmd"})
|
|
| 533 | 533 |
if err != nil {
|
| 534 | 534 |
t.Fatal(err) |
| 535 | 535 |
} |
| ... | ... |
@@ -537,11 +542,11 @@ func TestParseModes(t *testing.T) {
|
| 537 | 537 |
t.Fatalf("Expected a valid UTSMode, got %v", hostconfig.UTSMode)
|
| 538 | 538 |
} |
| 539 | 539 |
// shm-size ko |
| 540 |
- if _, _, _, _, err = parseRun([]string{"--shm-size=a128m", "img", "cmd"}); err == nil || err.Error() != "invalid size: 'a128m'" {
|
|
| 540 |
+ if _, _, _, err = parseRun([]string{"--shm-size=a128m", "img", "cmd"}); err == nil || err.Error() != "invalid size: 'a128m'" {
|
|
| 541 | 541 |
t.Fatalf("Expected an error with message 'invalid size: a128m', got %v", err)
|
| 542 | 542 |
} |
| 543 | 543 |
// shm-size ok |
| 544 |
- _, hostconfig, _, _, err = parseRun([]string{"--shm-size=128m", "img", "cmd"})
|
|
| 544 |
+ _, hostconfig, _, err = parseRun([]string{"--shm-size=128m", "img", "cmd"})
|
|
| 545 | 545 |
if err != nil {
|
| 546 | 546 |
t.Fatal(err) |
| 547 | 547 |
} |
| ... | ... |
@@ -570,12 +575,12 @@ func TestParseRestartPolicy(t *testing.T) {
|
| 570 | 570 |
}, |
| 571 | 571 |
} |
| 572 | 572 |
for restart, expectedError := range invalids {
|
| 573 |
- if _, _, _, _, err := parseRun([]string{fmt.Sprintf("--restart=%s", restart), "img", "cmd"}); err == nil || err.Error() != expectedError {
|
|
| 573 |
+ if _, _, _, err := parseRun([]string{fmt.Sprintf("--restart=%s", restart), "img", "cmd"}); err == nil || err.Error() != expectedError {
|
|
| 574 | 574 |
t.Fatalf("Expected an error with message '%v' for %v, got %v", expectedError, restart, err)
|
| 575 | 575 |
} |
| 576 | 576 |
} |
| 577 | 577 |
for restart, expected := range valids {
|
| 578 |
- _, hostconfig, _, _, err := parseRun([]string{fmt.Sprintf("--restart=%v", restart), "img", "cmd"})
|
|
| 578 |
+ _, hostconfig, _, err := parseRun([]string{fmt.Sprintf("--restart=%v", restart), "img", "cmd"})
|
|
| 579 | 579 |
if err != nil {
|
| 580 | 580 |
t.Fatal(err) |
| 581 | 581 |
} |
| ... | ... |
@@ -626,11 +631,11 @@ func TestParseHealth(t *testing.T) {
|
| 626 | 626 |
|
| 627 | 627 |
func TestParseLoggingOpts(t *testing.T) {
|
| 628 | 628 |
// logging opts ko |
| 629 |
- if _, _, _, _, err := parseRun([]string{"--log-driver=none", "--log-opt=anything", "img", "cmd"}); err == nil || err.Error() != "invalid logging opts for driver none" {
|
|
| 629 |
+ if _, _, _, err := parseRun([]string{"--log-driver=none", "--log-opt=anything", "img", "cmd"}); err == nil || err.Error() != "invalid logging opts for driver none" {
|
|
| 630 | 630 |
t.Fatalf("Expected an error with message 'invalid logging opts for driver none', got %v", err)
|
| 631 | 631 |
} |
| 632 | 632 |
// logging opts ok |
| 633 |
- _, hostconfig, _, _, err := parseRun([]string{"--log-driver=syslog", "--log-opt=something", "img", "cmd"})
|
|
| 633 |
+ _, hostconfig, _, err := parseRun([]string{"--log-driver=syslog", "--log-opt=something", "img", "cmd"})
|
|
| 634 | 634 |
if err != nil {
|
| 635 | 635 |
t.Fatal(err) |
| 636 | 636 |
} |
| ... | ... |
@@ -645,18 +650,18 @@ func TestParseEnvfileVariables(t *testing.T) {
|
| 645 | 645 |
e = "open nonexistent: The system cannot find the file specified." |
| 646 | 646 |
} |
| 647 | 647 |
// env ko |
| 648 |
- if _, _, _, _, err := parseRun([]string{"--env-file=nonexistent", "img", "cmd"}); err == nil || err.Error() != e {
|
|
| 648 |
+ if _, _, _, err := parseRun([]string{"--env-file=nonexistent", "img", "cmd"}); err == nil || err.Error() != e {
|
|
| 649 | 649 |
t.Fatalf("Expected an error with message '%s', got %v", e, err)
|
| 650 | 650 |
} |
| 651 | 651 |
// env ok |
| 652 |
- config, _, _, _, err := parseRun([]string{"--env-file=fixtures/valid.env", "img", "cmd"})
|
|
| 652 |
+ config, _, _, err := parseRun([]string{"--env-file=fixtures/valid.env", "img", "cmd"})
|
|
| 653 | 653 |
if err != nil {
|
| 654 | 654 |
t.Fatal(err) |
| 655 | 655 |
} |
| 656 | 656 |
if len(config.Env) != 1 || config.Env[0] != "ENV1=value1" {
|
| 657 | 657 |
t.Fatalf("Expected a config with [ENV1=value1], got %v", config.Env)
|
| 658 | 658 |
} |
| 659 |
- config, _, _, _, err = parseRun([]string{"--env-file=fixtures/valid.env", "--env=ENV2=value2", "img", "cmd"})
|
|
| 659 |
+ config, _, _, err = parseRun([]string{"--env-file=fixtures/valid.env", "--env=ENV2=value2", "img", "cmd"})
|
|
| 660 | 660 |
if err != nil {
|
| 661 | 661 |
t.Fatal(err) |
| 662 | 662 |
} |
| ... | ... |
@@ -671,18 +676,18 @@ func TestParseLabelfileVariables(t *testing.T) {
|
| 671 | 671 |
e = "open nonexistent: The system cannot find the file specified." |
| 672 | 672 |
} |
| 673 | 673 |
// label ko |
| 674 |
- if _, _, _, _, err := parseRun([]string{"--label-file=nonexistent", "img", "cmd"}); err == nil || err.Error() != e {
|
|
| 674 |
+ if _, _, _, err := parseRun([]string{"--label-file=nonexistent", "img", "cmd"}); err == nil || err.Error() != e {
|
|
| 675 | 675 |
t.Fatalf("Expected an error with message '%s', got %v", e, err)
|
| 676 | 676 |
} |
| 677 | 677 |
// label ok |
| 678 |
- config, _, _, _, err := parseRun([]string{"--label-file=fixtures/valid.label", "img", "cmd"})
|
|
| 678 |
+ config, _, _, err := parseRun([]string{"--label-file=fixtures/valid.label", "img", "cmd"})
|
|
| 679 | 679 |
if err != nil {
|
| 680 | 680 |
t.Fatal(err) |
| 681 | 681 |
} |
| 682 | 682 |
if len(config.Labels) != 1 || config.Labels["LABEL1"] != "value1" {
|
| 683 | 683 |
t.Fatalf("Expected a config with [LABEL1:value1], got %v", config.Labels)
|
| 684 | 684 |
} |
| 685 |
- config, _, _, _, err = parseRun([]string{"--label-file=fixtures/valid.label", "--label=LABEL2=value2", "img", "cmd"})
|
|
| 685 |
+ config, _, _, err = parseRun([]string{"--label-file=fixtures/valid.label", "--label=LABEL2=value2", "img", "cmd"})
|
|
| 686 | 686 |
if err != nil {
|
| 687 | 687 |
t.Fatal(err) |
| 688 | 688 |
} |
| ... | ... |
@@ -692,7 +697,7 @@ func TestParseLabelfileVariables(t *testing.T) {
|
| 692 | 692 |
} |
| 693 | 693 |
|
| 694 | 694 |
func TestParseEntryPoint(t *testing.T) {
|
| 695 |
- config, _, _, _, err := parseRun([]string{"--entrypoint=anything", "cmd", "img"})
|
|
| 695 |
+ config, _, _, err := parseRun([]string{"--entrypoint=anything", "cmd", "img"})
|
|
| 696 | 696 |
if err != nil {
|
| 697 | 697 |
t.Fatal(err) |
| 698 | 698 |
} |