diff --git a/libnetwork/config/config.go b/libnetwork/config/config.go index 320eb39e00..8da92f7a0e 100644 --- a/libnetwork/config/config.go +++ b/libnetwork/config/config.go @@ -61,6 +61,22 @@ func ParseConfig(tomlCfgFile string) (*Config, error) { return cfg, nil } +// ParseConfigOptions parses the configuration options and returns +// a reference to the corresponding Config structure +func ParseConfigOptions(cfgOptions ...Option) *Config { + cfg := &Config{ + Daemon: DaemonCfg{ + DriverCfg: make(map[string]interface{}), + }, + Scopes: make(map[string]*datastore.ScopeCfg), + } + + cfg.ProcessOptions(cfgOptions...) + cfg.LoadDefaultScopes(cfg.Daemon.DataDir) + + return cfg +} + // Option is an option setter function type used to pass various configurations // to the controller type Option func(c *Config) diff --git a/libnetwork/controller.go b/libnetwork/controller.go index 274eab2861..3a5e188cec 100644 --- a/libnetwork/controller.go +++ b/libnetwork/controller.go @@ -106,6 +106,9 @@ type NetworkController interface { // Stop network controller Stop() + + // ReloadCondfiguration updates the controller configuration + ReloadConfiguration(cfgOptions ...config.Option) error } // NetworkWalker is a client provided function which will be used to walk the Networks. @@ -129,7 +132,6 @@ type ipamData struct { } type driverTable map[string]*driverData - type ipamTable map[string]*ipamData type sandboxTable map[string]*sandbox @@ -153,22 +155,9 @@ type controller struct { // New creates a new instance of network controller. func New(cfgOptions ...config.Option) (NetworkController, error) { - var cfg *config.Config - cfg = &config.Config{ - Daemon: config.DaemonCfg{ - DriverCfg: make(map[string]interface{}), - }, - Scopes: make(map[string]*datastore.ScopeCfg), - } - - if len(cfgOptions) > 0 { - cfg.ProcessOptions(cfgOptions...) - } - cfg.LoadDefaultScopes(cfg.Daemon.DataDir) - c := &controller{ id: stringid.GenerateRandomID(), - cfg: cfg, + cfg: config.ParseConfigOptions(cfgOptions...), sandboxes: sandboxTable{}, drivers: driverTable{}, ipamDrivers: ipamTable{}, @@ -179,8 +168,8 @@ func New(cfgOptions ...config.Option) (NetworkController, error) { return nil, err } - if cfg != nil && cfg.Cluster.Watcher != nil { - if err := c.initDiscovery(cfg.Cluster.Watcher); err != nil { + if c.cfg != nil && c.cfg.Cluster.Watcher != nil { + if err := c.initDiscovery(c.cfg.Cluster.Watcher); err != nil { // Failing to initalize discovery is a bad situation to be in. // But it cannot fail creating the Controller log.Errorf("Failed to Initialize Discovery : %v", err) @@ -206,6 +195,83 @@ func New(cfgOptions ...config.Option) (NetworkController, error) { return c, nil } +var procReloadConfig = make(chan (bool), 1) + +func (c *controller) ReloadConfiguration(cfgOptions ...config.Option) error { + procReloadConfig <- true + defer func() { <-procReloadConfig }() + + // For now we accept the configuration reload only as a mean to provide a global store config after boot. + // Refuse the configuration if it alters an existing datastore client configuration. + update := false + cfg := config.ParseConfigOptions(cfgOptions...) + for s := range c.cfg.Scopes { + if _, ok := cfg.Scopes[s]; !ok { + return types.ForbiddenErrorf("cannot accept new configuration because it removes an existing datastore client") + } + } + for s, nSCfg := range cfg.Scopes { + if eSCfg, ok := c.cfg.Scopes[s]; ok { + if eSCfg.Client.Provider != nSCfg.Client.Provider || + eSCfg.Client.Address != nSCfg.Client.Address { + return types.ForbiddenErrorf("cannot accept new configuration because it modifies an existing datastore client") + } + } else { + update = true + } + } + if !update { + return nil + } + + c.Lock() + c.cfg = cfg + c.Unlock() + + if err := c.initStores(); err != nil { + return err + } + + if c.discovery == nil && c.cfg.Cluster.Watcher != nil { + if err := c.initDiscovery(c.cfg.Cluster.Watcher); err != nil { + log.Errorf("Failed to Initialize Discovery after configuration update: %v", err) + } + } + + var dsConfig *discoverapi.DatastoreConfigData + for scope, sCfg := range cfg.Scopes { + if scope == datastore.LocalScope || !sCfg.IsValid() { + continue + } + dsConfig = &discoverapi.DatastoreConfigData{ + Scope: scope, + Provider: sCfg.Client.Provider, + Address: sCfg.Client.Address, + Config: sCfg.Client.Config, + } + break + } + if dsConfig == nil { + return nil + } + + for nm, id := range c.getIpamDrivers() { + err := id.driver.DiscoverNew(discoverapi.DatastoreConfig, *dsConfig) + if err != nil { + log.Errorf("Failed to set datastore in driver %s: %v", nm, err) + } + } + + for nm, id := range c.getNetDrivers() { + err := id.driver.DiscoverNew(discoverapi.DatastoreConfig, *dsConfig) + if err != nil { + log.Errorf("Failed to set datastore in driver %s: %v", nm, err) + } + } + + return nil +} + func (c *controller) ID() string { return c.id } @@ -726,6 +792,26 @@ func (c *controller) getIpamDriver(name string) (ipamapi.Ipam, error) { return id.driver, nil } +func (c *controller) getIpamDrivers() ipamTable { + c.Lock() + defer c.Unlock() + table := ipamTable{} + for i, d := range c.ipamDrivers { + table[i] = d + } + return table +} + +func (c *controller) getNetDrivers() driverTable { + c.Lock() + defer c.Unlock() + table := driverTable{} + for i, d := range c.drivers { + table[i] = d + } + return table +} + func (c *controller) Stop() { c.closeStores() c.stopExternalKeyListener() diff --git a/libnetwork/datastore/datastore.go b/libnetwork/datastore/datastore.go index 4fc4a59740..687473d275 100644 --- a/libnetwork/datastore/datastore.go +++ b/libnetwork/datastore/datastore.go @@ -256,8 +256,13 @@ func NewDataStore(scope string, cfg *ScopeCfg) (DataStore, error) { // NewDataStoreFromConfig creates a new instance of LibKV data store starting from the datastore config data func NewDataStoreFromConfig(dsc discoverapi.DatastoreConfigData) (DataStore, error) { - sCfg, ok := dsc.Config.(*store.Config) - if !ok { + var ( + ok bool + sCfgP *store.Config + ) + + sCfgP, ok = dsc.Config.(*store.Config) + if !ok && dsc.Config != nil { return nil, fmt.Errorf("cannot parse store configuration: %v", dsc.Config) } @@ -265,7 +270,7 @@ func NewDataStoreFromConfig(dsc discoverapi.DatastoreConfigData) (DataStore, err Client: ScopeClientCfg{ Address: dsc.Address, Provider: dsc.Provider, - Config: sCfg, + Config: sCfgP, }, } diff --git a/libnetwork/store.go b/libnetwork/store.go index 4e639c4761..990eae6a86 100644 --- a/libnetwork/store.go +++ b/libnetwork/store.go @@ -14,6 +14,7 @@ func (c *controller) initStores() error { return nil } scopeConfigs := c.cfg.Scopes + c.stores = nil c.Unlock() for scope, scfg := range scopeConfigs { @@ -417,6 +418,9 @@ func (c *controller) watchLoop() { } func (c *controller) startWatch() { + if c.watchCh != nil { + return + } c.watchCh = make(chan *endpoint) c.unWatchCh = make(chan *endpoint) c.nmap = make(map[string]*netWatch)