@@ -6,11 +6,13 @@ package validation
66import (
77 "fmt"
88
9+ "github.com/gardener/gardener/extensions/pkg/util"
910 gardenercore "github.com/gardener/gardener/pkg/apis/core"
10- gardenercorehelper "github.com/gardener/gardener/pkg/apis/core/helper"
1111 v1beta1constants "github.com/gardener/gardener/pkg/apis/core/v1beta1/constants"
12+ "github.com/gardener/gardener/pkg/utils"
1213 apivalidation "k8s.io/apimachinery/pkg/api/validation"
1314 "k8s.io/apimachinery/pkg/util/validation/field"
15+ "k8s.io/utils/ptr"
1416 "k8s.io/utils/strings/slices"
1517
1618 apisironcore "github.com/ironcore-dev/gardener-extension-provider-ironcore/pkg/apis/ironcore"
@@ -21,19 +23,12 @@ func ValidateCloudProfileConfig(cpConfig *apisironcore.CloudProfileConfig, machi
2123 allErrs := field.ErrorList {}
2224 machineImagesPath := fldPath .Child ("machineImages" )
2325
24- for _ , image := range machineImages {
25- var processed bool
26- for i , imageConfig := range cpConfig .MachineImages {
27- if image .Name == imageConfig .Name {
28- allErrs = append (allErrs , validateVersions (imageConfig .Versions , gardenercorehelper .ToExpirableVersions (image .Versions ), machineImagesPath .Index (i ).Child ("versions" ))... )
29- processed = true
30- break
31- }
32- }
33- if ! processed && len (image .Versions ) > 0 {
34- allErrs = append (allErrs , field .Required (machineImagesPath , fmt .Sprintf ("must provide an image mapping for image %q" , image .Name )))
35- }
26+ // validate all provider images fields
27+ for i , machineImage := range cpConfig .MachineImages {
28+ idxPath := machineImagesPath .Index (i )
29+ allErrs = append (allErrs , ValidateProviderMachineImage (idxPath , machineImage )... )
3630 }
31+ allErrs = append (allErrs , validateProviderImagesMapping (cpConfig .MachineImages , machineImages , field .NewPath ("spec" ).Child ("machineImages" ))... )
3732
3833 if cpConfig .StorageClasses .Default != nil {
3934 for _ , msg := range apivalidation .NameIsDNSLabel (cpConfig .StorageClasses .Default .Name , false ) {
@@ -50,27 +45,81 @@ func ValidateCloudProfileConfig(cpConfig *apisironcore.CloudProfileConfig, machi
5045 return allErrs
5146}
5247
53- func validateVersions (versionsConfig []apisironcore.MachineImageVersion , versions []gardenercore.ExpirableVersion , fldPath * field.Path ) field.ErrorList {
48+ // ValidateProviderMachineImage validates a CloudProfileConfig MachineImages entry.
49+ func ValidateProviderMachineImage (validationPath * field.Path , machineImage apisironcore.MachineImages ) field.ErrorList {
5450 allErrs := field.ErrorList {}
5551
56- for _ , version := range versions {
57- var processed bool
58- for j , versionConfig := range versionsConfig {
59- jdxPath := fldPath .Index (j )
60- if version .Version == versionConfig .Version {
61- if len (versionConfig .Image ) == 0 {
62- allErrs = append (allErrs , field .Required (jdxPath .Child ("image" ), "must provide an image" ))
63- }
64- if ! slices .Contains (v1beta1constants .ValidArchitectures , * versionConfig .Architecture ) {
65- allErrs = append (allErrs , field .NotSupported (jdxPath .Child ("architecture" ), * versionConfig .Architecture , v1beta1constants .ValidArchitectures ))
52+ if len (machineImage .Name ) == 0 {
53+ allErrs = append (allErrs , field .Required (validationPath .Child ("name" ), "must provide a name" ))
54+ }
55+
56+ if len (machineImage .Versions ) == 0 {
57+ allErrs = append (allErrs , field .Required (validationPath .Child ("versions" ), fmt .Sprintf ("must provide at least one version for machine image %q" , machineImage .Name )))
58+ }
59+ for j , version := range machineImage .Versions {
60+ jdxPath := validationPath .Child ("versions" ).Index (j )
61+ if len (version .Version ) == 0 {
62+ allErrs = append (allErrs , field .Required (jdxPath .Child ("version" ), "must provide a version" ))
63+ }
64+ if len (version .Image ) == 0 {
65+ allErrs = append (allErrs , field .Required (jdxPath .Child ("image" ), "must provide an image" ))
66+ }
67+ versionArch := ptr .Deref (version .Architecture , v1beta1constants .ArchitectureAMD64 )
68+ if ! slices .Contains (v1beta1constants .ValidArchitectures , versionArch ) {
69+ allErrs = append (allErrs , field .NotSupported (jdxPath .Child ("architecture" ), versionArch , v1beta1constants .ValidArchitectures ))
70+ }
71+ }
72+
73+ return allErrs
74+ }
75+
76+ // NewProviderImagesContext creates a new ImagesContext for provider images.
77+ func NewProviderImagesContext (providerImages []apisironcore.MachineImages ) * util.ImagesContext [apisironcore.MachineImages , apisironcore.MachineImageVersion ] {
78+ return util .NewImagesContext (
79+ utils .CreateMapFromSlice (providerImages , func (mi apisironcore.MachineImages ) string { return mi .Name }),
80+ func (mi apisironcore.MachineImages ) map [string ]apisironcore.MachineImageVersion {
81+ return utils .CreateMapFromSlice (mi .Versions , func (v apisironcore.MachineImageVersion ) string { return providerMachineImageKey (v ) })
82+ },
83+ )
84+ }
85+
86+ func providerMachineImageKey (v apisironcore.MachineImageVersion ) string {
87+ return VersionArchitectureKey (v .Version , ptr .Deref (v .Architecture , v1beta1constants .ArchitectureAMD64 ))
88+ }
89+
90+ // VersionArchitectureKey returns a key for a version and architecture.
91+ func VersionArchitectureKey (version , architecture string ) string {
92+ return version + "-" + architecture
93+ }
94+
95+ // verify that for each cp image a provider image exists
96+ func validateProviderImagesMapping (cpConfigImages []apisironcore.MachineImages , machineImages []gardenercore.MachineImage , fldPath * field.Path ) field.ErrorList {
97+ allErrs := field.ErrorList {}
98+
99+ providerImages := NewProviderImagesContext (cpConfigImages )
100+
101+ // for each image in the CloudProfile, check if it exists in the CloudProfileConfig
102+ for idxImage , machineImage := range machineImages {
103+ if len (machineImage .Versions ) == 0 {
104+ continue
105+ }
106+ machineImagePath := fldPath .Index (idxImage )
107+ if _ , existsInParent := providerImages .GetImage (machineImage .Name ); ! existsInParent {
108+ allErrs = append (allErrs , field .Required (machineImagePath , fmt .Sprintf ("must provide a provider image mapping for image %q" , machineImage .Name )))
109+ continue
110+ }
111+
112+ // validate that for each version and architecture of an image in the cloud profile a
113+ // corresponding provider specific image in the cloud profile config exists
114+ for versionIdx , version := range machineImage .Versions {
115+ imageVersionPath := machineImagePath .Child ("versions" ).Index (versionIdx )
116+ for _ , expectedArchitecture := range version .Architectures {
117+ if _ , exists := providerImages .GetImageVersion (machineImage .Name , VersionArchitectureKey (version .Version , expectedArchitecture )); ! exists {
118+ allErrs = append (allErrs , field .Required (imageVersionPath ,
119+ fmt .Sprintf ("must provide an image mapping for version %q and architecture: %s" , version .Version , expectedArchitecture )))
66120 }
67- processed = true
68- break
69121 }
70122 }
71- if ! processed {
72- allErrs = append (allErrs , field .Required (fldPath , fmt .Sprintf ("must provide an image mapping for version %q" , version .Version )))
73- }
74123 }
75124
76125 return allErrs
0 commit comments