From b4b3888bc99c225747c6a0345a86521a623b1ffd Mon Sep 17 00:00:00 2001 From: Vegard Engen Date: Mon, 14 Apr 2025 13:47:53 +0200 Subject: [PATCH 1/4] Add configmap support --- internal/config/config.go | 50 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 internal/config/config.go diff --git a/internal/config/config.go b/internal/config/config.go new file mode 100644 index 0000000..7487d98 --- /dev/null +++ b/internal/config/config.go @@ -0,0 +1,50 @@ +package config + +import ( + "context" + "fmt" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +type OperatorConfig struct { + DefaultNamespace string +} + +type ConfigLoader struct { + Client client.Client + Name string + Namespace string +} + +func New(client client.Client, name, namespace string) *ConfigLoader { + return &ConfigLoader{ + Client: client, + Name: name, + Namespace: namespace, + } +} + +func (cl *ConfigLoader) Load(ctx context.Context) (*OperatorConfig, error) { + cm := &corev1.ConfigMap{} + err := cl.Client.Get(ctx, types.NamespacedName{ + Name: cl.Name, + Namespace: cl.Namespace, + }, cm) + if err != nil { + return nil, fmt.Errorf("failed to load configmap: %w", err) + } + + cfg := &OperatorConfig{ + DefaultNamespace: "default", // fallback + } + + if val, ok := cm.Data["defaultNamespace"]; ok && val != "" { + cfg.DefaultNamespace = val + } + + return cfg, nil +} + From c61a555d8a775cbeebc43f4a3763cc20d61cbb2c Mon Sep 17 00:00:00 2001 From: Vegard Engen Date: Mon, 14 Apr 2025 13:58:49 +0200 Subject: [PATCH 2/4] Add OperatorConfig to all controllers --- internal/controller/firewallgroup_controller.go | 2 ++ internal/controller/firewallrule_controller.go | 4 ++++ internal/controller/firewallzone_controller.go | 2 ++ internal/controller/networkconfiguration_controller.go | 10 ++++++++++ 4 files changed, 18 insertions(+) diff --git a/internal/controller/firewallgroup_controller.go b/internal/controller/firewallgroup_controller.go index b17e603..2105f10 100644 --- a/internal/controller/firewallgroup_controller.go +++ b/internal/controller/firewallgroup_controller.go @@ -40,6 +40,7 @@ import ( goUnifi "github.com/vegardengen/go-unifi/unifi" unifiv1beta1 "github.com/vegardengen/unifi-network-operator/api/v1beta1" "github.com/vegardengen/unifi-network-operator/internal/unifi" + "github.com/vegardengen/unifi-network-operator/internal/config" ) // FirewallGroupReconciler reconciles a FirewallGroup object @@ -47,6 +48,7 @@ type FirewallGroupReconciler struct { client.Client Scheme *runtime.Scheme UnifiClient *unifi.UnifiClient + OperatorConfig *config.OperatorConfig } // +kubebuilder:rbac:groups=unifi.engen.priv.no,resources=firewallgroups,verbs=get;list;watch;create;update;patch;delete diff --git a/internal/controller/firewallrule_controller.go b/internal/controller/firewallrule_controller.go index 77886d1..02f8efb 100644 --- a/internal/controller/firewallrule_controller.go +++ b/internal/controller/firewallrule_controller.go @@ -25,12 +25,16 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" unifiv1beta1 "github.com/vegardengen/unifi-network-operator/api/v1beta1" + "github.com/vegardengen/unifi-network-operator/internal/unifi" + "github.com/vegardengen/unifi-network-operator/internal/config" ) // FirewallRuleReconciler reconciles a FirewallRule object type FirewallRuleReconciler struct { client.Client Scheme *runtime.Scheme + UnifiClient *unifi.UnifiClient + OperatorConfig *config.OperatorConfig } // +kubebuilder:rbac:groups=unifi.engen.priv.no,resources=firewallrules,verbs=get;list;watch;create;update;patch;delete diff --git a/internal/controller/firewallzone_controller.go b/internal/controller/firewallzone_controller.go index 826df26..47a3418 100644 --- a/internal/controller/firewallzone_controller.go +++ b/internal/controller/firewallzone_controller.go @@ -30,6 +30,7 @@ import ( unifiv1beta1 "github.com/vegardengen/unifi-network-operator/api/v1beta1" "github.com/vegardengen/unifi-network-operator/internal/unifi" + "github.com/vegardengen/unifi-network-operator/internal/config" ) // FirewallZoneReconciler reconciles a FirewallZone object @@ -37,6 +38,7 @@ type FirewallZoneReconciler struct { client.Client Scheme *runtime.Scheme UnifiClient *unifi.UnifiClient + OperatorConfig *config.OperatorConfig } func toKubeName(input string) string { diff --git a/internal/controller/networkconfiguration_controller.go b/internal/controller/networkconfiguration_controller.go index e8f346b..d73ad52 100644 --- a/internal/controller/networkconfiguration_controller.go +++ b/internal/controller/networkconfiguration_controller.go @@ -27,6 +27,7 @@ import ( unifiv1 "github.com/vegardengen/unifi-network-operator/api/v1beta1" "github.com/vegardengen/unifi-network-operator/internal/unifi" + "github.com/vegardengen/unifi-network-operator/internal/config" ) // NetworkconfigurationReconciler reconciles a Networkconfiguration object @@ -34,6 +35,7 @@ type NetworkconfigurationReconciler struct { client.Client Scheme *runtime.Scheme UnifiClient *unifi.UnifiClient + OperatorConfig *config.OperatorConfig } // +kubebuilder:rbac:groups=unifi.engen.priv.no,resources=networkconfigurations,verbs=get;list;watch;create;update;patch;delete @@ -103,6 +105,14 @@ func (r *NetworkconfigurationReconciler) Reconcile(ctx context.Context, req ctrl // SetupWithManager sets up the controller with the Manager. func (r *NetworkconfigurationReconciler) SetupWithManager(mgr ctrl.Manager) error { + ctx := context.Background() + cfgLoader := config.New(mgr.GetClient(), "unifi-operator-config", "unifi-network-operator-system") + + cfg, err := cfgLoader.Load(ctx) + if err != nil { + return err + } + r.OperatorConfig = cfg return ctrl.NewControllerManagedBy(mgr). For(&unifiv1.Networkconfiguration{}). Named("networkconfiguration"). From 46a0832aea320f5ccd54f2ef7f670dfbfb4bd9e7 Mon Sep 17 00:00:00 2001 From: Vegard Engen Date: Mon, 14 Apr 2025 14:05:48 +0200 Subject: [PATCH 3/4] Use configmap in firewallzone controller --- internal/controller/firewallzone_controller.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/controller/firewallzone_controller.go b/internal/controller/firewallzone_controller.go index 47a3418..3586a5d 100644 --- a/internal/controller/firewallzone_controller.go +++ b/internal/controller/firewallzone_controller.go @@ -80,7 +80,7 @@ func (r *FirewallZoneReconciler) Reconcile(ctx context.Context, req ctrl.Request log := log.FromContext(ctx) var fwzCRDs unifiv1beta1.FirewallZoneList - _ = r.List(ctx, &fwzCRDs) + _ = r.List(ctx, &fwzCRDs, client.InNamespace(r.OperatorConfig.DefaultNamespace)) firewall_zones, err := r.UnifiClient.Client.ListFirewallZones(context.Background(), r.UnifiClient.SiteID) if err != nil { @@ -113,7 +113,7 @@ func (r *FirewallZoneReconciler) Reconcile(ctx context.Context, req ctrl.Request zoneCRD := &unifiv1beta1.FirewallZone { ObjectMeta : ctrl.ObjectMeta { Name: toKubeName(unifizone.Name), - Namespace: "default", + Namespace: r.OperatorConfig.DefaultNamespace, }, Spec: unifiv1beta1.FirewallZoneSpec { Name : unifizone.Name, From 7b2acb168ab60f52c7d52d91ea4ec36c1fb7a12a Mon Sep 17 00:00:00 2001 From: Vegard Engen Date: Mon, 14 Apr 2025 15:07:49 +0200 Subject: [PATCH 4/4] Add namespace --- cmd/main.go | 8 +++ config/rbac/role.yaml | 7 ++ internal/config/config.go | 71 +++++++++---------- .../controller/firewallgroup_controller.go | 14 +++- .../controller/firewallrule_controller.go | 13 +++- .../controller/firewallzone_controller.go | 14 +++- .../networkconfiguration_controller.go | 18 ++--- 7 files changed, 91 insertions(+), 54 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index fbdcbe9..fd19fe3 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -40,6 +40,7 @@ import ( unifiv1beta1 "github.com/vegardengen/unifi-network-operator/api/v1beta1" "github.com/vegardengen/unifi-network-operator/internal/controller" "github.com/vegardengen/unifi-network-operator/internal/unifi" + "github.com/vegardengen/unifi-network-operator/internal/config" // +kubebuilder:scaffold:imports ) @@ -203,6 +204,8 @@ func main() { os.Exit(1) } + configLoader := config.NewConfigLoader(mgr.GetClient()) + // Unifi client setupLog.Info("Setting up UniFi client") unifiClient, err := unifi.CreateUnifiClient() @@ -216,6 +219,7 @@ func main() { Client: mgr.GetClient(), Scheme: mgr.GetScheme(), UnifiClient: unifiClient, + ConfigLoader: configLoader, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Networkconfiguration") os.Exit(1) @@ -224,6 +228,7 @@ func main() { Client: mgr.GetClient(), Scheme: mgr.GetScheme(), UnifiClient: unifiClient, + ConfigLoader: configLoader, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "FirewallZone") os.Exit(1) @@ -231,6 +236,8 @@ func main() { if err = (&controller.FirewallRuleReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), + UnifiClient: unifiClient, + ConfigLoader: configLoader, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "FirewallRule") os.Exit(1) @@ -241,6 +248,7 @@ func main() { Client: mgr.GetClient(), Scheme: mgr.GetScheme(), UnifiClient: unifiClient, + ConfigLoader: configLoader, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "FirewallGroup") os.Exit(1) diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 6974e58..7c519e0 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -4,6 +4,13 @@ kind: ClusterRole metadata: name: manager-role rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list - apiGroups: - "" resources: diff --git a/internal/config/config.go b/internal/config/config.go index 7487d98..61a28f9 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -1,50 +1,45 @@ package config import ( - "context" - "fmt" + "context" + "sync" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" ) -type OperatorConfig struct { - DefaultNamespace string +type ConfigLoaderType struct { + Client client.Client + + mu sync.Mutex + loaded bool + config *corev1.ConfigMap + err error } -type ConfigLoader struct { - Client client.Client - Name string - Namespace string +func NewConfigLoader(k8sClient client.Client) *ConfigLoaderType { + return &ConfigLoaderType{Client: k8sClient} } -func New(client client.Client, name, namespace string) *ConfigLoader { - return &ConfigLoader{ - Client: client, - Name: name, - Namespace: namespace, - } -} - -func (cl *ConfigLoader) Load(ctx context.Context) (*OperatorConfig, error) { - cm := &corev1.ConfigMap{} - err := cl.Client.Get(ctx, types.NamespacedName{ - Name: cl.Name, - Namespace: cl.Namespace, - }, cm) - if err != nil { - return nil, fmt.Errorf("failed to load configmap: %w", err) - } - - cfg := &OperatorConfig{ - DefaultNamespace: "default", // fallback - } - - if val, ok := cm.Data["defaultNamespace"]; ok && val != "" { - cfg.DefaultNamespace = val - } - - return cfg, nil +func (c *ConfigLoaderType) GetConfig(ctx context.Context, name string) (*corev1.ConfigMap, error) { + c.mu.Lock() + defer c.mu.Unlock() + + if c.loaded { + return c.config, c.err + } + + cm := &corev1.ConfigMap{} + err := c.Client.Get(ctx, types.NamespacedName{ + Name: name, + Namespace: "unifi-network-operator-system", + }, cm) + + c.loaded = true + c.config = cm + c.err = err + + return cm, err } diff --git a/internal/controller/firewallgroup_controller.go b/internal/controller/firewallgroup_controller.go index 2105f10..d0aaa77 100644 --- a/internal/controller/firewallgroup_controller.go +++ b/internal/controller/firewallgroup_controller.go @@ -48,13 +48,14 @@ type FirewallGroupReconciler struct { client.Client Scheme *runtime.Scheme UnifiClient *unifi.UnifiClient - OperatorConfig *config.OperatorConfig + ConfigLoader *config.ConfigLoaderType } // +kubebuilder:rbac:groups=unifi.engen.priv.no,resources=firewallgroups,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=unifi.engen.priv.no,resources=firewallgroups/status,verbs=get;update;patch // +kubebuilder:rbac:groups=unifi.engen.priv.no,resources=firewallgroups/finalizers,verbs=update // +kubebuilder:rbac:groups="",resources=services,verbs=list;get;watch +// +kubebuilder:rbac:groups="",resources=configmaps,verbs=list;get // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. @@ -68,6 +69,15 @@ type FirewallGroupReconciler struct { func (r *FirewallGroupReconciler) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) { log := log.FromContext(ctx) + + cfg, err := r.ConfigLoader.GetConfig(ctx, "unifi-operator-config") + if err != nil { + return ctrl.Result{}, err + } + + defaultNs := cfg.Data["defaultNamespace"] + log.Info(defaultNs) + var nwObj unifiv1beta1.FirewallGroup if err := r.Get(ctx, req.NamespacedName, &nwObj); err != nil { return reconcile.Result{}, client.IgnoreNotFound(err) @@ -172,7 +182,7 @@ func (r *FirewallGroupReconciler) Reconcile(ctx context.Context, req reconcile.R nwObj.Status.LastSyncTime = ¤tTime nwObj.Status.SyncedWithUnifi = true - err := r.UnifiClient.Reauthenticate() + err = r.UnifiClient.Reauthenticate() if err != nil { return reconcile.Result{}, err } diff --git a/internal/controller/firewallrule_controller.go b/internal/controller/firewallrule_controller.go index 02f8efb..45605ef 100644 --- a/internal/controller/firewallrule_controller.go +++ b/internal/controller/firewallrule_controller.go @@ -34,12 +34,13 @@ type FirewallRuleReconciler struct { client.Client Scheme *runtime.Scheme UnifiClient *unifi.UnifiClient - OperatorConfig *config.OperatorConfig + ConfigLoader *config.ConfigLoaderType } // +kubebuilder:rbac:groups=unifi.engen.priv.no,resources=firewallrules,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=unifi.engen.priv.no,resources=firewallrules/status,verbs=get;update;patch // +kubebuilder:rbac:groups=unifi.engen.priv.no,resources=firewallrules/finalizers,verbs=update +// +kubebuilder:rbac:groups="",resources=configmaps,verbs=list;get // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. @@ -51,10 +52,18 @@ type FirewallRuleReconciler struct { // For more details, check Reconcile and its Result here: // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.20.2/pkg/reconcile func (r *FirewallRuleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - _ = log.FromContext(ctx) + log := log.FromContext(ctx) // TODO(user): your logic here + cfg, err := r.ConfigLoader.GetConfig(ctx, "unifi-operator-config") + if err != nil { + return ctrl.Result{}, err + } + + defaultNs := cfg.Data["defaultNamespace"] + log.Info(defaultNs) + return ctrl.Result{}, nil } diff --git a/internal/controller/firewallzone_controller.go b/internal/controller/firewallzone_controller.go index 3586a5d..626ca31 100644 --- a/internal/controller/firewallzone_controller.go +++ b/internal/controller/firewallzone_controller.go @@ -38,7 +38,7 @@ type FirewallZoneReconciler struct { client.Client Scheme *runtime.Scheme UnifiClient *unifi.UnifiClient - OperatorConfig *config.OperatorConfig + ConfigLoader *config.ConfigLoaderType } func toKubeName(input string) string { @@ -66,6 +66,7 @@ func toKubeName(input string) string { // +kubebuilder:rbac:groups=unifi.engen.priv.no,resources=firewallzones,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=unifi.engen.priv.no,resources=firewallzones/status,verbs=get;update;patch // +kubebuilder:rbac:groups=unifi.engen.priv.no,resources=firewallzones/finalizers,verbs=update +// +kubebuilder:rbac:groups="",resources=configmaps,verbs=list;get // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. @@ -79,8 +80,15 @@ func toKubeName(input string) string { func (r *FirewallZoneReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log := log.FromContext(ctx) + cfg, err := r.ConfigLoader.GetConfig(ctx, "unifi-operator-config") + if err != nil { + return ctrl.Result{}, err + } + + defaultNs := cfg.Data["defaultNamespace"] + var fwzCRDs unifiv1beta1.FirewallZoneList - _ = r.List(ctx, &fwzCRDs, client.InNamespace(r.OperatorConfig.DefaultNamespace)) + _ = r.List(ctx, &fwzCRDs, client.InNamespace(defaultNs)) firewall_zones, err := r.UnifiClient.Client.ListFirewallZones(context.Background(), r.UnifiClient.SiteID) if err != nil { @@ -113,7 +121,7 @@ func (r *FirewallZoneReconciler) Reconcile(ctx context.Context, req ctrl.Request zoneCRD := &unifiv1beta1.FirewallZone { ObjectMeta : ctrl.ObjectMeta { Name: toKubeName(unifizone.Name), - Namespace: r.OperatorConfig.DefaultNamespace, + Namespace: defaultNs, }, Spec: unifiv1beta1.FirewallZoneSpec { Name : unifizone.Name, diff --git a/internal/controller/networkconfiguration_controller.go b/internal/controller/networkconfiguration_controller.go index d73ad52..7f7fcb3 100644 --- a/internal/controller/networkconfiguration_controller.go +++ b/internal/controller/networkconfiguration_controller.go @@ -35,12 +35,13 @@ type NetworkconfigurationReconciler struct { client.Client Scheme *runtime.Scheme UnifiClient *unifi.UnifiClient - OperatorConfig *config.OperatorConfig + ConfigLoader *config.ConfigLoaderType } // +kubebuilder:rbac:groups=unifi.engen.priv.no,resources=networkconfigurations,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=unifi.engen.priv.no,resources=networkconfigurations/status,verbs=get;update;patch // +kubebuilder:rbac:groups=unifi.engen.priv.no,resources=networkconfigurations/finalizers,verbs=update +// +kubebuilder:rbac:groups="",resources=configmaps,verbs=list;get // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. @@ -53,6 +54,13 @@ type NetworkconfigurationReconciler struct { // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.20.2/pkg/reconcile func (r *NetworkconfigurationReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log := log.FromContext(ctx) + cfg, err := r.ConfigLoader.GetConfig(ctx, "unifi-operator-config") + if err != nil { + return ctrl.Result{}, err + } + + defaultNs := cfg.Data["defaultNamespace"] + log.Info(defaultNs) var networkCRDs unifiv1.NetworkconfigurationList if err := r.List(ctx, &networkCRDs); err != nil { @@ -105,14 +113,6 @@ func (r *NetworkconfigurationReconciler) Reconcile(ctx context.Context, req ctrl // SetupWithManager sets up the controller with the Manager. func (r *NetworkconfigurationReconciler) SetupWithManager(mgr ctrl.Manager) error { - ctx := context.Background() - cfgLoader := config.New(mgr.GetClient(), "unifi-operator-config", "unifi-network-operator-system") - - cfg, err := cfgLoader.Load(ctx) - if err != nil { - return err - } - r.OperatorConfig = cfg return ctrl.NewControllerManagedBy(mgr). For(&unifiv1.Networkconfiguration{}). Named("networkconfiguration").