Skip to content

Commit

Permalink
Fix cloud profile machineImages validation
Browse files Browse the repository at this point in the history
  • Loading branch information
hebelsan committed Dec 23, 2024
1 parent c361900 commit 1ebc768
Show file tree
Hide file tree
Showing 4 changed files with 166 additions and 43 deletions.
2 changes: 1 addition & 1 deletion pkg/admission/validator/cloudprofile.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,5 @@ func (cp *cloudProfile) Validate(_ context.Context, newObj, _ client.Object) err
return err
}

return openstackvalidation.ValidateCloudProfileConfig(cpConfig, providerConfigPath).ToAggregate()
return openstackvalidation.ValidateCloudProfileConfig(cpConfig, cloudProfile.Spec.MachineImages, providerConfigPath).ToAggregate()
}
14 changes: 2 additions & 12 deletions pkg/admission/validator/namespacedcloudprofile.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
"github.com/gardener/gardener/pkg/apis/core"
gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1"
"github.com/gardener/gardener/pkg/apis/core/v1beta1/constants"
"github.com/gardener/gardener/pkg/utils"
"k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer"
Expand Down Expand Up @@ -97,12 +96,12 @@ func (p *namespacedCloudProfile) validateMachineImages(providerConfig *api.Cloud
machineImagesPath := field.NewPath("spec.providerConfig.machineImages")
for i, machineImage := range providerConfig.MachineImages {
idxPath := machineImagesPath.Index(i)
allErrs = append(allErrs, validation.ValidateMachineImage(idxPath, machineImage)...)
allErrs = append(allErrs, validation.ValidateProviderMachineImage(idxPath, machineImage)...)
}

profileImages := util.NewCoreImagesContext(machineImages)
parentImages := util.NewV1beta1ImagesContext(parentSpec.MachineImages)
providerImages := newProviderImagesContext(providerConfig.MachineImages)
providerImages := validation.NewProviderImagesContext(providerConfig.MachineImages)

for _, machineImage := range profileImages.Images {
// Check that for each new image version defined in the NamespacedCloudProfile, the image is also defined in the providerConfig.
Expand Down Expand Up @@ -198,12 +197,3 @@ func validateMachineImageArchitectures(machineImage core.MachineImage, version c

return allErrs
}

func newProviderImagesContext(providerImages []api.MachineImages) *util.ImagesContext[api.MachineImages, api.MachineImageVersion] {
return util.NewImagesContext(
utils.CreateMapFromSlice(providerImages, func(mi api.MachineImages) string { return mi.Name }),
func(mi api.MachineImages) map[string]api.MachineImageVersion {
return utils.CreateMapFromSlice(mi.Versions, func(v api.MachineImageVersion) string { return v.Version })
},
)
}
77 changes: 73 additions & 4 deletions pkg/apis/openstack/validation/cloudprofile.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ package validation

import (
"fmt"
"maps"
"net"
"slices"

"github.com/gardener/gardener/extensions/pkg/util"
"github.com/gardener/gardener/pkg/apis/core"
v1beta1constants "github.com/gardener/gardener/pkg/apis/core/v1beta1/constants"
"github.com/gardener/gardener/pkg/utils"
"k8s.io/apimachinery/pkg/util/sets"
Expand All @@ -19,7 +22,7 @@ import (
)

// ValidateCloudProfileConfig validates a CloudProfileConfig object.
func ValidateCloudProfileConfig(cloudProfile *api.CloudProfileConfig, fldPath *field.Path) field.ErrorList {
func ValidateCloudProfileConfig(cloudProfile *api.CloudProfileConfig, machineImages []core.MachineImage, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}

floatingPoolPath := fldPath.Child("constraints", "floatingPools")
Expand Down Expand Up @@ -93,8 +96,9 @@ func ValidateCloudProfileConfig(cloudProfile *api.CloudProfileConfig, fldPath *f
}
for i, machineImage := range cloudProfile.MachineImages {
idxPath := machineImagesPath.Index(i)
allErrs = append(allErrs, ValidateMachineImage(idxPath, machineImage)...)
allErrs = append(allErrs, ValidateProviderMachineImage(idxPath, machineImage)...)
}
allErrs = append(allErrs, validateMachineImageMapping(machineImages, cloudProfile, field.NewPath("spec").Child("machineImages"))...)

if len(cloudProfile.KeyStoneURL) == 0 && len(cloudProfile.KeyStoneURLs) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("keyStoneURL"), "must provide the URL to KeyStone"))
Expand Down Expand Up @@ -153,8 +157,8 @@ func ValidateCloudProfileConfig(cloudProfile *api.CloudProfileConfig, fldPath *f
return allErrs
}

// ValidateMachineImage validates a CloudProfileConfig MachineImages entry.
func ValidateMachineImage(validationPath *field.Path, machineImage api.MachineImages) field.ErrorList {
// ValidateProviderMachineImage validates a CloudProfileConfig MachineImages entry.
func ValidateProviderMachineImage(validationPath *field.Path, machineImage api.MachineImages) field.ErrorList {
allErrs := field.ErrorList{}

if len(machineImage.Name) == 0 {
Expand Down Expand Up @@ -189,6 +193,71 @@ func ValidateMachineImage(validationPath *field.Path, machineImage api.MachineIm
return allErrs
}

// NewProviderImagesContext creates a new ImagesContext for provider images.
func NewProviderImagesContext(providerImages []api.MachineImages) *util.ImagesContext[api.MachineImages, api.MachineImageVersion] {
return util.NewImagesContext(
utils.CreateMapFromSlice(providerImages, func(mi api.MachineImages) string { return mi.Name }),
func(mi api.MachineImages) map[string]api.MachineImageVersion {
return utils.CreateMapFromSlice(mi.Versions, func(v api.MachineImageVersion) string { return v.Version })
},
)
}

// validateMachineImageMapping validates that for each machine image there is a corresponding cpConfig image.
func validateMachineImageMapping(machineImages []core.MachineImage, cpConfig *api.CloudProfileConfig, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
providerImages := NewProviderImagesContext(cpConfig.MachineImages)

// validate machine images
for idxImage, machineImage := range machineImages {
if len(machineImage.Versions) == 0 {
continue
}
machineImagePath := fldPath.Index(idxImage)
// validate that for each machine image there is a corresponding cpConfig image
if _, existsInConfig := providerImages.GetImage(machineImage.Name); !existsInConfig {
allErrs = append(allErrs, field.Required(machineImagePath,
fmt.Sprintf("must provide an image mapping for image %q in providerConfig", machineImage.Name)))
continue
}
// validate that for each machine image version entry a mapped entry in cpConfig exists
for idxVersion, version := range machineImage.Versions {
machineImageVersionPath := machineImagePath.Child("versions").Index(idxVersion)
for _, expectedArchitecture := range version.Architectures {
// validate machine image version architectures
if !slices.Contains(v1beta1constants.ValidArchitectures, expectedArchitecture) {
allErrs = append(allErrs, field.NotSupported(
machineImageVersionPath.Child("architectures"),
expectedArchitecture, v1beta1constants.ValidArchitectures))
}
// validate that machine image version exists in cpConfig
imageVersion, exists := providerImages.GetImageVersion(machineImage.Name, version.Version)
if !exists {
allErrs = append(allErrs, field.Required(machineImageVersionPath,
fmt.Sprintf("machine image version %s@%s is not defined in the providerConfig",
machineImage.Name, version.Version),
))
continue
}
// validate that machine image version with architecture x exists in cpConfig
architecturesMap := utils.CreateMapFromSlice(imageVersion.Regions, func(re api.RegionIDMapping) string {
return ptr.Deref(re.Architecture, v1beta1constants.ArchitectureAMD64)
})
architectures := slices.Collect(maps.Keys(architecturesMap))
if !slices.Contains(architectures, expectedArchitecture) {
allErrs = append(allErrs, field.Required(machineImageVersionPath,
fmt.Sprintf("missing providerConfig mapping for machine image version %s@%s and architecture: %s",
machineImage.Name, version.Version, expectedArchitecture),
))
continue
}
}
}
}

return allErrs
}

// validateLoadBalancerClass validates LoadBalancerClass object.
func validateLoadBalancerClass(lbClass api.LoadBalancerClass, fldPath *field.Path) field.ErrorList {
var allErrs = field.ErrorList{}
Expand Down
Loading

0 comments on commit 1ebc768

Please sign in to comment.