From 381004dc307625bba1f02d8838fef7619a0a9b5d Mon Sep 17 00:00:00 2001 From: Johan Euphrosine Date: Wed, 30 Apr 2014 15:46:56 -0700 Subject: [PATCH 1/7] runconfig: add -net container:name option Docker-DCO-1.1-Signed-off-by: Johan Euphrosine (github: proppy) Upstream-commit: a60159f3b102244fc5470642bd32eb99d5ac329c Component: engine --- components/engine/daemon/container.go | 15 +++++- components/engine/daemon/execdriver/driver.go | 5 +- .../engine/daemon/execdriver/native/create.go | 16 ++++++ components/engine/runconfig/hostconfig.go | 24 +++++---- components/engine/runconfig/parse.go | 53 +++++++++++++------ 5 files changed, 84 insertions(+), 29 deletions(-) diff --git a/components/engine/daemon/container.go b/components/engine/daemon/container.go index 1c6dc077dc..22c2ef3abe 100644 --- a/components/engine/daemon/container.go +++ b/components/engine/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/components/engine/daemon/execdriver/driver.go b/components/engine/daemon/execdriver/driver.go index 27a575cb3a..994a27e501 100644 --- a/components/engine/daemon/execdriver/driver.go +++ b/components/engine/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/components/engine/daemon/execdriver/native/create.go b/components/engine/daemon/execdriver/native/create.go index 5562d08986..b2dd395bb5 100644 --- a/components/engine/daemon/execdriver/native/create.go +++ b/components/engine/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/components/engine/runconfig/hostconfig.go b/components/engine/runconfig/hostconfig.go index 3235bf1f4e..dce88c4460 100644 --- a/components/engine/runconfig/hostconfig.go +++ b/components/engine/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/components/engine/runconfig/parse.go b/components/engine/runconfig/parse.go index d395b49e80..06e380d4fa 100644 --- a/components/engine/runconfig/parse.go +++ b/components/engine/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 +} From bb0461e5641a33c624263db4253415d49dd0ac06 Mon Sep 17 00:00:00 2001 From: Johan Euphrosine Date: Fri, 2 May 2014 01:47:12 -0700 Subject: [PATCH 2/7] runconfig/parse: add test for parseNetMode Docker-DCO-1.1-Signed-off-by: Johan Euphrosine (github: proppy) Upstream-commit: 7118416aeeb779373685d192c26a329e9acdef89 Component: engine --- components/engine/runconfig/parse.go | 17 ++++++------ components/engine/runconfig/parse_test.go | 33 ++++++++++++++++++++++- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/components/engine/runconfig/parse.go b/components/engine/runconfig/parse.go index 06e380d4fa..262a04eb4b 100644 --- a/components/engine/runconfig/parse.go +++ b/components/engine/runconfig/parse.go @@ -284,16 +284,17 @@ func parseKeyValueOpts(opts opts.ListOpts) ([]utils.KeyValuePair, error) { 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 { + switch mode := parts[0]; mode { + case "bridge", "disable": + return mode, "", nil + case "container": + var container string + if len(parts) < 2 || parts[1] == "" { return "", "", fmt.Errorf("'container:' netmode requires a container id or name", netMode) } container = parts[1] + return mode, container, nil + default: + return "", "", fmt.Errorf("invalid netmode: %q", netMode) } - return mode, container, nil } diff --git a/components/engine/runconfig/parse_test.go b/components/engine/runconfig/parse_test.go index fd28c4593e..9ac925f2ac 100644 --- a/components/engine/runconfig/parse_test.go +++ b/components/engine/runconfig/parse_test.go @@ -1,8 +1,9 @@ package runconfig import ( - "github.com/dotcloud/docker/utils" "testing" + + "github.com/dotcloud/docker/utils" ) func TestParseLxcConfOpt(t *testing.T) { @@ -21,3 +22,33 @@ func TestParseLxcConfOpt(t *testing.T) { } } } + +func TestParseNetMode(t *testing.T) { + testFlags := []struct { + flag string + mode string + container string + err bool + }{ + {"", "", "", true}, + {"bridge", "bridge", "", false}, + {"disable", "disable", "", false}, + {"container:foo", "container", "foo", false}, + {"container:", "", "", true}, + {"container", "", "", true}, + {"unknown", "", "", true}, + } + + for _, to := range testFlags { + mode, container, err := parseNetMode(to.flag) + if mode != to.mode { + t.Fatalf("-net %s: expected net mode: %q, got: %q", to.flag, to.mode, mode) + } + if container != to.container { + t.Fatalf("-net %s: expected net container: %q, got: %q", to.flag, to.container, container) + } + if (err != nil) != to.err { + t.Fatal("-net %s: expected an error got none", to.flag) + } + } +} From ad111d41992b7f422d67df10f9853c888d3565f5 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Fri, 2 May 2014 14:06:05 -0700 Subject: [PATCH 3/7] Update --net flags and container mode Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) Upstream-commit: 2c2cc051d831f54d1bb070642edcd876ff669e78 Component: engine --- components/engine/daemon/container.go | 35 ++++++++++-------- components/engine/runconfig/hostconfig.go | 26 +++++++------- components/engine/runconfig/parse.go | 44 +++++++++++------------ components/engine/runconfig/parse_test.go | 2 +- 4 files changed, 54 insertions(+), 53 deletions(-) diff --git a/components/engine/daemon/container.go b/components/engine/daemon/container.go index 22c2ef3abe..bbd4aa6a58 100644 --- a/components/engine/daemon/container.go +++ b/components/engine/daemon/container.go @@ -338,27 +338,32 @@ func populateCommand(c *Container, env []string) error { Interface: nil, } - if !c.Config.NetworkDisabled { - network := c.NetworkSettings - en.Interface = &execdriver.NetworkInterface{ - Gateway: network.Gateway, - Bridge: network.Bridge, - IPAddress: network.IPAddress, - IPPrefixLen: network.IPPrefixLen, + parts := strings.SplitN(c.hostConfig.NetworkMode, ":", 2) + switch parts[0] { + case "none": + case "bridge": + if !c.Config.NetworkDisabled { + network := c.NetworkSettings + en.Interface = &execdriver.NetworkInterface{ + Gateway: network.Gateway, + Bridge: network.Bridge, + IPAddress: network.IPAddress, + IPPrefixLen: network.IPPrefixLen, + } } + case "container": + nc := c.daemon.Get(parts[1]) + if nc == nil { + return fmt.Errorf("no such container to join network: %q", parts[1]) + } + en.ContainerID = nc.ID + default: + return fmt.Errorf("invalid network mode: %s", c.hostConfig.NetworkMode) } // 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, diff --git a/components/engine/runconfig/hostconfig.go b/components/engine/runconfig/hostconfig.go index dce88c4460..83688367e3 100644 --- a/components/engine/runconfig/hostconfig.go +++ b/components/engine/runconfig/hostconfig.go @@ -7,17 +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 - UseContainerNetwork string + Binds []string + ContainerIDFile string + LxcConf []utils.KeyValuePair + Privileged bool + PortBindings nat.PortMap + Links []string + PublishAllPorts bool + Dns []string + DnsSearch []string + VolumesFrom []string + NetworkMode string } func ContainerHostConfigFromJob(job *engine.Job) *HostConfig { @@ -25,6 +25,7 @@ func ContainerHostConfigFromJob(job *engine.Job) *HostConfig { ContainerIDFile: job.Getenv("ContainerIDFile"), Privileged: job.GetenvBool("Privileged"), PublishAllPorts: job.GetenvBool("PublishAllPorts"), + NetworkMode: job.Getenv("NetworkMode"), } job.GetenvJson("LxcConf", &hostConfig.LxcConf) job.GetenvJson("PortBindings", &hostConfig.PortBindings) @@ -43,8 +44,5 @@ 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/components/engine/runconfig/parse.go b/components/engine/runconfig/parse.go index 262a04eb4b..42ad5c1958 100644 --- a/components/engine/runconfig/parse.go +++ b/components/engine/runconfig/parse.go @@ -50,7 +50,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf flAutoRemove = cmd.Bool([]string{"#rm", "-rm"}, false, "Automatically remove the container when it exits (incompatible with -d)") flDetach = cmd.Bool([]string{"d", "-detach"}, false, "Detached mode: Run container in the background, print new container id") - flNetwork = cmd.Bool([]string{"n", "-networking"}, true, "Enable networking for this container") + flNetwork = cmd.Bool([]string{"#n", "#-networking"}, true, "Enable networking for this container") flPrivileged = cmd.Bool([]string{"#privileged", "-privileged"}, false, "Give extended privileges to this container") flPublishAll = cmd.Bool([]string{"P", "-publish-all"}, false, "Publish all exposed ports to the host interfaces") flStdin = cmd.Bool([]string{"i", "-interactive"}, false, "Keep stdin open even if not attached") @@ -62,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)") + flNetMode = cmd.String([]string{"-net"}, "bridge", "Set the Network mode for the container ('bridge': creates a new network stack for the container on the docker bridge, 'none': no 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") @@ -198,7 +198,7 @@ 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) + netMode, err := parseNetMode(*flNetMode) if err != nil { return nil, nil, cmd, fmt.Errorf("-net: invalid net mode: %v", err) } @@ -210,7 +210,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf ExposedPorts: ports, User: *flUser, Tty: *flTty, - NetworkDisabled: !*flNetwork || netMode == "disable", + NetworkDisabled: !*flNetwork, OpenStdin: *flStdin, Memory: flMemory, CpuShares: *flCpuShares, @@ -226,17 +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(), - UseContainerNetwork: useContainerNetwork, + Binds: binds, + ContainerIDFile: *flContainerIDFile, + LxcConf: lxcConf, + Privileged: *flPrivileged, + PortBindings: portBindings, + Links: flLinks.GetAll(), + PublishAllPorts: *flPublishAll, + Dns: flDns.GetAll(), + DnsSearch: flDnsSearch.GetAll(), + VolumesFrom: flVolumesFrom.GetAll(), + NetworkMode: netMode, } if sysInfo != nil && flMemory > 0 && !sysInfo.SwapLimit { @@ -282,19 +282,17 @@ func parseKeyValueOpts(opts opts.ListOpts) ([]utils.KeyValuePair, error) { return out, nil } -func parseNetMode(netMode string) (string, string, error) { +func parseNetMode(netMode string) (string, error) { parts := strings.Split(netMode, ":") switch mode := parts[0]; mode { - case "bridge", "disable": - return mode, "", nil + case "bridge", "none": + return mode, nil case "container": - var container string if len(parts) < 2 || parts[1] == "" { - return "", "", fmt.Errorf("'container:' netmode requires a container id or name", netMode) + return "", fmt.Errorf("'container:' netmode requires a container id or name", netMode) } - container = parts[1] - return mode, container, nil + return netMode, nil default: - return "", "", fmt.Errorf("invalid netmode: %q", netMode) + return "", fmt.Errorf("invalid netmode: %q", netMode) } } diff --git a/components/engine/runconfig/parse_test.go b/components/engine/runconfig/parse_test.go index 9ac925f2ac..e1b4cf9f93 100644 --- a/components/engine/runconfig/parse_test.go +++ b/components/engine/runconfig/parse_test.go @@ -40,7 +40,7 @@ func TestParseNetMode(t *testing.T) { } for _, to := range testFlags { - mode, container, err := parseNetMode(to.flag) + mode, err := parseNetMode(to.flag) if mode != to.mode { t.Fatalf("-net %s: expected net mode: %q, got: %q", to.flag, to.mode, mode) } From 0fcf7381830ec612f6069a174d03704db8b9dd7d Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Fri, 2 May 2014 14:17:31 -0700 Subject: [PATCH 4/7] Setup host networking for lxc and native Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) Upstream-commit: a785882b29b9f0b24ace8249576c5d8d7f8c1d94 Component: engine --- components/engine/daemon/container.go | 2 ++ components/engine/daemon/execdriver/driver.go | 7 ++++--- components/engine/daemon/execdriver/lxc/init.go | 9 +++++---- components/engine/daemon/execdriver/lxc/lxc_template.go | 5 +++-- components/engine/daemon/execdriver/native/create.go | 5 ++++- components/engine/runconfig/parse.go | 2 ++ 6 files changed, 20 insertions(+), 10 deletions(-) diff --git a/components/engine/daemon/container.go b/components/engine/daemon/container.go index bbd4aa6a58..6769da9b25 100644 --- a/components/engine/daemon/container.go +++ b/components/engine/daemon/container.go @@ -341,6 +341,8 @@ func populateCommand(c *Container, env []string) error { parts := strings.SplitN(c.hostConfig.NetworkMode, ":", 2) switch parts[0] { case "none": + case "host": + en.HostNetworking = true case "bridge": if !c.Config.NetworkDisabled { network := c.NetworkSettings diff --git a/components/engine/daemon/execdriver/driver.go b/components/engine/daemon/execdriver/driver.go index 994a27e501..4837a398ea 100644 --- a/components/engine/daemon/execdriver/driver.go +++ b/components/engine/daemon/execdriver/driver.go @@ -89,9 +89,10 @@ 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"` - ContainerID string `json:"container_id"` // id of the container to join network. + 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. + HostNetworking bool `json:"host_networking"` } type NetworkInterface struct { diff --git a/components/engine/daemon/execdriver/lxc/init.go b/components/engine/daemon/execdriver/lxc/init.go index 52d75fc9f8..e21e717645 100644 --- a/components/engine/daemon/execdriver/lxc/init.go +++ b/components/engine/daemon/execdriver/lxc/init.go @@ -3,15 +3,16 @@ package lxc import ( "encoding/json" "fmt" - "github.com/dotcloud/docker/daemon/execdriver" - "github.com/dotcloud/docker/pkg/netlink" - "github.com/dotcloud/docker/pkg/user" - "github.com/syndtr/gocapability/capability" "io/ioutil" "net" "os" "strings" "syscall" + + "github.com/dotcloud/docker/daemon/execdriver" + "github.com/dotcloud/docker/pkg/netlink" + "github.com/dotcloud/docker/pkg/user" + "github.com/syndtr/gocapability/capability" ) // Clear environment pollution introduced by lxc-start diff --git a/components/engine/daemon/execdriver/lxc/lxc_template.go b/components/engine/daemon/execdriver/lxc/lxc_template.go index 19fa43c4c2..7fdc5ce92b 100644 --- a/components/engine/daemon/execdriver/lxc/lxc_template.go +++ b/components/engine/daemon/execdriver/lxc/lxc_template.go @@ -14,12 +14,13 @@ const LxcTemplate = ` lxc.network.type = veth lxc.network.link = {{.Network.Interface.Bridge}} lxc.network.name = eth0 -{{else}} +lxc.network.mtu = {{.Network.Mtu}} +{{else if not .Network.HostNetworking}} # network is disabled (-n=false) lxc.network.type = empty lxc.network.flags = up -{{end}} lxc.network.mtu = {{.Network.Mtu}} +{{end}} # root filesystem {{$ROOTFS := .Rootfs}} diff --git a/components/engine/daemon/execdriver/native/create.go b/components/engine/daemon/execdriver/native/create.go index b2dd395bb5..5070ef7838 100644 --- a/components/engine/daemon/execdriver/native/create.go +++ b/components/engine/daemon/execdriver/native/create.go @@ -53,6 +53,10 @@ func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Container } func (d *driver) createNetwork(container *libcontainer.Container, c *execdriver.Command) error { + if c.Network.HostNetworking { + container.Namespaces.Get("NEWNET").Enabled = false + return nil + } container.Networks = []*libcontainer.Network{ { Mtu: c.Network.Mtu, @@ -90,7 +94,6 @@ func (d *driver) createNetwork(container *libcontainer.Container, c *execdriver. }, }) } - return nil } diff --git a/components/engine/runconfig/parse.go b/components/engine/runconfig/parse.go index 42ad5c1958..eb9886bb97 100644 --- a/components/engine/runconfig/parse.go +++ b/components/engine/runconfig/parse.go @@ -292,6 +292,8 @@ func parseNetMode(netMode string) (string, error) { return "", fmt.Errorf("'container:' netmode requires a container id or name", netMode) } return netMode, nil + case "host": + return netMode, nil default: return "", fmt.Errorf("invalid netmode: %q", netMode) } From 5fdf7d4579bf32fc72e317252a11123edcfaa9e0 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Fri, 2 May 2014 14:45:39 -0700 Subject: [PATCH 5/7] Update host networking with hostname and files Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) Upstream-commit: 5ca6532011436eee85ccb555a0832a82450454ea Component: engine --- components/engine/daemon/container.go | 43 +++++++++++++++++++---- components/engine/runconfig/parse_test.go | 30 ---------------- 2 files changed, 37 insertions(+), 36 deletions(-) diff --git a/components/engine/daemon/container.go b/components/engine/daemon/container.go index 6769da9b25..9a08f87133 100644 --- a/components/engine/daemon/container.go +++ b/components/engine/daemon/container.go @@ -343,7 +343,7 @@ func populateCommand(c *Container, env []string) error { case "none": case "host": en.HostNetworking = true - case "bridge": + case "bridge", "": // empty string to support existing containers if !c.Config.NetworkDisabled { network := c.NetworkSettings en.Interface = &execdriver.NetworkInterface{ @@ -503,9 +503,18 @@ func (container *Container) StderrLogPipe() io.ReadCloser { return utils.NewBufReader(reader) } -func (container *Container) buildHostnameAndHostsFiles(IP string) { +func (container *Container) buildHostname() { container.HostnamePath = path.Join(container.root, "hostname") - ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644) + + if container.Config.Domainname != "" { + ioutil.WriteFile(container.HostnamePath, []byte(fmt.Sprintf("%s.%s\n", container.Config.Hostname, container.Config.Domainname)), 0644) + } else { + ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644) + } +} + +func (container *Container) buildHostnameAndHostsFiles(IP string) { + container.buildHostname() hostsContent := []byte(` 127.0.0.1 localhost @@ -523,12 +532,11 @@ ff02::2 ip6-allrouters } else if !container.Config.NetworkDisabled { hostsContent = append([]byte(fmt.Sprintf("%s\t%s\n", IP, container.Config.Hostname)), hostsContent...) } - ioutil.WriteFile(container.HostsPath, hostsContent, 0644) } func (container *Container) allocateNetwork() error { - if container.Config.NetworkDisabled { + if container.Config.NetworkDisabled || container.hostConfig.NetworkMode == "host" { return nil } @@ -981,14 +989,22 @@ func (container *Container) setupContainerDns() error { if container.ResolvConfPath != "" { return nil } + var ( config = container.hostConfig daemon = container.daemon ) + + if config.NetworkMode == "host" { + container.ResolvConfPath = "/etc/resolv.conf" + return nil + } + resolvConf, err := utils.GetResolvConf() if err != nil { return err } + // If custom dns exists, then create a resolv.conf for the container if len(config.Dns) > 0 || len(daemon.config.Dns) > 0 || len(config.DnsSearch) > 0 || len(daemon.config.DnsSearch) > 0 { var ( @@ -1028,7 +1044,22 @@ func (container *Container) setupContainerDns() error { } func (container *Container) initializeNetworking() error { - if container.daemon.config.DisableNetwork { + var err error + if container.hostConfig.NetworkMode == "host" { + container.Config.Hostname, err = os.Hostname() + if err != nil { + return err + } + + parts := strings.SplitN(container.Config.Hostname, ".", 2) + if len(parts) > 1 { + container.Config.Hostname = parts[0] + container.Config.Domainname = parts[1] + } + container.HostsPath = "/etc/hosts" + + container.buildHostname() + } else if container.daemon.config.DisableNetwork { container.Config.NetworkDisabled = true container.buildHostnameAndHostsFiles("127.0.1.1") } else { diff --git a/components/engine/runconfig/parse_test.go b/components/engine/runconfig/parse_test.go index e1b4cf9f93..8ad40b9d2d 100644 --- a/components/engine/runconfig/parse_test.go +++ b/components/engine/runconfig/parse_test.go @@ -22,33 +22,3 @@ func TestParseLxcConfOpt(t *testing.T) { } } } - -func TestParseNetMode(t *testing.T) { - testFlags := []struct { - flag string - mode string - container string - err bool - }{ - {"", "", "", true}, - {"bridge", "bridge", "", false}, - {"disable", "disable", "", false}, - {"container:foo", "container", "foo", false}, - {"container:", "", "", true}, - {"container", "", "", true}, - {"unknown", "", "", true}, - } - - for _, to := range testFlags { - mode, err := parseNetMode(to.flag) - if mode != to.mode { - t.Fatalf("-net %s: expected net mode: %q, got: %q", to.flag, to.mode, mode) - } - if container != to.container { - t.Fatalf("-net %s: expected net container: %q, got: %q", to.flag, to.container, container) - } - if (err != nil) != to.err { - t.Fatal("-net %s: expected an error got none", to.flag) - } - } -} From ab8f143867b931052ae2b6e66edb834159d28981 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Fri, 2 May 2014 15:32:26 -0700 Subject: [PATCH 6/7] Add docs for --net flag Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) Upstream-commit: c1c6b3ccd915084bc9472992afa16f677a074785 Component: engine --- .../engine/docs/sources/reference/run.md | 46 ++++++++++++++++++- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/components/engine/docs/sources/reference/run.md b/components/engine/docs/sources/reference/run.md index 0125394d4f..521e8010e2 100644 --- a/components/engine/docs/sources/reference/run.md +++ b/components/engine/docs/sources/reference/run.md @@ -136,8 +136,8 @@ PID files): ## Network Settings - -n=true : Enable networking for this container - --dns=[] : Set custom dns servers for the container + --dns=[] : Set custom dns servers for the container + --net=bridge : Set the network mode By default, all containers have networking enabled and they can make any outgoing connections. The operator can completely disable networking @@ -148,6 +148,48 @@ files or STDIN/STDOUT only. Your container will use the same DNS servers as the host by default, but you can override this with `--dns`. +Supported networking modes are: + +* none - no networking in the container +* bridge - (default) connect the container to the bridge via veth interfaces +* host - use the host's network stack inside the container +* container - use another container's network stack + +#### Mode: none +With the networking mode set to `none` a container will not have a access to +any external routes. The container will still have a `loopback` interface +enabled in the container but it does not have any routes to external traffic. + +#### Mode: bridge +With the networking mode set to `bridge` a container will use docker's default +networking setup. A bridge is setup on the host, commonly named `docker0`, +and a pair of veth interfaces will be created for the container. One side of +the veth pair will remain on the host attached to the bridge while the other +side of the pair will be placed inside the container's namespaces in addition +to the `loopback` interface. An IP address will be allocated for containers +on the bridge's network and trafic will be routed though this bridge to the +container. + +#### Mode: host +With the networking mode set to `host` a container will share the host's +network stack and all interfaces from the host will be available to the +container. The container's hostname will match the hostname on the host +system. Publishing ports and linking to other containers will not work +when sharing the host's network stack. + +#### Mode: container +With the networking mode set to `container` a container will share the +network stack of another container. The other container's name must be +provided in the format of `--net container:`. + +Example running a redis container with redis binding to localhost then +running the redis-cli and connecting to the redis server over the +localhost interface. + + $ docker run -d --name redis example/redis --bind 127.0.0.1 + $ # use the redis container's network stack to access localhost + $ docker run --rm -ti --net container:redis example/redis-cli -h 127.0.0.1 + ## Clean Up (–rm) By default a container's file system persists even after the container From 1cb3e810da2d0bb5b95aeceaed52b50031c8fd98 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Fri, 2 May 2014 16:59:28 -0700 Subject: [PATCH 7/7] Address code review feedback Also make sure we copy the joining containers hosts and resolv.conf with the hostname if we are joining it's network stack. Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) Upstream-commit: 0b187b909be1dac60194250bc6e9ff292a0bd5c9 Component: engine --- components/engine/daemon/container.go | 40 +++++++++++++++++++---- components/engine/runconfig/hostconfig.go | 17 ++++++++-- components/engine/runconfig/parse.go | 17 ++++------ 3 files changed, 56 insertions(+), 18 deletions(-) diff --git a/components/engine/daemon/container.go b/components/engine/daemon/container.go index 9a08f87133..123eca0263 100644 --- a/components/engine/daemon/container.go +++ b/components/engine/daemon/container.go @@ -338,7 +338,7 @@ func populateCommand(c *Container, env []string) error { Interface: nil, } - parts := strings.SplitN(c.hostConfig.NetworkMode, ":", 2) + parts := strings.SplitN(string(c.hostConfig.NetworkMode), ":", 2) switch parts[0] { case "none": case "host": @@ -354,9 +354,9 @@ func populateCommand(c *Container, env []string) error { } } case "container": - nc := c.daemon.Get(parts[1]) - if nc == nil { - return fmt.Errorf("no such container to join network: %q", parts[1]) + nc, err := c.getNetworkedContainer() + if err != nil { + return err } en.ContainerID = nc.ID default: @@ -536,7 +536,8 @@ ff02::2 ip6-allrouters } func (container *Container) allocateNetwork() error { - if container.Config.NetworkDisabled || container.hostConfig.NetworkMode == "host" { + mode := container.hostConfig.NetworkMode + if container.Config.NetworkDisabled || mode.IsContainer() || mode.IsHost() { return nil } @@ -1045,7 +1046,7 @@ func (container *Container) setupContainerDns() error { func (container *Container) initializeNetworking() error { var err error - if container.hostConfig.NetworkMode == "host" { + if container.hostConfig.NetworkMode.IsHost() { container.Config.Hostname, err = os.Hostname() if err != nil { return err @@ -1059,6 +1060,16 @@ func (container *Container) initializeNetworking() error { container.HostsPath = "/etc/hosts" container.buildHostname() + } else if container.hostConfig.NetworkMode.IsContainer() { + // we need to get the hosts files from the container to join + nc, err := container.getNetworkedContainer() + if err != nil { + return err + } + container.HostsPath = nc.HostsPath + container.ResolvConfPath = nc.ResolvConfPath + container.Config.Hostname = nc.Config.Hostname + container.Config.Domainname = nc.Config.Domainname } else if container.daemon.config.DisableNetwork { container.Config.NetworkDisabled = true container.buildHostnameAndHostsFiles("127.0.1.1") @@ -1268,3 +1279,20 @@ func (container *Container) GetMountLabel() string { } return container.MountLabel } + +func (container *Container) getNetworkedContainer() (*Container, error) { + parts := strings.SplitN(string(container.hostConfig.NetworkMode), ":", 2) + switch parts[0] { + case "container": + nc := container.daemon.Get(parts[1]) + if nc == nil { + return nil, fmt.Errorf("no such container to join network: %s", parts[1]) + } + if !nc.State.IsRunning() { + return nil, fmt.Errorf("cannot join network of a non running container: %s", parts[1]) + } + return nc, nil + default: + return nil, fmt.Errorf("network mode not set to container") + } +} diff --git a/components/engine/runconfig/hostconfig.go b/components/engine/runconfig/hostconfig.go index 83688367e3..79ffad723b 100644 --- a/components/engine/runconfig/hostconfig.go +++ b/components/engine/runconfig/hostconfig.go @@ -1,11 +1,24 @@ package runconfig import ( + "strings" + "github.com/dotcloud/docker/engine" "github.com/dotcloud/docker/nat" "github.com/dotcloud/docker/utils" ) +type NetworkMode string + +func (n NetworkMode) IsHost() bool { + return n == "host" +} + +func (n NetworkMode) IsContainer() bool { + parts := strings.SplitN(string(n), ":", 2) + return len(parts) > 1 && parts[0] == "container" +} + type HostConfig struct { Binds []string ContainerIDFile string @@ -17,7 +30,7 @@ type HostConfig struct { Dns []string DnsSearch []string VolumesFrom []string - NetworkMode string + NetworkMode NetworkMode } func ContainerHostConfigFromJob(job *engine.Job) *HostConfig { @@ -25,7 +38,7 @@ func ContainerHostConfigFromJob(job *engine.Job) *HostConfig { ContainerIDFile: job.Getenv("ContainerIDFile"), Privileged: job.GetenvBool("Privileged"), PublishAllPorts: job.GetenvBool("PublishAllPorts"), - NetworkMode: job.Getenv("NetworkMode"), + NetworkMode: NetworkMode(job.Getenv("NetworkMode")), } job.GetenvJson("LxcConf", &hostConfig.LxcConf) job.GetenvJson("PortBindings", &hostConfig.PortBindings) diff --git a/components/engine/runconfig/parse.go b/components/engine/runconfig/parse.go index eb9886bb97..0d511ef2ec 100644 --- a/components/engine/runconfig/parse.go +++ b/components/engine/runconfig/parse.go @@ -62,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"}, "bridge", "Set the Network mode for the container ('bridge': creates a new network stack for the container on the docker bridge, 'none': no networking for this container, 'container:name_or_id': reuses another container network stack)") + flNetMode = cmd.String([]string{"-net"}, "bridge", "Set the Network mode for the container ('bridge': creates a new network stack for the container on the docker bridge, 'none': no networking for this container, 'container:': 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") @@ -200,7 +200,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf netMode, err := parseNetMode(*flNetMode) if err != nil { - return nil, nil, cmd, fmt.Errorf("-net: invalid net mode: %v", err) + return nil, nil, cmd, fmt.Errorf("--net: invalid net mode: %v", err) } config := &Config{ @@ -282,19 +282,16 @@ func parseKeyValueOpts(opts opts.ListOpts) ([]utils.KeyValuePair, error) { return out, nil } -func parseNetMode(netMode string) (string, error) { +func parseNetMode(netMode string) (NetworkMode, error) { parts := strings.Split(netMode, ":") switch mode := parts[0]; mode { - case "bridge", "none": - return mode, nil + case "bridge", "none", "host": case "container": if len(parts) < 2 || parts[1] == "" { - return "", fmt.Errorf("'container:' netmode requires a container id or name", netMode) + return "", fmt.Errorf("invalid container format container:") } - return netMode, nil - case "host": - return netMode, nil default: - return "", fmt.Errorf("invalid netmode: %q", netMode) + return "", fmt.Errorf("invalid --net: %s", netMode) } + return NetworkMode(netMode), nil }