diff --git a/daemon/container.go b/daemon/container.go index 1c6dc077dc..22c2ef3abe 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -325,7 +325,7 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s }) } -func populateCommand(c *Container, env []string) { +func populateCommand(c *Container, env []string) error { var ( en *execdriver.Network context = make(map[string][]string) @@ -351,6 +351,14 @@ func populateCommand(c *Container, env []string) { // TODO: this can be removed after lxc-conf is fully deprecated mergeLxcConfIntoOptions(c.hostConfig, context) + if netContainer := c.hostConfig.UseContainerNetwork; netContainer != "" { + nc := c.daemon.Get(netContainer) + if nc == nil { + return fmt.Errorf("no such container to join network: %q", netContainer) + } + en.ContainerID = nc.ID + } + resources := &execdriver.Resources{ Memory: c.Config.Memory, MemorySwap: c.Config.MemorySwap, @@ -372,6 +380,7 @@ func populateCommand(c *Container, env []string) { } c.command.SysProcAttr = &syscall.SysProcAttr{Setsid: true} c.command.Env = env + return nil } func (container *Container) Start() (err error) { @@ -415,7 +424,9 @@ func (container *Container) Start() (err error) { if err := container.setupWorkingDirectory(); err != nil { return err } - populateCommand(container, env) + if err := populateCommand(container, env); err != nil { + return err + } if err := setupMountsForContainer(container); err != nil { return err } diff --git a/daemon/execdriver/driver.go b/daemon/execdriver/driver.go index 27a575cb3a..994a27e501 100644 --- a/daemon/execdriver/driver.go +++ b/daemon/execdriver/driver.go @@ -89,8 +89,9 @@ type Driver interface { // Network settings of the container type Network struct { - Interface *NetworkInterface `json:"interface"` // if interface is nil then networking is disabled - Mtu int `json:"mtu"` + Interface *NetworkInterface `json:"interface"` // if interface is nil then networking is disabled + Mtu int `json:"mtu"` + ContainerID string `json:"container_id"` // id of the container to join network. } type NetworkInterface struct { diff --git a/daemon/execdriver/native/create.go b/daemon/execdriver/native/create.go index 5562d08986..b2dd395bb5 100644 --- a/daemon/execdriver/native/create.go +++ b/daemon/execdriver/native/create.go @@ -3,6 +3,7 @@ package native import ( "fmt" "os" + "path/filepath" "github.com/dotcloud/docker/daemon/execdriver" "github.com/dotcloud/docker/daemon/execdriver/native/configuration" @@ -75,6 +76,21 @@ func (d *driver) createNetwork(container *libcontainer.Container, c *execdriver. } container.Networks = append(container.Networks, &vethNetwork) } + + if c.Network.ContainerID != "" { + cmd := d.activeContainers[c.Network.ContainerID] + if cmd == nil || cmd.Process == nil { + return fmt.Errorf("%s is not a valid running container to join", c.Network.ContainerID) + } + nspath := filepath.Join("/proc", fmt.Sprint(cmd.Process.Pid), "ns", "net") + container.Networks = append(container.Networks, &libcontainer.Network{ + Type: "netns", + Context: libcontainer.Context{ + "nspath": nspath, + }, + }) + } + return nil } diff --git a/runconfig/hostconfig.go b/runconfig/hostconfig.go index 3235bf1f4e..dce88c4460 100644 --- a/runconfig/hostconfig.go +++ b/runconfig/hostconfig.go @@ -7,16 +7,17 @@ import ( ) type HostConfig struct { - Binds []string - ContainerIDFile string - LxcConf []utils.KeyValuePair - Privileged bool - PortBindings nat.PortMap - Links []string - PublishAllPorts bool - Dns []string - DnsSearch []string - VolumesFrom []string + Binds []string + ContainerIDFile string + LxcConf []utils.KeyValuePair + Privileged bool + PortBindings nat.PortMap + Links []string + PublishAllPorts bool + Dns []string + DnsSearch []string + VolumesFrom []string + UseContainerNetwork string } func ContainerHostConfigFromJob(job *engine.Job) *HostConfig { @@ -42,5 +43,8 @@ func ContainerHostConfigFromJob(job *engine.Job) *HostConfig { if VolumesFrom := job.GetenvList("VolumesFrom"); VolumesFrom != nil { hostConfig.VolumesFrom = VolumesFrom } + if UseContainerNetwork := job.Getenv("UseContainerNetwork"); UseContainerNetwork != "" { + hostConfig.UseContainerNetwork = UseContainerNetwork + } return hostConfig } diff --git a/runconfig/parse.go b/runconfig/parse.go index d395b49e80..06e380d4fa 100644 --- a/runconfig/parse.go +++ b/runconfig/parse.go @@ -2,14 +2,15 @@ package runconfig import ( "fmt" + "io/ioutil" + "path" + "strings" + "github.com/dotcloud/docker/nat" "github.com/dotcloud/docker/opts" flag "github.com/dotcloud/docker/pkg/mflag" "github.com/dotcloud/docker/pkg/sysinfo" "github.com/dotcloud/docker/utils" - "io/ioutil" - "path" - "strings" ) var ( @@ -61,7 +62,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf flUser = cmd.String([]string{"u", "-user"}, "", "Username or UID") flWorkingDir = cmd.String([]string{"w", "-workdir"}, "", "Working directory inside the container") flCpuShares = cmd.Int64([]string{"c", "-cpu-shares"}, 0, "CPU shares (relative weight)") - + flNetMode = cmd.String([]string{"#net", "-net"}, "bridge", "Set the Network mode for the container ('bridge': creates a new network stack for the container on the docker bridge, 'disable': disable networking for this container, 'container:name_or_id': reuses another container network stack)") // For documentation purpose _ = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxify all received signal to the process (even in non-tty mode)") _ = cmd.String([]string{"#name", "-name"}, "", "Assign a name to the container") @@ -197,6 +198,11 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf // boo, there's no debug output for docker run //utils.Debugf("Environment variables for the container: %#v", envVariables) + netMode, useContainerNetwork, err := parseNetMode(*flNetMode) + if err != nil { + return nil, nil, cmd, fmt.Errorf("-net: invalid net mode: %v", err) + } + config := &Config{ Hostname: hostname, Domainname: domainname, @@ -204,7 +210,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf ExposedPorts: ports, User: *flUser, Tty: *flTty, - NetworkDisabled: !*flNetwork, + NetworkDisabled: !*flNetwork || netMode == "disable", OpenStdin: *flStdin, Memory: flMemory, CpuShares: *flCpuShares, @@ -220,16 +226,17 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf } hostConfig := &HostConfig{ - Binds: binds, - ContainerIDFile: *flContainerIDFile, - LxcConf: lxcConf, - Privileged: *flPrivileged, - PortBindings: portBindings, - Links: flLinks.GetAll(), - PublishAllPorts: *flPublishAll, - Dns: flDns.GetAll(), - DnsSearch: flDnsSearch.GetAll(), - VolumesFrom: flVolumesFrom.GetAll(), + Binds: binds, + ContainerIDFile: *flContainerIDFile, + LxcConf: lxcConf, + Privileged: *flPrivileged, + PortBindings: portBindings, + Links: flLinks.GetAll(), + PublishAllPorts: *flPublishAll, + Dns: flDns.GetAll(), + DnsSearch: flDnsSearch.GetAll(), + VolumesFrom: flVolumesFrom.GetAll(), + UseContainerNetwork: useContainerNetwork, } if sysInfo != nil && flMemory > 0 && !sysInfo.SwapLimit { @@ -274,3 +281,19 @@ func parseKeyValueOpts(opts opts.ListOpts) ([]utils.KeyValuePair, error) { } return out, nil } + +func parseNetMode(netMode string) (string, string, error) { + parts := strings.Split(netMode, ":") + if len(parts) < 1 { + return "", "", fmt.Errorf("'netmode' cannot be empty", netMode) + } + mode := parts[0] + var container string + if mode == "container" { + if len(parts) < 2 { + return "", "", fmt.Errorf("'container:' netmode requires a container id or name", netMode) + } + container = parts[1] + } + return mode, container, nil +}