Skip to content

Commit f40d962

Browse files
Merge pull request #71 from Roblox/extra_hosts
Add support for extra_hosts.
2 parents b05eab6 + c94fd39 commit f40d962

File tree

8 files changed

+221
-0
lines changed

8 files changed

+221
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ More detailed instructions are in the [`example README.md`](https://github.com/R
101101
| **seccomp_profile** | string | no | Path to custom seccomp profile. `seccomp` must be set to `true` in order to use `seccomp_profile`. The default `docker` seccomp profile found [`here`](https://github.com/moby/moby/blob/master/profiles/seccomp/default.json) can be used as a reference, and modified to create a custom seccomp profile. |
102102
| **readonly_rootfs** | bool | no | Container root filesystem will be read-only. |
103103
| **host_network** | bool | no | Enable host network. This is equivalent to `--net=host` in docker. |
104+
| **extra_hosts** | []string | no | A list of hosts, given as host:IP, to be added to /etc/hosts. |
104105
| **cap_add** | []string | no | Add individual capabilities. |
105106
| **cap_drop** | []string | no | Drop invidual capabilities. |
106107
| **devices** | []string | no | A list of devices to be exposed to the container. |

containerd/containerd.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"syscall"
2525
"time"
2626

27+
etchosts "github.com/Roblox/nomad-driver-containerd/etchosts"
2728
"github.com/containerd/containerd"
2829
"github.com/containerd/containerd/cio"
2930
"github.com/containerd/containerd/contrib/seccomp"
@@ -185,6 +186,35 @@ func (d *Driver) createContainer(containerConfig *ContainerConfig, config *TaskC
185186
mounts = append(mounts, allocMount)
186187
}
187188

189+
// User will specify extra_hosts to be added to container's /etc/hosts.
190+
// If host_network=true, extra_hosts will be added to host's /etc/hosts.
191+
// If host_network=false, extra hosts will be added to the default /etc/hosts provided to the container.
192+
// If the user doesn't set anything (host_network, extra_hosts), a default /etc/hosts will be provided to the container.
193+
var extraHostsMount specs.Mount
194+
hostsFile := containerConfig.TaskDirSrc + "/etc_hosts"
195+
if len(config.ExtraHosts) > 0 {
196+
if config.HostNetwork {
197+
if err := etchosts.CopyEtcHosts(hostsFile); err != nil {
198+
return nil, err
199+
}
200+
} else {
201+
if err := etchosts.BuildEtcHosts(hostsFile); err != nil {
202+
return nil, err
203+
}
204+
}
205+
if err := etchosts.AddExtraHosts(hostsFile, config.ExtraHosts); err != nil {
206+
return nil, err
207+
}
208+
extraHostsMount = buildMountpoint("bind", "/etc/hosts", hostsFile, []string{"rbind", "rw"})
209+
mounts = append(mounts, extraHostsMount)
210+
} else if !config.HostNetwork {
211+
if err := etchosts.BuildEtcHosts(hostsFile); err != nil {
212+
return nil, err
213+
}
214+
extraHostsMount = buildMountpoint("bind", "/etc/hosts", hostsFile, []string{"rbind", "rw"})
215+
mounts = append(mounts, extraHostsMount)
216+
}
217+
188218
if len(mounts) > 0 {
189219
opts = append(opts, oci.WithMounts(mounts))
190220
}

containerd/driver.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ var (
9898
hclspec.NewAttr("host_dns", "bool", false),
9999
hclspec.NewLiteral("true"),
100100
),
101+
"extra_hosts": hclspec.NewAttr("extra_hosts", "list(string)", false),
101102
"seccomp": hclspec.NewAttr("seccomp", "bool", false),
102103
"seccomp_profile": hclspec.NewAttr("seccomp_profile", "string", false),
103104
"readonly_rootfs": hclspec.NewAttr("readonly_rootfs", "bool", false),
@@ -154,6 +155,7 @@ type TaskConfig struct {
154155
SeccompProfile string `codec:"seccomp_profile"`
155156
Privileged bool `codec:"privileged"`
156157
HostDNS bool `codec:"host_dns"`
158+
ExtraHosts []string `codec:"extra_hosts"`
157159
ReadOnlyRootfs bool `codec:"readonly_rootfs"`
158160
HostNetwork bool `codec:"host_network"`
159161
Mounts []Mount `codec:"mounts"`

etchosts/etchosts.go

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/*
2+
Copyright 2020 Roblox Corporation
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
*/
17+
18+
package etchosts
19+
20+
import (
21+
"bytes"
22+
"fmt"
23+
"io"
24+
"io/ioutil"
25+
"os"
26+
"strings"
27+
28+
"github.com/docker/docker/opts"
29+
)
30+
31+
// Code referenced from https://github.com/moby/libnetwork/blob/master/etchosts/etchosts.go
32+
33+
// Record Structure for a single host record
34+
type Record struct {
35+
Hosts string
36+
IP string
37+
}
38+
39+
// WriteTo writes record to file and returns bytes written or error
40+
func (r Record) WriteTo(w io.Writer) (int64, error) {
41+
n, err := fmt.Fprintf(w, "%s\t%s\n", r.IP, r.Hosts)
42+
return int64(n), err
43+
}
44+
45+
var (
46+
// Default hosts config records slice
47+
defaultContent = []Record{
48+
{Hosts: "localhost", IP: "127.0.0.1"},
49+
{Hosts: "localhost ip6-localhost ip6-loopback", IP: "::1"},
50+
{Hosts: "ip6-localnet", IP: "fe00::0"},
51+
{Hosts: "ip6-mcastprefix", IP: "ff00::0"},
52+
{Hosts: "ip6-allnodes", IP: "ff02::1"},
53+
{Hosts: "ip6-allrouters", IP: "ff02::2"},
54+
}
55+
)
56+
57+
// BuildEtcHosts builds NOMAD_TASK_DIR/etc_hosts with defaults.
58+
func BuildEtcHosts(hostsFile string) error {
59+
content := bytes.NewBuffer(nil)
60+
61+
// Write defaultContent slice to buffer
62+
for _, r := range defaultContent {
63+
if _, err := r.WriteTo(content); err != nil {
64+
return err
65+
}
66+
}
67+
return ioutil.WriteFile(hostsFile, content.Bytes(), 0644)
68+
}
69+
70+
// CopyEtcHosts copies /etc/hosts to NOMAD_TASK_DIR/etc_hosts
71+
func CopyEtcHosts(hostsFile string) error {
72+
srcFile, err := os.Open("/etc/hosts")
73+
if err != nil {
74+
return err
75+
}
76+
defer srcFile.Close()
77+
78+
destFile, err := os.Create(hostsFile)
79+
if err != nil {
80+
return err
81+
}
82+
defer destFile.Close()
83+
84+
_, err = io.Copy(destFile, srcFile)
85+
if err != nil {
86+
return err
87+
}
88+
return nil
89+
}
90+
91+
// AddExtraHosts add hosts, given as name:IP to container /etc/hosts.
92+
func AddExtraHosts(hostsFile string, extraHosts []string) error {
93+
fd, err := os.OpenFile(hostsFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
94+
if err != nil {
95+
return err
96+
}
97+
defer fd.Close()
98+
99+
for _, extraHost := range extraHosts {
100+
// allow IPv6 addresses in extra hosts; only split on first ":"
101+
if _, err := opts.ValidateExtraHost(extraHost); err != nil {
102+
return err
103+
}
104+
105+
hostnameIP := strings.SplitN(extraHost, ":", 2)
106+
msg := fmt.Sprintf("%s\t%s\n", hostnameIP[1], hostnameIP[0])
107+
if _, err := fd.WriteString(msg); err != nil {
108+
return err
109+
}
110+
}
111+
return nil
112+
}

example/extra_hosts.nomad

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
job "extra_hosts" {
2+
datacenters = ["dc1"]
3+
4+
group "extra_hosts-group" {
5+
task "extra_hosts-task" {
6+
driver = "containerd-driver"
7+
config {
8+
image = "docker.io/library/ubuntu:16.04"
9+
extra_hosts = ["postgres:127.0.1.1", "redis:127.0.1.2"]
10+
host_network = true
11+
command = "sleep"
12+
args = ["600s"]
13+
}
14+
15+
resources {
16+
cpu = 500
17+
memory = 256
18+
}
19+
}
20+
}
21+
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ require (
1818
github.com/coreos/go-iptables v0.4.3 // indirect
1919
github.com/coreos/go-semver v0.3.0 // indirect
2020
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect
21+
github.com/docker/docker v17.12.0-ce-rc1.0.20200330121334-7f8b4b621b5d+incompatible
2122
github.com/docker/docker-credential-helpers v0.6.3 // indirect
2223
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
2324
github.com/docker/go-metrics v0.0.1 // indirect

go.sum

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,8 +192,10 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
192192
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
193193
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=
194194
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
195+
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c=
195196
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
196197
github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
198+
github.com/coreos/go-systemd/v22 v22.1.0 h1:kq/SbG2BCKLkDKkjQf5OWwKWUKj1lgs3lFI4PxnR5lg=
197199
github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
198200
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
199201
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
@@ -202,6 +204,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsr
202204
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
203205
github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg=
204206
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
207+
github.com/cyphar/filepath-securejoin v0.2.3-0.20190205144030-7efe413b52e1 h1:dCqRswe3ZAwkQWdvFLwRqmJCpGP3DWb7bFogdqY3+QU=
205208
github.com/cyphar/filepath-securejoin v0.2.3-0.20190205144030-7efe413b52e1/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
206209
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ=
207210
github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s=
@@ -306,7 +309,9 @@ github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3a
306309
github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
307310
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e h1:BWhy2j3IXJhjCbC68FptL43tDKIq8FladmaTs3Xs7Z8=
308311
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
312+
github.com/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7nQPrNITa4=
309313
github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
314+
github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME=
310315
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
311316
github.com/gogo/googleapis v1.1.0 h1:kFkMAZBNAn4j7K0GiZr8cRYzejq68VbheufiV3YuyFI=
312317
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=

tests/007-test-extra-hosts.sh

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#!/bin/bash
2+
3+
source $SRCDIR/utils.sh
4+
5+
job_name=extra_hosts
6+
7+
test_extra_hosts_nomad_job() {
8+
pushd ~/go/src/github.com/Roblox/nomad-driver-containerd/example
9+
10+
echo "INFO: Starting nomad $job_name job using nomad-driver-containerd."
11+
nomad job run $job_name.nomad
12+
13+
# Even though $(nomad job status) reports job status as "running"
14+
# The actual container process might not be running yet.
15+
# We need to wait for actual container to start running before trying exec.
16+
echo "INFO: Wait for ${job_name} container to get into RUNNING state, before trying exec."
17+
is_container_active ${job_name} true
18+
19+
echo "INFO: Checking status of $job_name job."
20+
job_status=$(nomad job status -short $job_name|grep Status|awk '{split($0,a,"="); print a[2]}'|tr -d ' ')
21+
if [ "$job_status" != "running" ];then
22+
echo "ERROR: Error in getting ${job_name} job status."
23+
return 1
24+
fi
25+
26+
echo "INFO: Checking extra hosts info in /etc/hosts."
27+
output=$(nomad alloc exec -job ${job_name} cat /etc/hosts)
28+
for host in "127.0.1.1 postgres" "127.0.1.2 redis" ; do
29+
echo -e "$output" |grep "$host" &>/dev/null
30+
if [ $? -ne 0 ];then
31+
echo "ERROR: extra host $host not found."
32+
return 1
33+
fi
34+
done
35+
36+
echo "INFO: Stopping nomad ${job_name} job."
37+
nomad job stop ${job_name}
38+
job_status=$(nomad job status -short ${job_name}|grep Status|awk '{split($0,a,"="); print a[2]}'|tr -d ' ')
39+
if [ $job_status != "dead(stopped)" ];then
40+
echo "ERROR: Error in stopping ${job_name} job."
41+
exit 1
42+
fi
43+
44+
echo "INFO: purge nomad ${job_name} job."
45+
nomad job stop -purge ${job_name}
46+
popd
47+
}
48+
49+
test_extra_hosts_nomad_job

0 commit comments

Comments
 (0)