From ecffb6d58cf89371e3f4a20f55c2e614dbdfe880 Mon Sep 17 00:00:00 2001 From: Lei Jitang Date: Tue, 14 Jun 2016 09:13:53 -0700 Subject: [PATCH] Daemon to support network restore Signed-off-by: Lei Jitang --- daemon/cluster/cluster.go | 10 +------ daemon/cluster/executor/backend.go | 1 + daemon/config_solaris.go | 4 +++ daemon/config_unix.go | 11 +++++++ daemon/config_windows.go | 4 +++ daemon/container_operations.go | 30 +++++++++++-------- daemon/daemon.go | 48 +++++++++++++++++++++++------- daemon/daemon_solaris.go | 2 +- daemon/daemon_unix.go | 22 +++++++++----- daemon/daemon_unix_test.go | 4 +-- daemon/daemon_windows.go | 4 +-- daemon/network.go | 6 ++++ 12 files changed, 102 insertions(+), 44 deletions(-) diff --git a/daemon/cluster/cluster.go b/daemon/cluster/cluster.go index a83c2e5d14..2be4886a5b 100644 --- a/daemon/cluster/cluster.go +++ b/daemon/cluster/cluster.go @@ -121,14 +121,6 @@ func New(config Config) (*Cluster, error) { return c, nil } -func (c *Cluster) checkCompatibility() error { - info, _ := c.config.Backend.SystemInfo() - if info != nil && (info.ClusterStore != "" || info.ClusterAdvertise != "") { - return fmt.Errorf("swarm mode is incompatible with `--cluster-store` and `--cluster-advertise daemon configuration") - } - return nil -} - func (c *Cluster) saveState() error { dt, err := json.Marshal(state{ListenAddr: c.listenAddr}) if err != nil { @@ -173,7 +165,7 @@ func (c *Cluster) reconnectOnFailure(ctx context.Context) { } func (c *Cluster) startNewNode(forceNewCluster bool, listenAddr, joinAddr, secret, cahash string, ismanager bool) (*swarmagent.Node, context.Context, error) { - if err := c.checkCompatibility(); err != nil { + if err := c.config.Backend.IsSwarmCompatible(); err != nil { return nil, nil, err } c.node = nil diff --git a/daemon/cluster/executor/backend.go b/daemon/cluster/executor/backend.go index 6b0d0e5a48..3464a89cef 100644 --- a/daemon/cluster/executor/backend.go +++ b/daemon/cluster/executor/backend.go @@ -32,4 +32,5 @@ type Backend interface { ListContainersForNode(nodeID string) []string SetNetworkBootstrapKeys([]*networktypes.EncryptionKey) error SetClusterProvider(provider cluster.Provider) + IsSwarmCompatible() error } diff --git a/daemon/config_solaris.go b/daemon/config_solaris.go index 69b98165d5..d20fc07b91 100644 --- a/daemon/config_solaris.go +++ b/daemon/config_solaris.go @@ -37,3 +37,7 @@ func (config *Config) InstallFlags(cmd *flag.FlagSet, usageFn func(string) strin // Then platform-specific install flags config.attachExperimentalFlags(cmd, usageFn) } + +func (config *Config) isSwarmCompatible() error { + return nil +} diff --git a/daemon/config_unix.go b/daemon/config_unix.go index 4cb5390c21..d0fe0c01cd 100644 --- a/daemon/config_unix.go +++ b/daemon/config_unix.go @@ -3,6 +3,7 @@ package daemon import ( + "fmt" "net" "github.com/docker/docker/opts" @@ -120,3 +121,13 @@ func (config *Config) GetAllRuntimes() map[string]types.Runtime { config.reloadLock.Unlock() return rts } + +func (config *Config) isSwarmCompatible() error { + if config.IsValueSet("cluster-store") || config.IsValueSet("cluster-advertise") { + return fmt.Errorf("--cluster-store and --cluster-advertise daemon configurations are incompatible with swarm mode") + } + if config.LiveRestore { + return fmt.Errorf("--live-restore daemon configuration is incompatible with swarm mode") + } + return nil +} diff --git a/daemon/config_windows.go b/daemon/config_windows.go index f62ae95e73..5073f7ab61 100644 --- a/daemon/config_windows.go +++ b/daemon/config_windows.go @@ -57,3 +57,7 @@ func (config *Config) GetDefaultRuntimeName() string { func (config *Config) GetAllRuntimes() map[string]types.Runtime { return map[string]types.Runtime{} } + +func (config *Config) isSwarmCompatible() error { + return nil +} diff --git a/daemon/container_operations.go b/daemon/container_operations.go index c6fade219b..a335f5e0b0 100644 --- a/daemon/container_operations.go +++ b/daemon/container_operations.go @@ -30,7 +30,7 @@ var ( getPortMapInfo = container.GetSandboxPortMapInfo ) -func (daemon *Daemon) buildSandboxOptions(container *container.Container, n libnetwork.Network) ([]libnetwork.SandboxOption, error) { +func (daemon *Daemon) buildSandboxOptions(container *container.Container) ([]libnetwork.SandboxOption, error) { var ( sboxOptions []libnetwork.SandboxOption err error @@ -176,16 +176,19 @@ func (daemon *Daemon) buildSandboxOptions(container *container.Container, n libn // Legacy Link feature is supported only for the default bridge network. // return if this call to build join options is not for default bridge network - if n.Name() != defaultNetName { + // Legacy Link is only supported by docker run --link + if _, ok := container.NetworkSettings.Networks[defaultNetName]; !container.HostConfig.NetworkMode.IsDefault() || !ok { return sboxOptions, nil } - ep, _ := container.GetEndpointInNetwork(n) - if ep == nil { + if container.NetworkSettings.Networks[defaultNetName].EndpointID == "" { return sboxOptions, nil } - var childEndpoints, parentEndpoints []string + var ( + childEndpoints, parentEndpoints []string + cEndpointID string + ) children := daemon.children(container) for linkAlias, child := range children { @@ -200,9 +203,9 @@ func (daemon *Daemon) buildSandboxOptions(container *container.Container, n libn aliasList = aliasList + " " + child.Name[1:] } sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(aliasList, child.NetworkSettings.Networks[defaultNetName].IPAddress)) - cEndpoint, _ := child.GetEndpointInNetwork(n) - if cEndpoint != nil && cEndpoint.ID() != "" { - childEndpoints = append(childEndpoints, cEndpoint.ID()) + cEndpointID = child.NetworkSettings.Networks[defaultNetName].EndpointID + if cEndpointID != "" { + childEndpoints = append(childEndpoints, cEndpointID) } } @@ -219,8 +222,8 @@ func (daemon *Daemon) buildSandboxOptions(container *container.Container, n libn alias, bridgeSettings.IPAddress, )) - if ep.ID() != "" { - parentEndpoints = append(parentEndpoints, ep.ID()) + if cEndpointID != "" { + parentEndpoints = append(parentEndpoints, cEndpointID) } } @@ -312,7 +315,7 @@ func (daemon *Daemon) updateNetwork(container *container.Container) error { return nil } - options, err := daemon.buildSandboxOptions(container, n) + options, err := daemon.buildSandboxOptions(container) if err != nil { return fmt.Errorf("Update network failed: %v", err) } @@ -570,7 +573,7 @@ func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName } if sb == nil { - options, err := daemon.buildSandboxOptions(container, n) + options, err := daemon.buildSandboxOptions(container) if err != nil { return err } @@ -709,6 +712,9 @@ func (daemon *Daemon) getNetworkedContainer(containerID, connectedContainerID st } func (daemon *Daemon) releaseNetwork(container *container.Container) { + if daemon.netController == nil { + return + } if container.HostConfig.NetworkMode.IsContainer() || container.Config.NetworkDisabled { return } diff --git a/daemon/daemon.go b/daemon/daemon.go index 0c34c35359..03f08713ef 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -146,6 +146,7 @@ func (daemon *Daemon) restore() error { var migrateLegacyLinks bool restartContainers := make(map[*container.Container]chan struct{}) + activeSandboxes := make(map[string]interface{}) for _, c := range containers { if err := daemon.registerName(c); err != nil { logrus.Errorf("Failed to register container %s: %s", c.ID, err) @@ -178,6 +179,16 @@ func (daemon *Daemon) restore() error { logrus.Errorf("Failed to restore with containerd: %q", err) return } + if !c.HostConfig.NetworkMode.IsContainer() { + options, err := daemon.buildSandboxOptions(c) + if err != nil { + logrus.Warnf("Failed build sandbox option to restore container %s: %v", c.ID, err) + } + mapLock.Lock() + activeSandboxes[c.NetworkSettings.SandboxID] = options + mapLock.Unlock() + } + } // fixme: only if not running // get list of containers we need to restart @@ -209,6 +220,10 @@ func (daemon *Daemon) restore() error { }(c) } wg.Wait() + daemon.netController, err = daemon.initNetworkController(daemon.configStore, activeSandboxes) + if err != nil { + return fmt.Errorf("Error initializing network controller: %v", err) + } // migrate any legacy links from sqlite linkdbFile := filepath.Join(daemon.root, "linkgraph.db") @@ -356,6 +371,15 @@ func (daemon *Daemon) SetClusterProvider(clusterProvider cluster.Provider) { daemon.netController.SetClusterProvider(clusterProvider) } +// IsSwarmCompatible verifies if the current daemon +// configuration is compatible with the swarm mode +func (daemon *Daemon) IsSwarmCompatible() error { + if daemon.configStore == nil { + return nil + } + return daemon.configStore.isSwarmCompatible() +} + // NewDaemon sets up everything for the daemon to be able to service // requests from the webserver. func NewDaemon(config *Config, registryService registry.Service, containerdRemote libcontainerd.Remote) (daemon *Daemon, err error) { @@ -530,11 +554,6 @@ func NewDaemon(config *Config, registryService registry.Service, containerdRemot return nil, err } - d.netController, err = d.initNetworkController(config) - if err != nil { - return nil, fmt.Errorf("Error initializing network controller: %v", err) - } - sysInfo := sysinfo.New(false) // Check if Devices cgroup is mounted, it is hard requirement for container security, // on Linux. @@ -912,15 +931,17 @@ func (daemon *Daemon) reloadClusterDiscovery(config *Config) error { } } + if daemon.clusterProvider != nil { + if err := config.isSwarmCompatible(); err != nil { + return err + } + } + // check discovery modifications if !modifiedDiscoverySettings(daemon.configStore, newAdvertise, newClusterStore, config.ClusterOpts) { return nil } - if daemon.clusterProvider != nil { - return fmt.Errorf("--cluster-store and --cluster-advertise daemon configurations are incompatible with swarm mode") - } - // enable discovery for the first time if it was not previously enabled if daemon.discoveryWatcher == nil { discoveryWatcher, err := initDiscovery(newClusterStore, newAdvertise, config.ClusterOpts) @@ -947,7 +968,7 @@ func (daemon *Daemon) reloadClusterDiscovery(config *Config) error { if daemon.netController == nil { return nil } - netOptions, err := daemon.networkOptions(daemon.configStore) + netOptions, err := daemon.networkOptions(daemon.configStore, nil) if err != nil { logrus.Warnf("Failed to reload configuration with network controller: %v", err) return nil @@ -964,7 +985,7 @@ func isBridgeNetworkDisabled(config *Config) bool { return config.bridgeConfig.Iface == disableNetworkBridge } -func (daemon *Daemon) networkOptions(dconfig *Config) ([]nwconfig.Option, error) { +func (daemon *Daemon) networkOptions(dconfig *Config, activeSandboxes map[string]interface{}) ([]nwconfig.Option, error) { options := []nwconfig.Option{} if dconfig == nil { return options, nil @@ -999,6 +1020,11 @@ func (daemon *Daemon) networkOptions(dconfig *Config) ([]nwconfig.Option, error) options = append(options, nwconfig.OptionLabels(dconfig.Labels)) options = append(options, driverOptions(dconfig)...) + + if daemon.configStore != nil && daemon.configStore.LiveRestore && len(activeSandboxes) != 0 { + options = append(options, nwconfig.OptionActiveSandboxes(activeSandboxes)) + } + return options, nil } diff --git a/daemon/daemon_solaris.go b/daemon/daemon_solaris.go index 942525569a..48fed719fa 100644 --- a/daemon/daemon_solaris.go +++ b/daemon/daemon_solaris.go @@ -113,7 +113,7 @@ func configureKernelSecuritySupport(config *Config, driverName string) error { return nil } -func (daemon *Daemon) initNetworkController(config *Config) (libnetwork.NetworkController, error) { +func (daemon *Daemon) initNetworkController(config *Config, activeSandboxes map[string]interface{}) (libnetwork.NetworkController, error) { return nil, nil } diff --git a/daemon/daemon_unix.go b/daemon/daemon_unix.go index 03095b920e..7a868fc323 100644 --- a/daemon/daemon_unix.go +++ b/daemon/daemon_unix.go @@ -627,8 +627,8 @@ func configureKernelSecuritySupport(config *Config, driverName string) error { return nil } -func (daemon *Daemon) initNetworkController(config *Config) (libnetwork.NetworkController, error) { - netOptions, err := daemon.networkOptions(config) +func (daemon *Daemon) initNetworkController(config *Config, activeSandboxes map[string]interface{}) (libnetwork.NetworkController, error) { + netOptions, err := daemon.networkOptions(config, activeSandboxes) if err != nil { return nil, err } @@ -638,16 +638,24 @@ func (daemon *Daemon) initNetworkController(config *Config) (libnetwork.NetworkC return nil, fmt.Errorf("error obtaining controller instance: %v", err) } + if len(activeSandboxes) > 0 { + logrus.Infof("There are old running containers, the network config will not take affect") + return controller, nil + } + // Initialize default network on "null" - if _, err := controller.NewNetwork("null", "none", "", libnetwork.NetworkOptionPersist(false)); err != nil { - return nil, fmt.Errorf("Error creating default \"null\" network: %v", err) + if n, _ := controller.NetworkByName("none"); n == nil { + if _, err := controller.NewNetwork("null", "none", "", libnetwork.NetworkOptionPersist(true)); err != nil { + return nil, fmt.Errorf("Error creating default \"null\" network: %v", err) + } } // Initialize default network on "host" - if _, err := controller.NewNetwork("host", "host", "", libnetwork.NetworkOptionPersist(false)); err != nil { - return nil, fmt.Errorf("Error creating default \"host\" network: %v", err) + if n, _ := controller.NetworkByName("host"); n == nil { + if _, err := controller.NewNetwork("host", "host", "", libnetwork.NetworkOptionPersist(true)); err != nil { + return nil, fmt.Errorf("Error creating default \"host\" network: %v", err) + } } - if !config.DisableBridge { // Initialize default driver "bridge" if err := initBridgeDriver(controller, config); err != nil { diff --git a/daemon/daemon_unix_test.go b/daemon/daemon_unix_test.go index 7bf307cd7b..fae84bab6a 100644 --- a/daemon/daemon_unix_test.go +++ b/daemon/daemon_unix_test.go @@ -183,7 +183,7 @@ func TestNetworkOptions(t *testing.T) { }, } - if _, err := daemon.networkOptions(dconfigCorrect); err != nil { + if _, err := daemon.networkOptions(dconfigCorrect, nil); err != nil { t.Fatalf("Expect networkOptions success, got error: %v", err) } @@ -193,7 +193,7 @@ func TestNetworkOptions(t *testing.T) { }, } - if _, err := daemon.networkOptions(dconfigWrong); err == nil { + if _, err := daemon.networkOptions(dconfigWrong, nil); err == nil { t.Fatalf("Expected networkOptions error, got nil") } } diff --git a/daemon/daemon_windows.go b/daemon/daemon_windows.go index 423b86b5e5..fadb2b1fa6 100644 --- a/daemon/daemon_windows.go +++ b/daemon/daemon_windows.go @@ -189,8 +189,8 @@ func configureMaxThreads(config *Config) error { return nil } -func (daemon *Daemon) initNetworkController(config *Config) (libnetwork.NetworkController, error) { - netOptions, err := daemon.networkOptions(config) +func (daemon *Daemon) initNetworkController(config *Config, activeSandboxes map[string]interface{}) (libnetwork.NetworkController, error) { + netOptions, err := daemon.networkOptions(config, nil) if err != nil { return nil, err } diff --git a/daemon/network.go b/daemon/network.go index f3203621d9..420c3d98f2 100644 --- a/daemon/network.go +++ b/daemon/network.go @@ -59,6 +59,9 @@ func (daemon *Daemon) GetNetworkByID(partialID string) (libnetwork.Network, erro // GetNetworkByName function returns a network for a given network name. func (daemon *Daemon) GetNetworkByName(name string) (libnetwork.Network, error) { c := daemon.netController + if c == nil { + return nil, libnetwork.ErrNoSuchNetwork(name) + } if name == "" { name = c.Config().Daemon.DefaultNetwork } @@ -68,6 +71,9 @@ func (daemon *Daemon) GetNetworkByName(name string) (libnetwork.Network, error) // GetNetworksByID returns a list of networks whose ID partially matches zero or more networks func (daemon *Daemon) GetNetworksByID(partialID string) []libnetwork.Network { c := daemon.netController + if c == nil { + return nil + } list := []libnetwork.Network{} l := func(nw libnetwork.Network) bool { if strings.HasPrefix(nw.ID(), partialID) {