Parcourir la source

Merge pull request #8453 from estesp/8444-create-bridge-error

Gracefully handle network bridge without IP association at startup
Andrea Luzzardi il y a 10 ans
Parent
commit
a76c924e6a

+ 13 - 7
daemon/networkdriver/bridge/driver.go

@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"fmt"
 	"io/ioutil"
 	"io/ioutil"
 	"net"
 	"net"
+	"os"
 	"strings"
 	"strings"
 	"sync"
 	"sync"
 
 
@@ -104,8 +105,8 @@ func InitDriver(job *engine.Job) engine.Status {
 		if !usingDefaultBridge {
 		if !usingDefaultBridge {
 			return job.Error(err)
 			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)
 			return job.Error(err)
 		}
 		}
 
 
@@ -251,10 +252,12 @@ func setupIPTables(addr net.Addr, icc, ipmasq bool) error {
 	return nil
 	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{}
 	nameservers := []string{}
 	resolvConf, _ := resolvconf.Get()
 	resolvConf, _ := resolvconf.Get()
 	// we don't check for an error here, because we don't really care
 	// 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)
 	log.Debugf("Creating bridge %s with network %s", bridgeIface, ifaceAddr)
 
 
 	if err := createBridgeIface(bridgeIface); err != nil {
 	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)
 	iface, err := net.InterfaceByName(bridgeIface)

+ 36 - 0
integration-cli/docker_cli_daemon_test.go

@@ -3,6 +3,7 @@ package main
 import (
 import (
 	"encoding/json"
 	"encoding/json"
 	"os"
 	"os"
+	"os/exec"
 	"strings"
 	"strings"
 	"testing"
 	"testing"
 )
 )
@@ -92,3 +93,38 @@ func TestDaemonStartIptablesFalse(t *testing.T) {
 
 
 	logDone("daemon - started daemon with iptables=false")
 	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")
+}