Skip to content

Commit

Permalink
Merge branch 'main' into release_1.28
Browse files Browse the repository at this point in the history
  • Loading branch information
nesmabadr authored Jan 27, 2025
2 parents fbe67d7 + b426a0c commit 87a800a
Show file tree
Hide file tree
Showing 16 changed files with 717 additions and 99 deletions.
4 changes: 3 additions & 1 deletion .github/actions/deploy-lifecycle-manager-e2e/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,9 @@ runs:
kustomize edit add patch --path certificate_renewal.yaml --kind Certificate --group cert-manager.io --version v1 --name watcher-serving
popd
- name: Create and use maintenance window policy
if: ${{matrix.e2e-test == 'maintenance-window-with-module-downtime'}}
if: ${{matrix.e2e-test == 'maintenance-windows' ||
matrix.e2e-test == 'maintenance-windows-initial-installation' ||
matrix.e2e-test == 'maintenance-windows-skip'}}
working-directory: lifecycle-manager/config
shell: bash
run: |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ runs:
INCLUDE_DEFAULT_CR=true
MANDATORY=false
DEPLOY_MODULETEMPLATE=true
if [ "${{ matrix.e2e-test }}" == "modulereleasemeta-maintenance-window-with-module-downtime" ]; then
if [[ "${{ matrix.e2e-test }}" == "maintenance-windows" ]] ||
[[ "${{ matrix.e2e-test }}" == "maintenance-windows-initial-installation" ]] ||
[[ "${{ matrix.e2e-test }}" == "maintenance-windows-skip" ]]; then
REQUIRE_DOWNTIME=true
else
REQUIRE_DOWNTIME=false
Expand Down Expand Up @@ -85,7 +87,9 @@ runs:
matrix.e2e-test == 'modulereleasemeta-sync' ||
matrix.e2e-test == 'module-status-on-skr-connection-lost' ||
matrix.e2e-test == 'modulereleasemeta-not-allowed-installation' ||
matrix.e2e-test == 'modulereleasemeta-maintenance-window-with-module-downtime'
matrix.e2e-test == 'maintenance-windows' ||
matrix.e2e-test == 'maintenance-windows-initial-installation' ||
matrix.e2e-test == 'maintenance-windows-skip'
}}
shell: bash
run: |
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/test-e2e-with-modulereleasemeta.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,9 @@ jobs:
- modulereleasemeta-watch-trigger
- modulereleasemeta-not-allowed-installation
- labelling
- modulereleasemeta-maintenance-window-with-module-downtime
- maintenance-windows
- maintenance-windows-initial-installation
- maintenance-windows-skip

