@@ -66,7 +66,13 @@ const (
6666 // MachineInstanceTypeLabelName as annotation name for a machine instance type
6767 MachineInstanceTypeLabelName = "machine.openshift.io/instance-type"
6868
69- MachineSetLabelName = "machine.openshift.io/cluster-api-machineset"
69+ MachineSetLabelName = "machine.openshift.io/cluster-api-machineset"
70+ azureProviderIDPrefix = "azure://"
71+ azureProvidersKey = "providers"
72+ azureSubscriptionsKey = "subscriptions"
73+ azureResourceGroupsLowerKey = "resourcegroups"
74+ azureLocationsKey = "locations"
75+ azureBuiltInResourceNamespace = "Microsoft.Resources"
7076)
7177
7278// Reconciler are list of services required by cluster actuator, easy to create a fake
@@ -681,6 +687,9 @@ func (s *Reconciler) createVirtualMachine(ctx context.Context, nicName, asName s
681687 }
682688
683689 if s .scope .MachineConfig .CapacityReservationGroupID != "" {
690+ if err = validateAzureCapacityReservationGroupID (s .scope .MachineConfig .CapacityReservationGroupID ); err != nil {
691+ return fmt .Errorf ("failed to validate capacityReservationGroupID: %w" , err )
692+ }
684693 vmSpec .CapacityReservationGroupID = s .scope .MachineConfig .CapacityReservationGroupID
685694 }
686695
@@ -860,3 +869,65 @@ func createDiagnosticsConfig(config *machinev1.AzureMachineProviderSpec) (*compu
860869 )
861870 }
862871}
872+
873+ func validateAzureCapacityReservationGroupID (capacityReservationGroupID string ) error {
874+ id := strings .TrimPrefix (capacityReservationGroupID , azureProviderIDPrefix )
875+ err := parseAzureResourceID (id )
876+ if err != nil {
877+ return err
878+ }
879+ return nil
880+ }
881+
882+ // parseAzureResourceID parses a string to an instance of ResourceID
883+ func parseAzureResourceID (id string ) error {
884+ if len (id ) == 0 {
885+ return fmt .Errorf ("invalid resource ID: id cannot be empty" )
886+ }
887+ if ! strings .HasPrefix (id , "/" ) {
888+ return fmt .Errorf ("invalid resource ID: resource id '%s' must start with '/'" , id )
889+ }
890+ parts := splitStringAndOmitEmpty (id , "/" )
891+
892+ if len (parts ) < 2 {
893+ return fmt .Errorf ("invalid resource ID: %s" , id )
894+ }
895+ if ! strings .EqualFold (parts [0 ], azureSubscriptionsKey ) && ! strings .EqualFold (parts [0 ], azureProvidersKey ) {
896+ return fmt .Errorf ("invalid resource ID: %s" , id )
897+ }
898+ return appendNextAzureResourceIDValidation (parts , id )
899+ }
900+
901+ func splitStringAndOmitEmpty (v , sep string ) []string {
902+ r := make ([]string , 0 )
903+ for _ , s := range strings .Split (v , sep ) {
904+ if len (s ) == 0 {
905+ continue
906+ }
907+ r = append (r , s )
908+ }
909+ return r
910+ }
911+
912+ func appendNextAzureResourceIDValidation (parts []string , id string ) error {
913+ if len (parts ) == 0 {
914+ return nil
915+ }
916+ if len (parts ) == 1 {
917+ // subscriptions and resourceGroups are not valid ids without their names
918+ if strings .EqualFold (parts [0 ], azureSubscriptionsKey ) || strings .EqualFold (parts [0 ], azureResourceGroupsLowerKey ) {
919+ return fmt .Errorf ("invalid resource ID: %s" , id )
920+ }
921+ return nil
922+ }
923+ if strings .EqualFold (parts [0 ], azureProvidersKey ) && (len (parts ) == 2 || strings .EqualFold (parts [2 ], azureProvidersKey )) {
924+ return appendNextAzureResourceIDValidation (parts [2 :], id )
925+ }
926+ if len (parts ) > 3 && strings .EqualFold (parts [0 ], azureProvidersKey ) {
927+ return appendNextAzureResourceIDValidation (parts [4 :], id )
928+ }
929+ if len (parts ) > 1 && ! strings .EqualFold (parts [0 ], azureProvidersKey ) {
930+ return appendNextAzureResourceIDValidation (parts [2 :], id )
931+ }
932+ return fmt .Errorf ("invalid resource ID: %s" , id )
933+ }
0 commit comments