Skip to content

Commit

Permalink
Merge pull request #71 from Roblox/extra_hosts
Browse files Browse the repository at this point in the history
Add support for extra_hosts.
  • Loading branch information
shishir-a412ed authored Feb 24, 2021
2 parents b05eab6 + c94fd39 commit f40d962
Show file tree
Hide file tree
Showing 8 changed files with 221 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ More detailed instructions are in the [`example README.md`](https://github.com/R
| **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. |
| **readonly_rootfs** | bool | no | Container root filesystem will be read-only. |
| **host_network** | bool | no | Enable host network. This is equivalent to `--net=host` in docker. |
| **extra_hosts** | []string | no | A list of hosts, given as host:IP, to be added to /etc/hosts. |
| **cap_add** | []string | no | Add individual capabilities. |
| **cap_drop** | []string | no | Drop invidual capabilities. |
| **devices** | []string | no | A list of devices to be exposed to the container. |
Expand Down
30 changes: 30 additions & 0 deletions containerd/containerd.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"syscall"
"time"

etchosts "github.com/Roblox/nomad-driver-containerd/etchosts"
"github.com/containerd/containerd"
"github.com/containerd/containerd/cio"
"github.com/containerd/containerd/contrib/seccomp"
Expand Down Expand Up @@ -185,6 +186,35 @@ func (d *Driver) createContainer(containerConfig *ContainerConfig, config *TaskC
mounts = append(mounts, allocMount)
}

// User will specify extra_hosts to be added to container's /etc/hosts.
// If host_network=true, extra_hosts will be added to host's /etc/hosts.
// If host_network=false, extra hosts will be added to the default /etc/hosts provided to the container.
// If the user doesn't set anything (host_network, extra_hosts), a default /etc/hosts will be provided to the container.
var extraHostsMount specs.Mount
hostsFile := containerConfig.TaskDirSrc + "/etc_hosts"
if len(config.ExtraHosts) > 0 {
if config.HostNetwork {
if err := etchosts.CopyEtcHosts(hostsFile); err != nil {
return nil, err
}
} else {
if err := etchosts.BuildEtcHosts(hostsFile); err != nil {
return nil, err
}
}
if err := etchosts.AddExtraHosts(hostsFile, config.ExtraHosts); err != nil {
return nil, err
}
extraHostsMount = buildMountpoint("bind", "/etc/hosts", hostsFile, []string{"rbind", "rw"})
mounts = append(mounts, extraHostsMount)
} else if !config.HostNetwork {
if err := etchosts.BuildEtcHosts(hostsFile); err != nil {
return nil, err
}
extraHostsMount = buildMountpoint("bind", "/etc/hosts", hostsFile, []string{"rbind", "rw"})
mounts = append(mounts, extraHostsMount)
}

if len(mounts) > 0 {
opts = append(opts, oci.WithMounts(mounts))
}
Expand Down
2 changes: 2 additions & 0 deletions containerd/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ var (
hclspec.NewAttr("host_dns", "bool", false),
hclspec.NewLiteral("true"),
),
"extra_hosts": hclspec.NewAttr("extra_hosts", "list(string)", false),
"seccomp": hclspec.NewAttr("seccomp", "bool", false),
"seccomp_profile": hclspec.NewAttr("seccomp_profile", "string", false),
"readonly_rootfs": hclspec.NewAttr("readonly_rootfs", "bool", false),
Expand Down Expand Up @@ -154,6 +155,7 @@ type TaskConfig struct {
SeccompProfile string `codec:"seccomp_profile"`
Privileged bool `codec:"privileged"`
HostDNS bool `codec:"host_dns"`
ExtraHosts []string `codec:"extra_hosts"`
ReadOnlyRootfs bool `codec:"readonly_rootfs"`
HostNetwork bool `codec:"host_network"`
Mounts []Mount `codec:"mounts"`
Expand Down
112 changes: 112 additions & 0 deletions etchosts/etchosts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
Copyright 2020 Roblox Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package etchosts

import (
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"strings"

"github.com/docker/docker/opts"
)

// Code referenced from https://github.com/moby/libnetwork/blob/master/etchosts/etchosts.go

// Record Structure for a single host record
type Record struct {
Hosts string
IP string
}

// WriteTo writes record to file and returns bytes written or error
func (r Record) WriteTo(w io.Writer) (int64, error) {
n, err := fmt.Fprintf(w, "%s\t%s\n", r.IP, r.Hosts)
return int64(n), err
}

var (
// Default hosts config records slice
defaultContent = []Record{
{Hosts: "localhost", IP: "127.0.0.1"},
{Hosts: "localhost ip6-localhost ip6-loopback", IP: "::1"},
{Hosts: "ip6-localnet", IP: "fe00::0"},
{Hosts: "ip6-mcastprefix", IP: "ff00::0"},
{Hosts: "ip6-allnodes", IP: "ff02::1"},
{Hosts: "ip6-allrouters", IP: "ff02::2"},
}
)

// BuildEtcHosts builds NOMAD_TASK_DIR/etc_hosts with defaults.
func BuildEtcHosts(hostsFile string) error {
content := bytes.NewBuffer(nil)

// Write defaultContent slice to buffer
for _, r := range defaultContent {
if _, err := r.WriteTo(content); err != nil {
return err
}
}
return ioutil.WriteFile(hostsFile, content.Bytes(), 0644)
}

// CopyEtcHosts copies /etc/hosts to NOMAD_TASK_DIR/etc_hosts
func CopyEtcHosts(hostsFile string) error {
srcFile, err := os.Open("/etc/hosts")
if err != nil {
return err
}
defer srcFile.Close()

destFile, err := os.Create(hostsFile)
if err != nil {
return err
}
defer destFile.Close()

_, err = io.Copy(destFile, srcFile)
if err != nil {
return err
}
return nil
}

// AddExtraHosts add hosts, given as name:IP to container /etc/hosts.
func AddExtraHosts(hostsFile string, extraHosts []string) error {
fd, err := os.OpenFile(hostsFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return err
}
defer fd.Close()

for _, extraHost := range extraHosts {
// allow IPv6 addresses in extra hosts; only split on first ":"
if _, err := opts.ValidateExtraHost(extraHost); err != nil {
return err
}

hostnameIP := strings.SplitN(extraHost, ":", 2)
msg := fmt.Sprintf("%s\t%s\n", hostnameIP[1], hostnameIP[0])
if _, err := fd.WriteString(msg); err != nil {
return err
}
}
return nil
}
21 changes: 21 additions & 0 deletions example/extra_hosts.nomad
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
job "extra_hosts" {
datacenters = ["dc1"]

group "extra_hosts-group" {
task "extra_hosts-task" {
driver = "containerd-driver"
config {
image = "docker.io/library/ubuntu:16.04"
extra_hosts = ["postgres:127.0.1.1", "redis:127.0.1.2"]
host_network = true
command = "sleep"
args = ["600s"]
}

resources {
cpu = 500
memory = 256
}
}
}
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ require (
github.com/coreos/go-iptables v0.4.3 // indirect
github.com/coreos/go-semver v0.3.0 // indirect
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect
github.com/docker/docker v17.12.0-ce-rc1.0.20200330121334-7f8b4b621b5d+incompatible
github.com/docker/docker-credential-helpers v0.6.3 // indirect
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
github.com/docker/go-metrics v0.0.1 // indirect
Expand Down
5 changes: 5 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,10 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
github.com/coreos/go-systemd/v22 v22.1.0 h1:kq/SbG2BCKLkDKkjQf5OWwKWUKj1lgs3lFI4PxnR5lg=
github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
Expand All @@ -202,6 +204,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsr
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg=
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
github.com/cyphar/filepath-securejoin v0.2.3-0.20190205144030-7efe413b52e1 h1:dCqRswe3ZAwkQWdvFLwRqmJCpGP3DWb7bFogdqY3+QU=
github.com/cyphar/filepath-securejoin v0.2.3-0.20190205144030-7efe413b52e1/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ=
github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s=
Expand Down Expand Up @@ -306,7 +309,9 @@ github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3a
github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e h1:BWhy2j3IXJhjCbC68FptL43tDKIq8FladmaTs3Xs7Z8=
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
github.com/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7nQPrNITa4=
github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME=
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/googleapis v1.1.0 h1:kFkMAZBNAn4j7K0GiZr8cRYzejq68VbheufiV3YuyFI=
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
Expand Down
49 changes: 49 additions & 0 deletions tests/007-test-extra-hosts.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#!/bin/bash

source $SRCDIR/utils.sh

job_name=extra_hosts

test_extra_hosts_nomad_job() {
pushd ~/go/src/github.com/Roblox/nomad-driver-containerd/example

echo "INFO: Starting nomad $job_name job using nomad-driver-containerd."
nomad job run $job_name.nomad

# Even though $(nomad job status) reports job status as "running"
# The actual container process might not be running yet.
# We need to wait for actual container to start running before trying exec.
echo "INFO: Wait for ${job_name} container to get into RUNNING state, before trying exec."
is_container_active ${job_name} true

echo "INFO: Checking status of $job_name job."
job_status=$(nomad job status -short $job_name|grep Status|awk '{split($0,a,"="); print a[2]}'|tr -d ' ')
if [ "$job_status" != "running" ];then
echo "ERROR: Error in getting ${job_name} job status."
return 1
fi

echo "INFO: Checking extra hosts info in /etc/hosts."
output=$(nomad alloc exec -job ${job_name} cat /etc/hosts)
for host in "127.0.1.1 postgres" "127.0.1.2 redis" ; do
echo -e "$output" |grep "$host" &>/dev/null
if [ $? -ne 0 ];then
echo "ERROR: extra host $host not found."
return 1
fi
done

echo "INFO: Stopping nomad ${job_name} job."
nomad job stop ${job_name}
job_status=$(nomad job status -short ${job_name}|grep Status|awk '{split($0,a,"="); print a[2]}'|tr -d ' ')
if [ $job_status != "dead(stopped)" ];then
echo "ERROR: Error in stopping ${job_name} job."
exit 1
fi

echo "INFO: purge nomad ${job_name} job."
nomad job stop -purge ${job_name}
popd
}

test_extra_hosts_nomad_job

0 comments on commit f40d962

Please sign in to comment.