From b2f55bd7c71edb17fc49c4cf4c621157203e580a Mon Sep 17 00:00:00 2001 From: adrianc Date: Thu, 25 Jul 2024 18:44:49 +0300 Subject: [PATCH 1/8] add log package this is needed to later update log level using maintenenace operator config controller Signed-off-by: adrianc --- .golangci.yml | 1 - internal/log/log.go | 72 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 internal/log/log.go diff --git a/.golangci.yml b/.golangci.yml index 13b6047..f5efd42 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -56,7 +56,6 @@ linters: - errname - exportloopref - fatcontext - - forcetypeassert - funlen - ginkgolinter - goconst diff --git a/internal/log/log.go b/internal/log/log.go new file mode 100644 index 0000000..f033950 --- /dev/null +++ b/internal/log/log.go @@ -0,0 +1,72 @@ +/* + Copyright 2024, NVIDIA CORPORATION & AFFILIATES + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package log + +import ( + "flag" + + zzap "go.uber.org/zap" + "go.uber.org/zap/zapcore" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" +) + +// Options stores controller-runtime (zap) log config +var Options = &zap.Options{ + Development: true, + // we dont log with panic level, so this essentially + // disables stacktrace, for now, it avoids un-needed clutter in logs + StacktraceLevel: zapcore.DPanicLevel, + TimeEncoder: zapcore.RFC3339NanoTimeEncoder, + Level: zzap.NewAtomicLevelAt(zapcore.InfoLevel), + // log caller (file and line number) in "caller" key + EncoderConfigOptions: []zap.EncoderConfigOption{func(ec *zapcore.EncoderConfig) { ec.CallerKey = "caller" }}, + ZapOpts: []zzap.Option{zzap.AddCaller(), zzap.AddCallerSkip(1)}, +} + +// BindFlags binds controller-runtime logging flags to provided flag Set +func BindFlags(fs *flag.FlagSet) { + Options.BindFlags(fs) +} + +// InitLog initializes controller-runtime log (zap log) +// this should be called once Options have been initialized +// either by parsing flags or directly modifying Options. +func InitLog() { + log.SetLogger(zap.New(zap.UseFlagOptions(Options))) +} + +// SetLogLevel sets current logging level to the provided lvl +func SetLogLevel(lvl string) error { + newLevel, err := zapcore.ParseLevel(lvl) + if err != nil { + return err + } + + currLevel := Options.Level.(zzap.AtomicLevel).Level() + + if newLevel != currLevel { + log.Log.Info("set log verbose level", "newLevel", newLevel.String(), "currentLevel", currLevel.String()) + Options.Level.(zzap.AtomicLevel).SetLevel(newLevel) + } + return nil +} + +// GetLogLevel returns the current logging level +func GetLogLevel() string { + return Options.Level.(zzap.AtomicLevel).Level().String() +} From 76ed0f44d218bbfc982f8ae392c3fa65aaa2891a Mon Sep 17 00:00:00 2001 From: adrianc Date: Thu, 25 Jul 2024 18:45:25 +0300 Subject: [PATCH 2/8] expose operator namespace via vars package - add vars package which container operator namesepace taken from env variable - update manager to expose operator namespace via env var Signed-off-by: adrianc --- config/manager/manager.yaml | 6 ++++++ internal/controller/suite_test.go | 9 ++++++--- internal/vars/vars.go | 21 +++++++++++++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 internal/vars/vars.go diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index 5ab25fa..e1d1d56 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -98,5 +98,11 @@ spec: requests: cpu: 10m memory: 64Mi + env: + - name: OPERATOR_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + serviceAccountName: controller-manager terminationGracePeriodSeconds: 10 diff --git a/internal/controller/suite_test.go b/internal/controller/suite_test.go index 9efafb4..8b7c90c 100644 --- a/internal/controller/suite_test.go +++ b/internal/controller/suite_test.go @@ -29,10 +29,10 @@ import ( "k8s.io/client-go/rest" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/envtest" - ctrllog "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/log/zap" maintenancev1alpha1 "github.com/Mellanox/maintenance-operator/api/v1alpha1" + operatorlog "github.com/Mellanox/maintenance-operator/internal/log" + "github.com/Mellanox/maintenance-operator/internal/vars" //+kubebuilder:scaffold:imports ) @@ -50,7 +50,7 @@ func TestControllers(t *testing.T) { } var _ = BeforeSuite(func() { - ctrllog.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + operatorlog.InitLog() By("bootstrapping test environment") testEnv = &envtest.Environment{ @@ -81,6 +81,9 @@ var _ = BeforeSuite(func() { Expect(err).NotTo(HaveOccurred()) Expect(k8sClient).NotTo(BeNil()) + // init operator vars + vars.OperatorNamespace = "default" + }) var _ = AfterSuite(func() { diff --git a/internal/vars/vars.go b/internal/vars/vars.go new file mode 100644 index 0000000..92f11ab --- /dev/null +++ b/internal/vars/vars.go @@ -0,0 +1,21 @@ +/* + Copyright 2024, NVIDIA CORPORATION & AFFILIATES + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package vars + +import "os" + +var OperatorNamespace = os.Getenv("OPERATOR_NAMESPACE") From 83e5a7915077d00fffe7b8435614e30c5704509e Mon Sep 17 00:00:00 2001 From: adrianc Date: Thu, 25 Jul 2024 18:49:19 +0300 Subject: [PATCH 3/8] update default scheduler with missing comment Signed-off-by: adrianc --- internal/scheduler/default_scheduler.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/scheduler/default_scheduler.go b/internal/scheduler/default_scheduler.go index 1a617d1..5e3f6d8 100644 --- a/internal/scheduler/default_scheduler.go +++ b/internal/scheduler/default_scheduler.go @@ -59,8 +59,9 @@ func (ds *defaultScheduler) Schedule(clusterState *ClusterState, schedulerCtx *S types.NamespacedName{Namespace: b.Namespace, Name: b.Name}.String()) }) - // compact entries pointing to the same Node - // note(adrianc) for this to work we rely on the fact that entries with same node are adjacent (assured by the sort function above) + // compact entries pointing to the same Node, keeping the first (highest priority) NodeMainenance from the list. + // this is done as we can only recommend a single NodeMaintenance per node for scheduling. + // NOTE(adrianc): for this to work we rely on the fact that entries with same node are adjacent (assured by the sort function above) compacted := slices.CompactFunc(schedulerCtx.CandidateMaintenance, func(a *maintenancev1.NodeMaintenance, b *maintenancev1.NodeMaintenance) bool { return a.Spec.NodeName == b.Spec.NodeName }) From 35c4ddca3332352a1f39ed1cbbca4d40c1930ff7 Mon Sep 17 00:00:00 2001 From: adrianc Date: Thu, 25 Jul 2024 18:50:06 +0300 Subject: [PATCH 4/8] Add scheduler controller options - implement NodeMaintenanceSchedulerReconcilerOptions which allows to update reconciler options during runtime in a async manner - update controller to use NodeMaintenanceSchedulerReconcilerOptions - update unit tests Signed-off-by: adrianc --- .../nodemaintenancescheduler_controller.go | 74 ++++++++++++++++--- ...odemaintenancescheduler_controller_test.go | 58 ++++++++++----- 2 files changed, 105 insertions(+), 27 deletions(-) diff --git a/internal/controller/nodemaintenancescheduler_controller.go b/internal/controller/nodemaintenancescheduler_controller.go index 75c6cb5..b34769d 100644 --- a/internal/controller/nodemaintenancescheduler_controller.go +++ b/internal/controller/nodemaintenancescheduler_controller.go @@ -51,18 +51,68 @@ const ( schedulerSyncTime = 10 * time.Second ) +var defaultmaxParallelOperations = &intstr.IntOrString{Type: intstr.Int, IntVal: 1} + var schedulerResyncResult = ctrl.Result{RequeueAfter: schedulerSyncTime} +// NewNodeMaintenanceSchedulerReconcilerOptions creates new *NodeMaintenanceSchedulerReconcilerOptions +func NewNodeMaintenanceSchedulerReconcilerOptions() *NodeMaintenanceSchedulerReconcilerOptions { + return &NodeMaintenanceSchedulerReconcilerOptions{ + Mutex: sync.Mutex{}, + pendingMaxUnavailable: nil, + pendingMaxParallelOperations: defaultmaxParallelOperations, + maxUnavailable: nil, + maxParallelOperations: defaultmaxParallelOperations, + } +} + +// NodeMaintenanceSchedulerReconcilerOptions are options for NodeMaintenanceSchedulerReconciler where values +// are stored by external entity and read by NodeMaintenanceSchedulerReconciler. +type NodeMaintenanceSchedulerReconcilerOptions struct { + sync.Mutex + + pendingMaxUnavailable *intstr.IntOrString + pendingMaxParallelOperations *intstr.IntOrString + maxUnavailable *intstr.IntOrString + maxParallelOperations *intstr.IntOrString +} + +// Store maxUnavailable, maxParallelOperations options for NodeMaintenanceSchedulerReconciler +func (nmsro *NodeMaintenanceSchedulerReconcilerOptions) Store(maxUnavailable, maxParallelOperations *intstr.IntOrString) { + nmsro.Lock() + defer nmsro.Unlock() + + nmsro.pendingMaxUnavailable = maxUnavailable + nmsro.pendingMaxParallelOperations = maxParallelOperations +} + +// Load loads the last Stored options +func (nmsro *NodeMaintenanceSchedulerReconcilerOptions) Load() { + nmsro.Lock() + defer nmsro.Unlock() + + nmsro.maxUnavailable = nmsro.pendingMaxUnavailable + nmsro.maxParallelOperations = nmsro.pendingMaxParallelOperations +} + +// MaxUnavailable returns the last loaded MaxUnavailable option +func (nmsro *NodeMaintenanceSchedulerReconcilerOptions) MaxUnavailable() *intstr.IntOrString { + return nmsro.maxUnavailable +} + +// MaxParallelOperations returns the last loaded MaxParallelOperations option +func (nmsro *NodeMaintenanceSchedulerReconcilerOptions) MaxParallelOperations() *intstr.IntOrString { + return nmsro.maxParallelOperations +} + // NodeMaintenanceSchedulerReconciler reconciles a NodeMaintenance object type NodeMaintenanceSchedulerReconciler struct { client.Client Scheme *runtime.Scheme - MaxUnavailable *intstr.IntOrString - MaxParallelOperations *intstr.IntOrString - - Log logr.Logger - Sched scheduler.Scheduler + Options *NodeMaintenanceSchedulerReconcilerOptions + Log logr.Logger + Sched scheduler.Scheduler } //+kubebuilder:rbac:groups=maintenance.nvidia.com,resources=nodemaintenances,verbs=get;list;watch;create;update;patch;delete @@ -77,6 +127,10 @@ type NodeMaintenanceSchedulerReconciler struct { // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.16.3/pkg/reconcile func (r *NodeMaintenanceSchedulerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { r.Log.Info("run periodic scheduler loop") + // load any stored options + r.Options.Load() + r.Log.Info("loaded options", "maxUnavailable", + r.Options.MaxUnavailable(), "maxParallelOperations", r.Options.MaxParallelOperations()) // Check if we can schedule new NodeMaintenance requests, determine how many maintenance slots we have nl := &corev1.NodeList{} @@ -257,11 +311,12 @@ func (r *NodeMaintenanceSchedulerReconciler) preSchedule(nodes []*corev1.Node, n // getMaxUnavailableNodes returns the absolute number of unavailable nodes allowed or -1 if no limit. func (r *NodeMaintenanceSchedulerReconciler) getMaxUnavailableNodes(totalNumOfNodes int) (int, error) { + maxUnavailable := r.Options.MaxUnavailable() // unset means unlimited - if r.MaxUnavailable == nil { + if maxUnavailable == nil { return -1, nil } - maxUnavail, err := intstr.GetScaledValueFromIntOrPercent(r.MaxUnavailable, totalNumOfNodes, false) + maxUnavail, err := intstr.GetScaledValueFromIntOrPercent(maxUnavailable, totalNumOfNodes, false) if err != nil { return -1, err } @@ -271,11 +326,12 @@ func (r *NodeMaintenanceSchedulerReconciler) getMaxUnavailableNodes(totalNumOfNo // getMaxOperations returns the absolute number of operations allowed. func (r *NodeMaintenanceSchedulerReconciler) getMaxOperations(totalNumOfNodes int) (int, error) { + maxParallelOperations := r.Options.MaxParallelOperations() // unset, use defaut of 1 - if r.MaxParallelOperations == nil { + if maxParallelOperations == nil { return 1, nil } - maxOper, err := intstr.GetScaledValueFromIntOrPercent(r.MaxParallelOperations, totalNumOfNodes, true) + maxOper, err := intstr.GetScaledValueFromIntOrPercent(maxParallelOperations, totalNumOfNodes, true) if err != nil { return -1, err } diff --git a/internal/controller/nodemaintenancescheduler_controller_test.go b/internal/controller/nodemaintenancescheduler_controller_test.go index 911b04f..4950e36 100644 --- a/internal/controller/nodemaintenancescheduler_controller_test.go +++ b/internal/controller/nodemaintenancescheduler_controller_test.go @@ -73,10 +73,11 @@ var _ = Describe("NodeMaintenanceScheduler Controller", func() { By("create NodeMaintenanceSchedulerReconciler") nmSchedulerReconcilerLog := ctrllog.Log.WithName("NodeMaintenanceScheduler") reconciler = &NodeMaintenanceSchedulerReconciler{ - Client: k8sClient, - Scheme: k8sClient.Scheme(), - Log: nmSchedulerReconcilerLog, - Sched: scheduler.NewDefaultScheduler(nmSchedulerReconcilerLog.WithName("DefaultScheduler")), + Client: k8sClient, + Scheme: k8sClient.Scheme(), + Log: nmSchedulerReconcilerLog, + Sched: scheduler.NewDefaultScheduler(nmSchedulerReconcilerLog.WithName("DefaultScheduler")), + Options: NewNodeMaintenanceSchedulerReconcilerOptions(), } // setup reconciler with manager @@ -149,9 +150,8 @@ var _ = Describe("NodeMaintenanceScheduler Controller", func() { nodes = testutils.GetTestNodes("node", 5, false) maintenances = nil reconciler = &NodeMaintenanceSchedulerReconciler{ - Log: ctrllog.Log.WithName("NodeMaintenanceScheduler"), - MaxUnavailable: nil, - MaxParallelOperations: nil, + Log: ctrllog.Log.WithName("NodeMaintenanceScheduler"), + Options: NewNodeMaintenanceSchedulerReconcilerOptions(), } }) @@ -175,7 +175,8 @@ var _ = Describe("NodeMaintenanceScheduler Controller", func() { testutils.GetTestNodeMaintenance("test-nm-3", "node-1", "test-operator.nvidia.com", maintenancev1.ConditionReasonPending), testutils.GetTestNodeMaintenance("test-nm-4", "node-2", "test-operator.nvidia.com", maintenancev1.ConditionReasonPending), } - reconciler.MaxParallelOperations = &intstr.IntOrString{Type: intstr.Int, IntVal: 3} + reconciler.Options.Store(nil, &intstr.IntOrString{Type: intstr.Int, IntVal: 3}) + reconciler.Options.Load() schedCtx, err := reconciler.preSchedule(nodes, maintenances) Expect(err).ToNot(HaveOccurred()) @@ -190,7 +191,8 @@ var _ = Describe("NodeMaintenanceScheduler Controller", func() { testutils.GetTestNodeMaintenance("test-nm-1", "node-0", "test-operator.nvidia.com", maintenancev1.ConditionReasonPending), testutils.GetTestNodeMaintenance("test-nm-2", "node-1", "test-operator.nvidia.com", maintenancev1.ConditionReasonPending), } - reconciler.MaxParallelOperations = &intstr.IntOrString{Type: intstr.Int, IntVal: 1} + reconciler.Options.Store(nil, &intstr.IntOrString{Type: intstr.Int, IntVal: 1}) + reconciler.Options.Load() schedCtx, err := reconciler.preSchedule(nodes, maintenances) Expect(err).ToNot(HaveOccurred()) @@ -206,7 +208,8 @@ var _ = Describe("NodeMaintenanceScheduler Controller", func() { testutils.GetTestNodeMaintenance("test-nm-2", "node-0", "test-operator.nvidia.com", maintenancev1.ConditionReasonPending), testutils.GetTestNodeMaintenance("test-nm-3", "node-1", "test-operator.nvidia.com", maintenancev1.ConditionReasonPending), } - reconciler.MaxParallelOperations = &intstr.IntOrString{Type: intstr.Int, IntVal: 3} + reconciler.Options.Store(nil, &intstr.IntOrString{Type: intstr.Int, IntVal: 3}) + reconciler.Options.Load() schedCtx, err := reconciler.preSchedule(nodes, maintenances) Expect(err).ToNot(HaveOccurred()) @@ -223,8 +226,9 @@ var _ = Describe("NodeMaintenanceScheduler Controller", func() { testutils.GetTestNodeMaintenance("test-nm-3", "node-1", "test-operator.nvidia.com", maintenancev1.ConditionReasonPending), testutils.GetTestNodeMaintenance("test-nm-4", "node-2", "test-operator.nvidia.com", maintenancev1.ConditionReasonPending), } - reconciler.MaxParallelOperations = &intstr.IntOrString{Type: intstr.Int, IntVal: 5} - reconciler.MaxUnavailable = &intstr.IntOrString{Type: intstr.Int, IntVal: 3} + reconciler.Options.Store(&intstr.IntOrString{Type: intstr.Int, IntVal: 3}, + &intstr.IntOrString{Type: intstr.Int, IntVal: 5}) + reconciler.Options.Load() schedCtx, err := reconciler.preSchedule(nodes, maintenances) Expect(err).ToNot(HaveOccurred()) @@ -240,8 +244,9 @@ var _ = Describe("NodeMaintenanceScheduler Controller", func() { testutils.GetTestNodeMaintenance("test-nm-2", "node-2", "test-operator.nvidia.com", maintenancev1.ConditionReasonPending), testutils.GetTestNodeMaintenance("test-nm-3", "node-2", "test-operator.nvidia.com", maintenancev1.ConditionReasonPending), } - reconciler.MaxParallelOperations = &intstr.IntOrString{Type: intstr.Int, IntVal: 5} - reconciler.MaxUnavailable = &intstr.IntOrString{Type: intstr.String, StrVal: "50%"} + reconciler.Options.Store(&intstr.IntOrString{Type: intstr.String, StrVal: "50%"}, + &intstr.IntOrString{Type: intstr.Int, IntVal: 5}) + reconciler.Options.Load() schedCtx, err := reconciler.preSchedule(nodes, maintenances) Expect(err).ToNot(HaveOccurred()) @@ -260,8 +265,9 @@ var _ = Describe("NodeMaintenanceScheduler Controller", func() { } // node-2 is unavailable because of "other" reason nodes[2].Spec.Unschedulable = true - reconciler.MaxParallelOperations = &intstr.IntOrString{Type: intstr.Int, IntVal: 5} - reconciler.MaxUnavailable = &intstr.IntOrString{Type: intstr.Int, IntVal: 3} + reconciler.Options.Store(&intstr.IntOrString{Type: intstr.Int, IntVal: 3}, + &intstr.IntOrString{Type: intstr.Int, IntVal: 5}) + reconciler.Options.Load() schedCtx, err := reconciler.preSchedule(nodes, maintenances) Expect(err).ToNot(HaveOccurred()) @@ -279,8 +285,9 @@ var _ = Describe("NodeMaintenanceScheduler Controller", func() { testutils.GetTestNodeMaintenance("test-nm-5", "node-4", "test-operator.nvidia.com", maintenancev1.ConditionReasonPending), testutils.GetTestNodeMaintenance("test-nm-6", "node-4", "test-operator.nvidia.com", maintenancev1.ConditionReasonPending), } - reconciler.MaxParallelOperations = &intstr.IntOrString{Type: intstr.Int, IntVal: 2} - reconciler.MaxUnavailable = &intstr.IntOrString{Type: intstr.Int, IntVal: 2} + reconciler.Options.Store(&intstr.IntOrString{Type: intstr.Int, IntVal: 2}, + &intstr.IntOrString{Type: intstr.Int, IntVal: 2}) + reconciler.Options.Load() schedCtx, err := reconciler.preSchedule(nodes, maintenances) Expect(err).ToNot(HaveOccurred()) @@ -288,6 +295,21 @@ var _ = Describe("NodeMaintenanceScheduler Controller", func() { }) }) + + Context("NewNodeMaintenanceSchedulerReconcilerOptions", func() { + It("Works", func() { + options := NewNodeMaintenanceSchedulerReconcilerOptions() + Expect(options.MaxParallelOperations()).To(Equal(defaultmaxParallelOperations)) + Expect(options.MaxUnavailable()).To(BeNil()) + newVal := &intstr.IntOrString{Type: intstr.Int, IntVal: 3} + options.Store(newVal, newVal) + Expect(options.MaxParallelOperations()).To(Equal(defaultmaxParallelOperations)) + Expect(options.MaxUnavailable()).To(BeNil()) + options.Load() + Expect(options.MaxParallelOperations()).To(Equal(newVal)) + Expect(options.MaxUnavailable()).To(Equal(newVal)) + }) + }) }) }) From 62e24c9a4cf2c2fdc1507b4740ddf1e59deeeb47 Mon Sep 17 00:00:00 2001 From: adrianc Date: Thu, 25 Jul 2024 18:52:12 +0300 Subject: [PATCH 5/8] Add node manintenance controller options - implement NodeMaintenanceReconcilerOptions which allows to update reconciler options during runtime in a async manner - update controller to use NodeMaintenanceReconcilerOptions - update unit tests Signed-off-by: adrianc --- .../controller/nodemaintenance_controller.go | 52 +++++- .../nodemaintenance_controller_test.go | 176 ++++++++++-------- 2 files changed, 148 insertions(+), 80 deletions(-) diff --git a/internal/controller/nodemaintenance_controller.go b/internal/controller/nodemaintenance_controller.go index 7dbab67..4bcf363 100644 --- a/internal/controller/nodemaintenance_controller.go +++ b/internal/controller/nodemaintenance_controller.go @@ -18,9 +18,11 @@ package controller import ( "context" + "sync" + "time" "github.com/go-logr/logr" - "k8s.io/apimachinery/pkg/api/errors" + k8serrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" @@ -32,10 +34,52 @@ import ( "github.com/Mellanox/maintenance-operator/internal/k8sutils" ) +var defaultMaxNodeMaintenanceTime = 1600 * time.Second + +// NewNodeMaintenanceReconcilerOptions creates new *NodeMaintenanceReconcilerOptions +func NewNodeMaintenanceReconcilerOptions() *NodeMaintenanceReconcilerOptions { + return &NodeMaintenanceReconcilerOptions{ + pendingMaxNodeMaintenanceTime: defaultMaxNodeMaintenanceTime, + maxNodeMaintenanceTime: defaultMaxNodeMaintenanceTime, + } +} + +// NodeMaintenanceReconcilerOptions are options for NodeMaintenanceReconciler where values +// are stored by external entity and read by NodeMaintenanceReconciler. +type NodeMaintenanceReconcilerOptions struct { + sync.Mutex + + pendingMaxNodeMaintenanceTime time.Duration + maxNodeMaintenanceTime time.Duration +} + +// Store maxUnavailable, maxParallelOperations options for NodeMaintenanceReconciler +func (nmro *NodeMaintenanceReconcilerOptions) Store(maxNodeMaintenanceTime time.Duration) { + nmro.Lock() + defer nmro.Unlock() + + nmro.pendingMaxNodeMaintenanceTime = maxNodeMaintenanceTime +} + +// Load loads the last Stored options +func (nmro *NodeMaintenanceReconcilerOptions) Load() { + nmro.Lock() + defer nmro.Unlock() + + nmro.maxNodeMaintenanceTime = nmro.pendingMaxNodeMaintenanceTime +} + +// MaxNodeMaintenanceTime returns the last loaded MaxUnavailable option +func (nmro *NodeMaintenanceReconcilerOptions) MaxNodeMaintenanceTime() time.Duration { + return nmro.maxNodeMaintenanceTime +} + // NodeMaintenanceReconciler reconciles a NodeMaintenance object type NodeMaintenanceReconciler struct { client.Client Scheme *runtime.Scheme + + Options *NodeMaintenanceReconcilerOptions } //+kubebuilder:rbac:groups=maintenance.nvidia.com,resources=nodemaintenances,verbs=get;list;watch;create;update;patch;delete @@ -51,10 +95,14 @@ func (r *NodeMaintenanceReconciler) Reconcile(ctx context.Context, req ctrl.Requ reqLog := log.FromContext(ctx) reqLog.Info("got request", "name", req.NamespacedName) + // load any stored options + r.Options.Load() + reqLog.Info("loaded options", "maxNodeMaintenanceTime", r.Options.MaxNodeMaintenanceTime()) + // get NodeMaintenance object nm := &maintenancev1.NodeMaintenance{} if err := r.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: req.Name}, nm); err != nil { - if errors.IsNotFound(err) { + if k8serrors.IsNotFound(err) { reqLog.Info("NodeMaintenance object not found, nothing to do.") return reconcile.Result{}, nil } diff --git a/internal/controller/nodemaintenance_controller_test.go b/internal/controller/nodemaintenance_controller_test.go index 8b3493a..65cf4ea 100644 --- a/internal/controller/nodemaintenance_controller_test.go +++ b/internal/controller/nodemaintenance_controller_test.go @@ -34,89 +34,109 @@ import ( ) var _ = Describe("NodeMaintenance Controller", func() { - var nmObjectsToCleanup []*maintenancev1.NodeMaintenance - var nodeObjectsToCleanup []*corev1.Node - - var reconciler *NodeMaintenanceReconciler - - // test context, TODO(adrianc): use ginkgo spec context - var testCtx context.Context - - BeforeEach(func() { - testCtx = context.Background() - // create node objects for scheduler to use - By("create test nodes") - nodes := testutils.GetTestNodes("test-node", 1, false) - for i := range nodes { - Expect(k8sClient.Create(testCtx, nodes[i])).ToNot(HaveOccurred()) - nodeObjectsToCleanup = append(nodeObjectsToCleanup, nodes[i]) - } - - // create controller manager - By("create controller manager") - mgr, err := ctrl.NewManager(cfg, ctrl.Options{ - Scheme: k8sClient.Scheme(), - Metrics: metricsserver.Options{BindAddress: "0"}, - }) - Expect(err).ToNot(HaveOccurred()) - - // create reconciler - By("create NodeMaintenanceReconciler") - reconciler = &NodeMaintenanceReconciler{ - Client: k8sClient, - Scheme: k8sClient.Scheme(), - } - - // setup reconciler with manager - By("setup NodeMaintenanceReconciler with controller manager") - Expect(reconciler.SetupWithManager(mgr)).ToNot(HaveOccurred()) - - // start manager - testMgrCtx, cancel := context.WithCancel(testCtx) - By("start manager") - wg := sync.WaitGroup{} - wg.Add(1) - go func() { - defer wg.Done() - defer GinkgoRecover() - By("Start controller manager") - err := mgr.Start(testMgrCtx) + Context("Envtests", func() { + var nmObjectsToCleanup []*maintenancev1.NodeMaintenance + var nodeObjectsToCleanup []*corev1.Node + + var reconciler *NodeMaintenanceReconciler + var options *NodeMaintenanceReconcilerOptions + + // test context, TODO(adrianc): use ginkgo spec context + var testCtx context.Context + + BeforeEach(func() { + testCtx = context.Background() + // create node objects for scheduler to use + By("create test nodes") + nodes := testutils.GetTestNodes("test-node", 1, false) + for i := range nodes { + Expect(k8sClient.Create(testCtx, nodes[i])).ToNot(HaveOccurred()) + nodeObjectsToCleanup = append(nodeObjectsToCleanup, nodes[i]) + } + + // create controller manager + By("create controller manager") + mgr, err := ctrl.NewManager(cfg, ctrl.Options{ + Scheme: k8sClient.Scheme(), + Metrics: metricsserver.Options{BindAddress: "0"}, + }) Expect(err).ToNot(HaveOccurred()) - }() - DeferCleanup(func() { - By("Shut down controller manager") - cancel() - wg.Wait() - }) - }) + // create reconciler + By("create NodeMaintenanceReconciler") + options = NewNodeMaintenanceReconcilerOptions() + reconciler = &NodeMaintenanceReconciler{ + Client: k8sClient, + Scheme: k8sClient.Scheme(), + Options: options, + } - AfterEach(func() { - By("Cleanup NodeMaintenance resources") - for _, nm := range nmObjectsToCleanup { - Expect(k8sClient.Delete(testCtx, nm)).To(Succeed()) - } - nmObjectsToCleanup = make([]*maintenancev1.NodeMaintenance, 0) - - By("Cleanup Node resources") - for _, n := range nodeObjectsToCleanup { - Expect(k8sClient.Delete(testCtx, n)).To(Succeed()) - } - nodeObjectsToCleanup = make([]*corev1.Node, 0) - }) + // setup reconciler with manager + By("setup NodeMaintenanceReconciler with controller manager") + Expect(reconciler.SetupWithManager(mgr)).ToNot(HaveOccurred()) + + // start manager + testMgrCtx, cancel := context.WithCancel(testCtx) + By("start manager") + wg := sync.WaitGroup{} + wg.Add(1) + go func() { + defer wg.Done() + defer GinkgoRecover() + By("Start controller manager") + err := mgr.Start(testMgrCtx) + Expect(err).ToNot(HaveOccurred()) + }() + + DeferCleanup(func() { + By("Shut down controller manager") + cancel() + wg.Wait() + }) + }) - It("Should transition new NodeMaintenance Resource to Pending", func() { - nm := testutils.GetTestNodeMaintenance("test-nm", "test-node-0", "some-operator.nvidia.com", "") - Expect(k8sClient.Create(testCtx, nm)).ToNot(HaveOccurred()) - nmObjectsToCleanup = append(nmObjectsToCleanup, nm) + AfterEach(func() { + By("Cleanup NodeMaintenance resources") + for _, nm := range nmObjectsToCleanup { + Expect(k8sClient.Delete(testCtx, nm)).To(Succeed()) + } + nmObjectsToCleanup = make([]*maintenancev1.NodeMaintenance, 0) - Eventually(func() string { - nm := &maintenancev1.NodeMaintenance{} - err := k8sClient.Get(testCtx, types.NamespacedName{Namespace: "default", Name: "test-nm"}, nm) - if err == nil { - return k8sutils.GetReadyConditionReason(nm) + By("Cleanup Node resources") + for _, n := range nodeObjectsToCleanup { + Expect(k8sClient.Delete(testCtx, n)).To(Succeed()) } - return "" - }).WithTimeout(10 * time.Second).WithPolling(1 * time.Second).Should(Equal(maintenancev1.ConditionReasonPending)) + nodeObjectsToCleanup = make([]*corev1.Node, 0) + }) + + It("Should transition new NodeMaintenance Resource to Pending", func() { + nm := testutils.GetTestNodeMaintenance("test-nm", "test-node-0", "some-operator.nvidia.com", "") + Expect(k8sClient.Create(testCtx, nm)).ToNot(HaveOccurred()) + nmObjectsToCleanup = append(nmObjectsToCleanup, nm) + + Eventually(func() string { + nm := &maintenancev1.NodeMaintenance{} + err := k8sClient.Get(testCtx, types.NamespacedName{Namespace: "default", Name: "test-nm"}, nm) + if err == nil { + return k8sutils.GetReadyConditionReason(nm) + } + return "" + }).WithTimeout(10 * time.Second).WithPolling(1 * time.Second).Should(Equal(maintenancev1.ConditionReasonPending)) + }) }) + + Context("UnitTests", func() { + Context("NodeMaintenanceReconcilerOptions", func() { + It("Works", func() { + options := NewNodeMaintenanceReconcilerOptions() + Expect(options.MaxNodeMaintenanceTime()).To(Equal(defaultMaxNodeMaintenanceTime)) + newTime := 300 * time.Second + options.Store(newTime) + Expect(options.MaxNodeMaintenanceTime()).To(Equal(defaultMaxNodeMaintenanceTime)) + options.Load() + Expect(options.MaxNodeMaintenanceTime()).To(Equal(newTime)) + }) + }) + }) + }) From f4f0ba203feee5285a99dac11ac2d45b136390c7 Mon Sep 17 00:00:00 2001 From: adrianc Date: Thu, 25 Jul 2024 18:53:39 +0300 Subject: [PATCH 6/8] implement maintenance operator config reconciler the reconciler reconciles default MaintenanceOperatorConfig CR by updating log and reconciler options of both controllers Signed-off-by: adrianc --- .../maintenanceoperatorconfig_controller.go | 42 ++++++- ...intenanceoperatorconfig_controller_test.go | 103 +++++++++++++++++- 2 files changed, 142 insertions(+), 3 deletions(-) diff --git a/internal/controller/maintenanceoperatorconfig_controller.go b/internal/controller/maintenanceoperatorconfig_controller.go index 65038f3..d9d0aca 100644 --- a/internal/controller/maintenanceoperatorconfig_controller.go +++ b/internal/controller/maintenanceoperatorconfig_controller.go @@ -18,19 +18,30 @@ package controller import ( "context" + "time" + k8serrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" - maintenancev1alpha1 "github.com/Mellanox/maintenance-operator/api/v1alpha1" + maintenancev1 "github.com/Mellanox/maintenance-operator/api/v1alpha1" + operatorlog "github.com/Mellanox/maintenance-operator/internal/log" + "github.com/Mellanox/maintenance-operator/internal/vars" +) + +const ( + defaultMaintenanceOperatorConifgName = "default" ) // MaintenanceOperatorConfigReconciler reconciles a MaintenanceOperatorConfig object type MaintenanceOperatorConfigReconciler struct { client.Client Scheme *runtime.Scheme + + SchedulerReconcierOptions *NodeMaintenanceSchedulerReconcilerOptions + NodeMaintenanceReconcierOptions *NodeMaintenanceReconcilerOptions } //+kubebuilder:rbac:groups=maintenance.nvidia.com,resources=maintenanceoperatorconfigs,verbs=get;list;watch;create;update;patch;delete @@ -45,6 +56,33 @@ type MaintenanceOperatorConfigReconciler struct { func (r *MaintenanceOperatorConfigReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { reqLog := log.FromContext(ctx) reqLog.Info("got request", "name", req.NamespacedName) + if req.Name != defaultMaintenanceOperatorConifgName || req.Namespace != vars.OperatorNamespace { + reqLog.Info("request for non default MaintenanceOperatorConfig, ignoring") + return ctrl.Result{}, nil + } + + cfg := &maintenancev1.MaintenanceOperatorConfig{} + err := r.Client.Get(ctx, req.NamespacedName, cfg) + if err != nil { + if k8serrors.IsNotFound(err) { + return ctrl.Result{}, nil + } + return ctrl.Result{}, err + } + + // handle reconcilers options + reqLog.Info("store scheduler reconciler options", "MaxUnavailable", cfg.Spec.MaxUnavailable, + "MaxParallelOperations", cfg.Spec.MaxParallelOperations) + r.SchedulerReconcierOptions.Store(cfg.Spec.MaxUnavailable, cfg.Spec.MaxParallelOperations) + reqLog.Info("store nodeMaintenance reconciler options", "MaxNodeMaintenanceTimeSeconds", cfg.Spec.MaxNodeMaintenanceTimeSeconds) + r.NodeMaintenanceReconcierOptions.Store(time.Second * time.Duration(cfg.Spec.MaxNodeMaintenanceTimeSeconds)) + + // handle log level + reqLog.Info("setting operator log level", "LogLevel", cfg.Spec.LogLevel) + err = operatorlog.SetLogLevel(string(cfg.Spec.LogLevel)) + if err != nil { + return ctrl.Result{}, err + } return ctrl.Result{}, nil } @@ -52,6 +90,6 @@ func (r *MaintenanceOperatorConfigReconciler) Reconcile(ctx context.Context, req // SetupWithManager sets up the controller with the Manager. func (r *MaintenanceOperatorConfigReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). - For(&maintenancev1alpha1.MaintenanceOperatorConfig{}). + For(&maintenancev1.MaintenanceOperatorConfig{}). Complete(r) } diff --git a/internal/controller/maintenanceoperatorconfig_controller_test.go b/internal/controller/maintenanceoperatorconfig_controller_test.go index bc5bb6e..59f527e 100644 --- a/internal/controller/maintenanceoperatorconfig_controller_test.go +++ b/internal/controller/maintenanceoperatorconfig_controller_test.go @@ -18,23 +18,71 @@ package controller import ( "context" + "sync" + "time" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + ctrl "sigs.k8s.io/controller-runtime" + metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" maintenancev1 "github.com/Mellanox/maintenance-operator/api/v1alpha1" + operatorlog "github.com/Mellanox/maintenance-operator/internal/log" ) var _ = Describe("MaintenanceOperatorConfig Controller", func() { + var reconciler *MaintenanceOperatorConfigReconciler + // test context, TODO(adrianc): use ginkgo spec context var testCtx context.Context BeforeEach(func() { testCtx = context.Background() + + // create controller manager + By("create controller manager") + mgr, err := ctrl.NewManager(cfg, ctrl.Options{ + Scheme: k8sClient.Scheme(), + Metrics: metricsserver.Options{BindAddress: "0"}, + }) + Expect(err).ToNot(HaveOccurred()) + + // create reconciler + By("create MaintenanceOperatorConfigReconciler") + reconciler = &MaintenanceOperatorConfigReconciler{ + Client: k8sClient, + Scheme: k8sClient.Scheme(), + SchedulerReconcierOptions: NewNodeMaintenanceSchedulerReconcilerOptions(), + NodeMaintenanceReconcierOptions: NewNodeMaintenanceReconcilerOptions(), + } + + // setup reconciler with manager + By("setup MaintenanceOperatorConfigReconciler with controller manager") + Expect(reconciler.SetupWithManager(mgr)).ToNot(HaveOccurred()) + + // start manager + testMgrCtx, cancel := context.WithCancel(testCtx) + By("start manager") + wg := sync.WaitGroup{} + wg.Add(1) + go func() { + defer wg.Done() + defer GinkgoRecover() + By("Start controller manager") + err := mgr.Start(testMgrCtx) + Expect(err).ToNot(HaveOccurred()) + }() + + DeferCleanup(func() { + By("Shut down controller manager") + cancel() + wg.Wait() + }) }) - It("Should Reconcile MaintenanceOperatorConfig resource", func() { + It("Should Reconcile MaintenanceOperatorConfig resource with defaults", func() { oc := &maintenancev1.MaintenanceOperatorConfig{ ObjectMeta: metav1.ObjectMeta{Name: "default", Namespace: "default"}, Spec: maintenancev1.MaintenanceOperatorConfigSpec{}, @@ -43,5 +91,58 @@ var _ = Describe("MaintenanceOperatorConfig Controller", func() { DeferCleanup(func() { Expect(k8sClient.Delete(testCtx, oc)).ToNot(HaveOccurred()) }) + + Consistently(func(g Gomega) { + reconciler.SchedulerReconcierOptions.Load() + reconciler.NodeMaintenanceReconcierOptions.Load() + g.Expect(reconciler.SchedulerReconcierOptions.MaxParallelOperations()).To(Equal(&intstr.IntOrString{Type: intstr.Int, IntVal: 1})) + g.Expect(reconciler.SchedulerReconcierOptions.MaxUnavailable()).To(BeNil()) + g.Expect(reconciler.NodeMaintenanceReconcierOptions.MaxNodeMaintenanceTime()).To(Equal(defaultMaxNodeMaintenanceTime)) + }).ProbeEvery(100 * time.Millisecond).Within(time.Second).Should(Succeed()) + }) + + It("Should Reconcile MaintenanceOperatorConfig resource with specified values", func() { + By("create MaintenanceOperatorConfig with non default values") + oc := &maintenancev1.MaintenanceOperatorConfig{ + ObjectMeta: metav1.ObjectMeta{Name: "default", Namespace: "default"}, + Spec: maintenancev1.MaintenanceOperatorConfigSpec{ + MaxParallelOperations: &intstr.IntOrString{Type: intstr.Int, IntVal: 3}, + MaxUnavailable: &intstr.IntOrString{Type: intstr.Int, IntVal: 3}, + MaxNodeMaintenanceTimeSeconds: 300, + LogLevel: "debug", + }, + } + Expect(k8sClient.Create(testCtx, oc)).ToNot(HaveOccurred()) + DeferCleanup(func() { + Expect(k8sClient.Delete(testCtx, oc)).ToNot(HaveOccurred()) + }) + + By("check MaintenanceOperatorConfig values were updated") + Eventually(func(g Gomega) { + reconciler.SchedulerReconcierOptions.Load() + reconciler.NodeMaintenanceReconcierOptions.Load() + g.Expect(reconciler.SchedulerReconcierOptions.MaxParallelOperations()).To(Equal(oc.Spec.MaxParallelOperations)) + g.Expect(reconciler.SchedulerReconcierOptions.MaxUnavailable()).To(Equal(oc.Spec.MaxUnavailable)) + g.Expect(reconciler.NodeMaintenanceReconcierOptions.MaxNodeMaintenanceTime()). + To(Equal(time.Second * time.Duration(oc.Spec.MaxNodeMaintenanceTimeSeconds))) + g.Expect(operatorlog.GetLogLevel()).To(BeEquivalentTo(oc.Spec.LogLevel)) + }).ProbeEvery(100 * time.Millisecond).Within(time.Second).Should(Succeed()) + + By("update MaintenanceOperatorConfig") + oc.Spec.MaxParallelOperations = &intstr.IntOrString{Type: intstr.Int, IntVal: 5} + oc.Spec.MaxUnavailable = nil + oc.Spec.LogLevel = "info" + Expect(k8sClient.Update(testCtx, oc)).ToNot(HaveOccurred()) + + By("check MaintenanceOperatorConfig values were updated") + Eventually(func(g Gomega) { + reconciler.SchedulerReconcierOptions.Load() + reconciler.NodeMaintenanceReconcierOptions.Load() + g.Expect(reconciler.SchedulerReconcierOptions.MaxParallelOperations()).To(Equal(oc.Spec.MaxParallelOperations)) + g.Expect(reconciler.SchedulerReconcierOptions.MaxUnavailable()).To(Equal(oc.Spec.MaxUnavailable)) + g.Expect(reconciler.NodeMaintenanceReconcierOptions.MaxNodeMaintenanceTime()). + To(Equal(time.Second * time.Duration(oc.Spec.MaxNodeMaintenanceTimeSeconds))) + g.Expect(operatorlog.GetLogLevel()).To(BeEquivalentTo(oc.Spec.LogLevel)) + }).ProbeEvery(100 * time.Millisecond).Within(time.Second).Should(Succeed()) }) }) From f304160a5be3f59ee0a9a15ab5b7d86d9f5c33bd Mon Sep 17 00:00:00 2001 From: adrianc Date: Thu, 25 Jul 2024 18:55:13 +0300 Subject: [PATCH 7/8] update main update main with changes introduced in this set of commits - use log from log package - initialize options for reconcilers - pass reconciler options to operator config reconciler Signed-off-by: adrianc --- cmd/maintenance-manager/main.go | 46 +++++++++++++++++---------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/cmd/maintenance-manager/main.go b/cmd/maintenance-manager/main.go index c05086a..c8d19c0 100644 --- a/cmd/maintenance-manager/main.go +++ b/cmd/maintenance-manager/main.go @@ -31,12 +31,12 @@ import ( clientgoscheme "k8s.io/client-go/kubernetes/scheme" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/healthz" - "sigs.k8s.io/controller-runtime/pkg/log/zap" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" "sigs.k8s.io/controller-runtime/pkg/webhook" maintenancev1alpha1 "github.com/Mellanox/maintenance-operator/api/v1alpha1" "github.com/Mellanox/maintenance-operator/internal/controller" + operatorlog "github.com/Mellanox/maintenance-operator/internal/log" "github.com/Mellanox/maintenance-operator/internal/scheduler" "github.com/Mellanox/maintenance-operator/internal/version" //+kubebuilder:scaffold:imports @@ -73,18 +73,14 @@ func main() { "If set, HTTP/2 will be enabled for the metrics and webhook servers") flag.BoolVar(&printVersion, "version", false, "print version and exit") - opts := zap.Options{ - Development: true, - } - opts.BindFlags(flag.CommandLine) + operatorlog.BindFlags(flag.CommandLine) flag.Parse() if printVersion { fmt.Printf("%s\n", version.GetVersionString()) os.Exit(0) } - - ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) + operatorlog.InitLog() setupLog.Info("Maintenance Operator", "version", version.GetVersionString()) @@ -136,30 +132,36 @@ func main() { os.Exit(1) } + nmrOptions := controller.NewNodeMaintenanceReconcilerOptions() if err = (&controller.NodeMaintenanceReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Options: nmrOptions, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "NodeMaintenance") os.Exit(1) } - if err = (&controller.MaintenanceOperatorConfigReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), + + nmsrOptions := controller.NewNodeMaintenanceSchedulerReconcilerOptions() + nmsrLog := ctrl.Log.WithName("NodeMaintenanceScheduler") + if err = (&controller.NodeMaintenanceSchedulerReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Options: nmsrOptions, + Log: nmsrLog, + Sched: scheduler.NewDefaultScheduler(nmsrLog.WithName("DefaultScheduler")), }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "MaintenanceOperatorConfig") + setupLog.Error(err, "unable to create controller", "controller", "NodeMaintenanceScheduler") os.Exit(1) } - nmSchedulerReconcilerLog := ctrl.Log.WithName("NodeMaintenanceScheduler") - if err = (&controller.NodeMaintenanceSchedulerReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - MaxUnavailable: nil, - MaxParallelOperations: nil, - Log: nmSchedulerReconcilerLog, - Sched: scheduler.NewDefaultScheduler(nmSchedulerReconcilerLog.WithName("DefaultScheduler")), + + if err = (&controller.MaintenanceOperatorConfigReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + NodeMaintenanceReconcierOptions: nmrOptions, + SchedulerReconcierOptions: nmsrOptions, }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "NodeMaintenanceScheduler") + setupLog.Error(err, "unable to create controller", "controller", "MaintenanceOperatorConfig") os.Exit(1) } //+kubebuilder:scaffold:builder From 724e665a7a4ab55f4555ba4a888f8ab1fd864e07 Mon Sep 17 00:00:00 2001 From: adrianc Date: Sun, 28 Jul 2024 14:16:48 +0300 Subject: [PATCH 8/8] run go mod tidy Signed-off-by: adrianc --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 7d223b3..fde8c74 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/onsi/ginkgo/v2 v2.19.0 github.com/onsi/gomega v1.33.1 github.com/pkg/errors v0.9.1 + go.uber.org/zap v1.26.0 k8s.io/api v0.30.2 k8s.io/apimachinery v0.30.2 k8s.io/client-go v0.30.2 @@ -49,7 +50,6 @@ require ( github.com/prometheus/procfs v0.12.0 // indirect github.com/spf13/pflag v1.0.5 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.26.0 // indirect golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect golang.org/x/net v0.25.0 // indirect golang.org/x/oauth2 v0.12.0 // indirect