diff --git a/internal/controller/kyma/controller.go b/internal/controller/kyma/controller.go index 442ebc1bb2..bb8ddb8739 100644 --- a/internal/controller/kyma/controller.go +++ b/internal/controller/kyma/controller.go @@ -104,6 +104,10 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu return ctrl.Result{}, fmt.Errorf("KymaController: %w", err) } + if err := r.UpdateModuleTemplatesIfNeeded(ctx); err != nil { + return ctrl.Result{}, fmt.Errorf("KymaController: %w", err) + } + status.InitConditions(kyma, r.SyncKymaEnabled(kyma), r.WatcherEnabled(kyma)) if kyma.SkipReconciliation() { @@ -342,6 +346,7 @@ func (r *Reconciler) handleProcessingState(ctx context.Context, kyma *v1beta2.Ky } return nil }) + if r.SyncKymaEnabled(kyma) { errGroup.Go(func() error { if err := r.syncModuleCatalog(ctx, kyma); err != nil { @@ -512,7 +517,8 @@ func (r *Reconciler) reconcileManifests(ctx context.Context, kyma *v1beta2.Kyma) return nil } -func (r *Reconciler) syncModuleCatalog(ctx context.Context, kyma *v1beta2.Kyma) error { +func (r *Reconciler) syncModuleCatalog(ctx context.Context, kyma *v1beta2.Kyma, +) error { moduleTemplateList := &v1beta2.ModuleTemplateList{} if err := r.List(ctx, moduleTemplateList, &client.ListOptions{}); err != nil { return fmt.Errorf("could not aggregate module templates for module catalog sync: %w", err) @@ -531,7 +537,8 @@ func (r *Reconciler) syncModuleCatalog(ctx context.Context, kyma *v1beta2.Kyma) } remoteCatalog := remote.NewRemoteCatalogFromKyma(r.Client, r.SkrContextFactory, r.RemoteSyncNamespace) - if err := remoteCatalog.Sync(ctx, kyma.GetNamespacedName(), modulesToSync, moduleReleaseMetaList.Items); err != nil { + if err := remoteCatalog.Sync(ctx, kyma.GetNamespacedName(), modulesToSync, + moduleReleaseMetaList.Items); err != nil { return err } @@ -616,3 +623,53 @@ func (r *Reconciler) SyncKymaEnabled(kyma *v1beta2.Kyma) bool { func (r *Reconciler) IsKymaManaged() bool { return r.IsManagedKyma } + +func (r *Reconciler) GetModuleTemplateList(ctx context.Context) (*v1beta2.ModuleTemplateList, error) { + moduleTemplateList := &v1beta2.ModuleTemplateList{} + if err := r.List(ctx, moduleTemplateList, &client.ListOptions{}); err != nil { + return nil, fmt.Errorf("could not aggregate module templates for module catalog sync: %w", err) + } + + return moduleTemplateList, nil +} + +func (r *Reconciler) UpdateModuleTemplatesIfNeeded(ctx context.Context) error { + moduleTemplateList, err := r.GetModuleTemplateList(ctx) + if err != nil { + return err + } + + for _, mt := range moduleTemplateList.Items { + if needUpdateForMandatoryModuleLabel(mt) { + if err = r.Update(ctx, &mt); err != nil { + return fmt.Errorf("failed to update ModuleTemplate, %w", err) + } + } + } + + return nil +} + +func needUpdateForMandatoryModuleLabel(moduleTemplate v1beta2.ModuleTemplate) bool { + if moduleTemplate.Labels == nil { + moduleTemplate.Labels = make(map[string]string) + } + + if moduleTemplate.Spec.Mandatory { + if moduleTemplate.Labels[shared.IsMandatoryModule] == shared.EnableLabelValue { + return false + } + + moduleTemplate.Labels[shared.IsMandatoryModule] = shared.EnableLabelValue + return true + } + + if !moduleTemplate.Spec.Mandatory { + if moduleTemplate.Labels[shared.IsMandatoryModule] == shared.EnableLabelValue { + delete(moduleTemplate.Labels, shared.IsMandatoryModule) + return true + } + } + + return false +} diff --git a/pkg/templatelookup/mandatory.go b/pkg/templatelookup/mandatory.go index 2242373f81..8ac871afad 100644 --- a/pkg/templatelookup/mandatory.go +++ b/pkg/templatelookup/mandatory.go @@ -4,8 +4,11 @@ import ( "context" "fmt" + "k8s.io/apimachinery/pkg/fields" + k8slabels "k8s.io/apimachinery/pkg/labels" "sigs.k8s.io/controller-runtime/pkg/client" + "github.com/kyma-project/lifecycle-manager/api/shared" "github.com/kyma-project/lifecycle-manager/api/v1beta2" ) @@ -13,18 +16,19 @@ import ( func GetMandatory(ctx context.Context, kymaClient client.Reader) (ModuleTemplatesByModuleName, error, ) { - moduleTemplateList := &v1beta2.ModuleTemplateList{} - if err := kymaClient.List(ctx, moduleTemplateList); err != nil { - return nil, fmt.Errorf("could not list ModuleTemplates: %w", err) + mandatoryModuleTemplateList := &v1beta2.ModuleTemplateList{} + labelSelector := k8slabels.SelectorFromSet(k8slabels.Set{shared.IsMandatoryModule: shared.EnableLabelValue}) + fieldSelector := fields.SelectorFromSet(fields.Set{"metadata.deletionTimestamp": ""}) + listOptions := &client.ListOptions{LabelSelector: labelSelector, FieldSelector: fieldSelector} + if err := kymaClient.List(ctx, mandatoryModuleTemplateList, listOptions); err != nil { + return nil, fmt.Errorf("could not list mandatory ModuleTemplates: %w", err) } mandatoryModules := make(map[string]*ModuleTemplateInfo) - for _, moduleTemplate := range moduleTemplateList.Items { - if moduleTemplate.Spec.Mandatory && moduleTemplate.DeletionTimestamp.IsZero() { - mandatoryModules[moduleTemplate.Name] = &ModuleTemplateInfo{ - ModuleTemplate: &moduleTemplate, - Err: nil, - } + for _, moduleTemplate := range mandatoryModuleTemplateList.Items { + mandatoryModules[moduleTemplate.Name] = &ModuleTemplateInfo{ + ModuleTemplate: &moduleTemplate, + Err: nil, } } return mandatoryModules, nil diff --git a/pkg/testutils/moduletemplate.go b/pkg/testutils/moduletemplate.go index 7f6e6ee91c..8af656d1b9 100644 --- a/pkg/testutils/moduletemplate.go +++ b/pkg/testutils/moduletemplate.go @@ -7,6 +7,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" + "github.com/kyma-project/lifecycle-manager/api/shared" "github.com/kyma-project/lifecycle-manager/api/v1beta2" "github.com/kyma-project/lifecycle-manager/internal/descriptor/provider" "github.com/kyma-project/lifecycle-manager/pkg/templatelookup" @@ -79,6 +80,31 @@ func UpdateModuleTemplateSpec(ctx context.Context, return nil } +func MandatoryModuleTemplateHasExpectedLabel(ctx context.Context, clnt client.Client, moduleName, key, value string, +) error { + mandatoryModuleTemplates, err := templatelookup.GetMandatory(ctx, clnt) + if err != nil { + return err + } + + var moduleTemplate *v1beta2.ModuleTemplate + for _, moduleTemplateInfo := range mandatoryModuleTemplates { + if moduleTemplateInfo.ModuleTemplate.Labels[shared.ModuleName] == moduleName { + moduleTemplate = moduleTemplateInfo.ModuleTemplate + break + } + } + + if moduleTemplate == nil { + return fmt.Errorf("module template not found, %s", moduleName) + } + + if moduleTemplate.Labels[key] != value { + return fmt.Errorf("label %s:%s not found", key, value) + } + return nil +} + func DeleteModuleTemplate(ctx context.Context, clnt client.Client, module v1beta2.Module, kymaChannel string, namespace string, ) error { diff --git a/tests/e2e/mandatory_module_test.go b/tests/e2e/mandatory_module_test.go index 4bc333a092..76d7593b20 100644 --- a/tests/e2e/mandatory_module_test.go +++ b/tests/e2e/mandatory_module_test.go @@ -39,6 +39,14 @@ var _ = Describe("Mandatory Module Installation and Deletion", Ordered, func() { WithArguments(kyma.GetName(), kyma.GetNamespace(), kcpClient, shared.StateReady). Should(Succeed()) }) + By("And the Mandatory ModuleTemplate has the correct mandatory-module label", func() { + Eventually(MandatoryModuleTemplateHasExpectedLabel). + WithContext(ctx). + WithArguments(kcpClient, "template-operator", shared.IsMandatoryModule, + shared.EnableLabelValue). + Should(Succeed()) + }) + By("And the mandatory ModuleTemplate is not synchronised to the SKR cluster", func() { Consistently(CheckIfExists). WithContext(ctx). diff --git a/tests/integration/controller/mandatorymodule/deletion/controller_test.go b/tests/integration/controller/mandatorymodule/deletion/controller_test.go index 84407bc44c..991eac339e 100644 --- a/tests/integration/controller/mandatorymodule/deletion/controller_test.go +++ b/tests/integration/controller/mandatorymodule/deletion/controller_test.go @@ -83,8 +83,9 @@ func registerControlPlaneLifecycleForKyma(kyma *v1beta2.Kyma) { WithLabelModuleName(mandatoryModule). WithChannel(mandatoryChannel). WithMandatory(true). - WithOCM(compdescv2.SchemaVersion).Build() - mandatoryManifest := NewTestManifest(mandatoryModule) + WithOCM(compdescv2.SchemaVersion). + WithLabel(shared.IsMandatoryModule, shared.EnableLabelValue).Build() + mandatoryManifest := NewTestManifest("mandatory-module") mandatoryManifest.Labels[shared.IsMandatoryModule] = "true" BeforeAll(func() { diff --git a/tests/integration/controller/mandatorymodule/installation/controller_test.go b/tests/integration/controller/mandatorymodule/installation/controller_test.go index 3d6268025f..64dd63e0b7 100644 --- a/tests/integration/controller/mandatorymodule/installation/controller_test.go +++ b/tests/integration/controller/mandatorymodule/installation/controller_test.go @@ -81,6 +81,7 @@ func registerControlPlaneLifecycleForKyma(kyma *v1beta2.Kyma) { template := builder.NewModuleTemplateBuilder(). WithNamespace(ControlPlaneNamespace). WithLabelModuleName("mandatory-module"). + WithLabel(shared.IsMandatoryModule, shared.EnableLabelValue). WithChannel(mandatoryChannel). WithMandatory(true). WithOCM(compdescv2.SchemaVersion).Build() diff --git a/unit-test-coverage.yaml b/unit-test-coverage.yaml index 010ae49160..06e425d0b9 100644 --- a/unit-test-coverage.yaml +++ b/unit-test-coverage.yaml @@ -16,4 +16,4 @@ packages: internal/pkg/resources: 91.7 internal/remote: 13.2 internal/util/collections: 86 - pkg/templatelookup: 77.7 + pkg/templatelookup: 77.3