@@ -189,6 +189,24 @@ func instanceServerCreateRun(ctx context.Context, argsI interface{}) (i interfac
189189 serverReq .Image = args .Image
190190 }
191191
192+ getImageResponse , err := apiInstance .GetImage (& instance.GetImageRequest {
193+ Zone : args .Zone ,
194+ ImageID : serverReq .Image ,
195+ })
196+ if err != nil {
197+ logger .Warningf ("cannot get image %s: %s" , serverReq .Image , err )
198+ }
199+
200+ serverType := getServeType (apiInstance , serverReq .Zone , serverReq .CommercialType )
201+
202+ if serverType != nil && getImageResponse != nil {
203+ if err := validateImageServerTypeCompatibility (getImageResponse .Image , serverType , serverReq .CommercialType ); err != nil {
204+ return nil , err
205+ }
206+ } else {
207+ logger .Warningf ("skipping image server-type compatibility validation" )
208+ }
209+
192210 //
193211 // IP.
194212 //
@@ -241,13 +259,17 @@ func instanceServerCreateRun(ctx context.Context, argsI interface{}) (i interfac
241259 }
242260
243261 // Validate root volume type and size.
244- if err := validateRootVolume (apiInstance , args . Zone , serverReq . Image , volumes ["0" ]); err != nil {
262+ if err := validateRootVolume (getImageResponse . Image . RootVolume . Size , volumes ["0" ]); err != nil {
245263 return nil , err
246264 }
247265
248266 // Validate total local volume sizes.
249- if err := validateLocalVolumeSizes (apiInstance , volumes , args .Zone , serverReq .CommercialType ); err != nil {
250- return nil , err
267+ if serverType != nil {
268+ if err := validateLocalVolumeSizes (volumes , serverType , serverReq .CommercialType ); err != nil {
269+ return nil , err
270+ }
271+ } else {
272+ logger .Warningf ("skip local volume size validation" )
251273 }
252274
253275 // Sanitize the volume map to respect API schemas
@@ -453,8 +475,22 @@ func buildVolumeTemplateFromUUID(api *instance.API, zone scw.Zone, volumeUUID st
453475 }, nil
454476}
455477
478+ func validateImageServerTypeCompatibility (image * instance.Image , serverType * instance.ServerType , CommercialType string ) error {
479+ if image .RootVolume .Size > serverType .VolumesConstraint .MaxSize {
480+ return fmt .Errorf ("image %s requires %s on root volume, but root volume is constrained between %s and %s on %s" ,
481+ image .ID ,
482+ humanize .Bytes (uint64 (image .RootVolume .Size )),
483+ humanize .Bytes (uint64 (serverType .VolumesConstraint .MinSize )),
484+ humanize .Bytes (uint64 (serverType .VolumesConstraint .MaxSize )),
485+ CommercialType ,
486+ )
487+ }
488+
489+ return nil
490+ }
491+
456492// validateLocalVolumeSizes validates the total size of local volumes.
457- func validateLocalVolumeSizes (api * instance. API , volumes map [string ]* instance.VolumeTemplate , zone scw. Zone , commercialType string ) error {
493+ func validateLocalVolumeSizes (volumes map [string ]* instance.VolumeTemplate , serverType * instance. ServerType , commercialType string ) error {
458494 // Calculate local volume total size.
459495 var localVolumeTotalSize scw.Size
460496 for _ , volume := range volumes {
@@ -463,44 +499,27 @@ func validateLocalVolumeSizes(api *instance.API, volumes map[string]*instance.Vo
463499 }
464500 }
465501
466- // Get server types.
467- serverTypesRes , err := api .ListServersTypes (& instance.ListServersTypesRequest {
468- Zone : zone ,
469- })
470- if err != nil {
471- // Ignore root volume size check.
472- logger .Warningf ("cannot get server types: %s" , err )
473- logger .Warningf ("skip local volume size validation" )
474- return nil
475- }
502+ volumeConstraint := serverType .VolumesConstraint
476503
477- // Validate total size.
478- if serverType , exists := serverTypesRes .Servers [commercialType ]; exists {
479- volumeConstraint := serverType .VolumesConstraint
504+ // If no root volume provided, count the default root volume size added by the API.
505+ if rootVolume := volumes ["0" ]; rootVolume == nil {
506+ localVolumeTotalSize += volumeConstraint .MinSize
507+ }
480508
481- // If no root volume provided, count the default root volume size added by the API.
482- if rootVolume := volumes ["0" ]; rootVolume == nil {
483- localVolumeTotalSize += volumeConstraint .MinSize
509+ if localVolumeTotalSize < volumeConstraint .MinSize || localVolumeTotalSize > volumeConstraint .MaxSize {
510+ min := humanize .Bytes (uint64 (volumeConstraint .MinSize ))
511+ if volumeConstraint .MinSize == volumeConstraint .MaxSize {
512+ return fmt .Errorf ("%s total local volume size must be equal to %s" , commercialType , min )
484513 }
485514
486- if localVolumeTotalSize < volumeConstraint .MinSize || localVolumeTotalSize > volumeConstraint .MaxSize {
487- min := humanize .Bytes (uint64 (volumeConstraint .MinSize ))
488- if volumeConstraint .MinSize == volumeConstraint .MaxSize {
489- return fmt .Errorf ("%s total local volume size must be equal to %s" , commercialType , min )
490- }
491-
492- max := humanize .Bytes (uint64 (volumeConstraint .MaxSize ))
493- return fmt .Errorf ("%s total local volume size must be between %s and %s" , commercialType , min , max )
494- }
495- } else {
496- logger .Warningf ("unrecognized server type: %s" , commercialType )
497- logger .Warningf ("skip local volume size validation" )
515+ max := humanize .Bytes (uint64 (volumeConstraint .MaxSize ))
516+ return fmt .Errorf ("%s total local volume size must be between %s and %s" , commercialType , min , max )
498517 }
499518
500519 return nil
501520}
502521
503- func validateRootVolume (api * instance. API , zone scw.Zone , image string , rootVolume * instance.VolumeTemplate ) error {
522+ func validateRootVolume (imageRequiredSize scw.Size , rootVolume * instance.VolumeTemplate ) error {
504523 if rootVolume == nil {
505524 return nil
506525 }
@@ -516,17 +535,8 @@ func validateRootVolume(api *instance.API, zone scw.Zone, image string, rootVolu
516535 }
517536 }
518537
519- res , err := api .GetImage (& instance.GetImageRequest {
520- Zone : zone ,
521- ImageID : image ,
522- })
523- if err != nil {
524- logger .Warningf ("cannot get image %s: %s" , image , err )
525- logger .Warningf ("skip root volume size validation" )
526- }
527-
528- if rootVolume .Size < res .Image .RootVolume .Size {
529- return fmt .Errorf ("first volume size must be at least %s for this image" , humanize .Bytes (uint64 (res .Image .RootVolume .Size )))
538+ if rootVolume .Size < imageRequiredSize {
539+ return fmt .Errorf ("first volume size must be at least %s for this image" , humanize .Bytes (uint64 (imageRequiredSize )))
530540 }
531541
532542 return nil
@@ -573,3 +583,22 @@ func instanceServerCreateImageAutoCompleteFunc(ctx context.Context, prefix strin
573583
574584 return suggestions
575585}
586+
587+ // getServeType is a util to get a instance.ServerType by its commercialType
588+ func getServeType (apiInstance * instance.API , zone scw.Zone , commercialType string ) * instance.ServerType {
589+ serverType := (* instance .ServerType )(nil )
590+
591+ serverTypesRes , err := apiInstance .ListServersTypes (& instance.ListServersTypesRequest {
592+ Zone : zone ,
593+ })
594+ if err != nil {
595+ logger .Warningf ("cannot get server types: %s" , err )
596+ } else {
597+ serverType = serverTypesRes .Servers [commercialType ]
598+ if serverType == nil {
599+ logger .Warningf ("unrecognized server type: %s" , commercialType )
600+ }
601+ }
602+
603+ return serverType
604+ }
0 commit comments