Skip to content

Commit 658e33a

Browse files
MachineSet feature gating
This change adds feature gate support to the MachineSet controller. We will need this when adding the paused condition, as it will sit behind a feature gate.
1 parent 706c4ab commit 658e33a

File tree

6 files changed

+113
-57
lines changed

6 files changed

+113
-57
lines changed

cmd/machineset/main.go

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,34 +20,41 @@ import (
2020
"flag"
2121
"fmt"
2222
"log"
23+
"strings"
2324
"time"
2425

26+
osconfigv1 "github.com/openshift/api/config/v1"
27+
apifeatures "github.com/openshift/api/features"
28+
machinev1 "github.com/openshift/api/machine/v1beta1"
29+
mapiwebhooks "github.com/openshift/machine-api-operator/pkg/webhooks"
2530
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
31+
k8sflag "k8s.io/component-base/cli/flag"
32+
ctrl "sigs.k8s.io/controller-runtime"
33+
34+
"github.com/openshift/library-go/pkg/config/leaderelection"
35+
"github.com/openshift/library-go/pkg/features"
36+
"github.com/openshift/machine-api-operator/pkg/controller"
37+
"github.com/openshift/machine-api-operator/pkg/controller/machineset"
38+
"github.com/openshift/machine-api-operator/pkg/metrics"
39+
"github.com/openshift/machine-api-operator/pkg/operator"
40+
"github.com/openshift/machine-api-operator/pkg/util"
41+
"k8s.io/apiserver/pkg/util/feature"
2642
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
43+
"k8s.io/component-base/featuregate"
2744
"k8s.io/klog/v2"
28-
ctrl "sigs.k8s.io/controller-runtime"
2945
"sigs.k8s.io/controller-runtime/pkg/cache"
3046
"sigs.k8s.io/controller-runtime/pkg/client/config"
3147
"sigs.k8s.io/controller-runtime/pkg/healthz"
3248
"sigs.k8s.io/controller-runtime/pkg/manager"
3349
"sigs.k8s.io/controller-runtime/pkg/manager/signals"
3450
"sigs.k8s.io/controller-runtime/pkg/metrics/server"
3551
"sigs.k8s.io/controller-runtime/pkg/webhook"
36-
37-
osconfigv1 "github.com/openshift/api/config/v1"
38-
machinev1 "github.com/openshift/api/machine/v1beta1"
39-
"github.com/openshift/library-go/pkg/config/leaderelection"
40-
"github.com/openshift/machine-api-operator/pkg/controller"
41-
"github.com/openshift/machine-api-operator/pkg/controller/machineset"
42-
"github.com/openshift/machine-api-operator/pkg/metrics"
43-
"github.com/openshift/machine-api-operator/pkg/operator"
44-
"github.com/openshift/machine-api-operator/pkg/util"
45-
mapiwebhooks "github.com/openshift/machine-api-operator/pkg/webhooks"
4652
)
4753

4854
const (
4955
defaultWebhookPort = operator.MachineSetWebhookPort
5056
defaultWebhookCertdir = "/etc/machine-api-operator/tls"
57+
timeout = 10 * time.Minute
5158
)
5259

