ocicni: Support asynchronous network config creation
We need to support cases where InitCNI() is called before any CNI configuration files have been installed. This is for example happening when deploying a k8s cluster with kubeadm. kubeadm will start the DNS pod and it is left to the caller to pick a network overlay and create the corresponding pods, that will typically install a CNI configuration file first. Here we address that issue by doing 2 things: - Not returning an error when the default CNI config files directory is empty. - If it is empty, we start a monitoring thread (fsnotify based) that will synchronize the network configuration when a CNI file is installed there. Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
This commit is contained in:
parent
bc4ac7ce04
commit
63c7a7c99b
1 changed files with 63 additions and 16 deletions
|
@ -6,11 +6,11 @@ import (
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"sort"
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/containernetworking/cni/libcni"
|
"github.com/containernetworking/cni/libcni"
|
||||||
cnitypes "github.com/containernetworking/cni/pkg/types"
|
cnitypes "github.com/containernetworking/cni/pkg/types"
|
||||||
|
"github.com/fsnotify/fsnotify"
|
||||||
)
|
)
|
||||||
|
|
||||||
type cniNetworkPlugin struct {
|
type cniNetworkPlugin struct {
|
||||||
|
@ -23,6 +23,8 @@ type cniNetworkPlugin struct {
|
||||||
pluginDir string
|
pluginDir string
|
||||||
cniDirs []string
|
cniDirs []string
|
||||||
vendorCNIDirPrefix string
|
vendorCNIDirPrefix string
|
||||||
|
|
||||||
|
monitorNetDirChan chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
type cniNetwork struct {
|
type cniNetwork struct {
|
||||||
|
@ -31,6 +33,49 @@ type cniNetwork struct {
|
||||||
CNIConfig libcni.CNI
|
CNIConfig libcni.CNI
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var errMissingDefaultNetwork = errors.New("Missing CNI default network")
|
||||||
|
|
||||||
|
func (plugin *cniNetworkPlugin) monitorNetDir() {
|
||||||
|
watcher, err := fsnotify.NewWatcher()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("could not create new watcher %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer watcher.Close()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case event := <-watcher.Events:
|
||||||
|
logrus.Debugf("CNI monitoring event %v", event)
|
||||||
|
if event.Op&fsnotify.Create != fsnotify.Create {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = plugin.syncNetworkConfig(); err == nil {
|
||||||
|
logrus.Debugf("CNI asynchronous setting succeeded")
|
||||||
|
close(plugin.monitorNetDirChan)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Errorf("CNI setting failed, continue monitoring: %v", err)
|
||||||
|
|
||||||
|
case err := <-watcher.Errors:
|
||||||
|
logrus.Errorf("CNI monitoring error %v", err)
|
||||||
|
close(plugin.monitorNetDirChan)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err = watcher.Add(plugin.pluginDir); err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
<-plugin.monitorNetDirChan
|
||||||
|
}
|
||||||
|
|
||||||
// InitCNI takes the plugin directory and cni directories where the cni files should be searched for
|
// InitCNI takes the plugin directory and cni directories where the cni files should be searched for
|
||||||
// Returns a valid plugin object and any error
|
// Returns a valid plugin object and any error
|
||||||
func InitCNI(pluginDir string, cniDirs ...string) (CNIPlugin, error) {
|
func InitCNI(pluginDir string, cniDirs ...string) (CNIPlugin, error) {
|
||||||
|
@ -44,19 +89,16 @@ func InitCNI(pluginDir string, cniDirs ...string) (CNIPlugin, error) {
|
||||||
// check if a default network exists, otherwise dump the CNI search and return a noop plugin
|
// check if a default network exists, otherwise dump the CNI search and return a noop plugin
|
||||||
_, err = getDefaultCNINetwork(plugin.pluginDir, plugin.cniDirs, plugin.vendorCNIDirPrefix)
|
_, err = getDefaultCNINetwork(plugin.pluginDir, plugin.cniDirs, plugin.vendorCNIDirPrefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Warningf("Error in finding usable CNI plugin - %v", err)
|
if err != errMissingDefaultNetwork {
|
||||||
// create a noop plugin instead
|
logrus.Warningf("Error in finding usable CNI plugin - %v", err)
|
||||||
return &cniNoOp{}, nil
|
// create a noop plugin instead
|
||||||
|
return &cniNoOp{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// We do not have a default network, we start the monitoring thread.
|
||||||
|
go plugin.monitorNetDir()
|
||||||
}
|
}
|
||||||
|
|
||||||
// sync network config from pluginDir periodically to detect network config updates
|
|
||||||
go func() {
|
|
||||||
t := time.NewTimer(10 * time.Second)
|
|
||||||
for {
|
|
||||||
plugin.syncNetworkConfig()
|
|
||||||
<-t.C
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return plugin, nil
|
return plugin, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,10 +109,13 @@ func probeNetworkPluginsWithVendorCNIDirPrefix(pluginDir string, cniDirs []strin
|
||||||
pluginDir: pluginDir,
|
pluginDir: pluginDir,
|
||||||
cniDirs: cniDirs,
|
cniDirs: cniDirs,
|
||||||
vendorCNIDirPrefix: vendorCNIDirPrefix,
|
vendorCNIDirPrefix: vendorCNIDirPrefix,
|
||||||
|
monitorNetDirChan: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
// sync NetworkConfig in best effort during probing.
|
// sync NetworkConfig in best effort during probing.
|
||||||
plugin.syncNetworkConfig()
|
if err := plugin.syncNetworkConfig(); err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
}
|
||||||
return plugin
|
return plugin
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +132,7 @@ func getDefaultCNINetwork(pluginDir string, cniDirs []string, vendorCNIDirPrefix
|
||||||
case err != nil:
|
case err != nil:
|
||||||
return nil, err
|
return nil, err
|
||||||
case len(files) == 0:
|
case len(files) == 0:
|
||||||
return nil, fmt.Errorf("No networks found in %s", pluginDir)
|
return nil, errMissingDefaultNetwork
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Strings(files)
|
sort.Strings(files)
|
||||||
|
@ -142,13 +187,15 @@ func getLoNetwork(cniDirs []string, vendorDirPrefix string) *cniNetwork {
|
||||||
return loNetwork
|
return loNetwork
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plugin *cniNetworkPlugin) syncNetworkConfig() {
|
func (plugin *cniNetworkPlugin) syncNetworkConfig() error {
|
||||||
network, err := getDefaultCNINetwork(plugin.pluginDir, plugin.cniDirs, plugin.vendorCNIDirPrefix)
|
network, err := getDefaultCNINetwork(plugin.pluginDir, plugin.cniDirs, plugin.vendorCNIDirPrefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Errorf("error updating cni config: %s", err)
|
logrus.Errorf("error updating cni config: %s", err)
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
plugin.setDefaultNetwork(network)
|
plugin.setDefaultNetwork(network)
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plugin *cniNetworkPlugin) getDefaultNetwork() *cniNetwork {
|
func (plugin *cniNetworkPlugin) getDefaultNetwork() *cniNetwork {
|
||||||
|
|
Loading…
Reference in a new issue