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

feat: Implement MaintenanceWindow determination logic #2196

Merged
merged 18 commits into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from 10 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
9 changes: 6 additions & 3 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ import (
"github.com/kyma-project/lifecycle-manager/pkg/log"
"github.com/kyma-project/lifecycle-manager/pkg/matcher"
"github.com/kyma-project/lifecycle-manager/pkg/queue"
"github.com/kyma-project/lifecycle-manager/pkg/templatelookup"
"github.com/kyma-project/lifecycle-manager/pkg/watcher"

_ "k8s.io/client-go/plugin/pkg/client/auth"
Expand Down Expand Up @@ -194,13 +195,14 @@ func setupManager(flagVar *flags.FlagVar, cacheOptions cache.Options, scheme *ma
moduleMetrics := metrics.NewModuleMetrics()

// The maintenance windows policy should be passed to the reconciler to be resolved: https://github.com/kyma-project/lifecycle-manager/issues/2101
_, err = maintenancewindows.InitializeMaintenanceWindowsPolicy(setupLog, maintenanceWindowPoliciesDirectory,
maintenanceWindow, err := maintenancewindows.InitializeMaintenanceWindow(setupLog,
c-pius marked this conversation as resolved.
Show resolved Hide resolved
maintenanceWindowPoliciesDirectory,
c-pius marked this conversation as resolved.
Show resolved Hide resolved
maintenanceWindowPolicyName)
if err != nil {
setupLog.Error(err, "unable to set maintenance windows policy")
}
setupKymaReconciler(mgr, descriptorProvider, skrContextProvider, eventRecorder, flagVar, options, skrWebhookManager,
kymaMetrics, moduleMetrics, setupLog)
kymaMetrics, moduleMetrics, setupLog, maintenanceWindow)
setupManifestReconciler(mgr, flagVar, options, sharedMetrics, mandatoryModulesMetrics, moduleMetrics, setupLog,
eventRecorder)
setupMandatoryModuleReconciler(mgr, descriptorProvider, flagVar, options, mandatoryModulesMetrics, setupLog)
Expand Down Expand Up @@ -280,7 +282,7 @@ func scheduleMetricsCleanup(kymaMetrics *metrics.KymaMetrics, cleanupIntervalInM
func setupKymaReconciler(mgr ctrl.Manager, descriptorProvider *provider.CachedDescriptorProvider,
skrContextFactory remote.SkrContextProvider, event event.Event, flagVar *flags.FlagVar, options ctrlruntime.Options,
skrWebhookManager *watcher.SKRWebhookManifestManager, kymaMetrics *metrics.KymaMetrics,
moduleMetrics *metrics.ModuleMetrics, setupLog logr.Logger,
moduleMetrics *metrics.ModuleMetrics, setupLog logr.Logger, maintenanceWindow templatelookup.MaintenanceWindow,
) {
options.RateLimiter = internal.RateLimiter(flagVar.FailureBaseDelay,
flagVar.FailureMaxDelay, flagVar.RateLimiterFrequency, flagVar.RateLimiterBurst)
Expand All @@ -307,6 +309,7 @@ func setupKymaReconciler(mgr ctrl.Manager, descriptorProvider *provider.CachedDe
ModuleMetrics: moduleMetrics,
RemoteCatalog: remote.NewRemoteCatalogFromKyma(mgr.GetClient(), skrContextFactory,
flagVar.RemoteSyncNamespace),
TemplateLookup: templatelookup.NewTemplateLookup(mgr.GetClient(), descriptorProvider, maintenanceWindow),
}).SetupWithManager(
mgr, options, kyma.SetupOptions{
ListenerAddr: flagVar.KymaListenerAddr,
Expand Down
3 changes: 2 additions & 1 deletion internal/controller/kyma/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ type Reconciler struct {
Metrics *metrics.KymaMetrics
ModuleMetrics *metrics.ModuleMetrics
RemoteCatalog *remote.RemoteCatalog
TemplateLookup *templatelookup.TemplateLookup
}

// +kubebuilder:rbac:groups=operator.kyma-project.io,resources=kymas,verbs=get;list;watch;create;update;patch;delete
Expand Down Expand Up @@ -506,7 +507,7 @@ func (r *Reconciler) updateKyma(ctx context.Context, kyma *v1beta2.Kyma) error {
}

func (r *Reconciler) reconcileManifests(ctx context.Context, kyma *v1beta2.Kyma) error {
templates := templatelookup.NewTemplateLookup(client.Reader(r), r.DescriptorProvider).GetRegularTemplates(ctx, kyma)
templates := r.TemplateLookup.GetRegularTemplates(ctx, kyma)
prsr := parser.NewParser(r.Client, r.DescriptorProvider, r.InKCPMode, r.RemoteSyncNamespace)
modules := prsr.GenerateModulesFromTemplates(kyma, templates)

Expand Down
74 changes: 70 additions & 4 deletions internal/maintenancewindows/maintenance_policy_handler.go
Original file line number Diff line number Diff line change
@@ -1,25 +1,42 @@
package maintenancewindows

import (
"errors"
"fmt"
"os"
"time"

"github.com/go-logr/logr"

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

func InitializeMaintenanceWindowsPolicy(log logr.Logger,
var ErrNoMaintenanceWindowPolicyConfigured = errors.New("no maintenance window policy configured")

type MaintenanceWindowPolicy interface {
Resolve(runtime *resolver.Runtime, opts ...interface{}) (*resolver.ResolvedWindow, error)
}

type MaintenanceWindow struct {
// make this private once we refactor the API
// https://github.com/kyma-project/lifecycle-manager/issues/2190
MaintenanceWindowPolicy MaintenanceWindowPolicy
}

func InitializeMaintenanceWindow(log logr.Logger,
policiesDirectory, policyName string,
) (*resolver.MaintenanceWindowPolicy, error) {
) (*MaintenanceWindow, error) {
if err := os.Setenv(resolver.PolicyPathENV, policiesDirectory); err != nil {
return nil, fmt.Errorf("failed to set the policy path env variable, %w", err)
}

policyFilePath := fmt.Sprintf("%s/%s.json", policiesDirectory, policyName)
if !MaintenancePolicyFileExists(policyFilePath) {
log.Info("maintenance windows policy file does not exist")
return nil, nil //nolint:nilnil //use nil to indicate an empty Maintenance Window Policy
return &MaintenanceWindow{
MaintenanceWindowPolicy: nil,
}, nil
}

maintenancePolicyPool, err := resolver.GetMaintenancePolicyPool()
Expand All @@ -32,7 +49,9 @@ func InitializeMaintenanceWindowsPolicy(log logr.Logger,
return nil, fmt.Errorf("failed to get maintenance window policy, %w", err)
}

return maintenancePolicy, nil
return &MaintenanceWindow{
MaintenanceWindowPolicy: maintenancePolicy,
}, nil
}

func MaintenancePolicyFileExists(policyFilePath string) bool {
Expand All @@ -42,3 +61,50 @@ func MaintenancePolicyFileExists(policyFilePath string) bool {

return true
}

// IsRequired determines if a maintenance window is required to update the given module.
func (h MaintenanceWindow) IsRequired(moduleTemplate *v1beta2.ModuleTemplate, kyma *v1beta2.Kyma) bool {
if !moduleTemplate.Spec.RequiresDowntime {
return false
}

if kyma.Spec.SkipMaintenanceWindows {
return false
}

// module not installed yet => no need for maintenance window
moduleStatus := kyma.Status.GetModuleStatus(moduleTemplate.Spec.ModuleName)
if moduleStatus == nil {
return false
}

// module already installed in this version => no need for maintenance window
installedVersion := moduleStatus.Version
return installedVersion != moduleTemplate.Spec.Version
}

// IsActive determines if a maintenance window is currently active.
func (h MaintenanceWindow) IsActive(kyma *v1beta2.Kyma) (bool, error) {
if h.MaintenanceWindowPolicy == nil {
return false, ErrNoMaintenanceWindowPolicyConfigured
}

runtime := &resolver.Runtime{
GlobalAccountID: kyma.GetGlobalAccount(),
Region: kyma.GetRegion(),
PlatformRegion: kyma.GetPlatformRegion(),
Plan: kyma.GetPlan(),
}

resolvedWindow, err := h.MaintenanceWindowPolicy.Resolve(runtime)
if err != nil {
return false, err
}

now := time.Now()
if now.After(resolvedWindow.Begin) && now.Before(resolvedWindow.End) {
return true, nil
}

return false, nil
}
Loading
Loading