Skip to content

Commit 341c450

Browse files
committed
Add support to disable CAPZ components through a manager flag
1 parent df608bd commit 341c450

File tree

4 files changed

+158
-18
lines changed

4 files changed

+158
-18
lines changed

api/v1beta1/types.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1220,3 +1220,23 @@ const (
12201220
// AKSAssignedIdentityUserAssigned ...
12211221
AKSAssignedIdentityUserAssigned AKSAssignedIdentity = "UserAssigned"
12221222
)
1223+
1224+
// DisableComponent defines a component to be disabled in CAPZ such as a controller or webhook.
1225+
// +kubebuilder:validation:Enum=DisableASOSecretController;DisableAzureJSONMachineController
1226+
type DisableComponent string
1227+
1228+
// NOTE: when adding a new DisableComponent, please also add it to the ValidDisableableComponents map.
1229+
const (
1230+
// DisableASOController disables the ASOSecretController from being deployed.
1231+
DisableASOSecretController DisableComponent = "DisableASOSecretController"
1232+
1233+
// DisableAzureJSONMachineController disables the AzureJSONMachineController from being deployed.
1234+
DisableAzureJSONMachineController DisableComponent = "DisableAzureJSONMachineController"
1235+
)
1236+
1237+
// ValidDisableableComponents is a map of valid disableable components used to quickly validate whether a component is
1238+
// valid or not.
1239+
var ValidDisableableComponents = map[DisableComponent]struct{}{
1240+
DisableASOSecretController: {},
1241+
DisableAzureJSONMachineController: {},
1242+
}

main.go

Lines changed: 40 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ import (
6666
"sigs.k8s.io/cluster-api-provider-azure/feature"
6767
"sigs.k8s.io/cluster-api-provider-azure/pkg/coalescing"
6868
"sigs.k8s.io/cluster-api-provider-azure/pkg/ot"
69+
"sigs.k8s.io/cluster-api-provider-azure/util/components"
6970
"sigs.k8s.io/cluster-api-provider-azure/util/reconciler"
7071
"sigs.k8s.io/cluster-api-provider-azure/version"
7172
)
@@ -118,6 +119,7 @@ var (
118119
managerOptions = flags.ManagerOptions{}
119120
timeouts reconciler.Timeouts
120121
enableTracing bool
122+
disableControllersOrWebhooks []string
121123
)
122124

123125
// InitFlags initializes all command-line flags.
@@ -264,6 +266,12 @@ func InitFlags(fs *pflag.FlagSet) {
264266
"(Deprecated) Provide fully qualified GVK string to override default kubeadm config watch source, in the form of Kind.version.group (default: KubeadmConfig.v1beta1.bootstrap.cluster.x-k8s.io)",
265267
)
266268

269+
fs.StringSliceVar(&disableControllersOrWebhooks,
270+
"disable-controllers-or-webhooks",
271+
[]string{},
272+
"Comma-separated list of controllers or webhooks to disable. The list can contain the following values: DisableASOSecretController,DisableAzureJSONMachineController",
273+
)
274+
267275
flags.AddManagerOptions(fs, &managerOptions)
268276

269277
feature.MutableGates.AddFlag(fs)
@@ -306,6 +314,16 @@ func main() {
306314
}
307315
}
308316

