Skip to content

Commit 3c61283

Browse files
authored
Merge pull request #4792 from RadekManak/disableExtensionOperations
Add DisableExtensionOperations field
2 parents 76240e7 + 087217a commit 3c61283

12 files changed

+172
-3
lines changed

api/v1beta1/azuremachine_types.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,12 @@ type AzureMachineSpec struct {
132132
// +optional
133133
DNSServers []string `json:"dnsServers,omitempty"`
134134

135+
// DisableExtensionOperations specifies whether extension operations should be disabled on the virtual machine.
136+
// Use this setting only if VMExtensions are not supported by your image, as it disables CAPZ bootstrapping extension used for detecting Kubernetes bootstrap failure.
137+
// This may only be set to True when no extensions are configured on the virtual machine.
138+
// +optional
139+
DisableExtensionOperations *bool `json:"disableExtensionOperations,omitempty"`
140+
135141
// VMExtensions specifies a list of extensions to be added to the virtual machine.
136142
// +optional
137143
VMExtensions []VMExtension `json:"vmExtensions,omitempty"`

api/v1beta1/azuremachine_validation.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/google/uuid"
2525
"golang.org/x/crypto/ssh"
2626
"k8s.io/apimachinery/pkg/util/validation/field"
27+
"k8s.io/utils/ptr"
2728
azureutil "sigs.k8s.io/cluster-api-provider-azure/util/azure"
2829
)
2930

@@ -71,6 +72,10 @@ func ValidateAzureMachineSpec(spec AzureMachineSpec) field.ErrorList {
7172
allErrs = append(allErrs, errs...)
7273
}
7374

75+
if errs := ValidateVMExtensions(spec.DisableExtensionOperations, spec.VMExtensions, field.NewPath("vmExtensions")); len(errs) > 0 {
76+
allErrs = append(allErrs, errs...)
77+
}
78+
7479
return allErrs
7580
}
7681

@@ -471,3 +476,14 @@ func ValidateCapacityReservationGroupID(capacityReservationGroupID *string, fldP
471476

472477
return allErrs
473478
}
479+
480+
// ValidateVMExtensions validates the VMExtensions spec.
481+
func ValidateVMExtensions(disableExtensionOperations *bool, vmExtensions []VMExtension, fldPath *field.Path) field.ErrorList {
482+
allErrs := field.ErrorList{}
483+
484+
if ptr.Deref(disableExtensionOperations, false) && len(vmExtensions) > 0 {
485+
allErrs = append(allErrs, field.Forbidden(field.NewPath("AzureMachineTemplate", "spec", "template", "spec", "VMExtensions"), "VMExtensions must be empty when DisableExtensionOperations is true"))
486+
}
487+
488+
return allErrs
489+
}

api/v1beta1/azuremachine_webhook.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,13 @@ func (mw *azureMachineWebhook) ValidateUpdate(ctx context.Context, oldObj, newOb
213213
allErrs = append(allErrs, err)
214214
}
215215

216+
if err := webhookutils.ValidateImmutable(
217+
field.NewPath("spec", "disableExtensionOperations"),
218+
old.Spec.DisableExtensionOperations,
219+
m.Spec.DisableExtensionOperations); err != nil {
220+
allErrs = append(allErrs, err)
221+
}
222+
216223
if len(allErrs) == 0 {
217224
return nil, nil
218225
}

