@@ -15,6 +15,7 @@ package main
15
15
16
16
import (
17
17
"context"
18
+ "errors"
18
19
"flag"
19
20
"fmt"
20
21
"os"
@@ -49,8 +50,11 @@ import (
49
50
crwebhook "sigs.k8s.io/controller-runtime/pkg/webhook"
50
51
51
52
configv1 "github.com/openshift/api/config/v1"
53
+ "github.com/openshift/api/features"
52
54
mapiv1 "github.com/openshift/api/machine/v1"
53
55
mapiv1beta1 "github.com/openshift/api/machine/v1beta1"
56
+ configv1client "github.com/openshift/client-go/config/clientset/versioned"
57
+ configinformers "github.com/openshift/client-go/config/informers/externalversions"
54
58
"github.com/openshift/cluster-capi-operator/pkg/controllers"
55
59
"github.com/openshift/cluster-capi-operator/pkg/controllers/capiinstaller"
56
60
"github.com/openshift/cluster-capi-operator/pkg/controllers/clusteroperator"
@@ -61,12 +65,17 @@ import (
61
65
"github.com/openshift/cluster-capi-operator/pkg/operatorstatus"
62
66
"github.com/openshift/cluster-capi-operator/pkg/util"
63
67
"github.com/openshift/cluster-capi-operator/pkg/webhook"
68
+ featuregates "github.com/openshift/library-go/pkg/operator/configobserver/featuregates"
69
+ "github.com/openshift/library-go/pkg/operator/events"
64
70
)
65
71
66
72
const (
67
73
defaultImagesLocation = "./dev-images.json"
68
74
)
69
75
76
+ // errTimedOutWaitingForFeatureGates is returned when the feature gates are not initialized within the timeout.
77
+ var errTimedOutWaitingForFeatureGates = errors .New ("timed out waiting for feature gates to be initialized" )
78
+
70
79
func initScheme (scheme * runtime.Scheme ) {
71
80
utilruntime .Must (clientgoscheme .AddToScheme (scheme ))
72
81
utilruntime .Must (configv1 .AddToScheme (scheme ))
@@ -226,7 +235,22 @@ func main() {
226
235
os .Exit (1 )
227
236
}
228
237
229
- setupPlatformReconcilers (mgr , infra , platform , containerImages , applyClient , apiextensionsClient , * managedNamespace , * sourceNamespace )
238
+ featureGateAccessor , err := getFeatureGates (mgr )
239
+ if err != nil {
240
+ klog .Error (err , "unable to get feature gates" )
241
+ os .Exit (1 )
242
+ }
243
+
244
+ currentFeatureGates , err := featureGateAccessor .CurrentFeatureGates ()
245
+ if err != nil {
246
+ klog .Error (err , "unable to get current feature gates" )
247
+ os .Exit (1 )
248
+ }
249
+
250
+ if currentFeatureGates .Enabled (features .FeatureGateMachineAPIMigration ) {
251
+ }
252
+
253
+ setupPlatformReconcilers (mgr , infra , platform , containerImages , applyClient , apiextensionsClient , currentFeatureGates , * managedNamespace , * sourceNamespace )
230
254
231
255
// +kubebuilder:scaffold:builder
232
256
@@ -248,57 +272,60 @@ func main() {
248
272
}
249
273
}
250
274
251
- func getClusterOperatorStatusClient (mgr manager.Manager , controller string , managedNamespace string ) operatorstatus.ClusterOperatorStatusClient {
275
+ func getClusterOperatorStatusClient (mgr manager.Manager , currentFeatureGates featuregates. FeatureGate , controller string , managedNamespace string ) operatorstatus.ClusterOperatorStatusClient {
252
276
return operatorstatus.ClusterOperatorStatusClient {
253
277
Client : mgr .GetClient (),
254
278
Recorder : mgr .GetEventRecorderFor (controller ),
255
279
ReleaseVersion : util .GetReleaseVersion (),
256
280
ManagedNamespace : managedNamespace ,
281
+ FeatureGates : currentFeatureGates ,
257
282
}
258
283
}
259
284
260
- func setupPlatformReconcilers (mgr manager.Manager , infra * configv1.Infrastructure , platform configv1.PlatformType , containerImages map [string ]string , applyClient * kubernetes.Clientset , apiextensionsClient * apiextensionsclient.Clientset , managedNamespace , sourceNamespace string ) {
285
+ func setupPlatformReconcilers (mgr manager.Manager , infra * configv1.Infrastructure , platform configv1.PlatformType , containerImages map [string ]string , applyClient * kubernetes.Clientset ,
286
+ apiextensionsClient * apiextensionsclient.Clientset , currentFeatureGates featuregates.FeatureGate , managedNamespace , sourceNamespace string ) {
261
287
// Only setup reconcile controllers and webhooks when the platform is supported.
262
288
// This avoids unnecessary CAPI providers discovery, installs and reconciles when the platform is not supported.
263
289
switch platform {
264
290
case configv1 .AWSPlatformType :
265
- setupReconcilers (mgr , infra , platform , & awsv1.AWSCluster {}, containerImages , applyClient , apiextensionsClient , managedNamespace , sourceNamespace )
291
+ setupReconcilers (mgr , infra , platform , & awsv1.AWSCluster {}, containerImages , applyClient , apiextensionsClient , currentFeatureGates , managedNamespace , sourceNamespace )
266
292
setupWebhooks (mgr )
267
293
case configv1 .GCPPlatformType :
268
- setupReconcilers (mgr , infra , platform , & gcpv1.GCPCluster {}, containerImages , applyClient , apiextensionsClient , managedNamespace , sourceNamespace )
294
+ setupReconcilers (mgr , infra , platform , & gcpv1.GCPCluster {}, containerImages , applyClient , apiextensionsClient , currentFeatureGates , managedNamespace , sourceNamespace )
269
295
setupWebhooks (mgr )
270
296
case configv1 .AzurePlatformType :
271
297
azureCloudEnvironment := getAzureCloudEnvironment (infra .Status .PlatformStatus )
272
298
if azureCloudEnvironment == configv1 .AzureStackCloud {
273
299
klog .Infof ("Detected Azure Cloud Environment %q on platform %q is not supported, skipping capi controllers setup" , azureCloudEnvironment , platform )
274
- setupClusterOperatorController (mgr , managedNamespace , true )
300
+ setupClusterOperatorController (mgr , managedNamespace , currentFeatureGates , true )
275
301
} else {
276
302
// The ClusterOperator Controller must run in all cases.
277
- setupReconcilers (mgr , infra , platform , & azurev1.AzureCluster {}, containerImages , applyClient , apiextensionsClient , managedNamespace , sourceNamespace )
303
+ setupReconcilers (mgr , infra , platform , & azurev1.AzureCluster {}, containerImages , applyClient , apiextensionsClient , currentFeatureGates , managedNamespace , sourceNamespace )
278
304
setupWebhooks (mgr )
279
305
}
280
306
case configv1 .PowerVSPlatformType :
281
- setupReconcilers (mgr , infra , platform , & ibmpowervsv1.IBMPowerVSCluster {}, containerImages , applyClient , apiextensionsClient , managedNamespace , sourceNamespace )
307
+ setupReconcilers (mgr , infra , platform , & ibmpowervsv1.IBMPowerVSCluster {}, containerImages , applyClient , apiextensionsClient , currentFeatureGates , managedNamespace , sourceNamespace )
282
308
setupWebhooks (mgr )
283
309
case configv1 .VSpherePlatformType :
284
- setupReconcilers (mgr , infra , platform , & vspherev1.VSphereCluster {}, containerImages , applyClient , apiextensionsClient , managedNamespace , sourceNamespace )
310
+ setupReconcilers (mgr , infra , platform , & vspherev1.VSphereCluster {}, containerImages , applyClient , apiextensionsClient , currentFeatureGates , managedNamespace , sourceNamespace )
285
311
setupWebhooks (mgr )
286
312
case configv1 .OpenStackPlatformType :
287
- setupReconcilers (mgr , infra , platform , & openstackv1.OpenStackCluster {}, containerImages , applyClient , apiextensionsClient , managedNamespace , sourceNamespace )
313
+ setupReconcilers (mgr , infra , platform , & openstackv1.OpenStackCluster {}, containerImages , applyClient , apiextensionsClient , currentFeatureGates , managedNamespace , sourceNamespace )
288
314
setupWebhooks (mgr )
289
315
default :
290
316
klog .Infof ("Detected platform %q is not supported, skipping capi controllers setup" , platform )
291
317
// The ClusterOperator Controller must run under all circumstances as it manages the ClusterOperator object for this operator.
292
- setupClusterOperatorController (mgr , managedNamespace , true )
318
+ setupClusterOperatorController (mgr , managedNamespace , currentFeatureGates , true )
293
319
}
294
320
}
295
321
296
- func setupReconcilers (mgr manager.Manager , infra * configv1.Infrastructure , platform configv1.PlatformType , infraClusterObject client.Object , containerImages map [string ]string , applyClient * kubernetes.Clientset , apiextensionsClient * apiextensionsclient.Clientset , managedNamespace , sourceNamespace string ) {
322
+ func setupReconcilers (mgr manager.Manager , infra * configv1.Infrastructure , platform configv1.PlatformType , infraClusterObject client.Object , containerImages map [string ]string ,
323
+ applyClient * kubernetes.Clientset , apiextensionsClient * apiextensionsclient.Clientset , currentFeatureGates featuregates.FeatureGate , managedNamespace , sourceNamespace string ) {
297
324
// The ClusterOperator Controller must run under all circumstances as it manages the ClusterOperator object for this operator.
298
- setupClusterOperatorController (mgr , managedNamespace , false )
325
+ setupClusterOperatorController (mgr , managedNamespace , currentFeatureGates , false )
299
326
300
327
if err := (& corecluster.CoreClusterController {
301
- ClusterOperatorStatusClient : getClusterOperatorStatusClient (mgr , "cluster-capi-operator-cluster-resource-controller" , managedNamespace ),
328
+ ClusterOperatorStatusClient : getClusterOperatorStatusClient (mgr , currentFeatureGates , "cluster-capi-operator-cluster-resource-controller" , managedNamespace ),
302
329
Cluster : & clusterv1.Cluster {},
303
330
Platform : platform ,
304
331
Infra : infra ,
@@ -308,7 +335,7 @@ func setupReconcilers(mgr manager.Manager, infra *configv1.Infrastructure, platf
308
335
}
309
336
310
337
if err := (& secretsync.SecretSyncController {
311
- ClusterOperatorStatusClient : getClusterOperatorStatusClient (mgr , "cluster-capi-operator-secret-sync-controller" , managedNamespace ),
338
+ ClusterOperatorStatusClient : getClusterOperatorStatusClient (mgr , currentFeatureGates , "cluster-capi-operator-secret-sync-controller" , managedNamespace ),
312
339
Scheme : mgr .GetScheme (),
313
340
SourceNamespace : sourceNamespace ,
314
341
}).SetupWithManager (mgr ); err != nil {
@@ -317,7 +344,7 @@ func setupReconcilers(mgr manager.Manager, infra *configv1.Infrastructure, platf
317
344
}
318
345
319
346
if err := (& kubeconfig.KubeconfigController {
320
- ClusterOperatorStatusClient : getClusterOperatorStatusClient (mgr , "cluster-capi-operator-kubeconfig-controller" , managedNamespace ),
347
+ ClusterOperatorStatusClient : getClusterOperatorStatusClient (mgr , currentFeatureGates , "cluster-capi-operator-kubeconfig-controller" , managedNamespace ),
321
348
Scheme : mgr .GetScheme (),
322
349
RestCfg : mgr .GetConfig (),
323
350
}).SetupWithManager (mgr ); err != nil {
@@ -326,7 +353,7 @@ func setupReconcilers(mgr manager.Manager, infra *configv1.Infrastructure, platf
326
353
}
327
354
328
355
if err := (& capiinstaller.CapiInstallerController {
329
- ClusterOperatorStatusClient : getClusterOperatorStatusClient (mgr , "cluster-capi-operator-capi-installer-controller" , managedNamespace ),
356
+ ClusterOperatorStatusClient : getClusterOperatorStatusClient (mgr , currentFeatureGates , "cluster-capi-operator-capi-installer-controller" , managedNamespace ),
330
357
Scheme : mgr .GetScheme (),
331
358
Images : containerImages ,
332
359
RestCfg : mgr .GetConfig (),
@@ -339,7 +366,7 @@ func setupReconcilers(mgr manager.Manager, infra *configv1.Infrastructure, platf
339
366
}
340
367
341
368
if err := (& infracluster.InfraClusterController {
342
- ClusterOperatorStatusClient : getClusterOperatorStatusClient (mgr , "cluster-capi-operator-infracluster-controller" , managedNamespace ),
369
+ ClusterOperatorStatusClient : getClusterOperatorStatusClient (mgr , currentFeatureGates , "cluster-capi-operator-infracluster-controller" , managedNamespace ),
343
370
Scheme : mgr .GetScheme (),
344
371
Images : containerImages ,
345
372
RestCfg : mgr .GetConfig (),
@@ -383,14 +410,48 @@ func getAzureCloudEnvironment(ps *configv1.PlatformStatus) configv1.AzureCloudEn
383
410
return ps .Azure .CloudName
384
411
}
385
412
386
- func setupClusterOperatorController (mgr manager.Manager , ns string , isUnsupportedPlatform bool ) {
413
+ func setupClusterOperatorController (mgr manager.Manager , ns string , currentFeatureGates featuregates. FeatureGate , isUnsupportedPlatform bool ) {
387
414
// ClusterOperator watches and keeps the cluster-api ClusterObject up to date.
388
415
if err := (& clusteroperator.ClusterOperatorController {
389
- ClusterOperatorStatusClient : getClusterOperatorStatusClient (mgr , "cluster-capi-operator-clusteroperator-controller" , ns ),
416
+ ClusterOperatorStatusClient : getClusterOperatorStatusClient (mgr , currentFeatureGates , "cluster-capi-operator-clusteroperator-controller" , ns ),
390
417
Scheme : mgr .GetScheme (),
391
418
IsUnsupportedPlatform : isUnsupportedPlatform ,
392
419
}).SetupWithManager (mgr ); err != nil {
393
420
klog .Error (err , "unable to create clusteroperator controller" , "controller" , "ClusterOperator" )
394
421
os .Exit (1 )
395
422
}
396
423
}
424
+
425
+ // getFeatureGates is used to fetch the current feature gates from the cluster.
426
+ // We use this to check if the machine api migration is actually enabled or not.
427
+ func getFeatureGates (mgr ctrl.Manager ) (featuregates.FeatureGateAccess , error ) {
428
+ desiredVersion := util .GetReleaseVersion ()
429
+ missingVersion := "0.0.1-snapshot"
430
+
431
+ configClient , err := configv1client .NewForConfig (mgr .GetConfig ())
432
+ if err != nil {
433
+ return nil , fmt .Errorf ("failed to create config client: %w" , err )
434
+ }
435
+
436
+ configInformers := configinformers .NewSharedInformerFactory (configClient , 10 * time .Minute )
437
+
438
+ // By default, this will exit(0) if the featuregates change.
439
+ featureGateAccessor := featuregates .NewFeatureGateAccess (
440
+ desiredVersion , missingVersion ,
441
+ configInformers .Config ().V1 ().ClusterVersions (),
442
+ configInformers .Config ().V1 ().FeatureGates (),
443
+ events .NewLoggingEventRecorder ("machineapimigration" ),
444
+ )
445
+ go featureGateAccessor .Run (context .Background ())
446
+ go configInformers .Start (context .Background ().Done ())
447
+
448
+ select {
449
+ case <- featureGateAccessor .InitialFeatureGatesObserved ():
450
+ featureGates , _ := featureGateAccessor .CurrentFeatureGates ()
451
+ klog .Infof ("FeatureGates initialized: %v" , featureGates .KnownFeatures ())
452
+ case <- time .After (1 * time .Minute ):
453
+ return nil , errTimedOutWaitingForFeatureGates
454
+ }
455
+
456
+ return featureGateAccessor , nil
457
+ }
0 commit comments