diff --git a/internal/controllers/device_plugin_reconciler.go b/internal/controllers/device_plugin_reconciler.go index 79be74f0e..388b04dd4 100644 --- a/internal/controllers/device_plugin_reconciler.go +++ b/internal/controllers/device_plugin_reconciler.go @@ -125,7 +125,7 @@ func (r *DevicePluginReconciler) Reconcile(ctx context.Context, req ctrl.Request err = r.reconHelperAPI.moduleUpdateDevicePluginStatus(ctx, mod, existingDevicePluginDS) if err != nil { - return res, fmt.Errorf("failed to update status of the module: %w", err) + return res, fmt.Errorf("failed to update device-plugin status of the module: %w", err) } logger.Info("Reconcile loop finished successfully") @@ -283,6 +283,10 @@ func (dprh *devicePluginReconcilerHelper) moduleUpdateDevicePluginStatus(ctx con mod *kmmv1beta1.Module, existingDevicePluginDS []appsv1.DaemonSet) error { + if mod.Spec.DevicePlugin == nil { + return nil + } + // get the number of nodes targeted by selector (which also relevant for device plugin) numTargetedNodes, err := dprh.getNumTargetedNodes(ctx, mod.Spec.Selector) if err != nil { diff --git a/internal/controllers/device_plugin_reconciler_test.go b/internal/controllers/device_plugin_reconciler_test.go index 6676c5689..abd50755c 100644 --- a/internal/controllers/device_plugin_reconciler_test.go +++ b/internal/controllers/device_plugin_reconciler_test.go @@ -510,9 +510,19 @@ var _ = Describe("DevicePluginReconciler_moduleUpdateDevicePluginStatus", func() ctx := context.Background() + It("device plugin not defined in the module", func() { + mod := kmmv1beta1.Module{} + err := dprh.moduleUpdateDevicePluginStatus(ctx, &mod, nil) + Expect(err).NotTo(HaveOccurred()) + }) + DescribeTable("device-plugin status update", func(numTargetedNodes int, numAvailableInDaemonSets []int, nodesMatchingNumber, availableNumber int) { - mod := kmmv1beta1.Module{} + mod := kmmv1beta1.Module{ + Spec: kmmv1beta1.ModuleSpec{ + DevicePlugin: &kmmv1beta1.DevicePluginSpec{}, + }, + } expectedMod := mod.DeepCopy() expectedMod.Status.DevicePlugin.NodesMatchingSelectorNumber = int32(nodesMatchingNumber) expectedMod.Status.DevicePlugin.DesiredNumber = int32(nodesMatchingNumber) diff --git a/internal/controllers/mock_module_nmc_reconciler.go b/internal/controllers/mock_module_nmc_reconciler.go index 7bddfe703..b98ab626d 100644 --- a/internal/controllers/mock_module_nmc_reconciler.go +++ b/internal/controllers/mock_module_nmc_reconciler.go @@ -130,6 +130,20 @@ func (mr *MockmoduleNMCReconcilerHelperAPIMockRecorder) getRequestedModule(ctx, return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "getRequestedModule", reflect.TypeOf((*MockmoduleNMCReconcilerHelperAPI)(nil).getRequestedModule), ctx, namespacedName) } +// moduleUpdateWorkerPodsStatus mocks base method. +func (m *MockmoduleNMCReconcilerHelperAPI) moduleUpdateWorkerPodsStatus(ctx context.Context, mod *v1beta1.Module, targetedNodes []v1.Node) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "moduleUpdateWorkerPodsStatus", ctx, mod, targetedNodes) + ret0, _ := ret[0].(error) + return ret0 +} + +// moduleUpdateWorkerPodsStatus indicates an expected call of moduleUpdateWorkerPodsStatus. +func (mr *MockmoduleNMCReconcilerHelperAPIMockRecorder) moduleUpdateWorkerPodsStatus(ctx, mod, targetedNodes any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "moduleUpdateWorkerPodsStatus", reflect.TypeOf((*MockmoduleNMCReconcilerHelperAPI)(nil).moduleUpdateWorkerPodsStatus), ctx, mod, targetedNodes) +} + // prepareSchedulingData mocks base method. func (m *MockmoduleNMCReconcilerHelperAPI) prepareSchedulingData(ctx context.Context, mod *v1beta1.Module, targetedNodes []v1.Node, currentNMCs sets.Set[string]) (map[string]schedulingData, []error) { m.ctrl.T.Helper() diff --git a/internal/controllers/module_nmc_reconciler.go b/internal/controllers/module_nmc_reconciler.go index 529c8ad62..2563e74a9 100644 --- a/internal/controllers/module_nmc_reconciler.go +++ b/internal/controllers/module_nmc_reconciler.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "reflect" "strings" "github.com/hashicorp/go-multierror" @@ -129,6 +130,9 @@ func (mnr *ModuleNMCReconciler) Reconcile(ctx context.Context, req ctrl.Request) sumErr = multierror.Append(sumErr, err) } + err = mnr.reconHelper.moduleUpdateWorkerPodsStatus(ctx, mod, targetedNodes) + sumErr = multierror.Append(sumErr, err) + err = sumErr.ErrorOrNil() if err != nil { return ctrl.Result{}, fmt.Errorf("failed to reconcile module %s/%s config: %v", mod.Namespace, mod.Name, err) @@ -167,6 +171,7 @@ type moduleNMCReconcilerHelperAPI interface { prepareSchedulingData(ctx context.Context, mod *kmmv1beta1.Module, targetedNodes []v1.Node, currentNMCs sets.Set[string]) (map[string]schedulingData, []error) enableModuleOnNode(ctx context.Context, mld *api.ModuleLoaderData, node *v1.Node) error disableModuleOnNode(ctx context.Context, modNamespace, modName, nodeName string) error + moduleUpdateWorkerPodsStatus(ctx context.Context, mod *kmmv1beta1.Module, targetedNodes []v1.Node) error } type moduleNMCReconcilerHelper struct { @@ -295,15 +300,19 @@ func (mnrh *moduleNMCReconcilerHelper) getNodesListBySelector(ctx context.Contex } func (mnrh *moduleNMCReconcilerHelper) getNMCsByModuleSet(ctx context.Context, mod *kmmv1beta1.Module) (sets.Set[string], error) { - nmcNamesList, err := mnrh.getNMCsNamesForModule(ctx, mod) + nmcList, err := mnrh.getNMCsForModule(ctx, mod) if err != nil { return nil, fmt.Errorf("failed to get list of %s/%s module's NMC for map: %v", mod.Namespace, mod.Name, err) } - return sets.New[string](nmcNamesList...), nil + result := sets.New[string]() + for _, nmc := range nmcList { + result.Insert(nmc.Name) + } + return result, nil } -func (mnrh *moduleNMCReconcilerHelper) getNMCsNamesForModule(ctx context.Context, mod *kmmv1beta1.Module) ([]string, error) { +func (mnrh *moduleNMCReconcilerHelper) getNMCsForModule(ctx context.Context, mod *kmmv1beta1.Module) ([]kmmv1beta1.NodeModulesConfig, error) { logger := log.FromContext(ctx) moduleNMCLabel := nmc.ModuleConfiguredLabel(mod.Namespace, mod.Name) logger.V(1).Info("Listing nmcs", "selector", moduleNMCLabel) @@ -312,11 +321,8 @@ func (mnrh *moduleNMCReconcilerHelper) getNMCsNamesForModule(ctx context.Context if err := mnrh.client.List(ctx, &selectedNMCs, opt); err != nil { return nil, fmt.Errorf("could not list NMCs: %v", err) } - result := make([]string, len(selectedNMCs.Items)) - for i := range selectedNMCs.Items { - result[i] = selectedNMCs.Items[i].Name - } - return result, nil + + return selectedNMCs.Items, nil } // prepareSchedulingData prepare data needed to scheduling enable/disable module per node @@ -428,6 +434,37 @@ func (mnrh *moduleNMCReconcilerHelper) removeModuleFromNMC(ctx context.Context, return nil } +func (mnrh *moduleNMCReconcilerHelper) moduleUpdateWorkerPodsStatus(ctx context.Context, mod *kmmv1beta1.Module, targetedNodes []v1.Node) error { + logger := log.FromContext(ctx) + // get nmcs with configured + nmcs, err := mnrh.getNMCsForModule(ctx, mod) + if err != nil { + return fmt.Errorf("failed to get configured NMCs for module %s/%s: %v", mod.Namespace, mod.Name, err) + } + + numAvailable := 0 + for _, nmc := range nmcs { + modSpec, _ := mnrh.nmcHelper.GetModuleSpecEntry(&nmc, mod.Namespace, mod.Name) + if modSpec == nil { + logger.Info(utils.WarnString( + fmt.Sprintf("module %s/%s spec is missing in NMC %s although config label is present", mod.Namespace, mod.Name, nmc.Name))) + continue + } + modStatus := mnrh.nmcHelper.GetModuleStatusEntry(&nmc, mod.Namespace, mod.Name) + if modStatus != nil && reflect.DeepEqual(modSpec.Config, modStatus.Config) { + numAvailable += 1 + } + } + + unmodifiedMod := mod.DeepCopy() + + mod.Status.ModuleLoader.NodesMatchingSelectorNumber = int32(len(targetedNodes)) + mod.Status.ModuleLoader.DesiredNumber = int32(len(nmcs)) + mod.Status.ModuleLoader.AvailableNumber = int32(numAvailable) + + return mnrh.client.Status().Patch(ctx, mod, client.MergeFrom(unmodifiedMod)) +} + func prepareNodeSchedulingData(node v1.Node, mld *api.ModuleLoaderData, currentNMCs sets.Set[string]) schedulingData { versionLabel := "" present := false diff --git a/internal/controllers/module_nmc_reconciler_test.go b/internal/controllers/module_nmc_reconciler_test.go index 210d5c820..388e27095 100644 --- a/internal/controllers/module_nmc_reconciler_test.go +++ b/internal/controllers/module_nmc_reconciler_test.go @@ -94,7 +94,9 @@ var _ = Describe("Reconcile", func() { getNodesError, getNMCsMapError, prepareSchedulingError, - shouldBeOnNode bool) { + shouldBeOnNode, + disableEnableError, + moduleUpdateStatusErr bool) { nmcMLDConfigs := map[string]schedulingData{"nodeName": disableSchedulingData} if shouldBeOnNode { @@ -123,13 +125,28 @@ var _ = Describe("Reconcile", func() { mockReconHelper.EXPECT().getNMCsByModuleSet(ctx, &mod).Return(currentNMCs, nil) if prepareSchedulingError { mockReconHelper.EXPECT().prepareSchedulingData(ctx, &mod, targetedNodes, currentNMCs).Return(nil, []error{returnedError}) - goto executeTestFunction + goto moduleStatusUpdateFunction } mockReconHelper.EXPECT().prepareSchedulingData(ctx, &mod, targetedNodes, currentNMCs).Return(nmcMLDConfigs, []error{}) + if disableEnableError { + if shouldBeOnNode { + mockReconHelper.EXPECT().enableModuleOnNode(ctx, &mld, &node).Return(returnedError) + } else { + mockReconHelper.EXPECT().disableModuleOnNode(ctx, mod.Namespace, mod.Name, node.Name).Return(returnedError) + } + goto moduleStatusUpdateFunction + } if shouldBeOnNode { - mockReconHelper.EXPECT().enableModuleOnNode(ctx, &mld, &node).Return(returnedError) + mockReconHelper.EXPECT().enableModuleOnNode(ctx, &mld, &node).Return(nil) + } else { + mockReconHelper.EXPECT().disableModuleOnNode(ctx, mod.Namespace, mod.Name, node.Name).Return(nil) + } + + moduleStatusUpdateFunction: + if moduleUpdateStatusErr { + mockReconHelper.EXPECT().moduleUpdateWorkerPodsStatus(ctx, &mod, targetedNodes).Return(returnedError) } else { - mockReconHelper.EXPECT().disableModuleOnNode(ctx, mod.Namespace, mod.Name, node.Name).Return(returnedError) + mockReconHelper.EXPECT().moduleUpdateWorkerPodsStatus(ctx, &mod, targetedNodes).Return(nil) } executeTestFunction: @@ -139,13 +156,14 @@ var _ = Describe("Reconcile", func() { Expect(err).To(HaveOccurred()) }, - Entry("getRequestedModule failed", true, false, false, false, false, false), - Entry("setFinalizerAndStatus failed", false, true, false, false, false, false), - Entry("getNodesListBySelector failed", false, false, true, false, false, false), - Entry("getNMCsByModuleMap failed", false, false, false, true, false, false), - Entry("prepareSchedulingData failed", false, false, false, false, true, false), - Entry("enableModuleOnNode failed", false, false, false, false, false, true), - Entry("disableModuleOnNode failed", false, false, false, false, false, false), + Entry("getRequestedModule failed", true, false, false, false, false, false, false, false), + Entry("setFinalizerAndStatus failed", false, true, false, false, false, false, false, false), + Entry("getNodesListBySelector failed", false, false, true, false, false, false, false, false), + Entry("getNMCsByModuleMap failed", false, false, false, true, false, false, false, false), + Entry("prepareSchedulingData failed", false, false, false, false, true, false, false, false), + Entry("enableModuleOnNode failed", false, false, false, false, false, true, true, false), + Entry("disableModuleOnNode failed", false, false, false, false, false, false, true, false), + Entry(".moduleUpdateWorkerPodsStatus failed", false, false, false, false, false, false, false, true), ) It("Good flow, should run on node", func() { @@ -157,6 +175,7 @@ var _ = Describe("Reconcile", func() { mockReconHelper.EXPECT().getNMCsByModuleSet(ctx, &mod).Return(currentNMCs, nil), mockReconHelper.EXPECT().prepareSchedulingData(ctx, &mod, targetedNodes, currentNMCs).Return(nmcMLDConfigs, nil), mockReconHelper.EXPECT().enableModuleOnNode(ctx, &mld, &node).Return(nil), + mockReconHelper.EXPECT().moduleUpdateWorkerPodsStatus(ctx, &mod, targetedNodes).Return(nil), ) res, err := mnr.Reconcile(ctx, req) @@ -174,6 +193,7 @@ var _ = Describe("Reconcile", func() { mockReconHelper.EXPECT().getNMCsByModuleSet(ctx, &mod).Return(currentNMCs, nil), mockReconHelper.EXPECT().prepareSchedulingData(ctx, &mod, targetedNodes, currentNMCs).Return(nmcMLDConfigs, nil), mockReconHelper.EXPECT().disableModuleOnNode(ctx, mod.Namespace, mod.Name, node.Name).Return(nil), + mockReconHelper.EXPECT().moduleUpdateWorkerPodsStatus(ctx, &mod, targetedNodes).Return(nil), ) res, err := mnr.Reconcile(ctx, req) @@ -929,3 +949,148 @@ var _ = Describe("removeModuleFromNMC", func() { Expect(err).NotTo(HaveOccurred()) }) }) + +var _ = Describe("moduleUpdateWorkerPodsStatus", func() { + var ( + ctx context.Context + ctrl *gomock.Controller + clnt *client.MockClient + mod kmmv1beta1.Module + mnrh *moduleNMCReconcilerHelper + helper *nmc.MockHelper + statusWriter *client.MockStatusWriter + ) + + BeforeEach(func() { + ctx = context.Background() + ctrl = gomock.NewController(GinkgoT()) + clnt = client.NewMockClient(ctrl) + helper = nmc.NewMockHelper(ctrl) + statusWriter = client.NewMockStatusWriter(ctrl) + mod = kmmv1beta1.Module{ + ObjectMeta: metav1.ObjectMeta{ + Name: "modName", + Namespace: "modNamespace", + }, + } + mnrh = &moduleNMCReconcilerHelper{client: clnt, nmcHelper: helper} + }) + + It("faled to get configured NMCs", func() { + clnt.EXPECT().List(ctx, gomock.Any(), gomock.Any()).Return(fmt.Errorf("some error")) + err := mnrh.moduleUpdateWorkerPodsStatus(ctx, &mod, nil) + Expect(err).To(HaveOccurred()) + }) + + It("module missing from spec", func() { + nmc1 := kmmv1beta1.NodeModulesConfig{ + ObjectMeta: metav1.ObjectMeta{Name: "nmc1"}, + } + targetedNodes := []v1.Node{v1.Node{}, v1.Node{}} + expectedMod := mod.DeepCopy() + expectedMod.Status.ModuleLoader.NodesMatchingSelectorNumber = int32(2) + expectedMod.Status.ModuleLoader.DesiredNumber = int32(1) + expectedMod.Status.ModuleLoader.AvailableNumber = int32(0) + gomock.InOrder( + clnt.EXPECT().List(ctx, gomock.Any(), gomock.Any()).DoAndReturn( + func(_ interface{}, list *kmmv1beta1.NodeModulesConfigList, _ ...interface{}) error { + list.Items = []kmmv1beta1.NodeModulesConfig{nmc1} + return nil + }, + ), + helper.EXPECT().GetModuleSpecEntry(&nmc1, mod.Namespace, mod.Name).Return(nil, 0), + clnt.EXPECT().Status().Return(statusWriter), + statusWriter.EXPECT().Patch(ctx, expectedMod, gomock.Any()), + ) + + err := mnrh.moduleUpdateWorkerPodsStatus(ctx, &mod, targetedNodes) + Expect(err).NotTo(HaveOccurred()) + }) + + DescribeTable("module present in spec", func(numTargetedNodes int, + modulePresentInStatus, + configsEqual bool, + expectedNodesMatchingSelectorNumber, + expectedDesiredNumber, + expectedAvailableNumber int) { + expectedMod := mod.DeepCopy() + expectedMod.Status.ModuleLoader.NodesMatchingSelectorNumber = int32(expectedNodesMatchingSelectorNumber) + expectedMod.Status.ModuleLoader.DesiredNumber = int32(expectedDesiredNumber) + expectedMod.Status.ModuleLoader.AvailableNumber = int32(expectedAvailableNumber) + + targetedNodes := []v1.Node{} + for i := 0; i < numTargetedNodes; i++ { + targetedNodes = append(targetedNodes, v1.Node{}) + } + nmc1 := kmmv1beta1.NodeModulesConfig{ + ObjectMeta: metav1.ObjectMeta{Name: "nmc1"}, + } + moduleConfig1 := kmmv1beta1.ModuleConfig{ContainerImage: "some image1"} + moduleConfig2 := kmmv1beta1.ModuleConfig{ContainerImage: "some image2"} + nmcModuleSpec := kmmv1beta1.NodeModuleSpec{ + Config: moduleConfig1, + } + nmcModuleStatus := kmmv1beta1.NodeModuleStatus{} + if configsEqual { + nmcModuleStatus.Config = moduleConfig1 + } else { + nmcModuleStatus.Config = moduleConfig2 + } + clnt.EXPECT().List(ctx, gomock.Any(), gomock.Any()).DoAndReturn( + func(_ interface{}, list *kmmv1beta1.NodeModulesConfigList, _ ...interface{}) error { + list.Items = []kmmv1beta1.NodeModulesConfig{nmc1} + return nil + }, + ) + helper.EXPECT().GetModuleSpecEntry(&nmc1, mod.Namespace, mod.Name).Return(&nmcModuleSpec, 0) + if modulePresentInStatus { + helper.EXPECT().GetModuleStatusEntry(&nmc1, mod.Namespace, mod.Name).Return(&nmcModuleStatus) + } else { + helper.EXPECT().GetModuleStatusEntry(&nmc1, mod.Namespace, mod.Name).Return(nil) + } + clnt.EXPECT().Status().Return(statusWriter) + statusWriter.EXPECT().Patch(ctx, expectedMod, gomock.Any()) + + err := mnrh.moduleUpdateWorkerPodsStatus(ctx, &mod, targetedNodes) + Expect(err).NotTo(HaveOccurred()) + }, + Entry("2 targeted nodes, module not in status", 2, false, false, 2, 1, 0), + Entry("3 targeted nodes, module in status, configs not equal", 2, true, false, 2, 1, 0), + Entry("3 targeted nodes, module in status, configs equal", 2, true, true, 2, 1, 1), + ) + + It("multiple module in spec and status", func() { + moduleConfig1 := kmmv1beta1.ModuleConfig{ContainerImage: "some image1"} + //moduleConfig2 := kmmv1beta1.ModuleConfig{ContainerImage: "some image2",} + nmcModuleSpec := kmmv1beta1.NodeModuleSpec{ + Config: moduleConfig1, + } + nmcModuleStatus := kmmv1beta1.NodeModuleStatus{ + Config: moduleConfig1, + } + nmc1 := kmmv1beta1.NodeModulesConfig{ + ObjectMeta: metav1.ObjectMeta{Name: "nmc1"}, + } + targetedNodes := []v1.Node{v1.Node{}, v1.Node{}} + expectedMod := mod.DeepCopy() + expectedMod.Status.ModuleLoader.NodesMatchingSelectorNumber = int32(2) + expectedMod.Status.ModuleLoader.DesiredNumber = int32(1) + expectedMod.Status.ModuleLoader.AvailableNumber = int32(1) + gomock.InOrder( + clnt.EXPECT().List(ctx, gomock.Any(), gomock.Any()).DoAndReturn( + func(_ interface{}, list *kmmv1beta1.NodeModulesConfigList, _ ...interface{}) error { + list.Items = []kmmv1beta1.NodeModulesConfig{nmc1} + return nil + }, + ), + helper.EXPECT().GetModuleSpecEntry(&nmc1, mod.Namespace, mod.Name).Return(&nmcModuleSpec, 0), + helper.EXPECT().GetModuleStatusEntry(&nmc1, mod.Namespace, mod.Name).Return(&nmcModuleStatus), + clnt.EXPECT().Status().Return(statusWriter), + statusWriter.EXPECT().Patch(ctx, expectedMod, gomock.Any()), + ) + + err := mnrh.moduleUpdateWorkerPodsStatus(ctx, &mod, targetedNodes) + Expect(err).NotTo(HaveOccurred()) + }) + +}) diff --git a/internal/nmc/helper.go b/internal/nmc/helper.go index 17142eb20..a6f50899e 100644 --- a/internal/nmc/helper.go +++ b/internal/nmc/helper.go @@ -17,7 +17,8 @@ type Helper interface { Get(ctx context.Context, name string) (*kmmv1beta1.NodeModulesConfig, error) SetModuleConfig(nmc *kmmv1beta1.NodeModulesConfig, mld *api.ModuleLoaderData, moduleConfig *kmmv1beta1.ModuleConfig) error RemoveModuleConfig(nmc *kmmv1beta1.NodeModulesConfig, namespace, name string) error - GetModuleEntry(nmc *kmmv1beta1.NodeModulesConfig, modNamespace, modName string) (*kmmv1beta1.NodeModuleSpec, int) + GetModuleSpecEntry(nmc *kmmv1beta1.NodeModulesConfig, modNamespace, modName string) (*kmmv1beta1.NodeModuleSpec, int) + GetModuleStatusEntry(nmc *kmmv1beta1.NodeModulesConfig, modNamespace, modName string) *kmmv1beta1.NodeModuleStatus } type helper struct { @@ -47,7 +48,7 @@ func (h *helper) SetModuleConfig( mld *api.ModuleLoaderData, moduleConfig *kmmv1beta1.ModuleConfig) error { - foundEntry, _ := h.GetModuleEntry(nmc, mld.Namespace, mld.Name) + foundEntry, _ := h.GetModuleSpecEntry(nmc, mld.Namespace, mld.Name) if foundEntry == nil { nms := kmmv1beta1.NodeModuleSpec{ ModuleItem: kmmv1beta1.ModuleItem{ @@ -73,14 +74,14 @@ func (h *helper) SetModuleConfig( } func (h *helper) RemoveModuleConfig(nmc *kmmv1beta1.NodeModulesConfig, namespace, name string) error { - foundEntry, index := h.GetModuleEntry(nmc, namespace, name) + foundEntry, index := h.GetModuleSpecEntry(nmc, namespace, name) if foundEntry != nil { nmc.Spec.Modules = append(nmc.Spec.Modules[:index], nmc.Spec.Modules[index+1:]...) } return nil } -func (h *helper) GetModuleEntry(nmc *kmmv1beta1.NodeModulesConfig, modNamespace, modName string) (*kmmv1beta1.NodeModuleSpec, int) { +func (h *helper) GetModuleSpecEntry(nmc *kmmv1beta1.NodeModulesConfig, modNamespace, modName string) (*kmmv1beta1.NodeModuleSpec, int) { for i, moduleSpec := range nmc.Spec.Modules { if moduleSpec.Namespace == modNamespace && moduleSpec.Name == modName { return &nmc.Spec.Modules[i], i @@ -89,6 +90,15 @@ func (h *helper) GetModuleEntry(nmc *kmmv1beta1.NodeModulesConfig, modNamespace, return nil, 0 } +func (h *helper) GetModuleStatusEntry(nmc *kmmv1beta1.NodeModulesConfig, modNamespace, modName string) *kmmv1beta1.NodeModuleStatus { + for i, moduleStatus := range nmc.Status.Modules { + if moduleStatus.Namespace == modNamespace && moduleStatus.Name == modName { + return &nmc.Status.Modules[i] + } + } + return nil +} + func FindModuleStatus(statuses []kmmv1beta1.NodeModuleStatus, moduleNamespace, moduleName string) *kmmv1beta1.NodeModuleStatus { for i := 0; i < len(statuses); i++ { s := statuses[i] diff --git a/internal/nmc/helper_test.go b/internal/nmc/helper_test.go index 11030dc5b..3a60fc3a4 100644 --- a/internal/nmc/helper_test.go +++ b/internal/nmc/helper_test.go @@ -203,7 +203,7 @@ var _ = Describe("RemoveModuleConfig", func() { }) }) -var _ = Describe("GetModuleEntry", func() { +var _ = Describe("GetModuleSpecEntry", func() { var ( nmcHelper Helper ) @@ -212,17 +212,17 @@ var _ = Describe("GetModuleEntry", func() { nmcHelper = NewHelper(nil) }) - It("empty module list", func() { + It("empty module spec list", func() { nmc := kmmv1beta1.NodeModulesConfig{ Spec: kmmv1beta1.NodeModulesConfigSpec{}, } - res, _ := nmcHelper.GetModuleEntry(&nmc, "namespace", "name") + res, _ := nmcHelper.GetModuleSpecEntry(&nmc, "namespace", "name") Expect(res).To(BeNil()) }) - It("module missing from the list", func() { + It("module spec missing from the spec list", func() { nmc := kmmv1beta1.NodeModulesConfig{ Spec: kmmv1beta1.NodeModulesConfigSpec{ Modules: []kmmv1beta1.NodeModuleSpec{ @@ -242,12 +242,12 @@ var _ = Describe("GetModuleEntry", func() { }, } - res, _ := nmcHelper.GetModuleEntry(&nmc, "namespace", "name") + res, _ := nmcHelper.GetModuleSpecEntry(&nmc, "namespace", "name") Expect(res).To(BeNil()) }) - It("module present", func() { + It("module spec present", func() { nmc := kmmv1beta1.NodeModulesConfig{ Spec: kmmv1beta1.NodeModulesConfigSpec{ Modules: []kmmv1beta1.NodeModuleSpec{ @@ -267,7 +267,7 @@ var _ = Describe("GetModuleEntry", func() { }, } - res, index := nmcHelper.GetModuleEntry(&nmc, "some namespace 1", "some name 1") + res, index := nmcHelper.GetModuleSpecEntry(&nmc, "some namespace 1", "some name 1") Expect(res.Name).To(Equal("some name 1")) Expect(res.Namespace).To(Equal("some namespace 1")) @@ -275,6 +275,77 @@ var _ = Describe("GetModuleEntry", func() { }) }) +var _ = Describe("GetModuleStatusEntry", func() { + var ( + nmcHelper Helper + ) + + BeforeEach(func() { + nmcHelper = NewHelper(nil) + }) + + It("empty module status list", func() { + nmc := kmmv1beta1.NodeModulesConfig{ + Status: kmmv1beta1.NodeModulesConfigStatus{}, + } + + res := nmcHelper.GetModuleStatusEntry(&nmc, "namespace", "name") + + Expect(res).To(BeNil()) + }) + + It("module status missing from the status list", func() { + nmc := kmmv1beta1.NodeModulesConfig{ + Status: kmmv1beta1.NodeModulesConfigStatus{ + Modules: []kmmv1beta1.NodeModuleStatus{ + { + ModuleItem: kmmv1beta1.ModuleItem{ + Name: "some name 1", + Namespace: "some namespace 1", + }, + }, + { + ModuleItem: kmmv1beta1.ModuleItem{ + Name: "some name 2", + Namespace: "some namespace 2", + }, + }, + }, + }, + } + + res := nmcHelper.GetModuleStatusEntry(&nmc, "namespace", "name") + + Expect(res).To(BeNil()) + }) + + It("module status present", func() { + nmc := kmmv1beta1.NodeModulesConfig{ + Status: kmmv1beta1.NodeModulesConfigStatus{ + Modules: []kmmv1beta1.NodeModuleStatus{ + { + ModuleItem: kmmv1beta1.ModuleItem{ + Name: "some name 1", + Namespace: "some namespace 1", + }, + }, + { + ModuleItem: kmmv1beta1.ModuleItem{ + Name: "some name 2", + Namespace: "some namespace 2", + }, + }, + }, + }, + } + + res := nmcHelper.GetModuleStatusEntry(&nmc, "some namespace 1", "some name 1") + + Expect(res.Name).To(Equal("some name 1")) + Expect(res.Namespace).To(Equal("some namespace 1")) + }) +}) + var _ = Describe("RemoveModuleStatus", func() { const ( name = "test-name" diff --git a/internal/nmc/mock_helper.go b/internal/nmc/mock_helper.go index 6fe3f3b76..29a7c3772 100644 --- a/internal/nmc/mock_helper.go +++ b/internal/nmc/mock_helper.go @@ -55,19 +55,33 @@ func (mr *MockHelperMockRecorder) Get(ctx, name any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockHelper)(nil).Get), ctx, name) } -// GetModuleEntry mocks base method. -func (m *MockHelper) GetModuleEntry(nmc *v1beta1.NodeModulesConfig, modNamespace, modName string) (*v1beta1.NodeModuleSpec, int) { +// GetModuleSpecEntry mocks base method. +func (m *MockHelper) GetModuleSpecEntry(nmc *v1beta1.NodeModulesConfig, modNamespace, modName string) (*v1beta1.NodeModuleSpec, int) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetModuleEntry", nmc, modNamespace, modName) + ret := m.ctrl.Call(m, "GetModuleSpecEntry", nmc, modNamespace, modName) ret0, _ := ret[0].(*v1beta1.NodeModuleSpec) ret1, _ := ret[1].(int) return ret0, ret1 } -// GetModuleEntry indicates an expected call of GetModuleEntry. -func (mr *MockHelperMockRecorder) GetModuleEntry(nmc, modNamespace, modName any) *gomock.Call { +// GetModuleSpecEntry indicates an expected call of GetModuleSpecEntry. +func (mr *MockHelperMockRecorder) GetModuleSpecEntry(nmc, modNamespace, modName any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetModuleEntry", reflect.TypeOf((*MockHelper)(nil).GetModuleEntry), nmc, modNamespace, modName) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetModuleSpecEntry", reflect.TypeOf((*MockHelper)(nil).GetModuleSpecEntry), nmc, modNamespace, modName) +} + +// GetModuleStatusEntry mocks base method. +func (m *MockHelper) GetModuleStatusEntry(nmc *v1beta1.NodeModulesConfig, modNamespace, modName string) *v1beta1.NodeModuleStatus { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetModuleStatusEntry", nmc, modNamespace, modName) + ret0, _ := ret[0].(*v1beta1.NodeModuleStatus) + return ret0 +} + +// GetModuleStatusEntry indicates an expected call of GetModuleStatusEntry. +func (mr *MockHelperMockRecorder) GetModuleStatusEntry(nmc, modNamespace, modName any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetModuleStatusEntry", reflect.TypeOf((*MockHelper)(nil).GetModuleStatusEntry), nmc, modNamespace, modName) } // RemoveModuleConfig mocks base method.