Skip to content

Commit b323c29

Browse files
Merge pull request #107 from anirudhAgniRedhat/anirudh/capacityReservationGroups
CFE-1050: Add support of capacity reservation group in openshift/machine-api-provider-azure
2 parents d6be293 + d63b3cf commit b323c29

File tree

435 files changed

+26030
-42963
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

435 files changed

+26030
-42963
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,6 @@ bin/
4545

4646
# vscode
4747
.vscode
48+
49+
# idea
50+
.idea

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ LD_FLAGS ?= -X $(REPO_PATH)/pkg/version.Raw=$(VERSION) -extldflags -static
2727
BUILD_IMAGE ?= registry.ci.openshift.org/openshift/release:golang-1.21
2828

2929
# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary.
30-
ENVTEST_K8S_VERSION = 1.29
30+
ENVTEST_K8S_VERSION = 1.29.1
3131

3232
PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST))))
3333
CONTROLLER_GEN = go run ${PROJECT_DIR}/vendor/sigs.k8s.io/controller-tools/cmd/controller-gen
@@ -115,7 +115,7 @@ build: ## build binaries
115115
.PHONY: test
116116
test: ## Run tests
117117
@echo -e "\033[32mTesting...\033[0m"
118-
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path --bin-dir $(PROJECT_DIR)/bin)" ./hack/ci-test.sh
118+
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path --bin-dir $(PROJECT_DIR)/bin --remote-bucket openshift-kubebuilder-tools)" ./hack/ci-test.sh
119119

120120
.PHONY: unit
121121
unit: # Run unit test

cmd/manager/main.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"time"
2525

2626
configv1 "github.com/openshift/api/config/v1"
27+
apifeatures "github.com/openshift/api/features"
2728
machinev1 "github.com/openshift/api/machine/v1beta1"
2829
configclient "github.com/openshift/client-go/config/clientset/versioned"
2930
configinformers "github.com/openshift/client-go/config/informers/externalversions"
@@ -161,7 +162,7 @@ func main() {
161162
klog.Fatalf("Failed to get feature gates: %v", err)
162163
}
163164

164-
azureWorkloadIdentityEnabled := featureGates.Enabled(configv1.FeatureGateAzureWorkloadIdentity)
165+
azureWorkloadIdentityEnabled := featureGates.Enabled(apifeatures.FeatureGateAzureWorkloadIdentity)
165166

166167
// Initialize machine actuator.
167168
machineActuator := actuator.NewActuator(actuator.ActuatorParams{

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ require (
1616
github.com/mitchellh/mapstructure v1.5.0
1717
github.com/onsi/ginkgo/v2 v2.14.0
1818
github.com/onsi/gomega v1.30.0
19-
github.com/openshift/api v0.0.0-20240124164020-e2ce40831f2e
19+
github.com/openshift/api v0.0.0-20240521141249-8af21b7ed3e3
2020
github.com/openshift/machine-api-operator v0.2.1-0.20240125175440-c9de8bda0dd1
2121
github.com/pkg/errors v0.9.1
2222
github.com/spf13/cobra v1.8.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,8 +300,8 @@ github.com/onsi/ginkgo/v2 v2.14.0 h1:vSmGj2Z5YPb9JwCWT6z6ihcUvDhuXLc3sJiqd3jMKAY
300300
github.com/onsi/ginkgo/v2 v2.14.0/go.mod h1:JkUdW7JkN0V6rFvsHcJ478egV3XH9NxpD27Hal/PhZw=
301301
github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
302302
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
303-
github.com/openshift/api v0.0.0-20240124164020-e2ce40831f2e h1:cxgCNo/R769CO23AK5TCh45H9SMUGZ8RukiF2/Qif3o=
304-
github.com/openshift/api v0.0.0-20240124164020-e2ce40831f2e/go.mod h1:CxgbWAlvu2iQB0UmKTtRu1YfepRg1/vJ64n2DlIEVz4=
303+
github.com/openshift/api v0.0.0-20240521141249-8af21b7ed3e3 h1:fxqjG8C/fU1UfUalZhNB01jqIQDlBsCVsFnWZ1V17Rg=
304+
github.com/openshift/api v0.0.0-20240521141249-8af21b7ed3e3/go.mod h1:CxgbWAlvu2iQB0UmKTtRu1YfepRg1/vJ64n2DlIEVz4=
305305
github.com/openshift/client-go v0.0.0-20240115204758-e6bf7d631d5e h1:qGjfKX8i0h4efMNEnhgTdxcdx6gwwOwhTfBJ20WFqA8=
306306
github.com/openshift/client-go v0.0.0-20240115204758-e6bf7d631d5e/go.mod h1:2am3qrggh9LlDCf/MDGzcFWMhdaushxFQi0+ZZDhdVk=
307307
github.com/openshift/library-go v0.0.0-20240116081341-964bcb3f545c h1:gLylEQQryG+A6nqWYIwE1wUzn1eFUmthjADvflMWKnM=

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

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ import (
4747
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
4848
"k8s.io/klog/v2"
4949
"k8s.io/utils/ptr"
50-
5150
"sigs.k8s.io/controller-runtime/pkg/client"
5251
)
5352

@@ -67,7 +66,13 @@ const (
6766
// MachineInstanceTypeLabelName as annotation name for a machine instance type
6867
MachineInstanceTypeLabelName = "machine.openshift.io/instance-type"
6968

70-
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"
7176
)
7277

7378
// Reconciler are list of services required by cluster actuator, easy to create a fake
@@ -681,6 +686,13 @@ func (s *Reconciler) createVirtualMachine(ctx context.Context, nicName, asName s
681686
vmSpec.ManagedIdentity = azure.GenerateManagedIdentityName(s.scope.SubscriptionID, s.scope.MachineConfig.ResourceGroup, s.scope.MachineConfig.ManagedIdentity)
682687
}
683688

689+
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+
}
693+
vmSpec.CapacityReservationGroupID = s.scope.MachineConfig.CapacityReservationGroupID
694+
}
695+
684696
userData, userDataErr := s.getCustomUserData()
685697
if userDataErr != nil {
686698
return fmt.Errorf("failed to get custom script data: %w", userDataErr)
@@ -857,3 +869,65 @@ func createDiagnosticsConfig(config *machinev1.AzureMachineProviderSpec) (*compu
857869
)
858870
}
859871
}
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+
}

