From 508e9762d5ca97dab97c1eaaeca83c2e7b6d47a9 Mon Sep 17 00:00:00 2001 From: Luca Bernstein Date: Fri, 12 Jul 2024 18:04:55 +0200 Subject: [PATCH] Adapt shoot validations to also work with NamespacedCloudProfiles and CloudProfiles from reference --- pkg/admission/validator/shoot.go | 17 +++++--- pkg/admission/validator/shoot_test.go | 61 ++++++++++++++++++++++----- 2 files changed, 63 insertions(+), 15 deletions(-) diff --git a/pkg/admission/validator/shoot.go b/pkg/admission/validator/shoot.go index dd79d7b02..f990e8fe9 100644 --- a/pkg/admission/validator/shoot.go +++ b/pkg/admission/validator/shoot.go @@ -14,6 +14,7 @@ import ( "github.com/gardener/gardener/pkg/apis/core" gardencorehelper "github.com/gardener/gardener/pkg/apis/core/helper" gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1" + "github.com/gardener/gardener/pkg/utils/gardener" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/apimachinery/pkg/util/validation/field" @@ -126,7 +127,6 @@ func (s *shoot) validateShootUpdate(ctx context.Context, oldShoot, shoot *core.S var ( fldPath = field.NewPath("spec", "provider") infraConfigFldPath = fldPath.Child("infrastructureConfig") - cloudProfile = &gardencorev1beta1.CloudProfile{} ) // InfrastructureConfig update @@ -139,9 +139,13 @@ func (s *shoot) validateShootUpdate(ctx context.Context, oldShoot, shoot *core.S return err } - if err := s.client.Get(ctx, client.ObjectKey{Name: shoot.Spec.CloudProfileName}, cloudProfile); err != nil { + cloudProfile, err := gardener.GetCloudProfile(ctx, s.client, gardener.BuildCloudProfileReference(shoot.Spec.CloudProfileName, shoot.Spec.CloudProfile), shoot.Namespace) + if err != nil { return err } + if cloudProfile == nil { + return fmt.Errorf("cloudprofile could not be found") + } if oldShoot.Spec.Provider.InfrastructureConfig == nil { return field.InternalError(infraConfigFldPath, errors.New("InfrastructureConfig is not available on old shoot")) @@ -180,8 +184,7 @@ func (s *shoot) validateShootUpdate(ctx context.Context, oldShoot, shoot *core.S func (s *shoot) validateShootCreation(ctx context.Context, shoot *core.Shoot) error { var ( - fldPath = field.NewPath("spec", "provider") - cloudProfile = &gardencorev1beta1.CloudProfile{} + fldPath = field.NewPath("spec", "provider") ) if shoot.Spec.Provider.InfrastructureConfig == nil { return field.Required(fldPath.Child("infrastructureConfig"), "InfrastructureConfig must be set for AWS shoots") @@ -192,9 +195,13 @@ func (s *shoot) validateShootCreation(ctx context.Context, shoot *core.Shoot) er return err } - if err := s.client.Get(ctx, client.ObjectKey{Name: shoot.Spec.CloudProfileName}, cloudProfile); err != nil { + cloudProfile, err := gardener.GetCloudProfile(ctx, s.client, gardener.BuildCloudProfileReference(shoot.Spec.CloudProfileName, shoot.Spec.CloudProfile), shoot.Namespace) + if err != nil { return err } + if cloudProfile == nil { + return fmt.Errorf("cloudprofile could not be found") + } awsCloudProfile, err := decodeCloudProfileConfig(s.decoder, cloudProfile.Spec.ProviderConfig) if err != nil { diff --git a/pkg/admission/validator/shoot_test.go b/pkg/admission/validator/shoot_test.go index 36c6b1b94..0d87918dc 100644 --- a/pkg/admission/validator/shoot_test.go +++ b/pkg/admission/validator/shoot_test.go @@ -36,15 +36,17 @@ var _ = Describe("Shoot validator", func() { var ( shootValidator extensionswebhook.Validator - ctrl *gomock.Controller - mgr *mockmanager.MockManager - c *mockclient.MockClient - cloudProfile *gardencorev1beta1.CloudProfile - shoot *core.Shoot - - ctx = context.TODO() - cloudProfileKey = client.ObjectKey{Name: "aws"} - gp2type = string(apisaws.VolumeTypeGP2) + ctrl *gomock.Controller + mgr *mockmanager.MockManager + c *mockclient.MockClient + cloudProfile *gardencorev1beta1.CloudProfile + namespacedCloudProfile *gardencorev1beta1.NamespacedCloudProfile + shoot *core.Shoot + + ctx = context.TODO() + cloudProfileKey = client.ObjectKey{Name: "aws"} + namespacedCloudProfileKey = client.ObjectKey{Name: "aws-nscpfl", Namespace: namespace} + gp2type = string(apisaws.VolumeTypeGP2) regionName = "us-west" imageName = "Foo" @@ -114,13 +116,28 @@ var _ = Describe("Shoot validator", func() { }, } + namespacedCloudProfile = &gardencorev1beta1.NamespacedCloudProfile{ + ObjectMeta: metav1.ObjectMeta{ + Name: "aws-nscpfl", + }, + Spec: gardencorev1beta1.NamespacedCloudProfileSpec{ + Parent: gardencorev1beta1.CloudProfileReference{ + Kind: "CloudProfile", + Name: "aws", + }, + }, + Status: gardencorev1beta1.NamespacedCloudProfileStatus{ + CloudProfileSpec: cloudProfile.Spec, // TODO(LucaBernstein): Okay to mock like this? + }, + } + shoot = &core.Shoot{ ObjectMeta: metav1.ObjectMeta{ Name: "foo", Namespace: namespace, }, Spec: core.ShootSpec{ - CloudProfileName: cloudProfile.Name, + CloudProfileName: &cloudProfile.Name, Provider: core.Provider{ InfrastructureConfig: &runtime.RawExtension{ Raw: encode(&apisawsv1alpha1.InfrastructureConfig{ @@ -364,6 +381,30 @@ var _ = Describe("Shoot validator", func() { err := shootValidator.Validate(ctx, shoot, nil) Expect(err).NotTo(HaveOccurred()) }) + + It("should also work for CloudProfile reference instead of cloudProfileName in Shoot", func() { + shoot.Spec.CloudProfileName = nil + shoot.Spec.CloudProfile = &core.CloudProfileReference{ + Kind: "CloudProfile", + Name: "aws", + } + c.EXPECT().Get(ctx, cloudProfileKey, &gardencorev1beta1.CloudProfile{}).SetArg(2, *cloudProfile) + + err := shootValidator.Validate(ctx, shoot, nil) + Expect(err).NotTo(HaveOccurred()) + }) + + It("should also work for NamespacedCloudProfile referenced from Shoot", func() { + shoot.Spec.CloudProfileName = nil + shoot.Spec.CloudProfile = &core.CloudProfileReference{ + Kind: "NamespacedCloudProfile", + Name: "aws-nscpfl", + } + c.EXPECT().Get(ctx, namespacedCloudProfileKey, &gardencorev1beta1.NamespacedCloudProfile{}).SetArg(2, *namespacedCloudProfile) + + err := shootValidator.Validate(ctx, shoot, nil) + Expect(err).NotTo(HaveOccurred()) + }) }) Context("Workerless Shoot", func() {