-
Notifications
You must be signed in to change notification settings - Fork 2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
docker: generate /etc/hosts file for bridge network mode (#10766)
When `network.mode = "bridge"`, we create a pause container in Docker with no networking so that we have a process to hold the network namespace we create in Nomad. The default `/etc/hosts` file of that pause container is then used for all the Docker tasks that share that network namespace. Some applications rely on this file being populated. This changeset generates a `/etc/hosts` file and bind-mounts it to the container when Nomad owns the network, so that the container's hostname has an IP in the file as expected. The hosts file will include the entries added by the Docker driver's `extra_hosts` field. In this changeset, only the Docker task driver will take advantage of this option, as the `exec`/`java` drivers currently copy the host's `/etc/hosts` file and this can't be changed without breaking backwards compatibility. But the fields are available in the task driver protobuf for community task drivers to use if they'd like.
- Loading branch information
Showing
10 changed files
with
612 additions
and
271 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package hostnames | ||
|
||
import ( | ||
"fmt" | ||
"io/ioutil" | ||
"net" | ||
"path/filepath" | ||
"strings" | ||
|
||
"github.com/hashicorp/nomad/plugins/drivers" | ||
) | ||
|
||
// GenerateEtcHostsMount writes a /etc/hosts file using the network spec's | ||
// hosts configuration, and returns a mount config so that task drivers can | ||
// bind-mount it into the resulting task's filesystem. The extraHosts | ||
// parameter is expected to be the same format as the extra_hosts field from | ||
// the Docker or containerd drivers: []string{"<hostname>:<ip address>"} | ||
func GenerateEtcHostsMount(taskDir string, conf *drivers.NetworkIsolationSpec, extraHosts []string) (*drivers.MountConfig, error) { | ||
if conf == nil || conf.Mode != drivers.NetIsolationModeGroup { | ||
return nil, nil | ||
} | ||
hostsCfg := conf.HostsConfig | ||
if hostsCfg == nil || hostsCfg.Address == "" || hostsCfg.Hostname == "" { | ||
return nil, nil | ||
} | ||
|
||
var content strings.Builder | ||
fmt.Fprintf(&content, `# this file was generated by Nomad | ||
127.0.0.1 localhost | ||
::1 localhost | ||
::1 ip6-localhost ip6-loopback | ||
fe00::0 ip6-localnet | ||
ff00::0 ip6-mcastprefix | ||
ff02::1 ip6-allnodes | ||
ff02::2 ip6-allrouters | ||
ff02::3 ip6-allhosts | ||
# this entry is the IP address and hostname of the allocation | ||
# shared with tasks in the task group's network | ||
%s %s | ||
`, hostsCfg.Address, hostsCfg.Hostname) | ||
|
||
if len(extraHosts) > 0 { | ||
content.WriteString("\n# these entries are extra hosts added by the task config") | ||
for _, hostLine := range extraHosts { | ||
hostsEntry := strings.SplitN(hostLine, ":", 2) | ||
if len(hostsEntry) != 2 { | ||
return nil, fmt.Errorf("invalid hosts entry %q", hostLine) | ||
} | ||
if net.ParseIP(hostsEntry[1]) == nil { | ||
return nil, fmt.Errorf("invalid IP address %q", hostLine) | ||
} | ||
content.WriteString(fmt.Sprintf("\n%s %s", hostsEntry[1], hostsEntry[0])) | ||
} | ||
content.WriteString("\n") | ||
} | ||
|
||
path := filepath.Join(taskDir, "hosts") | ||
err := ioutil.WriteFile(path, []byte(content.String()), 0644) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// Note that we're not setting readonly. The file is in the task dir | ||
// anyways, so this lets the task overwrite its own hosts file if the | ||
// application knows better than Nomad here. Task drivers may override | ||
// this behavior. | ||
mount := &drivers.MountConfig{ | ||
TaskPath: "/etc/hosts", | ||
HostPath: path, | ||
Readonly: false, | ||
PropagationMode: "private", | ||
} | ||
|
||
return mount, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
// +build !windows | ||
|
||
package hostnames | ||
|
||
import ( | ||
"fmt" | ||
"io/ioutil" | ||
"os" | ||
"path/filepath" | ||
"testing" | ||
|
||
"github.com/hashicorp/nomad/plugins/drivers" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestGenerateEtcHostsMount(t *testing.T) { | ||
|
||
testCases := []struct { | ||
name string | ||
spec *drivers.NetworkIsolationSpec | ||
extraHosts []string | ||
expected []string | ||
expectedErr string | ||
}{ | ||
{ | ||
name: "no-spec", | ||
}, | ||
{ | ||
name: "no-hosts-config", | ||
spec: &drivers.NetworkIsolationSpec{Mode: drivers.NetIsolationModeGroup}, | ||
}, | ||
{ | ||
name: "base-case", | ||
spec: &drivers.NetworkIsolationSpec{ | ||
Mode: drivers.NetIsolationModeGroup, | ||
HostsConfig: &drivers.HostsConfig{ | ||
Address: "192.168.1.1", | ||
Hostname: "xyzzy", | ||
}, | ||
}, | ||
expected: []string{ | ||
"192.168.1.1 xyzzy", | ||
}, | ||
}, | ||
{ | ||
name: "with-valid-extra-hosts", | ||
spec: &drivers.NetworkIsolationSpec{ | ||
Mode: drivers.NetIsolationModeGroup, | ||
HostsConfig: &drivers.HostsConfig{ | ||
Address: "192.168.1.1", | ||
Hostname: "xyzzy", | ||
}, | ||
}, | ||
extraHosts: []string{ | ||
"apple:192.168.1.2", | ||
"banana:2001:0db8:85a3:0000:0000:8a2e:0370:7334", | ||
}, | ||
expected: []string{ | ||
"192.168.1.1 xyzzy", | ||
"192.168.1.2 apple", | ||
"2001:0db8:85a3:0000:0000:8a2e:0370:7334 banana", | ||
}, | ||
}, | ||
{ | ||
name: "invalid-extra-hosts-syntax", | ||
spec: &drivers.NetworkIsolationSpec{ | ||
Mode: drivers.NetIsolationModeGroup, | ||
HostsConfig: &drivers.HostsConfig{ | ||
Address: "192.168.1.1", | ||
Hostname: "xyzzy", | ||
}, | ||
}, | ||
extraHosts: []string{"apple192.168.1.2"}, | ||
expectedErr: "invalid hosts entry \"apple192.168.1.2\"", | ||
}, | ||
{ | ||
name: "invalid-extra-hosts-bad-ip", | ||
spec: &drivers.NetworkIsolationSpec{ | ||
Mode: drivers.NetIsolationModeGroup, | ||
HostsConfig: &drivers.HostsConfig{ | ||
Address: "192.168.1.1", | ||
Hostname: "xyzzy", | ||
}, | ||
}, | ||
extraHosts: []string{"apple:192.168.1.256"}, | ||
expectedErr: "invalid IP address \"apple:192.168.1.256\"", | ||
}, | ||
} | ||
for _, tc := range testCases { | ||
tc := tc | ||
t.Run(tc.name, func(t *testing.T) { | ||
require := require.New(t) | ||
|
||
taskDir, err := ioutil.TempDir("", | ||
fmt.Sprintf("generateEtcHosts_Test-%s", tc.name)) | ||
defer os.RemoveAll(taskDir) | ||
require.NoError(err) | ||
dest := filepath.Join(taskDir, "hosts") | ||
|
||
got, err := GenerateEtcHostsMount(taskDir, tc.spec, tc.extraHosts) | ||
|
||
if tc.expectedErr != "" { | ||
require.EqualError(err, tc.expectedErr) | ||
} else { | ||
require.NoError(err) | ||
} | ||
if len(tc.expected) == 0 { | ||
require.Nil(got) | ||
} else { | ||
require.NotNil(got) | ||
require.FileExists(dest) | ||
tmpHosts, err := ioutil.ReadFile(dest) | ||
require.NoError(err) | ||
for _, line := range tc.expected { | ||
require.Contains(string(tmpHosts), line) | ||
} | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.