317+
// Validate valid disable components were passed in the flag
318+
if len(disableControllersOrWebhooks) > 0 {
319+
for _, component := range disableControllersOrWebhooks {
320+
if ok := components.IsValidDisableComponent(component); !ok {
321+
setupLog.Error(fmt.Errorf("invalid disable-controllers-or-webhooks value %s", component), "Invalid argument")
322+
os.Exit(1)
323+
}
324+
}
325+
}
326+
309327
restConfig := ctrl.GetConfigOrDie()
310328
restConfig.UserAgent = "cluster-api-provider-azure-manager"
311329
mgr, err := ctrl.NewManager(restConfig, ctrl.Options{
@@ -418,26 +436,30 @@ func registerControllers(ctx context.Context, mgr manager.Manager) {
418436
os.Exit(1)
419437
}
420438

421-
if err := (&controllers.AzureJSONMachineReconciler{
422-
Client: mgr.GetClient(),
423-
Recorder: mgr.GetEventRecorderFor("azurejsonmachine-reconciler"),
424-
Timeouts: timeouts,
425-
WatchFilterValue: watchFilterValue,
426-
CredentialCache: credCache,
427-
}).SetupWithManager(ctx, mgr, controller.Options{MaxConcurrentReconciles: azureMachineConcurrency, SkipNameValidation: ptr.To(true)}); err != nil {
428-
setupLog.Error(err, "unable to create controller", "controller", "AzureJSONMachine")
429-
os.Exit(1)
439+
if !components.IsComponentDisabled(disableControllersOrWebhooks, infrav1.DisableAzureJSONMachineController) {
440+
if err := (&controllers.AzureJSONMachineReconciler{
441+
Client: mgr.GetClient(),
442+
Recorder: mgr.GetEventRecorderFor("azurejsonmachine-reconciler"),
443+
Timeouts: timeouts,
444+
WatchFilterValue: watchFilterValue,
445+
CredentialCache: credCache,
446+
}).SetupWithManager(ctx, mgr, controller.Options{MaxConcurrentReconciles: azureMachineConcurrency, SkipNameValidation: ptr.To(true)}); err != nil {
447+
setupLog.Error(err, "unable to create controller", "controller", "AzureJSONMachine")
448+
os.Exit(1)
449+
}
430450
}
431451

432-
if err := (&controllers.ASOSecretReconciler{
433-
Client: mgr.GetClient(),
434-
Recorder: mgr.GetEventRecorderFor("asosecret-reconciler"),
435-
Timeouts: timeouts,
436-
WatchFilterValue: watchFilterValue,
437-
CredentialCache: credCache,
438-
}).SetupWithManager(ctx, mgr, controller.Options{MaxConcurrentReconciles: azureClusterConcurrency}); err != nil {
439-
setupLog.Error(err, "unable to create controller", "controller", "ASOSecret")
440-
os.Exit(1)
452+
if !components.IsComponentDisabled(disableControllersOrWebhooks, infrav1.DisableASOSecretController) {
453+
if err := (&controllers.ASOSecretReconciler{
454+
Client: mgr.GetClient(),
455+
Recorder: mgr.GetEventRecorderFor("asosecret-reconciler"),
456+
Timeouts: timeouts,
457+
WatchFilterValue: watchFilterValue,
458+
CredentialCache: credCache,
459+
}).SetupWithManager(ctx, mgr, controller.Options{MaxConcurrentReconciles: azureClusterConcurrency}); err != nil {
460+
setupLog.Error(err, "unable to create controller", "controller", "ASOSecret")
461+
os.Exit(1)
462+
}
441463
}
442464

443465
// just use CAPI MachinePool feature flag rather than create a new one
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package components
2+
3+
import (
4+
"slices"
5+
6+
infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1"
7+
)
8+
9+
// IsValidDisableComponent validates if the provided value is a valid disable component by checking if the value exists
10+
// in the infrav1.ValidDisableableComponents map.
11+
func IsValidDisableComponent(value string) bool {
12+
_, ok := infrav1.ValidDisableableComponents[infrav1.DisableComponent(value)]
13+
return ok
14+
}
15+
16+
// IsComponentDisabled checks if the provided component is in the list of disabled components.
17+
func IsComponentDisabled(disabledComponents []string, component infrav1.DisableComponent) bool {
18+
return slices.Contains(disabledComponents, string(component))
19+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package components
2+
3+
import (
4+
"testing"
5+
6+
. "github.com/onsi/gomega"
7+
8+
infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1"
9+
)
10+
11+
func TestIsValidDisableComponent(t *testing.T) {
12+
g := NewWithT(t)
13+
14+
testCases := []struct {
15+
name string
16+
value string
17+
expected bool
18+
}{
19+
{
20+
name: "Valid component",
21+
value: string(infrav1.DisableASOSecretController),
22+
expected: true,
23+
},
24+
{
25+
name: "Invalid component",
26+
value: "InvalidComponent",
27+
expected: false,
28+
},
29+
{
30+
name: "Empty string",
31+
value: "",
32+
expected: false,
33+
},
34+
}
35+
36+
for _, tc := range testCases {
37+
t.Run(tc.name, func(t *testing.T) {
38+
result := IsValidDisableComponent(tc.value)
39+
g.Expect(result).To(Equal(tc.expected))
40+
})
41+
}
42+
}
43+
44+
func TestIsComponentDisabled(t *testing.T) {
45+
g := NewGomegaWithT(t)
46+
47+
testCases := []struct {
48+
name string
49+
disabledComponents []string
50+
component infrav1.DisableComponent
51+
expectedResult bool
52+
}{
53+
{
54+
name: "When DisableASOSecretController is in the list, expect true",
55+
disabledComponents: []string{"DisableASOSecretController", "component2"},
56+
component: infrav1.DisableASOSecretController,
57+
expectedResult: true,
58+
},
59+
{
60+
name: "When DisableASOSecretController is not in the list, expect false",
61+
disabledComponents: []string{"component", "component2"},
62+
component: infrav1.DisableASOSecretController,
63+
expectedResult: false,
64+
},
65+
{
66+
name: "When the list is empty, expect false",
67+
disabledComponents: []string{},
68+
component: infrav1.DisableComponent("component"),
69+
expectedResult: false,
70+
},
71+
}
72+
73+
for _, tc := range testCases {
74+
t.Run(tc.name, func(t *testing.T) {
75+
result := IsComponentDisabled(tc.disabledComponents, tc.component)
76+
g.Expect(result).To(Equal(tc.expectedResult))
77+
})
78+
}
79+
}

0 commit comments

Comments
 (0)