From 1262b5f605e8f897acb2b9ee261edbab7f1f8947 Mon Sep 17 00:00:00 2001 From: Phil Estes Date: Sun, 5 Oct 2014 00:21:59 -0400 Subject: [PATCH] Gracefully handle network bridge without IP association at startup Addresses #8444 Docker-DCO-1.1-Signed-off-by: Phil Estes (github: estesp) --- daemon/networkdriver/bridge/driver.go | 20 ++++++++----- integration-cli/docker_cli_daemon_test.go | 36 +++++++++++++++++++++++ 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/daemon/networkdriver/bridge/driver.go b/daemon/networkdriver/bridge/driver.go index e05a2c21a5..44d864e709 100644 --- a/daemon/networkdriver/bridge/driver.go +++ b/daemon/networkdriver/bridge/driver.go @@ -4,6 +4,7 @@ import ( "fmt" "io/ioutil" "net" + "os" "strings" "sync" @@ -104,8 +105,8 @@ func InitDriver(job *engine.Job) engine.Status { if !usingDefaultBridge { return job.Error(err) } - // If the iface is not found, try to create it - if err := createBridge(bridgeIP); err != nil { + // If the bridge interface is not found (or has no address), try to create it and/or add an address + if err := configureBridge(bridgeIP); err != nil { return job.Error(err) } @@ -251,10 +252,12 @@ func setupIPTables(addr net.Addr, icc, ipmasq bool) error { return nil } -// CreateBridgeIface creates a network bridge interface on the host system with the name `ifaceName`, -// and attempts to configure it with an address which doesn't conflict with any other interface on the host. -// If it can't find an address which doesn't conflict, it will return an error. -func createBridge(bridgeIP string) error { +// configureBridge attempts to create and configure a network bridge interface named `ifaceName` on the host +// If bridgeIP is empty, it will try to find a non-conflicting IP from the Docker-specified private ranges +// If the bridge `ifaceName` already exists, it will only perform the IP address association with the existing +// bridge (fixes issue #8444) +// If an address which doesn't conflict with existing interfaces can't be found, an error is returned. +func configureBridge(bridgeIP string) error { nameservers := []string{} resolvConf, _ := resolvconf.Get() // we don't check for an error here, because we don't really care @@ -295,7 +298,10 @@ func createBridge(bridgeIP string) error { log.Debugf("Creating bridge %s with network %s", bridgeIface, ifaceAddr) if err := createBridgeIface(bridgeIface); err != nil { - return err + // the bridge may already exist, therefore we can ignore an "exists" error + if !os.IsExist(err) { + return err + } } iface, err := net.InterfaceByName(bridgeIface) diff --git a/integration-cli/docker_cli_daemon_test.go b/integration-cli/docker_cli_daemon_test.go index 6160e57e94..9d238c15ee 100644 --- a/integration-cli/docker_cli_daemon_test.go +++ b/integration-cli/docker_cli_daemon_test.go @@ -3,6 +3,7 @@ package main import ( "encoding/json" "os" + "os/exec" "strings" "testing" ) @@ -92,3 +93,38 @@ func TestDaemonStartIptablesFalse(t *testing.T) { logDone("daemon - started daemon with iptables=false") } + +// Issue #8444: If docker0 bridge is modified (intentionally or unintentionally) and +// no longer has an IP associated, we should gracefully handle that case and associate +// an IP with it rather than fail daemon start +func TestDaemonStartBridgeWithoutIPAssociation(t *testing.T) { + d := NewDaemon(t) + // rather than depending on brctl commands to verify docker0 is created and up + // let's start the daemon and stop it, and then make a modification to run the + // actual test + if err := d.Start(); err != nil { + t.Fatalf("Could not start daemon: %v", err) + } + if err := d.Stop(); err != nil { + t.Fatalf("Could not stop daemon: %v", err) + } + + // now we will remove the ip from docker0 and then try starting the daemon + ipCmd := exec.Command("ip", "addr", "flush", "dev", "docker0") + stdout, stderr, _, err := runCommandWithStdoutStderr(ipCmd) + if err != nil { + t.Fatalf("failed to remove docker0 IP association: %v, stdout: %q, stderr: %q", err, stdout, stderr) + } + + if err := d.Start(); err != nil { + warning := "**WARNING: Docker bridge network in bad state--delete docker0 bridge interface to fix" + t.Fatalf("Could not start daemon when docker0 has no IP address: %v\n%s", err, warning) + } + + // cleanup - stop the daemon if test passed + if err := d.Stop(); err != nil { + t.Fatalf("Could not stop daemon: %v", err) + } + + logDone("daemon - successful daemon start when bridge has no IP association") +}