diff --git a/CHANGELOG.md b/CHANGELOG.md index a6b1fbe082e..e2e2e6a9e8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ IMPROVEMENTS: * driver/docker: Allow setting seccomp profiles [GH-2658] * driver/docker: Support Docker credential helpers [GH-2651] * driver/docker: Auth failures can optionally be ignored [GH-2786] + * driver/docker: Add `driver.docker.bridge_ip` node attribute [GH-2797] * driver/docker: Allow setting container IP with user defined networks [GH-2535] * driver/rkt: Support `no_overlay` [GH-2702] diff --git a/client/driver/docker.go b/client/driver/docker.go index 9a75cc66096..b49cd8ccc46 100644 --- a/client/driver/docker.go +++ b/client/driver/docker.go @@ -333,6 +333,24 @@ func (d *DockerDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool node.Attributes["driver."+dockerVolumesConfigOption] = "1" } + // Detect bridge IP address - #2785 + if nets, err := client.ListNetworks(); err != nil { + d.logger.Printf("[WARN] driver.docker: error discovering bridge IP: %v", err) + } else { + for _, n := range nets { + if n.Name != "bridge" { + continue + } + + if len(n.IPAM.Config) == 0 { + d.logger.Printf("[WARN] driver.docker: no IPAM config for bridge network") + break + } + + node.Attributes["driver.docker.bridge_ip"] = n.IPAM.Config[0].Gateway + } + } + d.fingerprintSuccess = helper.BoolToPtr(true) return true, nil } diff --git a/client/driver/docker_test.go b/client/driver/docker_test.go index 404b9513797..872852ba96d 100644 --- a/client/driver/docker_test.go +++ b/client/driver/docker_test.go @@ -15,6 +15,7 @@ import ( "time" docker "github.com/fsouza/go-dockerclient" + sockaddr "github.com/hashicorp/go-sockaddr" "github.com/hashicorp/nomad/client/allocdir" "github.com/hashicorp/nomad/client/config" "github.com/hashicorp/nomad/client/driver/env" @@ -177,6 +178,40 @@ func TestDockerDriver_Fingerprint(t *testing.T) { t.Logf("Found docker version %s", node.Attributes["driver.docker.version"]) } +// TestDockerDriver_Fingerprint_Bridge asserts that if Docker is running we set +// the bridge network's IP as a node attribute. See #2785 +func TestDockerDriver_Fingerprint_Bridge(t *testing.T) { + if !testutil.DockerIsConnected(t) { + t.Skip("requires Docker") + } + + // This seems fragile, so we might need to reconsider this test if it + // proves flaky + expectedAddr, err := sockaddr.GetInterfaceIP("docker0") + if err != nil { + t.Fatalf("unable to get ip for docker0: %v", err) + } + if expectedAddr == "" { + t.Fatalf("unable to get ip for docker bridge") + } + + conf := testConfig() + conf.Node = mock.Node() + dd := NewDockerDriver(NewDriverContext("", "", conf, conf.Node, testLogger(), nil)) + ok, err := dd.Fingerprint(conf, conf.Node) + if err != nil { + t.Fatalf("error fingerprinting docker: %v", err) + } + if !ok { + t.Fatalf("expected Docker to be enabled but false was returned") + } + + if found := conf.Node.Attributes["driver.docker.bridge_ip"]; found != expectedAddr { + t.Fatalf("expected bridge ip %q but found: %q", expectedAddr, found) + } + t.Logf("docker bridge ip: %q", conf.Node.Attributes["driver.docker.bridge_ip"]) +} + func TestDockerDriver_StartOpen_Wait(t *testing.T) { if !testutil.DockerIsConnected(t) { t.SkipNow() diff --git a/website/source/docs/drivers/docker.html.md b/website/source/docs/drivers/docker.html.md index ae1844cba8c..060274ee823 100644 --- a/website/source/docs/drivers/docker.html.md +++ b/website/source/docs/drivers/docker.html.md @@ -529,6 +529,8 @@ The `docker` driver will set the following client attributes: * `driver.docker` - This will be set to "1", indicating the driver is available. +* `driver.docker.bridge_ip` - The IP of the Docker bridge network if one + exists. * `driver.docker.version` - This will be set to version of the docker server. Here is an example of using these properties in a job file: