diff --git a/.github/workflows/build_push_concheck.yaml b/.github/workflows/build_push_concheck.yaml index 33b23a9d..6386afec 100644 --- a/.github/workflows/build_push_concheck.yaml +++ b/.github/workflows/build_push_concheck.yaml @@ -3,12 +3,12 @@ name: Build and push connection check image on: push: branches: - - v1.2.0 + - v1.2.3 paths: - connection-check/** env: - IMAGE_VERSION: '1.2.0' + IMAGE_VERSION: '1.2.3' IMAGE_REGISTRY: ghcr.io/${{ github.repository_owner }} jobs: diff --git a/.github/workflows/build_push_controller.yaml b/.github/workflows/build_push_controller.yaml index 57f11672..435f6bcf 100644 --- a/.github/workflows/build_push_controller.yaml +++ b/.github/workflows/build_push_controller.yaml @@ -3,7 +3,7 @@ name: Build and push controller and its bundle image on: push: branches: - - v1.2.0 + - v1.2.3 paths: - controllers/** - compute/** @@ -16,7 +16,7 @@ on: - ./Makefile env: - VERSION: '1.2.0' + VERSION: '1.2.3' IMAGE_REGISTRY: ghcr.io/${{ github.repository_owner }} DAEMON_REGISTRY: ghcr.io/${{ github.repository_owner }} diff --git a/.github/workflows/build_push_daemon.yaml b/.github/workflows/build_push_daemon.yaml index 01bd94e7..fa76a3a7 100644 --- a/.github/workflows/build_push_daemon.yaml +++ b/.github/workflows/build_push_daemon.yaml @@ -3,14 +3,14 @@ name: Build and push daemon image on: push: branches: - - v1.2.0 + - v1.2.3 paths: - daemon/** - cni/** - Makefile env: - IMAGE_VERSION: '1.2.0' + IMAGE_VERSION: '1.2.3' DAEMON_REGISTRY: ghcr.io/${{ github.repository_owner }} jobs: diff --git a/.github/workflows/daemon_unittest.yaml b/.github/workflows/daemon_unittest.yaml index 2df59684..bd41b555 100644 --- a/.github/workflows/daemon_unittest.yaml +++ b/.github/workflows/daemon_unittest.yaml @@ -3,7 +3,7 @@ name: Perform unittest for daemon on: pull_request: branches: - - v1.2.0 + - v1.2.3 push: paths: - daemon/** diff --git a/.github/workflows/integration_test.yaml b/.github/workflows/integration_test.yaml index 0e3df5a0..61f50ad2 100644 --- a/.github/workflows/integration_test.yaml +++ b/.github/workflows/integration_test.yaml @@ -3,7 +3,7 @@ name: e2e test on: pull_request: branches: - - v1.2.0 + - v1.2.3 push: paths: - controllers/** diff --git a/.github/workflows/unittest.yaml b/.github/workflows/unittest.yaml index 999c477e..6265b511 100644 --- a/.github/workflows/unittest.yaml +++ b/.github/workflows/unittest.yaml @@ -3,7 +3,7 @@ name: Perform unittest for controller on: pull_request: branches: - - v1.2.0 + - v1.2.3 push: paths: - controllers/** diff --git a/Makefile b/Makefile index 1688a96f..6707a1d1 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ endif # - use the VERSION as arg of the bundle target (e.g make bundle VERSION=0.0.2) # - use environment variables to overwrite this value (e.g export VERSION=0.0.2) # VERSION ?= 0.0.1 -VERSION ?= 1.2.0 +VERSION ?= 1.2.3 export CHANNELS = "beta" # CHANNELS define the bundle channels used in the bundle. diff --git a/README.md b/README.md index 118fbe02..9b6b545f 100644 --- a/README.md +++ b/README.md @@ -112,7 +112,7 @@ Recommended to deploy in the same default namespace for [health check service](. ``` ##### by bundle with operator-sdk ```bash - operator-sdk run bundle ghcr.io/foundation-model-stack/multi-nic-cni-bundle:v1.2.0 -n multi-nic-cni-operator + operator-sdk run bundle ghcr.io/foundation-model-stack/multi-nic-cni-bundle:v1.2.3 -n multi-nic-cni-operator ``` #### Deploy MultiNicNetwork resource 1. Prepare `network.yaml` as shown in the [example](#multinicnetwork) diff --git a/api/v1/hostinterface_types.go b/api/v1/hostinterface_types.go index 629735b3..baddf1df 100644 --- a/api/v1/hostinterface_types.go +++ b/api/v1/hostinterface_types.go @@ -9,16 +9,13 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. - type InterfaceInfoType struct { InterfaceName string `json:"interfaceName"` - NetAddress string `json:"netAddress"` - HostIP string `json:"hostIP"` - Vendor string `json:"vendor"` - Product string `json:"product"` - PciAddress string `json:"pciAddress"` + NetAddress string `json:"netAddress,omitempty"` + HostIP string `json:"hostIP,omitempty"` + Vendor string `json:"vendor,omitempty"` + Product string `json:"product,omitempty"` + PciAddress string `json:"pciAddress,omitempty"` } func (i InterfaceInfoType) Equal(cmp InterfaceInfoType) bool { diff --git a/config/crd/bases/multinic.fms.io_hostinterfaces.yaml b/config/crd/bases/multinic.fms.io_hostinterfaces.yaml index 0ea01fae..9e08f477 100644 --- a/config/crd/bases/multinic.fms.io_hostinterfaces.yaml +++ b/config/crd/bases/multinic.fms.io_hostinterfaces.yaml @@ -54,12 +54,7 @@ spec: vendor: type: string required: - - hostIP - interfaceName - - netAddress - - pciAddress - - product - - vendor type: object type: array required: diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 3abe2511..6d4cc2a7 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -13,4 +13,4 @@ kind: Kustomization images: - name: controller newName: ghcr.io/foundation-model-stack/multi-nic-cni-controller - newTag: v1.2.0 + newTag: v1.2.3 diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index a767bba5..62c4b78a 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -19,6 +19,8 @@ spec: replicas: 1 template: metadata: + annotations: + kubectl.kubernetes.io/default-container: manager labels: control-plane: controller-manager spec: diff --git a/config/manifests/bases/multi-nic-cni-operator.clusterserviceversion.template b/config/manifests/bases/multi-nic-cni-operator.clusterserviceversion.template index 28935dd8..66106a49 100644 --- a/config/manifests/bases/multi-nic-cni-operator.clusterserviceversion.template +++ b/config/manifests/bases/multi-nic-cni-operator.clusterserviceversion.template @@ -131,4 +131,4 @@ spec: provider: name: Foundation Model Stack version: 0.0.0 - replaces: multi-nic-cni-operator.v1.1.0 \ No newline at end of file + replaces: multi-nic-cni-operator.v1.2.0 \ No newline at end of file diff --git a/config/samples/config.yaml b/config/samples/config.yaml index a681bdaf..52aae449 100644 --- a/config/samples/config.yaml +++ b/config/samples/config.yaml @@ -11,7 +11,7 @@ spec: value: "11000" - name: RT_TABLE_PATH value: /opt/rt_tables - image: ghcr.io/foundation-model-stack/multi-nic-cni-daemon:v1.2.0 + image: ghcr.io/foundation-model-stack/multi-nic-cni-daemon:v1.2.3 imagePullPolicy: Always mounts: - hostpath: /var/lib/cni/bin diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index 101d3029..54cfe87d 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -15,4 +15,4 @@ kind: Kustomization images: - name: multi-nic-cni-daemon newName: ghcr.io/foundation-model-stack/multi-nic-cni-daemon - newTag: v1.2.0 + newTag: v1.2.3 diff --git a/connection-check/concheck.yaml b/connection-check/concheck.yaml index f2f52e34..58283208 100644 --- a/connection-check/concheck.yaml +++ b/connection-check/concheck.yaml @@ -71,7 +71,7 @@ spec: serviceAccountName: multi-nic-concheck-account containers: - name: concheck - image: ghcr.io/foundation-model-stack/multi-nic-cni-concheck:v1.2.0 + image: ghcr.io/foundation-model-stack/multi-nic-cni-concheck:v1.2.3 imagePullPolicy: Always securityContext: privileged: true diff --git a/controllers/multinicnetwork_controller.go b/controllers/multinicnetwork_controller.go index 65a9d761..ea7eee51 100644 --- a/controllers/multinicnetwork_controller.go +++ b/controllers/multinicnetwork_controller.go @@ -132,8 +132,6 @@ func (r *MultiNicNetworkReconciler) Reconcile(ctx context.Context, req ctrl.Requ instance.Status.DiscoverStatus.ExistDaemon = daemonSize instance.Status.InterfaceInfoAvailable = infoAvailableSize - // Get main plugin - mainPlugin, annotations, err := r.GetMainPluginConf(instance) multinicnetworkName := instance.GetName() if err != nil { message := fmt.Sprintf("Failed to get main config %s: %v", multinicnetworkName, err) @@ -143,10 +141,7 @@ func (r *MultiNicNetworkReconciler) Reconcile(ctx context.Context, req ctrl.Requ } vars.NetworkLog.V(2).Info(message) } else { - mainPlugin = plugin.RemoveEmpty(instance.Spec.MainPlugin.CNIArgs, mainPlugin) - vars.NetworkLog.V(2).Info(fmt.Sprintf("main plugin: %s", mainPlugin)) - // Create net attach def - err = r.NetAttachDefHandler.CreateOrUpdate(instance, mainPlugin, annotations) + err = r.GenerateNetAttachDef(instance) if err != nil { message := fmt.Sprintf("Failed to create %s: %v", multinicnetworkName, err) err = r.CIDRHandler.MultiNicNetworkHandler.UpdateNetConfigStatus(instance, multinicv1.ConfigFailed, message) @@ -204,6 +199,18 @@ func (r *MultiNicNetworkReconciler) Reconcile(ctx context.Context, req ctrl.Requ return ctrl.Result{}, nil } +func (r *MultiNicNetworkReconciler) GenerateNetAttachDef(instance *multinicv1.MultiNicNetwork) error { + // Get main plugin + mainPlugin, annotations, err := r.GetMainPluginConf(instance) + if err == nil { + mainPlugin = plugin.RemoveEmpty(instance.Spec.MainPlugin.CNIArgs, mainPlugin) + vars.NetworkLog.V(2).Info(fmt.Sprintf("main plugin: %s", mainPlugin)) + // Create net attach def + err = r.NetAttachDefHandler.CreateOrUpdate(instance, mainPlugin, annotations) + } + return err +} + func (r *MultiNicNetworkReconciler) GetMainPluginConf(instance *multinicv1.MultiNicNetwork) (string, map[string]string, error) { spec := instance.Spec.MainPlugin if p, exist := r.PluginMap[spec.Type]; exist { @@ -291,3 +298,20 @@ func (r *MultiNicNetworkReconciler) callFinalizer(reqLogger logr.Logger, instanc r.CIDRHandler.MultiNicNetworkHandler.SafeCache.UnsetCache(instance.Name) return nil } + +// HandleNewNamespace handles new namespace +// - generate NAD +func (r *MultiNicNetworkReconciler) HandleNewNamespace(namespace string) { + multinicnetworks := r.CIDRHandler.MultiNicNetworkHandler.FilterNetworksByNamespace(namespace) + for _, net := range multinicnetworks { + // Get main plugin + mainPlugin, annotations, err := r.GetMainPluginConf(&net) + if err == nil { + mainPlugin = plugin.RemoveEmpty(net.Spec.MainPlugin.CNIArgs, mainPlugin) + err = r.NetAttachDefHandler.CreateOrUpdateOnNamespace(namespace, &net, mainPlugin, annotations) + } + if err != nil { + vars.NetworkLog.V(2).Info(fmt.Sprintf("Failed to create networkAttachementDefinition for %s on %s: %v", net.Name, namespace, err)) + } + } +} diff --git a/controllers/multinicnetwork_handler.go b/controllers/multinicnetwork_handler.go index f8a80aa9..fcb2d67c 100644 --- a/controllers/multinicnetwork_handler.go +++ b/controllers/multinicnetwork_handler.go @@ -115,6 +115,7 @@ func (h *MultiNicNetworkHandler) updateStatus(instance *multinicv1.MultiNicNetwo if !NetStatusUpdated(instance, netStatus) { vars.NetworkLog.V(2).Info(fmt.Sprintf("No status update %s", instance.Name)) + h.SetCache(instance.Name, *instance) return netStatus, nil } @@ -126,7 +127,7 @@ func (h *MultiNicNetworkHandler) updateStatus(instance *multinicv1.MultiNicNetwo if err != nil { vars.NetworkLog.V(2).Info(fmt.Sprintf("Failed to update %s status: %v", instance.Name, err)) } else { - h.SetStatusCache(instance.Name, instance.Status) + h.SetCache(instance.Name, *instance) } return netStatus, err } @@ -171,12 +172,12 @@ func (h *MultiNicNetworkHandler) UpdateNetConfigStatus(instance *multinicv1.Mult if err != nil { vars.NetworkLog.V(2).Info(fmt.Sprintf("Failed to update %s status: %v", instance.Name, err)) } else { - h.SetStatusCache(instance.Name, instance.Status) + h.SetCache(instance.Name, *instance) } return err } -func (h *MultiNicNetworkHandler) SetStatusCache(key string, value multinicv1.MultiNicNetworkStatus) { +func (h *MultiNicNetworkHandler) SetCache(key string, value multinicv1.MultiNicNetwork) { h.SafeCache.SetCache(key, value) } @@ -185,15 +186,40 @@ func (h *MultiNicNetworkHandler) GetStatusCache(key string) (multinicv1.MultiNic if value == nil { return multinicv1.MultiNicNetworkStatus{}, fmt.Errorf(vars.NotFoundError) } - return value.(multinicv1.MultiNicNetworkStatus), nil + return value.(multinicv1.MultiNicNetwork).Status, nil } func (h *MultiNicNetworkHandler) ListStatusCache() map[string]multinicv1.MultiNicNetworkStatus { snapshot := make(map[string]multinicv1.MultiNicNetworkStatus) h.SafeCache.Lock() for key, value := range h.cache { - snapshot[key] = value.(multinicv1.MultiNicNetworkStatus) + snapshot[key] = value.(multinicv1.MultiNicNetwork).Status } h.SafeCache.Unlock() return snapshot } + +func (h *MultiNicNetworkHandler) FilterNetworksByNamespace(target string) []multinicv1.MultiNicNetwork { + filteredNetworks := []multinicv1.MultiNicNetwork{} + h.SafeCache.Lock() + for _, value := range h.cache { + net := value.(multinicv1.MultiNicNetwork) + namespaces := net.Spec.Namespaces + if len(namespaces) == 0 { + vars.NetworkLog.V(2).Info(fmt.Sprintf("FilterNetworksByNamespace %s has no namespace set", net.Name)) + // NOTE: if namespace list is not specified, append + filteredNetworks = append(filteredNetworks, net) + } else { + vars.NetworkLog.V(2).Info(fmt.Sprintf("FilterNetworksByNamespace %s set %v", net.Name, namespaces)) + // NOTE: if namespace list is specified and target is in the list, append + for _, ns := range namespaces { + if ns == target { + filteredNetworks = append(filteredNetworks, net) + break + } + } + } + } + h.SafeCache.Unlock() + return filteredNetworks +} diff --git a/controllers/namespace_watcher.go b/controllers/namespace_watcher.go new file mode 100644 index 00000000..fc862f37 --- /dev/null +++ b/controllers/namespace_watcher.go @@ -0,0 +1,59 @@ +/* + * Copyright 2022- IBM Inc. All rights reserved + * SPDX-License-Identifier: Apache2.0 + */ + +package controllers + +import ( + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/cache" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// NamespaceWatcher watches new namespace and generate net-attach-def +type NamespaceWatcher struct { + *kubernetes.Clientset + NamespaceQueue chan string + Quit chan struct{} + *MultiNicNetworkReconciler +} + +// NewNamespaceWatcher creates new namespace watcher +func NewNamespaceWatcher(client client.Client, config *rest.Config, multinicnetworkReconciler *MultiNicNetworkReconciler, quit chan struct{}) *NamespaceWatcher { + clientset, _ := kubernetes.NewForConfig(config) + watcher := &NamespaceWatcher{ + Clientset: clientset, + NamespaceQueue: make(chan string), + Quit: quit, + MultiNicNetworkReconciler: multinicnetworkReconciler, + } + factory := informers.NewSharedInformerFactory(clientset, 0) + nsInformer := factory.Core().V1().Namespaces() + + nsInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + if ns, ok := obj.(*v1.Namespace); ok { + watcher.NamespaceQueue <- ns.Name + } + }, + }) + factory.Start(watcher.Quit) + + return watcher +} + +// Run executes namespace watcher routine until get quit signal +func (w *NamespaceWatcher) Run() { + defer close(w.NamespaceQueue) + wait.Until(w.ProcessNamespaceQueue, 0, w.Quit) +} + +func (w *NamespaceWatcher) ProcessNamespaceQueue() { + ns := <-w.NamespaceQueue + w.MultiNicNetworkReconciler.HandleNewNamespace(ns) +} diff --git a/controllers/vars/vars.go b/controllers/vars/vars.go index 3c2d0dd0..93d64f4e 100644 --- a/controllers/vars/vars.go +++ b/controllers/vars/vars.go @@ -36,7 +36,7 @@ const ( DefaultOperatorNamespace = "multi-nic-cni-operator" DefaultCNIType = "multi-nic" DefaultIPAMType = "multi-nic-ipam" - DefaultDaemonImage = "ghcr.io/foundation-model-stack/multi-nic-cni-daemon:v1.2.0" + DefaultDaemonImage = "ghcr.io/foundation-model-stack/multi-nic-cni-daemon:v1.2.3" DefaultJoinPath = "/join" DefaultInterfacePath = "/interface" DefaultAddRoutePath = "/addl3" @@ -77,6 +77,7 @@ var ( // logger options to change log level on the fly ZapOpts *zap.Options + SetupLog logr.Logger DaemonLog logr.Logger DefLog logr.Logger CIDRLog logr.Logger @@ -103,6 +104,7 @@ func SetLog() { zapp := zap.New(zap.UseFlagOptions(ZapOpts)) dlog := logf.NewDelegatingLogSink(zapp.GetSink()) ctrl.Log = logr.New(dlog) + SetupLog = ctrl.Log.WithName("setup") DaemonLog = ctrl.Log.WithName("controllers").WithName("Daemon") DefLog = ctrl.Log.WithName("controllers").WithName("NetAttachDef") CIDRLog = ctrl.Log.WithName("controllers").WithName("CIDR") diff --git a/daemon/Makefile b/daemon/Makefile index be227b88..9e80aac9 100644 --- a/daemon/Makefile +++ b/daemon/Makefile @@ -6,7 +6,7 @@ export DAEMON_REGISTRY ?= ghcr.io/foundation-model-stack # DAEMON_IMG defines the image:tag used for daemon IMAGE_TAG_BASE = $(DAEMON_REGISTRY)/multi-nic-cni -IMAGE_VERSION ?= 1.2.0 +IMAGE_VERSION ?= 1.2.3 DAEMON_IMG ?= $(IMAGE_TAG_BASE)-daemon:v$(IMAGE_VERSION) diff --git a/daemon/dockerfiles/Dockerfile.kbuilder b/daemon/dockerfiles/Dockerfile.kbuilder index cc1580f7..3e3ebf42 100644 --- a/daemon/dockerfiles/Dockerfile.kbuilder +++ b/daemon/dockerfiles/Dockerfile.kbuilder @@ -18,7 +18,7 @@ RUN curl -sSLo kubebuilder_2.0.0-alpha.1_linux_amd64.tar.gz https://github.com/k RUN curl -sSLo setup-envtest.sh https://raw.githubusercontent.com/kubernetes-sigs/controller-runtime/v0.7.2/hack/setup-envtest.sh RUN cd /tmp && \ - git clone -b v1.2.0 https://github.com/containernetworking/plugins.git && \ + git clone -b v1.2.3 https://github.com/containernetworking/plugins.git && \ cd plugins && \ ./build_linux.sh && \ ls /tmp/plugins/bin && \ diff --git a/main.go b/main.go index fcba61f2..c8f90c04 100644 --- a/main.go +++ b/main.go @@ -37,7 +37,6 @@ import ( var ( scheme = runtime.NewScheme() - setupLog = ctrl.Log.WithName("setup") MultiNicNetworkReconcilerPointer *controllers.MultiNicNetworkReconciler ) @@ -75,7 +74,7 @@ func main() { // Become the leader before proceeding err := leader.Become(context.TODO(), "5aaf67fd.fms.io") if err != nil { - setupLog.Error(err, "cannot become leader") + vars.SetupLog.Error(err, "cannot become leader") os.Exit(1) } } @@ -92,7 +91,7 @@ func main() { }) if err != nil { - setupLog.Error(err, "unable to start manager") + vars.SetupLog.Error(err, "unable to start manager") os.Exit(1) } @@ -107,13 +106,13 @@ func main() { defHandler, err := plugin.GetNetAttachDefHandler(config) if err != nil { - setupLog.Error(err, "unable to create NetworkAttachmentdefinition handler") + vars.SetupLog.Error(err, "unable to create NetworkAttachmentdefinition handler") os.Exit(1) } clientset, err := kubernetes.NewForConfig(config) if err != nil { - setupLog.Error(err, "unable to init clientset") + vars.SetupLog.Error(err, "unable to init clientset") os.Exit(1) } @@ -121,14 +120,12 @@ func main() { go cidrHandler.Run() pluginMap := controllers.GetPluginMap(config) - setupLog.V(2).Info(fmt.Sprintf("Plugin Map: %v", pluginMap)) + vars.SetupLog.V(2).Info(fmt.Sprintf("Plugin Map: %v", pluginMap)) podQueue := make(chan *v1.Pod, vars.MaxQueueSize) - setupLog.V(7).Info("New Daemon Watcher") daemonWatcher := controllers.NewDaemonWatcher(mgr.GetClient(), config, hostInterfaceHandler, daemonCacheHandler, podQueue, quit) - setupLog.V(7).Info("Run Daemon Watcher") + vars.SetupLog.V(1).Info("Run Daemon Watcher") go daemonWatcher.Run() - setupLog.V(7).Info("New Reconcilers") cidrReconciler := &controllers.CIDRReconciler{ Client: mgr.GetClient(), @@ -137,7 +134,7 @@ func main() { DaemonWatcher: daemonWatcher, } if err = (cidrReconciler).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "CIDR") + vars.SetupLog.Error(err, "unable to create controller", "controller", "CIDR") os.Exit(1) } hostInterfaceReconciler := &controllers.HostInterfaceReconciler{ @@ -148,7 +145,7 @@ func main() { CIDRHandler: cidrHandler, } if err = (hostInterfaceReconciler).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "HostInterface") + vars.SetupLog.Error(err, "unable to create controller", "controller", "HostInterface") os.Exit(1) } @@ -158,9 +155,10 @@ func main() { CIDRHandler: cidrHandler, } if err = (ippoolReconciler).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "IPPool") + vars.SetupLog.Error(err, "unable to create controller", "controller", "IPPool") os.Exit(1) } + MultiNicNetworkReconcilerPointer = &controllers.MultiNicNetworkReconciler{ Client: mgr.GetClient(), NetAttachDefHandler: defHandler, @@ -169,9 +167,14 @@ func main() { PluginMap: pluginMap, } if err = (MultiNicNetworkReconcilerPointer).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "MultiNicNetwork") + vars.SetupLog.Error(err, "unable to create controller", "controller", "MultiNicNetwork") os.Exit(1) } + + namespaceWatcher := controllers.NewNamespaceWatcher(mgr.GetClient(), config, MultiNicNetworkReconcilerPointer, quit) + vars.SetupLog.V(1).Info("Run Namespace Watcher") + go namespaceWatcher.Run() + cfgReconciler := &controllers.ConfigReconciler{ Client: mgr.GetClient(), Clientset: clientset, @@ -181,21 +184,21 @@ func main() { Scheme: mgr.GetScheme(), } if err = (cfgReconciler).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "Config") + vars.SetupLog.Error(err, "unable to create controller", "controller", "Config") os.Exit(1) } err = cfgReconciler.CreateDefaultDaemonConfig() if err != nil { - setupLog.Info(fmt.Sprintf("fail to create default config: %v", err)) + vars.SetupLog.Info(fmt.Sprintf("fail to create default config: %v", err)) } //+kubebuilder:scaffold:builder if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { - setupLog.Error(err, "unable to set up health check") + vars.SetupLog.Error(err, "unable to set up health check") os.Exit(1) } if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil { - setupLog.Error(err, "unable to set up ready check") + vars.SetupLog.Error(err, "unable to set up ready check") os.Exit(1) } @@ -204,9 +207,9 @@ func main() { controllers.RunPeriodicUpdate(ticker, daemonWatcher, cidrHandler, hostInterfaceReconciler, quit) - setupLog.V(7).Info("starting manager") + vars.SetupLog.V(7).Info("starting manager") if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { - setupLog.Error(err, "problem running manager") + vars.SetupLog.Error(err, "problem running manager") os.Exit(1) } } diff --git a/plugin/net_attach_def.go b/plugin/net_attach_def.go index e877647e..15731d19 100644 --- a/plugin/net_attach_def.go +++ b/plugin/net_attach_def.go @@ -120,7 +120,6 @@ func CheckDefChanged(def, existingDef *NetworkAttachmentDefinition) bool { return false } -// CreateOrUpdate creates new NetworkAttachmentDefinition resource if not exists, otherwise update func (h *NetAttachDefHandler) CreateOrUpdate(net *multinicv1.MultiNicNetwork, pluginStr string, annotations map[string]string) error { defs, err := h.generate(net, pluginStr, annotations) if err != nil { @@ -128,32 +127,51 @@ func (h *NetAttachDefHandler) CreateOrUpdate(net *multinicv1.MultiNicNetwork, pl } errMsg := "" for _, def := range defs { - name := def.GetName() - namespace := def.GetNamespace() - result := &NetworkAttachmentDefinition{} - if h.IsExist(name, namespace) { - existingDef, _ := h.Get(name, namespace) - def.ObjectMeta = existingDef.ObjectMeta - if CheckDefChanged(def, existingDef) { - if namespace == "default" { - vars.NetworkLog.V(2).Info(fmt.Sprintf("Update net-attach-def %s", def.Name)) - } - err := h.DynamicHandler.Update(namespace, def, result) - if err != nil { - errMsg = fmt.Sprintf("%s\n%s: %v", errMsg, namespace, err) - } + errMsg = h.createOrUpdate(def, errMsg) + } + if errMsg != "" { + vars.NetworkLog.V(2).Info(errMsg) + } + return nil +} + +func (h *NetAttachDefHandler) CreateOrUpdateOnNamespace(ns string, net *multinicv1.MultiNicNetwork, pluginStr string, annotations map[string]string) error { + def, err := NetToDef(ns, net, pluginStr, annotations) + if err != nil { + return err + } + errMsg := h.createOrUpdate(def, "") + if errMsg != "" { + vars.NetworkLog.V(2).Info(errMsg) + return fmt.Errorf(errMsg) + } + return nil +} + +// createOrUpdate creates new NetworkAttachmentDefinition resource if not exists, otherwise update +func (h *NetAttachDefHandler) createOrUpdate(def *NetworkAttachmentDefinition, errMsg string) string { + name := def.GetName() + namespace := def.GetNamespace() + result := &NetworkAttachmentDefinition{} + if h.IsExist(name, namespace) { + existingDef, _ := h.Get(name, namespace) + def.ObjectMeta = existingDef.ObjectMeta + if CheckDefChanged(def, existingDef) { + if namespace == "default" { + vars.NetworkLog.V(2).Info(fmt.Sprintf("Update net-attach-def %s", def.Name)) } - } else { - err := h.DynamicHandler.Create(namespace, def, result) + err := h.DynamicHandler.Update(namespace, def, result) if err != nil { errMsg = fmt.Sprintf("%s\n%s: %v", errMsg, namespace, err) } } + } else { + err := h.DynamicHandler.Create(namespace, def, result) + if err != nil { + errMsg = fmt.Sprintf("%s\n%s: %v", errMsg, namespace, err) + } } - if errMsg != "" { - vars.NetworkLog.V(2).Info(errMsg) - } - return nil + return errMsg } // getNamespace returns all available namespaces if .Spec.Namespaces not specified diff --git a/unit-test/multinicnetwork_test.go b/unit-test/multinicnetwork_test.go index 9f860a3d..0937d8dc 100644 --- a/unit-test/multinicnetwork_test.go +++ b/unit-test/multinicnetwork_test.go @@ -6,6 +6,7 @@ package controllers import ( + "context" "fmt" multinicv1 "github.com/foundation-model-stack/multi-nic-cni/api/v1" @@ -13,10 +14,15 @@ import ( "github.com/foundation-model-stack/multi-nic-cni/plugin" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" //+kubebuilder:scaffold:imports ) +const ( + newNamespaceName = "new-namespace" +) + var _ = Describe("Test deploying MultiNicNetwork", func() { cniVersion := "0.3.0" cniType := "ipvlan" @@ -36,6 +42,20 @@ var _ = Describe("Test deploying MultiNicNetwork", func() { err = multinicnetworkReconciler.NetAttachDefHandler.DeleteNets(multinicnetwork) Expect(err).NotTo(HaveOccurred()) }) + It("successfully create/delete network attachment definition on new namespace", func() { + newNamespace := corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: newNamespaceName, + }, + } + mainPlugin, annotations, err := multinicnetworkReconciler.GetMainPluginConf(multinicnetwork) + Expect(err).NotTo(HaveOccurred()) + Expect(k8sClient.Create(context.TODO(), &newNamespace)).Should(Succeed()) + err = multinicnetworkReconciler.NetAttachDefHandler.CreateOrUpdateOnNamespace(newNamespaceName, multinicnetwork, mainPlugin, annotations) + Expect(err).NotTo(HaveOccurred()) + err = multinicnetworkReconciler.NetAttachDefHandler.Delete(multinicnetwork.Name, newNamespaceName) + Expect(err).NotTo(HaveOccurred()) + }) }) var _ = Describe("Test definition changes check", func() {