api/v1beta1/azuremachine_webhook_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,16 @@ func TestAzureMachine_ValidateCreate(t *testing.T) {
230230
machine: createMachineWithCapacityReservaionGroupID("invalid-capacity-group-id"),
231231
wantErr: true,
232232
},
233+
{
234+
name: "azuremachine with DisableExtensionOperations true and without VMExtensions",
235+
machine: createMachineWithDisableExtenionOperations(),
236+
wantErr: false,
237+
},
238+
{
239+
name: "azuremachine with DisableExtensionOperations true and with VMExtension",
240+
machine: createMachineWithDisableExtenionOperationsAndHasExtension(),
241+
wantErr: true,
242+
},
233243
}
234244
for _, tc := range tests {
235245
t.Run(tc.name, func(t *testing.T) {
@@ -778,6 +788,34 @@ func TestAzureMachine_ValidateUpdate(t *testing.T) {
778788
},
779789
wantErr: true,
780790
},
791+
{
792+
name: "invalidTest: azuremachine.spec.disableExtensionOperations is immutable",
793+
oldMachine: &AzureMachine{
794+
Spec: AzureMachineSpec{
795+
DisableExtensionOperations: ptr.To(true),
796+
},
797+
},
798+
newMachine: &AzureMachine{
799+
Spec: AzureMachineSpec{
800+
DisableExtensionOperations: ptr.To(false),
801+
},
802+
},
803+
wantErr: true,
804+
},
805+
{
806+
name: "validTest: azuremachine.spec.disableExtensionOperations is immutable",
807+
oldMachine: &AzureMachine{
808+
Spec: AzureMachineSpec{
809+
DisableExtensionOperations: ptr.To(true),
810+
},
811+
},
812+
newMachine: &AzureMachine{
813+
Spec: AzureMachineSpec{
814+
DisableExtensionOperations: ptr.To(true),
815+
},
816+
},
817+
wantErr: false,
818+
},
781819
{
782820
name: "validTest: azuremachine.spec.networkInterfaces is immutable",
783821
oldMachine: &AzureMachine{
@@ -1154,3 +1192,28 @@ func createMachineWithCapacityReservaionGroupID(capacityReservationGroupID strin
11541192
},
11551193
}
11561194
}
1195+
1196+
func createMachineWithDisableExtenionOperationsAndHasExtension() *AzureMachine {
1197+
return &AzureMachine{
1198+
Spec: AzureMachineSpec{
1199+
SSHPublicKey: validSSHPublicKey,
1200+
OSDisk: validOSDisk,
1201+
DisableExtensionOperations: ptr.To(true),
1202+
VMExtensions: []VMExtension{{
1203+
Name: "test-extension",
1204+
Publisher: "test-publiher",
1205+
Version: "v0.0.1-test",
1206+
}},
1207+
},
1208+
}
1209+
}
1210+
1211+
func createMachineWithDisableExtenionOperations() *AzureMachine {
1212+
return &AzureMachine{
1213+
Spec: AzureMachineSpec{
1214+
SSHPublicKey: validSSHPublicKey,
1215+
OSDisk: validOSDisk,
1216+
DisableExtensionOperations: ptr.To(true),
1217+
},
1218+
}
1219+
}

api/v1beta1/azuremachinetemplate_webhook.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
apierrors "k8s.io/apimachinery/pkg/api/errors"
2525
"k8s.io/apimachinery/pkg/runtime"
2626
"k8s.io/apimachinery/pkg/util/validation/field"
27+
"k8s.io/utils/ptr"
2728
"sigs.k8s.io/cluster-api/util/topology"
2829
ctrl "sigs.k8s.io/controller-runtime"
2930
"sigs.k8s.io/controller-runtime/pkg/webhook"
@@ -85,6 +86,10 @@ func (r *AzureMachineTemplate) ValidateCreate(ctx context.Context, obj runtime.O
8586
}
8687
}
8788

89+
if ptr.Deref(r.Spec.Template.Spec.DisableExtensionOperations, false) && len(r.Spec.Template.Spec.VMExtensions) > 0 {
90+
allErrs = append(allErrs, field.Forbidden(field.NewPath("AzureMachineTemplate", "spec", "template", "spec", "VMExtensions"), "VMExtensions must be empty when DisableExtensionOperations is true"))
91+
}
92+
8893
if len(allErrs) == 0 {
8994
return nil, nil
9095
}

api/v1beta1/azuremachinetemplate_webhook_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,16 @@ func TestAzureMachineTemplate_ValidateCreate(t *testing.T) {
143143
machineTemplate: createAzureMachineTemplateFromMachine(createMachineWithRoleAssignmentName()),
144144
wantErr: true,
145145
},
146+
{
147+
name: "azuremachinetemplate with DisableExtensionOperations true and without VMExtensions",
148+
machineTemplate: createAzureMachineTemplateFromMachine(createMachineWithDisableExtenionOperations()),
149+
wantErr: false,
150+
},
151+
{
152+
name: "azuremachinetempalte with DisableExtensionOperations true and with VMExtension",
153+
machineTemplate: createAzureMachineTemplateFromMachine(createMachineWithDisableExtenionOperationsAndHasExtension()),
154+
wantErr: true,
155+
},
146156
{
147157
name: "azuremachinetemplate without RoleAssignmentName",
148158
machineTemplate: createAzureMachineTemplateFromMachine(createMachineWithoutRoleAssignmentName()),

api/v1beta1/zz_generated.deepcopy.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

azure/scope/machine.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ func (m *MachineScope) VMSpec() azure.ResourceSpecGetter {
178178
SpotVMOptions: m.AzureMachine.Spec.SpotVMOptions,
179179
SecurityProfile: m.AzureMachine.Spec.SecurityProfile,
180180
DiagnosticsProfile: m.AzureMachine.Spec.Diagnostics,
181+
DisableExtensionOperations: ptr.Deref(m.AzureMachine.Spec.DisableExtensionOperations, false),
181182
AdditionalTags: m.AdditionalTags(),
182183
AdditionalCapabilities: m.AzureMachine.Spec.AdditionalCapabilities,
183184
CapacityReservationGroupID: m.GetCapacityReservationGroupID(),
@@ -374,6 +375,10 @@ func (m *MachineScope) HasSystemAssignedIdentity() bool {
374375

375376
// VMExtensionSpecs returns the VM extension specs.
376377
func (m *MachineScope) VMExtensionSpecs() []azure.ResourceSpecGetter {
378+
if ptr.Deref(m.AzureMachine.Spec.DisableExtensionOperations, false) {
379+
return []azure.ResourceSpecGetter{}
380+
}
381+
377382
var extensionSpecs = []azure.ResourceSpecGetter{}
378383
for _, extension := range m.AzureMachine.Spec.VMExtensions {
379384
extensionSpecs = append(extensionSpecs, &vmextensions.VMExtensionSpec{

azure/scope/machine_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,44 @@ func TestMachineScope_VMExtensionSpecs(t *testing.T) {
598598
},
599599
},
600600
},
601+
{
602+
name: "If OS type is Linux and cloud is AzurePublicCloud and DisableExtensionOperations is true, it returns empty",
603+
machineScope: MachineScope{
604+
Machine: &clusterv1.Machine{},
605+
AzureMachine: &infrav1.AzureMachine{
606+
ObjectMeta: metav1.ObjectMeta{
607+
Name: "machine-name",
608+
},
609+
Spec: infrav1.AzureMachineSpec{
610+
DisableExtensionOperations: ptr.To(true),
611+
OSDisk: infrav1.OSDisk{
612+
OSType: "Linux",
613+
},
614+
},
615+
},
616+
ClusterScoper: &ClusterScope{
617+
AzureClients: AzureClients{
618+
EnvironmentSettings: auth.EnvironmentSettings{
619+
Environment: azureautorest.Environment{
620+
Name: azureautorest.PublicCloud.Name,
621+
},
622+
},
623+
},
624+
AzureCluster: &infrav1.AzureCluster{
625+
Spec: infrav1.AzureClusterSpec{
626+
ResourceGroup: "my-rg",
627+
AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
628+
Location: "westus",
629+
},
630+
},
631+
},
632+
},
633+
cache: &MachineCache{
634+
VMSKU: resourceskus.SKU{},
635+
},
636+
},
637+
want: []azure.ResourceSpecGetter{},
638+
},
601639
{
602640
name: "If OS type is Linux and cloud is not AzurePublicCloud, it returns empty",
603641
machineScope: MachineScope{

azure/services/virtualmachines/spec.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ type VMSpec struct {
5353
AdditionalTags infrav1.Tags
5454
AdditionalCapabilities *infrav1.AdditionalCapabilities
5555
DiagnosticsProfile *infrav1.Diagnostics
56+
DisableExtensionOperations bool
5657
CapacityReservationGroupID string
5758
SKU resourceskus.SKU
5859
Image *infrav1.Image
@@ -263,9 +264,10 @@ func (s *VMSpec) generateOSProfile() (*armcompute.OSProfile, error) {
263264
}
264265

265266
osProfile := &armcompute.OSProfile{
266-
ComputerName: ptr.To(s.Name),
267-
AdminUsername: ptr.To(azure.DefaultUserName),
268-
CustomData: ptr.To(s.BootstrapData),
267+
ComputerName: ptr.To(s.Name),
268+
AdminUsername: ptr.To(azure.DefaultUserName),
269+
CustomData: ptr.To(s.BootstrapData),
270+
AllowExtensionOperations: ptr.To(!s.DisableExtensionOperations),
269271
}
270272

271273
switch s.OSDisk.OSType {

0 commit comments

Comments
 (0)