diff --git a/container.go b/container.go index 3f16c909f..fd85bb9d6 100644 --- a/container.go +++ b/container.go @@ -124,6 +124,10 @@ func (p Port) Port() string { return strings.Split(string(p), "/")[1] } +func NewPort(proto, port string) Port { + return Port(fmt.Sprintf("%s/%s", proto, port)) +} + func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, *flag.FlagSet, error) { cmd := Subcmd("run", "[OPTIONS] IMAGE [COMMAND] [ARG...]", "Run a command in a new container") if os.Getenv("TEST") != "" { @@ -152,8 +156,11 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, flCpuShares := cmd.Int64("c", 0, "CPU shares (relative weight)") - var flPorts ListOpts - cmd.Var(&flPorts, "p", "Expose a container's port to the host (use 'docker port' to see the actual mapping)") + var flPublish ListOpts + cmd.Var(&flPublish, "p", "Publish a container's port to the host (use 'docker port' to see the actual mapping)") + + var flExpose ListOpts + cmd.Var(&flExpose, "expose", "Expose a port from the container without publishing it to your host") var flEnv ListOpts cmd.Var(&flEnv, "e", "Set environment variables") @@ -238,11 +245,22 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, domainname = parts[1] } - ports, portBindings, err := parsePortSpecs(flPorts) + ports, portBindings, err := parsePortSpecs(flPublish) if err != nil { return nil, nil, cmd, err } + // Merge in exposed ports to the map of published ports + for _, e := range flExpose { + if strings.Contains(e, ":") { + return nil, nil, cmd, fmt.Errorf("Invalid port format for -expose: %s", e) + } + p := NewPort(splitProtoPort(e)) + if _, exists := ports[p]; !exists { + ports[p] = struct{}{} + } + } + config := &Config{ Hostname: *flHostname, Domainname: domainname, @@ -301,8 +319,15 @@ type NetworkSettings struct { func (settings *NetworkSettings) PortMappingAPI() []APIPort { var mapping []APIPort for port, bindings := range settings.Ports { + p, _ := parsePort(port.Port()) + if len(bindings) == 0 { + mapping = append(mapping, APIPort{ + PublicPort: int64(p), + Type: port.Proto(), + }) + continue + } for _, binding := range bindings { - p, _ := parsePort(port.Port()) h, _ := parsePort(binding.HostPort) mapping = append(mapping, APIPort{ PrivatePort: int64(p), @@ -945,9 +970,6 @@ func (container *Container) allocateNetwork(hostConfig *HostConfig) error { for port := range portSpecs { binding := bindings[port] - if len(binding) == 0 { - binding = append(binding, PortBinding{}) - } for i := 0; i < len(binding); i++ { b := binding[i] nat, err := iface.AllocatePort(port, b) diff --git a/docs/sources/commandline/command/run.rst b/docs/sources/commandline/command/run.rst index 41aa09d34..88aec87d6 100644 --- a/docs/sources/commandline/command/run.rst +++ b/docs/sources/commandline/command/run.rst @@ -12,7 +12,7 @@ Run a command in a new container - -a=map[]: Attach to stdin, stdout or stderr. + -a=map[]: Attach to stdin, stdout or stderr -c=0: CPU shares (relative weight) -cidfile="": Write the container ID to the file -d=false: Detached mode: Run container in the background, print new container id @@ -27,11 +27,12 @@ -t=false: Allocate a pseudo-tty -u="": Username or UID -dns=[]: Set custom dns servers for the container - -v=[]: Create a bind mount with: [host-dir]:[container-dir]:[rw|ro]. If "host-dir" is missing, then docker creates a new volume. - -volumes-from="": Mount all volumes from the given container. - -entrypoint="": Overwrite the default entrypoint set by the image. + -v=[]: Create a bind mount with: [host-dir]:[container-dir]:[rw|ro]. If "host-dir" is missing, then docker creates a new volume + -volumes-from="": Mount all volumes from the given container + -entrypoint="": Overwrite the default entrypoint set by the image -w="": Working directory inside the container -lxc-conf=[]: Add custom lxc options -lxc-conf="lxc.cgroup.cpuset.cpus = 0,1" + -expose=[]: Expose a port from the container without publishing it to your host Examples -------- diff --git a/utils.go b/utils.go index 4ddb5f577..c42718000 100644 --- a/utils.go +++ b/utils.go @@ -223,7 +223,8 @@ func parsePortSpecs(ports []string) (map[Port]struct{}, map[Port][]PortBinding, if containerPort == "" { return nil, nil, fmt.Errorf("No port specified: %s", rawPort) } - port := Port(fmt.Sprintf("%s/%s", proto, containerPort)) + + port := NewPort(proto, containerPort) if _, exists := exposedPorts[port]; !exists { exposedPorts[port] = struct{}{} } @@ -241,6 +242,18 @@ func parsePortSpecs(ports []string) (map[Port]struct{}, map[Port][]PortBinding, return exposedPorts, bindings, nil } +func splitProtoPort(rawPort string) (string, string) { + parts := strings.Split(rawPort, "/") + l := len(parts) + if l == 0 { + return "", "" + } + if l == 1 { + return "tcp", rawPort + } + return parts[1], parts[0] +} + func parsePort(rawPort string) (int, error) { port, err := strconv.ParseUint(rawPort, 10, 16) if err != nil { @@ -258,7 +271,13 @@ func migratePortMappings(config *Config) error { return err } config.PortSpecs = nil - config.ExposedPorts = ports + + if config.ExposedPorts == nil { + config.ExposedPorts = make(map[Port]struct{}, len(ports)) + } + for k, v := range ports { + config.ExposedPorts[k] = v + } } return nil }