Skip to content

Commit 7594f25

Browse files
Added Validation and tests
1 parent 0569c67 commit 7594f25

File tree

2 files changed

+113
-1
lines changed

2 files changed

+113
-1
lines changed

pkg/cloud/azure/actuators/machine/reconciler.go

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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+
}

pkg/cloud/azure/actuators/machine/reconciler_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,3 +620,44 @@ func TestCreateDiagnosticsConfig(t *testing.T) {
620620
})
621621
}
622622
}
623+
624+
func TestValidateCapacityReservationGroupID(t *testing.T) {
625+
testCases := []struct {
626+
name string
627+
inputID string
628+
expectedError error
629+
}{
630+
{
631+
name: "validation for capacityReservationGroupID should return nil error if field input is valid",
632+
inputID: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroupName/providers/Microsoft.Compute/capacityReservationGroups/myCapacityReservationGroup",
633+
expectedError: nil,
634+
},
635+
{
636+
name: "validation for capacityReservationGroupID should return error if field input does not start with '/'",
637+
inputID: "subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroupName/providers/Microsoft.Compute/capacityReservationGroups/myCapacityReservationGroup",
638+
expectedError: errors.New("invalid resource ID: resource id 'subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroupName/providers/Microsoft.Compute/capacityReservationGroups/myCapacityReservationGroup' must start with '/'"),
639+
},
640+
{
641+
name: "validation for capacityReservationGroupID should return error if field input does not have field name subscriptions",
642+
inputID: "/subscripti/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroupName/providers/Microsoft.Compute/capacityReservationGroups/myCapacityReservationGroup",
643+
expectedError: errors.New("invalid resource ID: /subscripti/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroupName/providers/Microsoft.Compute/capacityReservationGroups/myCapacityReservationGroup"),
644+
},
645+
{
646+
name: "validation for capacityReservationGroupID should return error if field input is empty",
647+
inputID: "",
648+
expectedError: errors.New("invalid resource ID: id cannot be empty"),
649+
},
650+
}
651+
652+
for _, tc := range testCases {
653+
t.Run(tc.name, func(t *testing.T) {
654+
g := NewWithT(t)
655+
err := validateAzureCapacityReservationGroupID(tc.inputID)
656+
if tc.expectedError != nil {
657+
g.Expect(err).To(MatchError(tc.expectedError))
658+
} else {
659+
g.Expect(err).ToNot(HaveOccurred())
660+
}
661+
})
662+
}
663+
}

0 commit comments

Comments
 (0)