pkg/cloud/azure/actuators/machineset/controller_suite_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ func TestReconciler(t *testing.T) {
5757
var _ = BeforeSuite(func() {
5858
testEnv = &envtest.Environment{
5959
CRDDirectoryPaths: []string{
60-
filepath.Join("..", "..", "..", "..", "..", "vendor", "github.com", "openshift", "api", "machine", "v1beta1"),
61-
filepath.Join("..", "..", "..", "..", "..", "vendor", "github.com", "openshift", "api", "config", "v1"),
60+
filepath.Join("..", "..", "..", "..", "..", "vendor", "github.com", "openshift", "api", "machine", "v1beta1", "zz_generated.crd-manifests"),
61+
filepath.Join("..", "..", "..", "..", "..", "vendor", "github.com", "openshift", "api", "config", "v1", "zz_generated.crd-manifests"),
6262
},
6363
}
6464

pkg/cloud/azure/services/virtualmachines/virtualmachines.go

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -69,24 +69,25 @@ const (
6969

7070
// Spec input specification for Get/CreateOrUpdate/Delete calls
7171
type Spec struct {
72-
Name string
73-
NICName string
74-
SSHKeyData string
75-
Size string
76-
Zone string
77-
Image machinev1.Image
78-
OSDisk machinev1.OSDisk
79-
DataDisks []machinev1.DataDisk
80-
CustomData string
81-
ManagedIdentity string
82-
Tags map[string]*string
83-
Priority compute.VirtualMachinePriorityTypes
84-
EvictionPolicy compute.VirtualMachineEvictionPolicyTypes
85-
BillingProfile *compute.BillingProfile
86-
SecurityProfile *machinev1.SecurityProfile
87-
DiagnosticsProfile *compute.DiagnosticsProfile
88-
UltraSSDCapability machinev1.AzureUltraSSDCapabilityState
89-
AvailabilitySetName string
72+
Name string
73+
NICName string
74+
SSHKeyData string
75+
Size string
76+
Zone string
77+
Image machinev1.Image
78+
OSDisk machinev1.OSDisk
79+
DataDisks []machinev1.DataDisk
80+
CustomData string
81+
ManagedIdentity string
82+
Tags map[string]*string
83+
Priority compute.VirtualMachinePriorityTypes
84+
EvictionPolicy compute.VirtualMachineEvictionPolicyTypes
85+
BillingProfile *compute.BillingProfile
86+
SecurityProfile *machinev1.SecurityProfile
87+
DiagnosticsProfile *compute.DiagnosticsProfile
88+
UltraSSDCapability machinev1.AzureUltraSSDCapabilityState
89+
AvailabilitySetName string
90+
CapacityReservationGroupID string
9091
}
9192

9293
// Get provides information about a virtual network.
@@ -336,6 +337,15 @@ func (s *Service) deriveVirtualMachineParameters(vmSpec *Spec, nic network.Inter
336337
}
337338
}
338339

340+
// configure capacity reservation ID
341+
if vmSpec.CapacityReservationGroupID != "" {
342+
virtualMachine.VirtualMachineProperties.CapacityReservation = &compute.CapacityReservationProfile{
343+
CapacityReservationGroup: &compute.SubResource{
344+
ID: &vmSpec.CapacityReservationGroupID,
345+
},
346+
}
347+
}
348+
339349
return virtualMachine, nil
340350
}
341351

pkg/cloud/azure/services/virtualmachines/virtualmachines_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,28 @@ func TestDeriveVirtualMachineParameters(t *testing.T) {
713713
},
714714
expectedError: nil,
715715
},
716+
{
717+
name: "capacity reservation group ID should be configured if the string is non empty",
718+
updateSpec: func(vmSpec *Spec) {
719+
vmSpec.Name = "testvm"
720+
vmSpec.CapacityReservationGroupID = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroupName/providers/Microsoft.Compute/capacityReservationGroups/myCapacityReservationGroup"
721+
},
722+
validate: func(g *WithT, vm *compute.VirtualMachine) {
723+
g.Expect(*vm.VirtualMachineProperties.CapacityReservation.CapacityReservationGroup.ID).Should(Equal("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroupName/providers/Microsoft.Compute/capacityReservationGroups/myCapacityReservationGroup"))
724+
},
725+
expectedError: nil,
726+
},
727+
{
728+
name: "capacity reservation group ID should be nil if the string is empty",
729+
updateSpec: func(vmSpec *Spec) {
730+
vmSpec.Name = "testvm"
731+
vmSpec.CapacityReservationGroupID = ""
732+
},
733+
validate: func(g *WithT, vm *compute.VirtualMachine) {
734+
g.Expect(vm.VirtualMachineProperties.CapacityReservation).To(BeNil())
735+
},
736+
expectedError: nil,
737+
},
716738
}
717739

718740
for _, tc := range testCases {

0 commit comments

Comments
 (0)