Skip to content

Commit

Permalink
Add NamespacedCloudProfile support to Shoot validation.
Browse files Browse the repository at this point in the history
Switch from CloudProfile to CloudProfileSpec as a generic foundation for CloudProfile and NamespacedCloudProfile.

Improve log message for possibly artificially crafted CloudProfile from NamespacedCloudProfile in Cluster resource.
  • Loading branch information
LucaBernstein committed Sep 11, 2024
1 parent 0da281b commit f0ba368
Show file tree
Hide file tree
Showing 5 changed files with 231 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ rules:
- core.gardener.cloud
resources:
- cloudprofiles
- namespacedcloudprofiles
- secretbindings
verbs:
- get
Expand Down
3 changes: 2 additions & 1 deletion docs/usage/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,8 @@ metadata:
name: johndoe-openstack
namespace: garden-dev
spec:
cloudProfileName: openstack
cloudProfile:
name: openstack
region: europe-1
secretBindingName: core-openstack
provider:
Expand Down
60 changes: 36 additions & 24 deletions pkg/admission/validator/shoot.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ 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"
kutil "github.com/gardener/gardener/pkg/utils/kubernetes"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apimachinery/pkg/util/validation/field"
Expand All @@ -33,6 +35,7 @@ func NewShootValidator(mgr manager.Manager) extensionswebhook.Validator {
apiReader: mgr.GetAPIReader(),
decoder: serializer.NewCodecFactory(mgr.GetScheme(), serializer.EnableStrict).UniversalDecoder(),
lenientDecoder: serializer.NewCodecFactory(mgr.GetScheme()).UniversalDecoder(),
scheme: mgr.GetScheme(),
}
}

Expand All @@ -41,13 +44,14 @@ type shoot struct {
apiReader client.Reader
decoder runtime.Decoder
lenientDecoder runtime.Decoder
scheme *runtime.Scheme
}

type validationContext struct {
shoot *core.Shoot
infraConfig *api.InfrastructureConfig
cpConfig *api.ControlPlaneConfig
cloudProfile *gardencorev1beta1.CloudProfile
cloudProfileSpec *gardencorev1beta1.CloudProfileSpec
cloudProfileConfig *api.CloudProfileConfig
}

Expand Down Expand Up @@ -85,19 +89,37 @@ func (s *shoot) Validate(ctx context.Context, new, old client.Object) error {
}
}

shootV1Beta1 := &gardencorev1beta1.Shoot{
TypeMeta: metav1.TypeMeta{
APIVersion: gardencorev1beta1.SchemeGroupVersion.String(),
Kind: "Shoot",
},
}
err := s.scheme.Convert(shoot, shootV1Beta1, ctx)
if err != nil {
return err
}
cloudProfile, err := gardener.GetCloudProfile(ctx, s.client, shootV1Beta1)
if err != nil {
return err
}
if cloudProfile == nil {
return fmt.Errorf("cloudprofile could not be found")
}

if old != nil {
oldShoot, ok := old.(*core.Shoot)
if !ok {
return fmt.Errorf("wrong object type %T for old object", old)
}
return s.validateShootUpdate(ctx, oldShoot, shoot, credentials)
return s.validateShootUpdate(oldShoot, shoot, credentials, &cloudProfile.Spec)
}

return s.validateShootCreation(ctx, shoot, credentials)
return s.validateShootCreation(shoot, credentials, &cloudProfile.Spec)
}

func (s *shoot) validateShootCreation(ctx context.Context, shoot *core.Shoot, credentials *openstack.Credentials) error {
valContext, err := newValidationContext(ctx, s.decoder, s.client, shoot)
func (s *shoot) validateShootCreation(shoot *core.Shoot, credentials *openstack.Credentials, cloudProfileSpec *gardencorev1beta1.CloudProfileSpec) error {
valContext, err := newValidationContext(s.decoder, shoot, cloudProfileSpec)
if err != nil {
return err
}
Expand All @@ -112,13 +134,13 @@ func (s *shoot) validateShootCreation(ctx context.Context, shoot *core.Shoot, cr
return allErrs.ToAggregate()
}

func (s *shoot) validateShootUpdate(ctx context.Context, oldShoot, shoot *core.Shoot, credentials *openstack.Credentials) error {
oldValContext, err := newValidationContext(ctx, s.lenientDecoder, s.client, oldShoot)
func (s *shoot) validateShootUpdate(oldShoot, shoot *core.Shoot, credentials *openstack.Credentials, cloudProfileSpec *gardencorev1beta1.CloudProfileSpec) error {
oldValContext, err := newValidationContext(s.lenientDecoder, oldShoot, cloudProfileSpec)
if err != nil {
return err
}

valContext, err := newValidationContext(ctx, s.decoder, s.client, shoot)
valContext, err := newValidationContext(s.decoder, shoot, cloudProfileSpec)
if err != nil {
return err
}
Expand Down Expand Up @@ -167,7 +189,7 @@ func (s *shoot) validateShoot(context *validationContext) field.ErrorList {
return allErrs
}

func newValidationContext(ctx context.Context, decoder runtime.Decoder, c client.Client, shoot *core.Shoot) (*validationContext, error) {
func newValidationContext(decoder runtime.Decoder, shoot *core.Shoot, cloudProfileSpec *gardencorev1beta1.CloudProfileSpec) (*validationContext, error) {
if shoot.Spec.Provider.InfrastructureConfig == nil {
return nil, field.Required(infraConfigPath, "infrastructureConfig must be set for OpenStack shoots")
}
Expand All @@ -184,29 +206,19 @@ func newValidationContext(ctx context.Context, decoder runtime.Decoder, c client
return nil, fmt.Errorf("error decoding controlPlaneConfig: %v", err)
}

cloudProfile := &gardencorev1beta1.CloudProfile{}

if shoot.Spec.CloudProfile == nil {
return nil, fmt.Errorf("shoot.spec.cloudprofile must not be nil <nil>")
}

if err := c.Get(ctx, client.ObjectKey{Name: shoot.Spec.CloudProfile.Name}, cloudProfile); err != nil {
return nil, err
}

if cloudProfile.Spec.ProviderConfig == nil {
return nil, fmt.Errorf("providerConfig is not given for cloud profile %q", cloudProfile.Name)
if cloudProfileSpec.ProviderConfig == nil {
return nil, fmt.Errorf("providerConfig is not given for cloud profile %q", shoot.Spec.CloudProfile)
}
cloudProfileConfig, err := decodeCloudProfileConfig(decoder, cloudProfile.Spec.ProviderConfig)
cloudProfileConfig, err := decodeCloudProfileConfig(decoder, cloudProfileSpec.ProviderConfig)
if err != nil {
return nil, fmt.Errorf("an error occurred while reading the cloud profile %q: %v", cloudProfile.Name, err)
return nil, fmt.Errorf("an error occurred while reading the cloud profile %q: %v", shoot.Spec.CloudProfile, err)
}

return &validationContext{
shoot: shoot,
infraConfig: infraConfig,
cpConfig: cpConfig,
cloudProfile: cloudProfile,
cloudProfileSpec: cloudProfileSpec,
cloudProfileConfig: cloudProfileConfig,
}, nil
}
Expand Down
Loading

0 comments on commit f0ba368

Please sign in to comment.