Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Refactor Mandatorymodule controller to 3 tier structure #2283

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 11 additions & 16 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ import (
"github.com/kyma-project/lifecycle-manager/internal/event"
"github.com/kyma-project/lifecycle-manager/internal/maintenancewindows"
"github.com/kyma-project/lifecycle-manager/internal/manifest/manifestclient"
"github.com/kyma-project/lifecycle-manager/internal/manifest/parser"
"github.com/kyma-project/lifecycle-manager/internal/pkg/flags"
"github.com/kyma-project/lifecycle-manager/internal/pkg/metrics"
"github.com/kyma-project/lifecycle-manager/internal/remote"
Expand Down Expand Up @@ -481,19 +482,15 @@ func setupMandatoryModuleReconciler(mgr ctrl.Manager,
options.CacheSyncTimeout = flagVar.CacheSyncTimeout
options.MaxConcurrentReconciles = flagVar.MaxConcurrentMandatoryModuleReconciles

if err := (&mandatorymodule.InstallationReconciler{
Client: mgr.GetClient(),
RequeueIntervals: queue.RequeueIntervals{
newParser := parser.NewParser(mgr.GetClient(), descriptorProvider, flagVar.InKCPMode, flagVar.RemoteSyncNamespace)

if err := mandatorymodule.NewInstallationReconciler(
mgr.GetClient(), queue.RequeueIntervals{
Success: flagVar.MandatoryModuleRequeueSuccessInterval,
Busy: flagVar.KymaRequeueBusyInterval,
Error: flagVar.KymaRequeueErrInterval,
Warning: flagVar.KymaRequeueWarningInterval,
},
RemoteSyncNamespace: flagVar.RemoteSyncNamespace,
InKCPMode: flagVar.InKCPMode,
DescriptorProvider: descriptorProvider,
Metrics: metrics,
}).SetupWithManager(mgr, options); err != nil {
}, newParser, metrics).SetupWithManager(mgr, options); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "MandatoryModule")
os.Exit(bootstrapFailedExitCode)
}
Expand All @@ -511,17 +508,15 @@ func setupMandatoryModuleDeletionReconciler(mgr ctrl.Manager,
options.CacheSyncTimeout = flagVar.CacheSyncTimeout
options.MaxConcurrentReconciles = flagVar.MaxConcurrentMandatoryModuleDeletionReconciles

if err := (&mandatorymodule.DeletionReconciler{
Client: mgr.GetClient(),
Event: event,
DescriptorProvider: descriptorProvider,
RequeueIntervals: queue.RequeueIntervals{
if err := mandatorymodule.NewDeletionReconciler(
mgr.GetClient(),
event, descriptorProvider,
queue.RequeueIntervals{
Success: flagVar.MandatoryModuleDeletionRequeueSuccessInterval,
Busy: flagVar.KymaRequeueBusyInterval,
Error: flagVar.KymaRequeueErrInterval,
Warning: flagVar.KymaRequeueWarningInterval,
},
}).SetupWithManager(mgr, options); err != nil {
}).SetupWithManager(mgr, options); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "MandatoryModule")
os.Exit(bootstrapFailedExitCode)
}
Expand Down
107 changes: 34 additions & 73 deletions internal/controller/mandatorymodule/deletion_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,14 @@ import (
"context"
"fmt"

k8slabels "k8s.io/apimachinery/pkg/labels"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
logf "sigs.k8s.io/controller-runtime/pkg/log"

"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/internal/event"
"github.com/kyma-project/lifecycle-manager/internal/service"
"github.com/kyma-project/lifecycle-manager/pkg/log"
"github.com/kyma-project/lifecycle-manager/pkg/queue"
"github.com/kyma-project/lifecycle-manager/pkg/util"
Expand All @@ -41,18 +39,30 @@ const (
)

type DeletionReconciler struct {
client.Client
event.Event
queue.RequeueIntervals
DescriptorProvider *provider.CachedDescriptorProvider
moduleTemplateService *service.ModuleTemplateService
mandatoryModuleDeletionService *service.MandatoryModuleDeletionService
}

func NewDeletionReconciler(client client.Client, event event.Event,
descriptorProvider *provider.CachedDescriptorProvider,
requeueIntervals queue.RequeueIntervals,
) *DeletionReconciler {
return &DeletionReconciler{
Event: event,
RequeueIntervals: requeueIntervals,
moduleTemplateService: service.NewModuleTemplateService(client),
mandatoryModuleDeletionService: service.NewMandatoryModuleDeletionService(client, descriptorProvider),
}
}

func (r *DeletionReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
logger := logf.FromContext(ctx)
logger.V(log.DebugLevel).Info("Mandatory Module Deletion Reconciliation started")

template := &v1beta2.ModuleTemplate{}
if err := r.Get(ctx, req.NamespacedName, template); err != nil {
template, err := r.moduleTemplateService.GetModuleTemplate(ctx, req.NamespacedName)
if err != nil {
if util.IsNotFound(err) {
logger.V(log.DebugLevel).Info(fmt.Sprintf("ModuleTemplate %s not found, probably already deleted",
req.NamespacedName))
Expand All @@ -66,84 +76,35 @@ func (r *DeletionReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
return ctrl.Result{}, nil
}

updateRequired := controllerutil.AddFinalizer(template, shared.MandatoryModuleFinalizer)
updateRequired, err := r.moduleTemplateService.UpdateFinalizer(ctx, template, shared.MandatoryModuleFinalizer)
if err != nil {
r.Event.Warning(template, settingFinalizerError, err)
return ctrl.Result{}, fmt.Errorf("failed to update ModuleTemplate finalizer: %w", err)
}
if updateRequired {
return r.updateTemplateFinalizer(ctx, template)
return ctrl.Result{Requeue: true}, nil
}

if template.DeletionTimestamp.IsZero() {
return ctrl.Result{}, nil
}

manifests, err := r.getCorrespondingManifests(ctx, template)
noManifestLeft, err := r.mandatoryModuleDeletionService.DeleteMandatoryModules(ctx, template)
if err != nil {
return ctrl.Result{}, fmt.Errorf("failed to get MandatoryModuleManifests: %w", err)
r.Event.Warning(template, deletingManifestError, err)
return ctrl.Result{}, fmt.Errorf("failed to delete MandatoryModuleManifests: %w", err)
}

if len(manifests) == 0 {
if controllerutil.RemoveFinalizer(template, shared.MandatoryModuleFinalizer) {
return r.updateTemplateFinalizer(ctx, template)
if noManifestLeft {
updateRequired, err := r.moduleTemplateService.RemoveFinalizer(ctx, template, shared.MandatoryModuleFinalizer)
if err != nil {
r.Event.Warning(template, settingFinalizerError, err)
return ctrl.Result{}, fmt.Errorf("failed to update ModuleTemplate finalizer: %w", err)
}
if updateRequired {
return ctrl.Result{Requeue: true}, nil
}
return ctrl.Result{}, nil
}

if err := r.removeManifests(ctx, manifests); err != nil {
r.Event.Warning(template, deletingManifestError, err)
return ctrl.Result{}, fmt.Errorf("failed to remove MandatoryModule Manifest: %w", err)
}

return ctrl.Result{Requeue: true}, nil
}

func (r *DeletionReconciler) updateTemplateFinalizer(ctx context.Context,
template *v1beta2.ModuleTemplate,
) (ctrl.Result, error) {
if err := r.Update(ctx, template); err != nil {
r.Event.Warning(template, settingFinalizerError, err)
return ctrl.Result{}, fmt.Errorf("failed to update MandatoryModuleTemplate finalizer: %w", err)
}
return ctrl.Result{Requeue: true}, nil
}

func (r *DeletionReconciler) getCorrespondingManifests(ctx context.Context,
template *v1beta2.ModuleTemplate) ([]v1beta2.Manifest,
error,
) {
manifests := &v1beta2.ManifestList{}
descriptor, err := r.DescriptorProvider.GetDescriptor(template)
if err != nil {
return nil, fmt.Errorf("not able to get descriptor from template: %w", err)
}
if err := r.List(ctx, manifests, &client.ListOptions{
Namespace: template.Namespace,
LabelSelector: k8slabels.SelectorFromSet(k8slabels.Set{shared.IsMandatoryModule: "true"}),
}); client.IgnoreNotFound(err) != nil {
return nil, fmt.Errorf("not able to list mandatory module manifests: %w", err)
}

filtered := filterManifestsByAnnotation(manifests.Items, shared.FQDN, descriptor.GetName())

return filtered, nil
}

func (r *DeletionReconciler) removeManifests(ctx context.Context, manifests []v1beta2.Manifest) error {
for _, manifest := range manifests {
if err := r.Delete(ctx, &manifest); err != nil {
return fmt.Errorf("not able to delete manifest %s/%s: %w", manifest.Namespace, manifest.Name, err)
}
}
logf.FromContext(ctx).V(log.DebugLevel).Info("Marked all MandatoryModule Manifests for deletion")
return nil
}

func filterManifestsByAnnotation(manifests []v1beta2.Manifest,
annotationKey, annotationValue string,
) []v1beta2.Manifest {
filteredManifests := make([]v1beta2.Manifest, 0)
for _, manifest := range manifests {
if manifest.Annotations[annotationKey] == annotationValue {
filteredManifests = append(filteredManifests, manifest)
}
}
return filteredManifests
}
72 changes: 20 additions & 52 deletions internal/controller/mandatorymodule/installation_controller.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,3 @@
/*
Copyright 2022.

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 mandatorymodule

import (
Expand All @@ -24,33 +8,40 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
logf "sigs.k8s.io/controller-runtime/pkg/log"

"github.com/kyma-project/lifecycle-manager/api/v1beta2"
"github.com/kyma-project/lifecycle-manager/internal/descriptor/provider"
"github.com/kyma-project/lifecycle-manager/internal/manifest/parser"
"github.com/kyma-project/lifecycle-manager/internal/pkg/metrics"
"github.com/kyma-project/lifecycle-manager/internal/service"
"github.com/kyma-project/lifecycle-manager/pkg/log"
"github.com/kyma-project/lifecycle-manager/pkg/module/common"
"github.com/kyma-project/lifecycle-manager/pkg/module/sync"
"github.com/kyma-project/lifecycle-manager/pkg/queue"
"github.com/kyma-project/lifecycle-manager/pkg/templatelookup"
"github.com/kyma-project/lifecycle-manager/pkg/util"
)

type InstallationReconciler struct {
client.Client
queue.RequeueIntervals
DescriptorProvider *provider.CachedDescriptorProvider
RemoteSyncNamespace string
InKCPMode bool
Metrics *metrics.MandatoryModulesMetrics
kymaService *service.KymaService
mandatoryModuleService *service.MandatoryModuleInstallationService
}

func NewInstallationReconciler(client client.Client,
requeueIntervals queue.RequeueIntervals,
parser *parser.Parser,
metrics *metrics.MandatoryModulesMetrics,
) *InstallationReconciler {
return &InstallationReconciler{
Client: client,
RequeueIntervals: requeueIntervals,
kymaService: service.NewKymaService(client),
mandatoryModuleService: service.NewMandatoryModuleInstallationService(client, metrics, parser),
}
}

func (r *InstallationReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
logger := logf.FromContext(ctx)
logger.V(log.DebugLevel).Info("Mandatory Module Reconciliation started")

kyma := &v1beta2.Kyma{}
if err := r.Get(ctx, req.NamespacedName, kyma); err != nil {
kyma, err := r.kymaService.GetKyma(ctx, req.NamespacedName)
if err != nil {
if util.IsNotFound(err) {
logger.V(log.DebugLevel).Info(fmt.Sprintf("Kyma %s not found, probably already deleted",
req.NamespacedName))
Expand All @@ -64,32 +55,9 @@ func (r *InstallationReconciler) Reconcile(ctx context.Context, req ctrl.Request
return ctrl.Result{RequeueAfter: r.RequeueIntervals.Success}, nil
}

mandatoryTemplates, err := templatelookup.GetMandatory(ctx, r.Client)
if err != nil {
return emptyResultWithErr(err)
}
r.Metrics.RecordMandatoryTemplatesCount(len(mandatoryTemplates))

modules, err := r.GenerateModulesFromTemplate(ctx, mandatoryTemplates, kyma)
if err != nil {
return emptyResultWithErr(err)
}

runner := sync.New(r)
if err := runner.ReconcileManifests(ctx, kyma, modules); err != nil {
return emptyResultWithErr(err)
if err := r.mandatoryModuleService.ReconcileMandatoryModules(ctx, kyma); err != nil {
return ctrl.Result{}, fmt.Errorf("failed to reconcile mandatory modules: %w", err)
}

return ctrl.Result{}, nil
}

func (r *InstallationReconciler) GenerateModulesFromTemplate(ctx context.Context,
templates templatelookup.ModuleTemplatesByModuleName, kyma *v1beta2.Kyma,
) (common.Modules, error) {
parser := parser.NewParser(r.Client, r.DescriptorProvider, r.InKCPMode, r.RemoteSyncNamespace)
return parser.GenerateMandatoryModulesFromTemplates(ctx, kyma, templates), nil
}

func emptyResultWithErr(err error) (ctrl.Result, error) {
return ctrl.Result{}, fmt.Errorf("MandatoryModuleController: %w", err)
}
26 changes: 26 additions & 0 deletions internal/repository/kyma.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package repository

import (
"context"
"fmt"

"sigs.k8s.io/controller-runtime/pkg/client"

"github.com/kyma-project/lifecycle-manager/api/v1beta2"
)

type KymaRepository struct {
Client client.Client
}

func NewKymaRepository(client client.Client) *KymaRepository {
return &KymaRepository{Client: client}
}

func (r *KymaRepository) Get(ctx context.Context, namespacedName client.ObjectKey) (*v1beta2.Kyma, error) {
kyma := &v1beta2.Kyma{}
if err := r.Client.Get(ctx, namespacedName, kyma); err != nil {
return nil, fmt.Errorf("failed to get Kyma: %w", err)
}
return kyma, nil
}
45 changes: 45 additions & 0 deletions internal/repository/manifest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package repository

import (
"context"
"fmt"

k8slabels "k8s.io/apimachinery/pkg/labels"
"sigs.k8s.io/controller-runtime/pkg/client"
logf "sigs.k8s.io/controller-runtime/pkg/log"

"github.com/kyma-project/lifecycle-manager/api/v1beta2"
"github.com/kyma-project/lifecycle-manager/pkg/log"
)

type ManifestRepository struct {
Client client.Client
}

func NewManifestRepository(client client.Client,
) *ManifestRepository {
return &ManifestRepository{
Client: client,
}
}

func (m *ManifestRepository) RemoveManifests(ctx context.Context, manifests []v1beta2.Manifest) error {
for _, manifest := range manifests {
if err := m.Client.Delete(ctx, &manifest); err != nil {
return fmt.Errorf("not able to delete manifest %s/%s: %w", manifest.Namespace, manifest.Name, err)
}
}
logf.FromContext(ctx).V(log.DebugLevel).Info("Marked all Manifests for deletion")
return nil
}

func (m *ManifestRepository) ListByLabel(ctx context.Context,
labelSelector k8slabels.Selector,
) (*v1beta2.ManifestList, error) {
manifestList := &v1beta2.ManifestList{}
if err := m.Client.List(ctx, manifestList,
&client.ListOptions{LabelSelector: labelSelector}); err != nil {
return nil, fmt.Errorf("could not list ManifestList: %w", err)
}
return manifestList, nil
}
Loading
Loading