5360
func main() {
@@ -102,6 +109,17 @@ func main() {
102109
fmt.Sprintf("The duration that non-leader candidates will wait after observing a leadership renewal until attempting to acquire leadership of a led but unrenewed leader slot. This is effectively the maximum duration that a leader can be stopped before it is replaced by another candidate. This is only applicable if leader election is enabled. Default: (%s)", defaultLeaderElectionValues.LeaseDuration.Duration),
103110
)
104111

112+
// Sets up feature gates
113+
defaultMutableGate := feature.DefaultMutableFeatureGate
114+
_, err := features.NewFeatureGateOptions(defaultMutableGate, apifeatures.SelfManaged, apifeatures.FeatureGateVSphereStaticIPs, apifeatures.FeatureGateMachineAPIMigration)
115+
if err != nil {
116+
klog.Fatalf("Error setting up feature gates: %v", err)
117+
}
118+
119+
featureGateArgs := map[string]bool{}
120+
flag.Var(k8sflag.NewMapStringBool(&featureGateArgs), "feature-gates", "A set of key=value pairs that describe feature gates for alpha/experimental features. "+
121+
"Options are:\n"+strings.Join(defaultMutableGate.KnownFeatures(), "\n"))
122+
105123
flag.Parse()
106124
if *watchNamespace != "" {
107125
log.Printf("Watching cluster-api objects only in namespace %q for reconciliation.", *watchNamespace)
@@ -120,7 +138,7 @@ func main() {
120138
})
121139

122140
// Create a new Cmd to provide shared dependencies and start components
123-
syncPeriod := 10 * time.Minute
141+
syncPeriod := timeout
124142
opts := manager.Options{
125143
Metrics: server.Options{
126144
BindAddress: *metricsAddress,
@@ -152,6 +170,14 @@ func main() {
152170
log.Fatal(err)
153171
}
154172

173+
// Sets feature gates from flags
174+
klog.Infof("Initializing feature gates: %s", strings.Join(defaultMutableGate.KnownFeatures(), ", "))
175+
err = defaultMutableGate.SetFromMap(featureGateArgs)
176+
if err != nil {
177+
klog.Fatalf("Error setting feature gates from flags: %v", err)
178+
}
179+
klog.Infof("FeatureGateMachineAPIMigration initialised: %t", defaultMutableGate.Enabled(featuregate.Feature(apifeatures.FeatureGateMachineAPIMigration)))
180+
155181
// Enable defaulting and validating webhooks
156182
machineDefaulter, err := mapiwebhooks.NewMachineDefaulter()
157183
if err != nil {
@@ -188,7 +214,7 @@ func main() {
188214
}
189215

190216
// Setup all Controllers
191-
if err := controller.AddToManager(mgr, opts, machineset.Add); err != nil {
217+
if err := controller.AddToManagerWithFeatureGates(mgr, opts, defaultMutableGate, machineset.Add); err != nil {
192218
log.Fatal(err)
193219
}
194220

pkg/controller/controller.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
11
package controller
22

33
import (
4+
"k8s.io/component-base/featuregate"
45
"sigs.k8s.io/controller-runtime/pkg/manager"
56
)
67

8+
// AddToManager adds all Controllers to the Manager along with a feature gate accessor
9+
func AddToManagerWithFeatureGates(m manager.Manager, opts manager.Options, featureGate featuregate.MutableFeatureGate, fnList ...func(manager.Manager, manager.Options, featuregate.MutableFeatureGate) error) error {
10+
for _, f := range fnList {
11+
if err := f(m, opts, featureGate); err != nil {
12+
return err
13+
}
14+
}
15+
return nil
16+
}
17+
718
// AddToManager adds all Controllers to the Manager
819
func AddToManager(m manager.Manager, opts manager.Options, fnList ...func(manager.Manager, manager.Options) error) error {
920
for _, f := range fnList {

pkg/controller/machineset/controller.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import (
3535
"k8s.io/apimachinery/pkg/runtime"
3636
"k8s.io/apimachinery/pkg/util/validation/field"
3737
"k8s.io/client-go/tools/record"
38+
"k8s.io/component-base/featuregate"
3839
"k8s.io/klog/v2"
3940
"sigs.k8s.io/controller-runtime/pkg/client"
4041
"sigs.k8s.io/controller-runtime/pkg/controller"
@@ -60,14 +61,18 @@ var (
6061

6162
// Add creates a new MachineSet Controller and adds it to the Manager with default RBAC.
6263
// The Manager will set fields on the Controller and Start it when the Manager is Started.
63-
func Add(mgr manager.Manager, opts manager.Options) error {
64-
r := newReconciler(mgr)
64+
func Add(mgr manager.Manager, opts manager.Options, gate featuregate.MutableFeatureGate) error {
65+
r := newReconciler(mgr, gate)
6566
return add(mgr, r, r.MachineToMachineSets)
6667
}
6768

6869
// newReconciler returns a new reconcile.Reconciler.
69-
func newReconciler(mgr manager.Manager) *ReconcileMachineSet {
70-
return &ReconcileMachineSet{Client: mgr.GetClient(), scheme: mgr.GetScheme(), recorder: mgr.GetEventRecorderFor(controllerName)}
70+
func newReconciler(mgr manager.Manager, gate featuregate.MutableFeatureGate) *ReconcileMachineSet {
71+
return &ReconcileMachineSet{
72+
Client: mgr.GetClient(), scheme: mgr.GetScheme(),
73+
recorder: mgr.GetEventRecorderFor(controllerName),
74+
gate: gate,
75+
}
7176
}
7277

7378
// add adds a new Controller to mgr with r as the reconcile.Reconciler.
@@ -108,6 +113,7 @@ type ReconcileMachineSet struct {
108113
client.Client
109114
scheme *runtime.Scheme
110115
recorder record.EventRecorder
116+
gate featuregate.MutableFeatureGate
111117
}
112118

113119
func (r *ReconcileMachineSet) MachineToMachineSets(ctx context.Context, o *machinev1.Machine) []reconcile.Request {

pkg/controller/machineset/controller_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
. "github.com/onsi/ginkgo/v2"
2626
. "github.com/onsi/gomega"
2727
machinev1 "github.com/openshift/api/machine/v1beta1"
28+
testutils "github.com/openshift/machine-api-operator/pkg/util/testing"
2829
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2930
"k8s.io/apimachinery/pkg/types"
3031
"k8s.io/client-go/kubernetes/scheme"
@@ -273,10 +274,13 @@ var _ = Describe("MachineSet Reconcile", func() {
273274

274275
BeforeEach(func() {
275276
rec = record.NewFakeRecorder(32)
277+
gate, err := testutils.NewDefaultMutableFeatureGate()
278+
Expect(err).NotTo(HaveOccurred())
276279

277280
r = &ReconcileMachineSet{
278281
scheme: scheme.Scheme,
279282
recorder: rec,
283+
gate: gate,
280284
}
281285
})
282286

pkg/controller/machineset/machineset_controller_test.go

Lines changed: 30 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,25 @@ import (
2222

2323
. "github.com/onsi/ginkgo/v2"
2424
. "github.com/onsi/gomega"
25+
2526
machinev1 "github.com/openshift/api/machine/v1beta1"
27+
machinev1resourcebuilder "github.com/openshift/cluster-api-actuator-pkg/testutils/resourcebuilder/machine/v1beta1"
28+
testutils "github.com/openshift/machine-api-operator/pkg/util/testing"
29+
2630
corev1 "k8s.io/api/core/v1"
2731
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2832
"sigs.k8s.io/controller-runtime/pkg/client"
2933
"sigs.k8s.io/controller-runtime/pkg/manager"
34+
3035
"sigs.k8s.io/controller-runtime/pkg/metrics/server"
3136
)
3237

3338
var _ = Describe("MachineSet Reconciler", func() {
34-
namespace := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "ms-test"}}
3539
var mgrCtxCancel context.CancelFunc
3640
var mgrStopped chan struct{}
41+
var namespace *corev1.Namespace
42+
var machineSetBuilder machinev1resourcebuilder.MachineSetBuilder
43+
var replicas int32 = int32(2)
3744

3845
BeforeEach(func() {
3946
By("Setting up a new manager")
@@ -46,11 +53,14 @@ var _ = Describe("MachineSet Reconciler", func() {
4653

4754
k8sClient = mgr.GetClient()
4855

56+
By("Setting up feature gates")
57+
gate, err := testutils.NewDefaultMutableFeatureGate()
58+
Expect(err).NotTo(HaveOccurred())
59+
4960
By("Setting up a new reconciler")
50-
reconciler := newReconciler(mgr)
61+
reconciler := newReconciler(mgr, gate)
5162

52-
err = add(mgr, reconciler, reconciler.MachineToMachineSets)
53-
Expect(err).NotTo(HaveOccurred())
63+
Expect(add(mgr, reconciler, reconciler.MachineToMachineSets)).To(Succeed())
5464

5565
var mgrCtx context.Context
5666
mgrCtx, mgrCtxCancel = context.WithCancel(ctx)
@@ -65,12 +75,21 @@ var _ = Describe("MachineSet Reconciler", func() {
6575
}()
6676

6777
By("Creating the namespace")
78+
namespace = &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{GenerateName: "ms-test"}}
6879
Expect(k8sClient.Create(ctx, namespace)).To(Succeed())
80+
81+
By("Setting up the machine set builder")
82+
machineSetBuilder = machinev1resourcebuilder.MachineSet().
83+
WithNamespace(namespace.ObjectMeta.Name).
84+
WithName("foo").
85+
WithReplicas(replicas).
86+
WithLabels(map[string]string{"foo": "bar"})
87+
6988
})
7089

7190
AfterEach(func() {
7291
By("Deleting the machinesets")
73-
Expect(cleanResources()).To(Succeed())
92+
Expect(cleanResources(namespace.Name)).To(Succeed())
7493

7594
By("Deleting the namespace")
7695
Expect(k8sClient.Delete(ctx, namespace)).To(Succeed())
@@ -81,29 +100,12 @@ var _ = Describe("MachineSet Reconciler", func() {
81100
})
82101

83102
It("Should reconcile a MachineSet", func() {
84-
replicas := int32(2)
85-
labels := map[string]string{"foo": "bar"}
86-
87-
instance := &machinev1.MachineSet{
88-
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: namespace.Name},
89-
Spec: machinev1.MachineSetSpec{
90-
Replicas: &replicas,
91-
Selector: metav1.LabelSelector{
92-
MatchLabels: labels,
93-
},
94-
Template: machinev1.MachineTemplateSpec{
95-
ObjectMeta: machinev1.ObjectMeta{
96-
Labels: labels,
97-
},
98-
},
99-
},
100-
}
103+
instance := machineSetBuilder.Build()
101104

102105
By("Creating the MachineSet")
103106
Expect(k8sClient.Create(ctx, instance)).To(Succeed())
104107

105108
machines := &machinev1.MachineList{}
106-
107109
By("Verifying that we have 2 replicas")
108110
Eventually(func() (int, error) {
109111
if err := k8sClient.List(ctx, machines, client.InNamespace(namespace.Name)); err != nil {
@@ -133,28 +135,16 @@ var _ = Describe("MachineSet Reconciler", func() {
133135
})
134136
})
135137

136-
func cleanResources() error {
137-
machineSets := &machinev1.MachineSetList{}
138-
if err := k8sClient.List(ctx, machineSets); err != nil {
138+
func cleanResources(namespace string) error {
139+
machineSet := &machinev1.MachineSet{}
140+
if err := k8sClient.DeleteAllOf(ctx, machineSet, client.InNamespace(namespace)); err != nil {
139141
return err
140142
}
141-
for _, machineSet := range machineSets.Items {
142-
ms := machineSet
143-
if err := k8sClient.Delete(ctx, &ms); err != nil {
144-
return err
145-
}
146-
}
147143

148-
machines := &machinev1.MachineList{}
149-
if err := k8sClient.List(ctx, machines); err != nil {
144+
machine := &machinev1.Machine{}
145+
if err := k8sClient.DeleteAllOf(ctx, machine, client.InNamespace(namespace)); err != nil {
150146
return err
151147
}
152-
for _, machine := range machines.Items {
153-
m := machine
154-
if err := k8sClient.Delete(ctx, &m); err != nil {
155-
return err
156-
}
157-
}
158148

159149
return nil
160150
}

pkg/util/testing/testing.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,14 @@ import (
66

77
machinev1 "github.com/openshift/api/machine/v1beta1"
88

9+
openshiftfeatures "github.com/openshift/api/features"
10+
"github.com/openshift/library-go/pkg/features"
911
corev1 "k8s.io/api/core/v1"
1012
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1113
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
1214
"k8s.io/apimachinery/pkg/util/uuid"
15+
"k8s.io/apiserver/pkg/util/feature"
16+
"k8s.io/component-base/featuregate"
1317
"k8s.io/utils/ptr"
1418
)
1519

@@ -169,3 +173,18 @@ func NewMachineHealthCheck(name string) *machinev1.MachineHealthCheck {
169173
Status: machinev1.MachineHealthCheckStatus{},
170174
}
171175
}
176+
177+
func NewDefaultMutableFeatureGate() (featuregate.MutableFeatureGate, error) {
178+
defaultMutableGate := feature.DefaultMutableFeatureGate
179+
_, err := features.NewFeatureGateOptions(defaultMutableGate, openshiftfeatures.SelfManaged, openshiftfeatures.FeatureGateMachineAPIMigration)
180+
if err != nil {
181+
return nil, err
182+
}
183+
184+
err = defaultMutableGate.SetFromMap(map[string]bool{"MachineAPIMigration": true})
185+
if err != nil {
186+
return nil, err
187+
}
188+
189+
return defaultMutableGate, nil
190+
}

0 commit comments

Comments
 (0)