From 476559458d49b6d4eacfdbe9025ddf69947ae2b7 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Fri, 22 Nov 2013 10:12:27 -0800 Subject: [PATCH 1/3] Reformatting parseRun and partParse --- commands.go | 112 ++++++++++++++++++++++++------------------------- utils.go | 16 ++++--- utils/utils.go | 8 ++-- 3 files changed, 70 insertions(+), 66 deletions(-) diff --git a/commands.go b/commands.go index ff14b6cb2b..858ae80a6b 100644 --- a/commands.go +++ b/commands.go @@ -1730,60 +1730,59 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, } func parseRun(cmd *flag.FlagSet, args []string, capabilities *Capabilities) (*Config, *HostConfig, *flag.FlagSet, error) { + var ( + // FIXME: use utils.ListOpts for attach and volumes? + flAttach = NewAttachOpts() + flVolumes = NewPathOpts() - flHostname := cmd.String("h", "", "Container host name") - flWorkingDir := cmd.String("w", "", "Working directory inside the container") - flUser := cmd.String("u", "", "Username or UID") - flDetach := cmd.Bool("d", false, "Detached mode: Run container in the background, print new container id") - flAttach := NewAttachOpts() + flPublish utils.ListOpts + flExpose utils.ListOpts + flEnv utils.ListOpts + flDns utils.ListOpts + flVolumesFrom utils.ListOpts + flLxcOpts utils.ListOpts + flLinks utils.ListOpts + + flAutoRemove = cmd.Bool("rm", false, "Automatically remove the container when it exits (incompatible with -d)") + flDetach = cmd.Bool("d", false, "Detached mode: Run container in the background, print new container id") + flNetwork = cmd.Bool("n", true, "Enable networking for this container") + flPrivileged = cmd.Bool("privileged", false, "Give extended privileges to this container") + flPublishAll = cmd.Bool("P", false, "Publish all exposed ports to the host interfaces") + flStdin = cmd.Bool("i", false, "Keep stdin open even if not attached") + flTty = cmd.Bool("t", false, "Allocate a pseudo-tty") + flContainerIDFile = cmd.String("cidfile", "", "Write the container ID to the file") + flEntrypoint = cmd.String("entrypoint", "", "Overwrite the default entrypoint of the image") + flHostname = cmd.String("h", "", "Container host name") + flMemoryString = cmd.String("m", "", "Memory limit (format: , where unit = b, k, m or g)") + flUser = cmd.String("u", "", "Username or UID") + flWorkingDir = cmd.String("w", "", "Working directory inside the container") + flCpuShares = cmd.Int64("c", 0, "CPU shares (relative weight)") + + // For documentation purpose + _ = cmd.Bool("sig-proxy", true, "Proxify all received signal to the process (even in non-tty mode)") + _ = cmd.String("name", "", "Assign a name to the container") + ) cmd.Var(flAttach, "a", "Attach to stdin, stdout or stderr.") - flStdin := cmd.Bool("i", false, "Keep stdin open even if not attached") - flTty := cmd.Bool("t", false, "Allocate a pseudo-tty") - flMemoryString := cmd.String("m", "", "Memory limit (format: , where unit = b, k, m or g)") - flContainerIDFile := cmd.String("cidfile", "", "Write the container ID to the file") - flNetwork := cmd.Bool("n", true, "Enable networking for this container") - flPrivileged := cmd.Bool("privileged", false, "Give extended privileges to this container") - flAutoRemove := cmd.Bool("rm", false, "Automatically remove the container when it exits (incompatible with -d)") - cmd.Bool("sig-proxy", true, "Proxify all received signal to the process (even in non-tty mode)") - cmd.String("name", "", "Assign a name to the container") - flPublishAll := cmd.Bool("P", false, "Publish all exposed ports to the host interfaces") - - if capabilities != nil && *flMemoryString != "" && !capabilities.MemoryLimit { - //fmt.Fprintf(stdout, "WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.\n") - *flMemoryString = "" - } - - flCpuShares := cmd.Int64("c", 0, "CPU shares (relative weight)") - - var flPublish utils.ListOpts - cmd.Var(&flPublish, "p", "Publish a container's port to the host (use 'docker port' to see the actual mapping)") - - var flExpose utils.ListOpts - cmd.Var(&flExpose, "expose", "Expose a port from the container without publishing it to your host") - - var flEnv utils.ListOpts - cmd.Var(&flEnv, "e", "Set environment variables") - - var flDns utils.ListOpts - cmd.Var(&flDns, "dns", "Set custom dns servers") - - flVolumes := NewPathOpts() cmd.Var(flVolumes, "v", "Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)") - var flVolumesFrom utils.ListOpts + cmd.Var(&flPublish, "p", "Publish a container's port to the host (use 'docker port' to see the actual mapping)") + cmd.Var(&flExpose, "expose", "Expose a port from the container without publishing it to your host") + cmd.Var(&flEnv, "e", "Set environment variables") + cmd.Var(&flDns, "dns", "Set custom dns servers") cmd.Var(&flVolumesFrom, "volumes-from", "Mount volumes from the specified container(s)") - - flEntrypoint := cmd.String("entrypoint", "", "Overwrite the default entrypoint of the image") - - var flLxcOpts utils.ListOpts cmd.Var(&flLxcOpts, "lxc-conf", "Add custom lxc options -lxc-conf=\"lxc.cgroup.cpuset.cpus = 0,1\"") - - var flLinks utils.ListOpts cmd.Var(&flLinks, "link", "Add link to another container (name:alias)") if err := cmd.Parse(args); err != nil { return nil, nil, cmd, err } + + // Check if the kernel supports memory limit cgroup. + if capabilities != nil && *flMemoryString != "" && !capabilities.MemoryLimit { + *flMemoryString = "" + } + + // Validate input params if *flDetach && len(flAttach) > 0 { return nil, nil, cmd, ErrConflictAttachDetach } @@ -1805,8 +1804,7 @@ func parseRun(cmd *flag.FlagSet, args []string, capabilities *Capabilities) (*Co } } - envs := []string{} - + var envs []string for _, env := range flEnv { arr := strings.Split(env, "=") if len(arr) > 1 { @@ -1818,19 +1816,15 @@ func parseRun(cmd *flag.FlagSet, args []string, capabilities *Capabilities) (*Co } var flMemory int64 - if *flMemoryString != "" { parsedMemory, err := utils.RAMInBytes(*flMemoryString) - if err != nil { return nil, nil, cmd, err } - flMemory = parsedMemory } var binds []string - // add any bind targets to the list of container volumes for bind := range flVolumes { arr := strings.Split(bind, ":") @@ -1845,10 +1839,12 @@ func parseRun(cmd *flag.FlagSet, args []string, capabilities *Capabilities) (*Co } } - parsedArgs := cmd.Args() - runCmd := []string{} - entrypoint := []string{} - image := "" + var ( + parsedArgs = cmd.Args() + runCmd []string + entrypoint []string + image string + ) if len(parsedArgs) >= 1 { image = cmd.Arg(0) } @@ -1859,16 +1855,16 @@ func parseRun(cmd *flag.FlagSet, args []string, capabilities *Capabilities) (*Co entrypoint = []string{*flEntrypoint} } - var lxcConf []KeyValuePair lxcConf, err := parseLxcConfOpts(flLxcOpts) if err != nil { return nil, nil, cmd, err } - hostname := *flHostname - domainname := "" - - parts := strings.SplitN(hostname, ".", 2) + var ( + domainname string + hostname = *flHostname + parts = strings.SplitN(hostname, ".", 2) + ) if len(parts) > 1 { hostname = parts[0] domainname = parts[1] diff --git a/utils.go b/utils.go index 82b163c608..367caa2342 100644 --- a/utils.go +++ b/utils.go @@ -209,11 +209,14 @@ func parseLxcOpt(opt string) (string, string, error) { // We will receive port specs in the format of ip:public:private/proto and these need to be // parsed in the internal types func parsePortSpecs(ports []string) (map[Port]struct{}, map[Port][]PortBinding, error) { - exposedPorts := make(map[Port]struct{}, len(ports)) - bindings := make(map[Port][]PortBinding) + var ( + exposedPorts = make(map[Port]struct{}, len(ports)) + bindings = make(map[Port][]PortBinding) + ) for _, rawPort := range ports { proto := "tcp" + if i := strings.LastIndex(rawPort, "/"); i != -1 { proto = rawPort[i+1:] rawPort = rawPort[:i] @@ -228,9 +231,12 @@ func parsePortSpecs(ports []string) (map[Port]struct{}, map[Port][]PortBinding, if err != nil { return nil, nil, err } - containerPort := parts["containerPort"] - rawIp := parts["ip"] - hostPort := parts["hostPort"] + + var ( + containerPort = parts["containerPort"] + rawIp = parts["ip"] + hostPort = parts["hostPort"] + ) if containerPort == "" { return nil, nil, fmt.Errorf("No port specified: %s", rawPort) diff --git a/utils/utils.go b/utils/utils.go index d1cdff31fd..a20cdc585a 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -1237,12 +1237,14 @@ func IsClosedError(err error) bool { func PartParser(template, data string) (map[string]string, error) { // ip:public:private - templateParts := strings.Split(template, ":") - parts := strings.Split(data, ":") + var ( + templateParts = strings.Split(template, ":") + parts = strings.Split(data, ":") + out = make(map[string]string, len(templateParts)) + ) if len(parts) != len(templateParts) { return nil, fmt.Errorf("Invalid format to parse. %s should match template %s", data, template) } - out := make(map[string]string, len(templateParts)) for i, t := range templateParts { value := "" From 1f9223a7c2c0e5c04936c56a071830f8792c3dd7 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Fri, 22 Nov 2013 10:36:49 -0800 Subject: [PATCH 2/3] Use a constant for PortSpecTemplate + display the template in the CmdRun help --- commands.go | 3 ++- utils.go | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/commands.go b/commands.go index 858ae80a6b..7b1b664e94 100644 --- a/commands.go +++ b/commands.go @@ -1762,10 +1762,11 @@ func parseRun(cmd *flag.FlagSet, args []string, capabilities *Capabilities) (*Co _ = cmd.Bool("sig-proxy", true, "Proxify all received signal to the process (even in non-tty mode)") _ = cmd.String("name", "", "Assign a name to the container") ) + cmd.Var(flAttach, "a", "Attach to stdin, stdout or stderr.") cmd.Var(flVolumes, "v", "Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)") - cmd.Var(&flPublish, "p", "Publish a container's port to the host (use 'docker port' to see the actual mapping)") + cmd.Var(&flPublish, "p", fmt.Sprintf("Publish a container's port to the host (format: %s) (use 'docker port' to see the actual mapping)", PortSpecTemplateFormat)) cmd.Var(&flExpose, "expose", "Expose a port from the container without publishing it to your host") cmd.Var(&flEnv, "e", "Set environment variables") cmd.Var(&flDns, "dns", "Set custom dns servers") diff --git a/utils.go b/utils.go index 367caa2342..45d5afe04d 100644 --- a/utils.go +++ b/utils.go @@ -206,6 +206,12 @@ func parseLxcOpt(opt string) (string, string, error) { return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]), nil } +// FIXME: network related stuff (including parsing) should be grouped in network file +const ( + PortSpecTemplate = "ip:hostPort:containerPort" + PortSpecTemplateFormat = "ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort" +) + // We will receive port specs in the format of ip:public:private/proto and these need to be // parsed in the internal types func parsePortSpecs(ports []string) (map[Port]struct{}, map[Port][]PortBinding, error) { @@ -227,7 +233,7 @@ func parsePortSpecs(ports []string) (map[Port]struct{}, map[Port][]PortBinding, rawPort = fmt.Sprintf(":%s", rawPort) } - parts, err := utils.PartParser("ip:hostPort:containerPort", rawPort) + parts, err := utils.PartParser(PortSpecTemplate, rawPort) if err != nil { return nil, nil, err } From 076c0eab704e5c4819611a42b1551f1d759fb376 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Fri, 22 Nov 2013 12:14:34 -0800 Subject: [PATCH 3/3] Format CmdRun --- commands.go | 115 +++++++++++++++++++++++++++------------------------- 1 file changed, 59 insertions(+), 56 deletions(-) diff --git a/commands.go b/commands.go index 7b1b664e94..74cbe74600 100644 --- a/commands.go +++ b/commands.go @@ -1943,30 +1943,33 @@ func (cli *DockerCli) CmdRun(args ...string) error { return nil } - flRm := cmd.Lookup("rm") - autoRemove, _ := strconv.ParseBool(flRm.Value.String()) + // Retrieve relevant client-side config + var ( + flName = cmd.Lookup("name") + flRm = cmd.Lookup("rm") + flSigProxy = cmd.Lookup("sig-proxy") + autoRemove, _ = strconv.ParseBool(flRm.Value.String()) + sigProxy, _ = strconv.ParseBool(flSigProxy.Value.String()) + ) - flSigProxy := cmd.Lookup("sig-proxy") - sigProxy, _ := strconv.ParseBool(flSigProxy.Value.String()) - flName := cmd.Lookup("name") + // Disable sigProxy in case on TTY if config.Tty { sigProxy = false } - var containerIDFile *os.File + var containerIDFile io.WriteCloser if len(hostConfig.ContainerIDFile) > 0 { - if _, err := ioutil.ReadFile(hostConfig.ContainerIDFile); err == nil { + if _, err := os.Stat(hostConfig.ContainerIDFile); err == nil { return fmt.Errorf("cid file found, make sure the other container isn't running or delete %s", hostConfig.ContainerIDFile) } - containerIDFile, err = os.Create(hostConfig.ContainerIDFile) - if err != nil { + if containerIDFile, err = os.Create(hostConfig.ContainerIDFile); err != nil { return fmt.Errorf("failed to create the container ID file: %s", err) } defer containerIDFile.Close() } + containerValues := url.Values{} - name := flName.Value.String() - if name != "" { + if name := flName.Value.String(); name != "" { containerValues.Set("name", name) } @@ -1987,8 +1990,7 @@ func (cli *DockerCli) CmdRun(args ...string) error { v.Set("tag", tag) // Resolve the Repository name from fqn to endpoint + name - var endpoint string - endpoint, _, err = registry.ResolveRepositoryName(repos) + endpoint, _, err := registry.ResolveRepositoryName(repos) if err != nil { return err } @@ -2006,14 +2008,10 @@ func (cli *DockerCli) CmdRun(args ...string) error { registryAuthHeader := []string{ base64.URLEncoding.EncodeToString(buf), } - err = cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.err, map[string][]string{ - "X-Registry-Auth": registryAuthHeader, - }) - if err != nil { + if err = cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.err, map[string][]string{"X-Registry-Auth": registryAuthHeader}); err != nil { return err } - body, _, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), config) - if err != nil { + if body, _, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), config); err != nil { return err } } @@ -2021,17 +2019,17 @@ func (cli *DockerCli) CmdRun(args ...string) error { return err } - runResult := &APIRun{} - err = json.Unmarshal(body, runResult) - if err != nil { + var runResult APIRun + if err := json.Unmarshal(body, &runResult); err != nil { return err } for _, warning := range runResult.Warnings { fmt.Fprintf(cli.err, "WARNING: %s\n", warning) } + if len(hostConfig.ContainerIDFile) > 0 { - if _, err = containerIDFile.WriteString(runResult.ID); err != nil { + if _, err = containerIDFile.Write([]byte(runResult.ID)); err != nil { return fmt.Errorf("failed to write the container ID to the file: %s", err) } } @@ -2042,27 +2040,29 @@ func (cli *DockerCli) CmdRun(args ...string) error { } var ( - wait chan struct{} - errCh chan error + waitDisplayId chan struct{} + errCh chan error ) if !config.AttachStdout && !config.AttachStderr { // Make this asynchrone in order to let the client write to stdin before having to read the ID - wait = make(chan struct{}) + waitDisplayId = make(chan struct{}) go func() { - defer close(wait) + defer close(waitDisplayId) fmt.Fprintf(cli.out, "%s\n", runResult.ID) }() } + // We need to make the chan because the select needs to have a closing + // chan, it can't be uninitialized hijacked := make(chan bool) - if config.AttachStdin || config.AttachStdout || config.AttachStderr { - - v := url.Values{} + var ( + out, stderr io.Writer + in io.ReadCloser + v = url.Values{} + ) v.Set("stream", "1") - var out, stderr io.Writer - var in io.ReadCloser if config.AttachStdin { v.Set("stdin", "1") @@ -2116,34 +2116,37 @@ func (cli *DockerCli) CmdRun(args ...string) error { } } + // Detached mode: wait for the id to be displayed and return. if !config.AttachStdout && !config.AttachStderr { // Detached mode - <-wait - } else { - var status int - - if autoRemove { - if _, _, err := cli.call("POST", "/containers/"+runResult.ID+"/wait", nil); err != nil { - return err - } - _, status, err = getExitCode(cli, runResult.ID) - if err != nil { - return err - } - if _, _, err := cli.call("DELETE", "/containers/"+runResult.ID, nil); err != nil { - return err - } - } else { - _, status, err = getExitCode(cli, runResult.ID) - if err != nil { - return err - } - } - if status != 0 { - return &utils.StatusError{Status: status} - } + <-waitDisplayId + return nil } + var status int + + // Attached mode + if autoRemove { + // Autoremove: wait for the container to finish, retrieve + // the exit code and remove the container + if _, _, err := cli.call("POST", "/containers/"+runResult.ID+"/wait", nil); err != nil { + return err + } + if _, status, err = getExitCode(cli, runResult.ID); err != nil { + return err + } + if _, _, err := cli.call("DELETE", "/containers/"+runResult.ID, nil); err != nil { + return err + } + } else { + // No Autoremove: Simply retrieve the exit code + if _, status, err = getExitCode(cli, runResult.ID); err != nil { + return err + } + } + if status != 0 { + return &utils.StatusError{Status: status} + } return nil }