runs-on: ubuntu-latest
timeout-minutes: 20
Expand Down
5 changes: 4 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,10 @@
"ModuleReleaseMeta Sync",
"KCP Kyma Module status on SKR connection lost",
"ModuleReleaseMeta Not Allowed Installation",
"Labelling SKR resources"
"Labelling SKR resources",
"Maintenance Windows - Wait for Maintenance Window",
"Maintenance Windows - No Wait for Maintenance Window on Initial Installation",
"Maintenance Windows - No Wait for Maintenance Widnow on Skip"
]
},
]
Expand Down
5 changes: 4 additions & 1 deletion .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,10 @@
"ocm-compatible-module-template",
"modulereleasemeta-sync",
"modulereleasemeta-not-allowed-installation",
"labelling"
"labelling",
"maintenance-windows",
"maintenance-windows-initial-installation",
"maintenance-windows-skip"
]
},
{
Expand Down
12 changes: 9 additions & 3 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ const (

maintenanceWindowPolicyName = "policy"
maintenanceWindowPoliciesDirectory = "/etc/maintenance-policy"
minMaintenanceWindowSize = 20 * time.Minute
)

var (
Expand Down Expand Up @@ -196,7 +197,11 @@ func setupManager(flagVar *flags.FlagVar, cacheOptions cache.Options, scheme *ma

maintenanceWindow, err := maintenancewindows.InitializeMaintenanceWindow(setupLog,
maintenanceWindowPoliciesDirectory,
maintenanceWindowPolicyName)
maintenanceWindowPolicyName,
// align the configuration values before rollout
// https://github.com/kyma-project/lifecycle-manager/issues/2165
true,
minMaintenanceWindowSize)
if err != nil {
setupLog.Error(err, "unable to set maintenance windows policy")
}
Expand Down Expand Up @@ -281,7 +286,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,
setupLog logr.Logger, _ *maintenancewindows.MaintenanceWindow,
setupLog logr.Logger, maintenanceWindow *maintenancewindows.MaintenanceWindow,
) {
options.RateLimiter = internal.RateLimiter(flagVar.FailureBaseDelay,
flagVar.FailureMaxDelay, flagVar.RateLimiterFrequency, flagVar.RateLimiterBurst)
Expand All @@ -291,7 +296,8 @@ func setupKymaReconciler(mgr ctrl.Manager, descriptorProvider *provider.CachedDe
moduleTemplateInfoLookupStrategies := moduletemplateinfolookup.NewModuleTemplateInfoLookupStrategies([]moduletemplateinfolookup.ModuleTemplateInfoLookupStrategy{
moduletemplateinfolookup.NewByVersionStrategy(mgr.GetClient()),
moduletemplateinfolookup.NewByChannelStrategy(mgr.GetClient()),
moduletemplateinfolookup.NewByModuleReleaseMetaStrategy(mgr.GetClient()),
moduletemplateinfolookup.NewWithMaintenanceWindowDecorator(maintenanceWindow,
moduletemplateinfolookup.NewByModuleReleaseMetaStrategy(mgr.GetClient())),
})

if err := (&kyma.Reconciler{
Expand Down
13 changes: 11 additions & 2 deletions internal/maintenancewindows/maintenance_window.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,15 @@ type MaintenanceWindow struct {
// make this private once we refactor the API
// https://github.com/kyma-project/lifecycle-manager/issues/2190
MaintenanceWindowPolicy MaintenanceWindowPolicy
ongoing resolver.OngoingWindow
minDuration resolver.MinWindowSize
}

func InitializeMaintenanceWindow(log logr.Logger,
policiesDirectory, policyName string,
policiesDirectory,
policyName string,
ongoingWindow bool,
minWindowSize time.Duration,
) (*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)
Expand All @@ -51,6 +56,8 @@ func InitializeMaintenanceWindow(log logr.Logger,

return &MaintenanceWindow{
MaintenanceWindowPolicy: maintenancePolicy,
ongoing: resolver.OngoingWindow(ongoingWindow),
minDuration: resolver.MinWindowSize(minWindowSize),
}, nil
}

Expand Down Expand Up @@ -95,7 +102,9 @@ func (mw MaintenanceWindow) IsActive(kyma *v1beta2.Kyma) (bool, error) {
Plan: kyma.GetPlan(),
}

resolvedWindow, err := mw.MaintenanceWindowPolicy.Resolve(runtime)
resolvedWindow, err := mw.MaintenanceWindowPolicy.Resolve(runtime,
mw.ongoing,
mw.minDuration)
if err != nil {
return false, err
}
Expand Down
24 changes: 20 additions & 4 deletions internal/maintenancewindows/maintenance_window_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,28 +31,44 @@ func TestMaintenancePolicyFileExists_FileExists(t *testing.T) {
}

func TestInitializeMaintenanceWindowsPolicy_FileNotExist_NoError(t *testing.T) {
got, err := maintenancewindows.InitializeMaintenanceWindow(logr.Logger{}, "testdata", "policy-1")
got, err := maintenancewindows.InitializeMaintenanceWindow(logr.Logger{},
"testdata",
"policy-1",
true,
20*time.Minute)

require.Nil(t, got.MaintenanceWindowPolicy)
require.NoError(t, err)
}

func TestInitializeMaintenanceWindowsPolicy_DirectoryNotExist_NoError(t *testing.T) {
got, err := maintenancewindows.InitializeMaintenanceWindow(logr.Logger{}, "files", "policy")
got, err := maintenancewindows.InitializeMaintenanceWindow(logr.Logger{},
"files",
"policy",
true,
20*time.Minute)

require.Nil(t, got.MaintenanceWindowPolicy)
require.NoError(t, err)
}

func TestInitializeMaintenanceWindowsPolicy_InvalidPolicy(t *testing.T) {
got, err := maintenancewindows.InitializeMaintenanceWindow(logr.Logger{}, "testdata", "invalid-policy")
got, err := maintenancewindows.InitializeMaintenanceWindow(logr.Logger{},
"testdata",
"invalid-policy",
true,
20*time.Minute)

require.Nil(t, got)
require.ErrorContains(t, err, "failed to get maintenance window policy")
}

func TestInitializeMaintenanceWindowsPolicy_WhenFileExists_CorrectPolicyIsRead(t *testing.T) {
got, err := maintenancewindows.InitializeMaintenanceWindow(logr.Logger{}, "testdata", "policy")
got, err := maintenancewindows.InitializeMaintenanceWindow(logr.Logger{},
"testdata",
"policy",
true,
20*time.Minute)
require.NoError(t, err)

ruleOneBeginTime, err := parseTime("01:00:00+00:00")
Expand Down
59 changes: 37 additions & 22 deletions pkg/module/sync/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,30 +270,10 @@ func updateModuleStatusFromExistingModules(
}

func generateModuleStatus(module *common.Module, existStatus *v1beta2.ModuleStatus) v1beta2.ModuleStatus {
if errors.Is(module.Template.Err, templatelookup.ErrTemplateUpdateNotAllowed) {
newModuleStatus := existStatus.DeepCopy()
newModuleStatus.State = shared.StateWarning
newModuleStatus.Message = module.Template.Err.Error()
return *newModuleStatus
}
if errors.Is(module.Template.Err, moduletemplateinfolookup.ErrNoTemplatesInListResult) {
return v1beta2.ModuleStatus{
Name: module.ModuleName,
Channel: module.Template.DesiredChannel,
FQDN: module.FQDN,
State: shared.StateWarning,
Message: module.Template.Err.Error(),
}
}
if module.Template.Err != nil {
return v1beta2.ModuleStatus{
Name: module.ModuleName,
Channel: module.Template.DesiredChannel,
FQDN: module.FQDN,
State: shared.StateError,
Message: module.Template.Err.Error(),
}
return generateModuleStatusFromError(module, existStatus)
}

manifestObject := module.Manifest
manifestAPIVersion, manifestKind := manifestObject.GetObjectKind().GroupVersionKind().ToAPIVersionAndKind()
templateAPIVersion, templateKind := module.Template.GetObjectKind().GroupVersionKind().ToAPIVersionAndKind()
Expand Down Expand Up @@ -350,6 +330,41 @@ func generateModuleStatus(module *common.Module, existStatus *v1beta2.ModuleStat
return moduleStatus
}

func generateModuleStatusFromError(module *common.Module, existStatus *v1beta2.ModuleStatus) v1beta2.ModuleStatus {
switch {
case errors.Is(module.Template.Err, templatelookup.ErrTemplateUpdateNotAllowed):
newModuleStatus := existStatus.DeepCopy()
newModuleStatus.State = shared.StateWarning
newModuleStatus.Message = module.Template.Err.Error()
return *newModuleStatus
case errors.Is(module.Template.Err, moduletemplateinfolookup.ErrNoTemplatesInListResult):
return v1beta2.ModuleStatus{
Name: module.ModuleName,
Channel: module.Template.DesiredChannel,
FQDN: module.FQDN,
State: shared.StateWarning,
Message: module.Template.Err.Error(),
}
case errors.Is(module.Template.Err, moduletemplateinfolookup.ErrWaitingForNextMaintenanceWindow):
newModuleStatus := existStatus.DeepCopy()
newModuleStatus.Message = module.Template.Err.Error()
return *newModuleStatus
case errors.Is(module.Template.Err, moduletemplateinfolookup.ErrFailedToDetermineIfMaintenanceWindowIsActive):
newModuleStatus := existStatus.DeepCopy()
newModuleStatus.Message = module.Template.Err.Error()
newModuleStatus.State = shared.StateError
return *newModuleStatus
default:
return v1beta2.ModuleStatus{
Name: module.ModuleName,
Channel: module.Template.DesiredChannel,
FQDN: module.FQDN,
State: shared.StateError,
Message: module.Template.Err.Error(),
}
}
}

func stateFromManifest(obj client.Object) shared.State {
switch manifest := obj.(type) {
case *v1beta2.Manifest:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package moduletemplateinfolookup

import (
"context"
"errors"
"fmt"

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

var (
ErrWaitingForNextMaintenanceWindow = errors.New("waiting for next maintenance window to update module version")
ErrFailedToDetermineIfMaintenanceWindowIsActive = errors.New("failed to determine if maintenance window is active")
)

type MaintenanceWindow interface {
IsRequired(moduleTemplate *v1beta2.ModuleTemplate, kyma *v1beta2.Kyma) bool
IsActive(kyma *v1beta2.Kyma) (bool, error)
}

type WithMaintenanceWindowDecorator struct {
maintenanceWindow MaintenanceWindow
decorated ModuleTemplateInfoLookupStrategy
}

func NewWithMaintenanceWindowDecorator(maintenanceWindow MaintenanceWindow, decorated ModuleTemplateInfoLookupStrategy) WithMaintenanceWindowDecorator {
return WithMaintenanceWindowDecorator{
maintenanceWindow: maintenanceWindow,
decorated: decorated,
}
}

func (p WithMaintenanceWindowDecorator) IsResponsible(moduleInfo *templatelookup.ModuleInfo, moduleReleaseMeta *v1beta2.ModuleReleaseMeta) bool {
return p.decorated.IsResponsible(moduleInfo, moduleReleaseMeta)
}

func (p WithMaintenanceWindowDecorator) Lookup(ctx context.Context,
moduleInfo *templatelookup.ModuleInfo,
kyma *v1beta2.Kyma,
moduleReleaseMeta *v1beta2.ModuleReleaseMeta,
) templatelookup.ModuleTemplateInfo {
moduleTemplateInfo := p.decorated.Lookup(ctx,
moduleInfo,
kyma,
moduleReleaseMeta)

// decorated returns an error case => return immediately
if moduleTemplateInfo.ModuleTemplate == nil || moduleTemplateInfo.Err != nil {
return moduleTemplateInfo
}

if !p.maintenanceWindow.IsRequired(moduleTemplateInfo.ModuleTemplate, kyma) {
return moduleTemplateInfo
}

active, err := p.maintenanceWindow.IsActive(kyma)
if err != nil {
moduleTemplateInfo.Err = fmt.Errorf("%w: %w", ErrFailedToDetermineIfMaintenanceWindowIsActive, err)
moduleTemplateInfo.ModuleTemplate = nil
return moduleTemplateInfo
}

if !active {
moduleTemplateInfo.Err = ErrWaitingForNextMaintenanceWindow
moduleTemplateInfo.ModuleTemplate = nil
return moduleTemplateInfo
}

return moduleTemplateInfo
}
Loading

0 comments on commit 87a800a

Please sign in to comment.