From 49013115354c20659297bc901b5c61d28be6e7fd Mon Sep 17 00:00:00 2001 From: Ekaterina Kazakova Date: Fri, 18 Oct 2024 22:59:21 +0400 Subject: [PATCH] Validate availability of the ManagedCluster update --- internal/webhook/managedcluster_webhook.go | 33 ++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/internal/webhook/managedcluster_webhook.go b/internal/webhook/managedcluster_webhook.go index 18b66b0eb..2e71e3e93 100644 --- a/internal/webhook/managedcluster_webhook.go +++ b/internal/webhook/managedcluster_webhook.go @@ -16,7 +16,9 @@ package webhook import ( "context" + "errors" "fmt" + "slices" "github.com/Masterminds/semver/v3" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" @@ -36,6 +38,8 @@ type ManagedClusterValidator struct { const invalidManagedClusterMsg = "the ManagedCluster is invalid" +var errClusterUpgradeForbidden = errors.New("cluster upgrade is forbidden") + func (v *ManagedClusterValidator) SetupWebhookWithManager(mgr ctrl.Manager) error { v.Client = mgr.GetClient() return ctrl.NewWebhookManagedBy(mgr). @@ -87,12 +91,21 @@ func (v *ManagedClusterValidator) ValidateUpdate(ctx context.Context, oldObj run if !ok { return nil, apierrors.NewBadRequest(fmt.Sprintf("expected ManagedCluster but got a %T", newObj)) } - template, err := v.getManagedClusterTemplate(ctx, newManagedCluster.Namespace, newManagedCluster.Spec.Template) + oldTemplate := oldManagedCluster.Spec.Template + newTemplate := newManagedCluster.Spec.Template + + template, err := v.getManagedClusterTemplate(ctx, newManagedCluster.Namespace, newTemplate) if err != nil { return nil, fmt.Errorf("%s: %v", invalidManagedClusterMsg, err) } - if oldManagedCluster.Spec.Template != newManagedCluster.Spec.Template { + if oldTemplate != newTemplate { + isUpgradeAvailable := vaidateAvailableUpgrade(oldManagedCluster, newTemplate) + if !isUpgradeAvailable { + msg := fmt.Sprintf("Cluster can't be upgraded from %s to %s. This upgrade sequence is not allowed", oldTemplate, newTemplate) + return admission.Warnings{msg}, errClusterUpgradeForbidden + } + if err := isTemplateValid(template); err != nil { return nil, fmt.Errorf("%s: %v", invalidManagedClusterMsg, err) } @@ -109,6 +122,22 @@ func (v *ManagedClusterValidator) ValidateUpdate(ctx context.Context, oldObj run return nil, nil } +func vaidateAvailableUpgrade(oldManagedCluster *hmcv1alpha1.ManagedCluster, newTemplate string) bool { + if oldManagedCluster.Status.AvailableUpgrades == nil { + return true + } + availableUpgrades := *oldManagedCluster.Status.AvailableUpgrades + if len(availableUpgrades) == 0 { + return false + } + if slices.ContainsFunc(availableUpgrades, func(au hmcv1alpha1.AvailableUpgrade) bool { + return newTemplate == au.Name + }) { + return true + } + return false +} + func validateK8sCompatibility(ctx context.Context, cl client.Client, template *hmcv1alpha1.ClusterTemplate, mc *hmcv1alpha1.ManagedCluster) error { if len(mc.Spec.Services) == 0 || template.Status.KubernetesVersion == "" { return nil // nothing to do