Skip to content

Commit 3dfb42d

Browse files
added leader election for runnables
1 parent e00985b commit 3dfb42d

File tree

11 files changed

+375
-127
lines changed

11 files changed

+375
-127
lines changed

pkg/builder/controller_test.go

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ var _ = Describe("application", func() {
6161
instance, err := ControllerManagedBy(m).
6262
For(&appsv1.ReplicaSet{}).
6363
Owns(&appsv1.ReplicaSet{}).
64+
WithOptions(controller.Options{
65+
LeaderElection: &controller.LeaderElectionOptions{
66+
NeedLeaderElection: false,
67+
},
68+
}).
6469
Build(noop)
6570
Expect(err).NotTo(HaveOccurred())
6671
Expect(instance).NotTo(BeNil())
@@ -75,6 +80,11 @@ var _ = Describe("application", func() {
7580
instance, err := ControllerManagedBy(m).
7681
For(&fakeType{}).
7782
Owns(&appsv1.ReplicaSet{}).
83+
WithOptions(controller.Options{
84+
LeaderElection: &controller.LeaderElectionOptions{
85+
NeedLeaderElection: false,
86+
},
87+
}).
7888
Build(noop)
7989
Expect(err).To(MatchError(ContainSubstring("no kind is registered for the type builder.fakeType")))
8090
Expect(instance).To(BeNil())
@@ -97,6 +107,11 @@ var _ = Describe("application", func() {
97107
instance, err := ControllerManagedBy(m).
98108
For(&appsv1.ReplicaSet{}).
99109
Owns(&appsv1.ReplicaSet{}).
110+
WithOptions(controller.Options{
111+
LeaderElection: &controller.LeaderElectionOptions{
112+
NeedLeaderElection: false,
113+
},
114+
}).
100115
Build(noop)
101116
Expect(err).To(HaveOccurred())
102117
Expect(err.Error()).To(ContainSubstring("expected error"))
@@ -120,7 +135,12 @@ var _ = Describe("application", func() {
120135
instance, err := ControllerManagedBy(m).
121136
For(&appsv1.ReplicaSet{}).
122137
Owns(&appsv1.ReplicaSet{}).
123-
WithOptions(controller.Options{MaxConcurrentReconciles: maxConcurrentReconciles}).
138+
WithOptions(controller.Options{
139+
MaxConcurrentReconciles: maxConcurrentReconciles,
140+
LeaderElection: &controller.LeaderElectionOptions{
141+
NeedLeaderElection: false,
142+
},
143+
}).
124144
Build(noop)
125145
Expect(err).NotTo(HaveOccurred())
126146
Expect(instance).NotTo(BeNil())
@@ -141,6 +161,11 @@ var _ = Describe("application", func() {
141161
ctrl1, err := ControllerManagedBy(m).
142162
For(&TestDefaultValidator{}).
143163
Owns(&appsv1.ReplicaSet{}).
164+
WithOptions(controller.Options{
165+
LeaderElection: &controller.LeaderElectionOptions{
166+
NeedLeaderElection: false,
167+
},
168+
}).
144169
Build(noop)
145170
Expect(err).NotTo(HaveOccurred())
146171
Expect(ctrl1).NotTo(BeNil())
@@ -149,6 +174,11 @@ var _ = Describe("application", func() {
149174
ctrl2, err := ControllerManagedBy(m).
150175
For(&TestDefaultValidator{}).
151176
Owns(&appsv1.ReplicaSet{}).
177+
WithOptions(controller.Options{
178+
LeaderElection: &controller.LeaderElectionOptions{
179+
NeedLeaderElection: false,
180+
},
181+
}).
152182
Build(noop)
153183
Expect(err).NotTo(HaveOccurred())
154184
Expect(ctrl2).NotTo(BeNil())
@@ -162,7 +192,12 @@ var _ = Describe("application", func() {
162192

163193
bldr := ControllerManagedBy(m).
164194
For(&appsv1.Deployment{}).
165-
Owns(&appsv1.ReplicaSet{})
195+
Owns(&appsv1.ReplicaSet{}).
196+
WithOptions(controller.Options{
197+
LeaderElection: &controller.LeaderElectionOptions{
198+
NeedLeaderElection: false,
199+
},
200+
})
166201
doReconcileTest("3", stop, bldr, m, false)
167202
close(done)
168203
}, 10)
@@ -173,6 +208,11 @@ var _ = Describe("application", func() {
173208

174209
bldr := ControllerManagedBy(m).
175210
For(&appsv1.Deployment{}).
211+
WithOptions(controller.Options{
212+
LeaderElection: &controller.LeaderElectionOptions{
213+
NeedLeaderElection: false,
214+
},
215+
}).
176216
Watches( // Equivalent of Owns
177217
&source.Kind{Type: &appsv1.ReplicaSet{}},
178218
&handler.EnqueueRequestForOwner{OwnerType: &appsv1.Deployment{}, IsController: true})

pkg/cache/informer_cache.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,12 @@ func (ip *informerCache) NeedLeaderElection() bool {
146146
return false
147147
}
148148

149+
// GetID implements the LeaderElectionRunnable interface.
150+
// It's dummy method that always returns empty string as informerCache doesn't need leader election.
151+
func (ip *informerCache) GetID() string {
152+
return ""
153+
}
154+
149155
// IndexField adds an indexer to the underlying cache, using extraction function to get
150156
// value(s) from the given field. This index can then be used by passing a field selector
151157
// to List. For one-to-one compatibility with "normal" field selectors, only return one value.

pkg/controller/controller.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,18 @@ type Options struct {
3535

3636
// Reconciler reconciles an object
3737
Reconciler reconcile.Reconciler
38+
39+
// Leader election is by default
40+
LeaderElection *LeaderElectionOptions
41+
}
42+
43+
// Leader Election options
44+
type LeaderElectionOptions struct {
45+
// NeedLeaderElection determines whether or not to use leader election when starting the controller.
46+
NeedLeaderElection bool
47+
48+
// LeaderElectionID determines the name of the configmap that leader election will use for holding the leader lock.
49+
LeaderElectionID string
3850
}
3951

4052
// Controller implements a Kubernetes API. A Controller manages a work queue fed reconcile.Requests
@@ -73,6 +85,13 @@ func New(name string, mgr manager.Manager, options Options) (Controller, error)
7385
options.MaxConcurrentReconciles = 1
7486
}
7587

88+
if options.LeaderElection == nil {
89+
options.LeaderElection = &LeaderElectionOptions{
90+
NeedLeaderElection: true,
91+
LeaderElectionID: name,
92+
}
93+
}
94+
7695
// Inject dependencies into Reconciler
7796
if err := mgr.SetFields(options.Reconciler); err != nil {
7897
return nil, err
@@ -91,6 +110,8 @@ func New(name string, mgr manager.Manager, options Options) (Controller, error)
91110
},
92111
MaxConcurrentReconciles: options.MaxConcurrentReconciles,
93112
Name: name,
113+
LeaderElection: options.LeaderElection.NeedLeaderElection,
114+
LeaderElectionID: options.LeaderElection.LeaderElectionID,
94115
}
95116

96117
// Add the controller as a Manager components

pkg/controller/controller_integration_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ var _ = Describe("controller", func() {
6464
reconciled <- request
6565
return reconcile.Result{}, nil
6666
}),
67+
LeaderElection: &controller.LeaderElectionOptions{
68+
NeedLeaderElection: false,
69+
},
6770
})
6871
Expect(err).NotTo(HaveOccurred())
6972

pkg/controller/controller_test.go

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,12 @@ var _ = Describe("controller.Controller", func() {
4848
It("should return an error if Name is not Specified", func(done Done) {
4949
m, err := manager.New(cfg, manager.Options{})
5050
Expect(err).NotTo(HaveOccurred())
51-
c, err := controller.New("", m, controller.Options{Reconciler: rec})
51+
c, err := controller.New("", m, controller.Options{
52+
Reconciler: rec,
53+
LeaderElection: &controller.LeaderElectionOptions{
54+
NeedLeaderElection: false,
55+
},
56+
})
5257
Expect(c).To(BeNil())
5358
Expect(err.Error()).To(ContainSubstring("must specify Name for Controller"))
5459

@@ -70,7 +75,12 @@ var _ = Describe("controller.Controller", func() {
7075
m, err := manager.New(cfg, manager.Options{})
7176
Expect(err).NotTo(HaveOccurred())
7277

73-
c, err := controller.New("foo", m, controller.Options{Reconciler: &failRec{}})
78+
c, err := controller.New("foo", m, controller.Options{
79+
Reconciler: &failRec{},
80+
LeaderElection: &controller.LeaderElectionOptions{
81+
NeedLeaderElection: false,
82+
},
83+
})
7484
Expect(c).To(BeNil())
7585
Expect(err).To(HaveOccurred())
7686
Expect(err.Error()).To(ContainSubstring("expected error"))
@@ -82,11 +92,21 @@ var _ = Describe("controller.Controller", func() {
8292
m, err := manager.New(cfg, manager.Options{})
8393
Expect(err).NotTo(HaveOccurred())
8494

85-
c1, err := controller.New("c1", m, controller.Options{Reconciler: rec})
95+
c1, err := controller.New("c1", m, controller.Options{
96+
Reconciler: rec,
97+
LeaderElection: &controller.LeaderElectionOptions{
98+
NeedLeaderElection: false,
99+
},
100+
})
86101
Expect(err).NotTo(HaveOccurred())
87102
Expect(c1).ToNot(BeNil())
88103

89-
c2, err := controller.New("c2", m, controller.Options{Reconciler: rec})
104+
c2, err := controller.New("c2", m, controller.Options{
105+
Reconciler: rec,
106+
LeaderElection: &controller.LeaderElectionOptions{
107+
NeedLeaderElection: false,
108+
},
109+
})
90110
Expect(err).NotTo(HaveOccurred())
91111
Expect(c2).ToNot(BeNil())
92112

pkg/internal/controller/controller.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@ type Controller struct {
5050
// MaxConcurrentReconciles is the maximum number of concurrent Reconciles which can be run. Defaults to 1.
5151
MaxConcurrentReconciles int
5252

53+
// LeaderElection determines whether or not to use leader election when
54+
// starting the controller.
55+
LeaderElection bool
56+
57+
// LeaderElectionID determines the name of the configmap that leader election
58+
// will use for holding the leader lock.
59+
LeaderElectionID string
60+
5361
// Reconciler is a function that can be called at any time with the Name / Namespace of an object and
5462
// ensures that the state of the system matches the state specified in the object.
5563
// Defaults to the DefaultReconcileFunc.
@@ -296,3 +304,11 @@ func (c *Controller) InjectFunc(f inject.Func) error {
296304
func (c *Controller) updateMetrics(reconcileTime time.Duration) {
297305
ctrlmetrics.ReconcileTime.WithLabelValues(c.Name).Observe(reconcileTime.Seconds())
298306
}
307+
308+
func (c *Controller) NeedLeaderElection() bool {
309+
return c.LeaderElection
310+
}
311+
312+
func (c *Controller) GetID() string {
313+
return c.LeaderElectionID
314+
}

pkg/internal/recorder/recorder_integration_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ var _ = Describe("recorder", func() {
6161
recorder.Event(dp, corev1.EventTypeNormal, "test-reason", "test-msg")
6262
return reconcile.Result{}, nil
6363
}),
64+
LeaderElection: &controller.LeaderElectionOptions{
65+
NeedLeaderElection: false,
66+
},
6467
})
6568
Expect(err).NotTo(HaveOccurred())
6669

0 commit comments

Comments
 (0)