Skip to content

Commit bcd0d1d

Browse files
committed
✨ Make leader election resourcelock configurable
1 parent ab55aa7 commit bcd0d1d

File tree

3 files changed

+56
-23
lines changed

3 files changed

+56
-23
lines changed

pkg/leaderelection/leader_election.go

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,22 +37,32 @@ type Options struct {
3737
// starting the manager.
3838
LeaderElection bool
3939

40+
// LeaderElectionResourceLock determines which resource lock to use for leader election,
41+
// defaults to "configmapsleases".
42+
LeaderElectionResourceLock string
43+
4044
// LeaderElectionNamespace determines the namespace in which the leader
41-
// election configmap will be created.
45+
// election resource will be created.
4246
LeaderElectionNamespace string
4347

44-
// LeaderElectionID determines the name of the configmap that leader election
48+
// LeaderElectionID determines the name of the resource that leader election
4549
// will use for holding the leader lock.
4650
LeaderElectionID string
4751
}
4852

49-
// NewResourceLock creates a new config map resource lock for use in a leader
50-
// election loop
53+
// NewResourceLock creates a new resource lock for use in a leader election loop.
5154
func NewResourceLock(config *rest.Config, recorderProvider recorder.Provider, options Options) (resourcelock.Interface, error) {
5255
if !options.LeaderElection {
5356
return nil, nil
5457
}
5558

59+
// Default resource lock to "configmapsleases". We must keep this default until we are sure all controller-runtime
60+
// users have upgraded from the original default ConfigMap lock to a controller-runtime version that has this new
61+
// default. Many users of controller-runtime skip versions, so we should be extremely conservative here.
62+
if options.LeaderElectionResourceLock == "" {
63+
options.LeaderElectionResourceLock = resourcelock.ConfigMapsLeasesResourceLock
64+
}
65+
5666
// LeaderElectionID must be provided to prevent clashes
5767
if options.LeaderElectionID == "" {
5868
return nil, errors.New("LeaderElectionID must be configured")
@@ -80,8 +90,7 @@ func NewResourceLock(config *rest.Config, recorderProvider recorder.Provider, op
8090
return nil, err
8191
}
8292

83-
// TODO(JoelSpeed): switch to leaderelection object in 1.12
84-
return resourcelock.New(resourcelock.ConfigMapsLeasesResourceLock,
93+
return resourcelock.New(options.LeaderElectionResourceLock,
8594
options.LeaderElectionNamespace,
8695
options.LeaderElectionID,
8796
client.CoreV1(),

pkg/manager/manager.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -142,11 +142,15 @@ type Options struct {
142142
// starting the manager.
143143
LeaderElection bool
144144

145+
// LeaderElectionResourceLock determines which resource lock to use for leader election,
146+
// defaults to "configmapsleases".
147+
LeaderElectionResourceLock string
148+
145149
// LeaderElectionNamespace determines the namespace in which the leader
146-
// election configmap will be created.
150+
// election resource will be created.
147151
LeaderElectionNamespace string
148152

149-
// LeaderElectionID determines the name of the configmap that leader election
153+
// LeaderElectionID determines the name of the resource that leader election
150154
// will use for holding the leader lock.
151155
LeaderElectionID string
152156

@@ -329,9 +333,10 @@ func New(config *rest.Config, options Options) (Manager, error) {
329333
leaderConfig = options.LeaderElectionConfig
330334
}
331335
resourceLock, err := options.newResourceLock(leaderConfig, recorderProvider, leaderelection.Options{
332-
LeaderElection: options.LeaderElection,
333-
LeaderElectionID: options.LeaderElectionID,
334-
LeaderElectionNamespace: options.LeaderElectionNamespace,
336+
LeaderElection: options.LeaderElection,
337+
LeaderElectionResourceLock: options.LeaderElectionResourceLock,
338+
LeaderElectionID: options.LeaderElectionID,
339+
LeaderElectionNamespace: options.LeaderElectionNamespace,
335340
})
336341
if err != nil {
337342
return nil, err

pkg/manager/manager_test.go

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929

3030
"github.com/go-logr/logr"
3131
. "github.com/onsi/ginkgo"
32+
. "github.com/onsi/ginkgo/extensions/table"
3233
. "github.com/onsi/gomega"
3334
"github.com/prometheus/client_golang/prometheus"
3435
"go.uber.org/goleak"
@@ -296,30 +297,48 @@ var _ = Describe("manger.Manager", func() {
296297
<-m2done
297298
})
298299

300+
It("should return an error if it can't create a ResourceLock", func() {
301+
m, err := New(cfg, Options{
302+
newResourceLock: func(_ *rest.Config, _ recorder.Provider, _ leaderelection.Options) (resourcelock.Interface, error) {
303+
return nil, fmt.Errorf("expected error")
304+
},
305+
})
306+
Expect(m).To(BeNil())
307+
Expect(err).To(MatchError(ContainSubstring("expected error")))
308+
})
299309
It("should return an error if namespace not set and not running in cluster", func() {
300310
m, err := New(cfg, Options{LeaderElection: true, LeaderElectionID: "controller-runtime"})
301311
Expect(m).To(BeNil())
302312
Expect(err).To(HaveOccurred())
303313
Expect(err.Error()).To(ContainSubstring("unable to find leader election namespace: not running in-cluster, please specify LeaderElectionNamespace"))
304314
})
305315

306-
// We must keep this default until we are sure all controller-runtime users have upgraded from the original default
307-
// ConfigMap lock to a controller-runtime version that has this new default. Many users of controller-runtime skip
308-
// versions, so we should be extremely conservative here.
309-
It("should default to ConfigMapsLeasesResourceLock", func() {
310-
m, err := New(cfg, Options{LeaderElection: true, LeaderElectionID: "controller-runtime", LeaderElectionNamespace: "my-ns"})
316+
DescribeTable("should create the correct ResourceLock", func(resourceLock string, expectMultiLock bool, expectedLocks ...resourcelock.Interface) {
317+
m, err := New(cfg, Options{LeaderElection: true, LeaderElectionResourceLock: resourceLock, LeaderElectionID: "controller-runtime", LeaderElectionNamespace: "my-ns"})
311318
Expect(m).ToNot(BeNil())
312319
Expect(err).ToNot(HaveOccurred())
313320
cm, ok := m.(*controllerManager)
314321
Expect(ok).To(BeTrue())
315-
multilock, isMultiLock := cm.resourceLock.(*resourcelock.MultiLock)
316-
Expect(isMultiLock).To(BeTrue())
317-
_, primaryIsConfigMapLock := multilock.Primary.(*resourcelock.ConfigMapLock)
318-
Expect(primaryIsConfigMapLock).To(BeTrue())
319-
_, secondaryIsLeaseLock := multilock.Secondary.(*resourcelock.LeaseLock)
320-
Expect(secondaryIsLeaseLock).To(BeTrue())
321322

322-
})
323+
if expectMultiLock {
324+
Expect(cm.resourceLock).To(BeAssignableToTypeOf(&resourcelock.MultiLock{}))
325+
multiLock := cm.resourceLock.(*resourcelock.MultiLock)
326+
Expect(multiLock.Primary).To(BeAssignableToTypeOf(expectedLocks[0]))
327+
Expect(multiLock.Secondary).To(BeAssignableToTypeOf(expectedLocks[1]))
328+
} else {
329+
Expect(cm.resourceLock).To(BeAssignableToTypeOf(expectedLocks[0]))
330+
}
331+
},
332+
// We must keep this default until we are sure all controller-runtime users have upgraded from the original default
333+
// ConfigMap lock to a controller-runtime version that has this new default. Many users of controller-runtime skip
334+
// versions, so we should be extremely conservative here.
335+
Entry("not specified (should default to ConfigMapsLeasesResourceLock)", "", true, &resourcelock.ConfigMapLock{}, &resourcelock.LeaseLock{}),
336+
Entry("EndpointsResourceLock", resourcelock.EndpointsResourceLock, false, &resourcelock.EndpointsLock{}),
337+
Entry("ConfigMapsResourceLock", resourcelock.ConfigMapsResourceLock, false, &resourcelock.ConfigMapLock{}),
338+
Entry("LeasesResourceLock", resourcelock.LeasesResourceLock, false, &resourcelock.LeaseLock{}),
339+
Entry("EndpointsLeasesResourceLock", resourcelock.EndpointsLeasesResourceLock, true, &resourcelock.EndpointsLock{}, &resourcelock.LeaseLock{}),
340+
Entry("ConfigMapsLeasesResourceLock", resourcelock.ConfigMapsLeasesResourceLock, true, &resourcelock.ConfigMapLock{}, &resourcelock.LeaseLock{}),
341+
)
323342
})
324343

325344
It("should create a listener for the metrics if a valid address is provided", func() {

0 commit comments

Comments
 (0)