Docker-DCO-1.1-Signed-off-by: Phil Estes <estesp@linux.vnet.ibm.com> (github: estesp)
| ... | ... |
@@ -4,6 +4,7 @@ import ( |
| 4 | 4 |
"fmt" |
| 5 | 5 |
"io/ioutil" |
| 6 | 6 |
"net" |
| 7 |
+ "os" |
|
| 7 | 8 |
"strings" |
| 8 | 9 |
"sync" |
| 9 | 10 |
|
| ... | ... |
@@ -104,8 +105,8 @@ func InitDriver(job *engine.Job) engine.Status {
|
| 104 | 104 |
if !usingDefaultBridge {
|
| 105 | 105 |
return job.Error(err) |
| 106 | 106 |
} |
| 107 |
- // If the iface is not found, try to create it |
|
| 108 |
- if err := createBridge(bridgeIP); err != nil {
|
|
| 107 |
+ // If the bridge interface is not found (or has no address), try to create it and/or add an address |
|
| 108 |
+ if err := configureBridge(bridgeIP); err != nil {
|
|
| 109 | 109 |
return job.Error(err) |
| 110 | 110 |
} |
| 111 | 111 |
|
| ... | ... |
@@ -251,10 +252,12 @@ func setupIPTables(addr net.Addr, icc, ipmasq bool) error {
|
| 251 | 251 |
return nil |
| 252 | 252 |
} |
| 253 | 253 |
|
| 254 |
-// CreateBridgeIface creates a network bridge interface on the host system with the name `ifaceName`, |
|
| 255 |
-// and attempts to configure it with an address which doesn't conflict with any other interface on the host. |
|
| 256 |
-// If it can't find an address which doesn't conflict, it will return an error. |
|
| 257 |
-func createBridge(bridgeIP string) error {
|
|
| 254 |
+// configureBridge attempts to create and configure a network bridge interface named `ifaceName` on the host |
|
| 255 |
+// If bridgeIP is empty, it will try to find a non-conflicting IP from the Docker-specified private ranges |
|
| 256 |
+// If the bridge `ifaceName` already exists, it will only perform the IP address association with the existing |
|
| 257 |
+// bridge (fixes issue #8444) |
|
| 258 |
+// If an address which doesn't conflict with existing interfaces can't be found, an error is returned. |
|
| 259 |
+func configureBridge(bridgeIP string) error {
|
|
| 258 | 260 |
nameservers := []string{}
|
| 259 | 261 |
resolvConf, _ := resolvconf.Get() |
| 260 | 262 |
// we don't check for an error here, because we don't really care |
| ... | ... |
@@ -295,7 +298,10 @@ func createBridge(bridgeIP string) error {
|
| 295 | 295 |
log.Debugf("Creating bridge %s with network %s", bridgeIface, ifaceAddr)
|
| 296 | 296 |
|
| 297 | 297 |
if err := createBridgeIface(bridgeIface); err != nil {
|
| 298 |
- return err |
|
| 298 |
+ // the bridge may already exist, therefore we can ignore an "exists" error |
|
| 299 |
+ if !os.IsExist(err) {
|
|
| 300 |
+ return err |
|
| 301 |
+ } |
|
| 299 | 302 |
} |
| 300 | 303 |
|
| 301 | 304 |
iface, err := net.InterfaceByName(bridgeIface) |
| ... | ... |
@@ -3,6 +3,7 @@ package main |
| 3 | 3 |
import ( |
| 4 | 4 |
"encoding/json" |
| 5 | 5 |
"os" |
| 6 |
+ "os/exec" |
|
| 6 | 7 |
"strings" |
| 7 | 8 |
"testing" |
| 8 | 9 |
) |
| ... | ... |
@@ -92,3 +93,38 @@ func TestDaemonStartIptablesFalse(t *testing.T) {
|
| 92 | 92 |
|
| 93 | 93 |
logDone("daemon - started daemon with iptables=false")
|
| 94 | 94 |
} |
| 95 |
+ |
|
| 96 |
+// Issue #8444: If docker0 bridge is modified (intentionally or unintentionally) and |
|
| 97 |
+// no longer has an IP associated, we should gracefully handle that case and associate |
|
| 98 |
+// an IP with it rather than fail daemon start |
|
| 99 |
+func TestDaemonStartBridgeWithoutIPAssociation(t *testing.T) {
|
|
| 100 |
+ d := NewDaemon(t) |
|
| 101 |
+ // rather than depending on brctl commands to verify docker0 is created and up |
|
| 102 |
+ // let's start the daemon and stop it, and then make a modification to run the |
|
| 103 |
+ // actual test |
|
| 104 |
+ if err := d.Start(); err != nil {
|
|
| 105 |
+ t.Fatalf("Could not start daemon: %v", err)
|
|
| 106 |
+ } |
|
| 107 |
+ if err := d.Stop(); err != nil {
|
|
| 108 |
+ t.Fatalf("Could not stop daemon: %v", err)
|
|
| 109 |
+ } |
|
| 110 |
+ |
|
| 111 |
+ // now we will remove the ip from docker0 and then try starting the daemon |
|
| 112 |
+ ipCmd := exec.Command("ip", "addr", "flush", "dev", "docker0")
|
|
| 113 |
+ stdout, stderr, _, err := runCommandWithStdoutStderr(ipCmd) |
|
| 114 |
+ if err != nil {
|
|
| 115 |
+ t.Fatalf("failed to remove docker0 IP association: %v, stdout: %q, stderr: %q", err, stdout, stderr)
|
|
| 116 |
+ } |
|
| 117 |
+ |
|
| 118 |
+ if err := d.Start(); err != nil {
|
|
| 119 |
+ warning := "**WARNING: Docker bridge network in bad state--delete docker0 bridge interface to fix" |
|
| 120 |
+ t.Fatalf("Could not start daemon when docker0 has no IP address: %v\n%s", err, warning)
|
|
| 121 |
+ } |
|
| 122 |
+ |
|
| 123 |
+ // cleanup - stop the daemon if test passed |
|
| 124 |
+ if err := d.Stop(); err != nil {
|
|
| 125 |
+ t.Fatalf("Could not stop daemon: %v", err)
|
|
| 126 |
+ } |
|
| 127 |
+ |
|
| 128 |
+ logDone("daemon - successful daemon start when bridge has no IP association")
|
|
| 129 |
+} |