From 68e48b65a64df10fc797cbaa89d6caa2188eadc9 Mon Sep 17 00:00:00 2001 From: Tim Hockin Date: Sat, 13 Sep 2014 04:35:59 +0000 Subject: [PATCH] Allow extra lines in /etc/hosts This adds a --add-host host:ip flag which appends lines to /etc/hosts. This is needed in places where you want the container to get a different name resolution than it would through DNS. This was submitted before as #5525, closed, and now I am re-opening. It has come up 2 or 3 times in the last couple days. Signed-off-by: Tim Hockin --- daemon/container.go | 5 +++++ docs/man/docker-run.1.md | 5 +++++ docs/sources/reference/commandline/cli.md | 1 + docs/sources/reference/run.md | 17 +++++++++++++++++ integration-cli/docker_cli_run_test.go | 17 +++++++++++++++++ opts/opts.go | 11 +++++++++++ runconfig/hostconfig.go | 4 ++++ runconfig/parse.go | 3 +++ 8 files changed, 63 insertions(+) diff --git a/daemon/container.go b/daemon/container.go index 014899fc3c..5808008d97 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -422,6 +422,11 @@ func (container *Container) buildHostsFiles(IP string) error { extraContent[alias] = child.NetworkSettings.IPAddress } + for _, extraHost := range container.hostConfig.ExtraHosts { + parts := strings.Split(extraHost, ":") + extraContent[parts[0]] = parts[1] + } + return etchosts.Build(container.HostsPath, IP, container.Config.Hostname, container.Config.Domainname, &extraContent) } diff --git a/docs/man/docker-run.1.md b/docs/man/docker-run.1.md index 953acf5897..971d196a12 100644 --- a/docs/man/docker-run.1.md +++ b/docs/man/docker-run.1.md @@ -7,6 +7,7 @@ docker-run - Run a command in a new container # SYNOPSIS **docker run** [**-a**|**--attach**[=*[]*]] +[**--add-host**[=*[]*]] [**-c**|**--cpu-shares**[=*0*]] [**--cap-add**[=*[]*]] [**--cap-drop**[=*[]*]] @@ -64,6 +65,10 @@ error. It can even pretend to be a TTY (this is what most commandline executables expect) and pass along signals. The **-a** option can be set for each of stdin, stdout, and stderr. +**--add-host**=*hostname*:*ip* + Add a line to /etc/hosts. The format is hostname:ip. The **--add-host** +option can be set multiple times. + **-c**, **--cpu-shares**=0 CPU shares in relative weight. You can increase the priority of a container with the -c option. By default, all containers run at the same priority and get diff --git a/docs/sources/reference/commandline/cli.md b/docs/sources/reference/commandline/cli.md index 0920917f93..731919711c 100644 --- a/docs/sources/reference/commandline/cli.md +++ b/docs/sources/reference/commandline/cli.md @@ -986,6 +986,7 @@ removed before the image is removed. Run a command in a new container -a, --attach=[] Attach to STDIN, STDOUT or STDERR. + --add-host=[] Add a custom host-to-IP mapping (host:ip) -c, --cpu-shares=0 CPU shares (relative weight) --cap-add=[] Add Linux capabilities --cap-drop=[] Drop Linux capabilities diff --git a/docs/sources/reference/run.md b/docs/sources/reference/run.md index 4334e2572e..81afb96cd1 100644 --- a/docs/sources/reference/run.md +++ b/docs/sources/reference/run.md @@ -139,6 +139,7 @@ example, `docker run ubuntu:14.04`. 'none': no networking for this container 'container:': reuses another container network stack 'host': use the host network stack inside the container + --add-host="" : Add a line to /etc/hosts (host:IP) By default, all containers have networking enabled and they can make any outgoing connections. The operator can completely disable networking @@ -196,6 +197,22 @@ running the `redis-cli` command and connecting to the Redis server over the $ # use the redis container's network stack to access localhost $ sudo docker run --rm -ti --net container:redis example/redis-cli -h 127.0.0.1 +### Managing /etc/hosts + +Your container will have lines in `/etc/hosts` which define the hostname of the +container itself as well as `localhost` and a few other common things. The +`--add-host` flag can be used to add additional lines to `/etc/hosts`. + + $ /docker run -ti --add-host db-static:86.75.30.9 ubuntu cat /etc/hosts + 172.17.0.22 09d03f76bf2c + fe00::0 ip6-localnet + ff00::0 ip6-mcastprefix + ff02::1 ip6-allnodes + ff02::2 ip6-allrouters + 127.0.0.1 localhost + ::1 localhost ip6-localhost ip6-loopback + 86.75.30.9 db-static + ## Clean Up (–-rm) By default a container's file system persists even after the container diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go index fc7cb4d32c..08b3693b51 100644 --- a/integration-cli/docker_cli_run_test.go +++ b/integration-cli/docker_cli_run_test.go @@ -1331,6 +1331,23 @@ func TestDnsOptionsBasedOnHostResolvConf(t *testing.T) { logDone("run - dns options based on host resolv.conf") } +func TestRunAddHost(t *testing.T) { + defer deleteAllContainers() + cmd := exec.Command(dockerBinary, "run", "--add-host=extra:86.75.30.9", "busybox", "grep", "extra", "/etc/hosts") + + out, _, err := runCommandWithOutput(cmd) + if err != nil { + t.Fatal(err, out) + } + + actual := strings.Trim(out, "\r\n") + if actual != "86.75.30.9\textra" { + t.Fatalf("expected '86.75.30.9\textra', but says: '%s'", actual) + } + + logDone("run - add-host option") +} + // Regression test for #6983 func TestAttachStdErrOnlyTTYMode(t *testing.T) { cmd := exec.Command(dockerBinary, "run", "-t", "-a", "stderr", "busybox", "true") diff --git a/opts/opts.go b/opts/opts.go index f57ef8b6d1..4ca7ec58ce 100644 --- a/opts/opts.go +++ b/opts/opts.go @@ -199,6 +199,17 @@ func validateDomain(val string) (string, error) { return "", fmt.Errorf("%s is not a valid domain", val) } +func ValidateExtraHost(val string) (string, error) { + arr := strings.Split(val, ":") + if len(arr) != 2 || len(arr[0]) == 0 { + return "", fmt.Errorf("bad format for add-host: %s", val) + } + if _, err := ValidateIPAddress(arr[1]); err != nil { + return "", fmt.Errorf("bad format for add-host: %s", val) + } + return val, nil +} + // Validates an HTTP(S) registry mirror func ValidateMirror(val string) (string, error) { uri, err := url.Parse(val) diff --git a/runconfig/hostconfig.go b/runconfig/hostconfig.go index 2c6b9feb9f..bbf047f71b 100644 --- a/runconfig/hostconfig.go +++ b/runconfig/hostconfig.go @@ -49,6 +49,7 @@ type HostConfig struct { PublishAllPorts bool Dns []string DnsSearch []string + ExtraHosts []string VolumesFrom []string Devices []DeviceMapping NetworkMode NetworkMode @@ -81,6 +82,9 @@ func ContainerHostConfigFromJob(job *engine.Job) *HostConfig { if DnsSearch := job.GetenvList("DnsSearch"); DnsSearch != nil { hostConfig.DnsSearch = DnsSearch } + if ExtraHosts := job.GetenvList("ExtraHosts"); ExtraHosts != nil { + hostConfig.ExtraHosts = ExtraHosts + } if VolumesFrom := job.GetenvList("VolumesFrom"); VolumesFrom != nil { hostConfig.VolumesFrom = VolumesFrom } diff --git a/runconfig/parse.go b/runconfig/parse.go index 7431b7611d..bb10838495 100644 --- a/runconfig/parse.go +++ b/runconfig/parse.go @@ -54,6 +54,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf flExpose = opts.NewListOpts(nil) flDns = opts.NewListOpts(opts.ValidateIPAddress) flDnsSearch = opts.NewListOpts(opts.ValidateDnsSearch) + flExtraHosts = opts.NewListOpts(opts.ValidateExtraHost) flVolumesFrom = opts.NewListOpts(nil) flLxcOpts = opts.NewListOpts(nil) flEnvFile = opts.NewListOpts(nil) @@ -93,6 +94,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf cmd.Var(&flExpose, []string{"#expose", "-expose"}, "Expose a port from the container without publishing it to your host") cmd.Var(&flDns, []string{"#dns", "-dns"}, "Set custom DNS servers") cmd.Var(&flDnsSearch, []string{"-dns-search"}, "Set custom DNS search domains") + cmd.Var(&flExtraHosts, []string{"-add-host"}, "Add a custom host-to-IP mapping (host:ip)") cmd.Var(&flVolumesFrom, []string{"#volumes-from", "-volumes-from"}, "Mount volumes from the specified container(s)") cmd.Var(&flLxcOpts, []string{"#lxc-conf", "-lxc-conf"}, "(lxc exec-driver only) Add custom lxc options --lxc-conf=\"lxc.cgroup.cpuset.cpus = 0,1\"") @@ -291,6 +293,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf PublishAllPorts: *flPublishAll, Dns: flDns.GetAll(), DnsSearch: flDnsSearch.GetAll(), + ExtraHosts: flExtraHosts.GetAll(), VolumesFrom: flVolumesFrom.GetAll(), NetworkMode: netMode, Devices: deviceMappings,