Skip to content

Commit e3ba31e

Browse files
committed
Test leaderelection code path with fake resource lock
1 parent 73031d7 commit e3ba31e

File tree

3 files changed

+206
-0
lines changed

3 files changed

+206
-0
lines changed

pkg/leaderelection/fake/doc.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
Copyright 2018 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
/*
18+
Package fake mocks a resource lock for testing purposes.
19+
Always returns leadership.
20+
*/
21+
22+
package fake
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
Copyright 2018 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package fake
18+
19+
import (
20+
"os"
21+
"time"
22+
23+
"github.com/pborman/uuid"
24+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
25+
"k8s.io/client-go/rest"
26+
"k8s.io/client-go/tools/leaderelection/resourcelock"
27+
"sigs.k8s.io/controller-runtime/pkg/leaderelection"
28+
"sigs.k8s.io/controller-runtime/pkg/recorder"
29+
)
30+
31+
// NewFakeResourceLock creates a new FakeResourceLock for use in testing
32+
// leader election.
33+
func NewFakeResourceLock(config *rest.Config, recorderProvider recorder.Provider, options leaderelection.Options) (resourcelock.Interface, error) {
34+
// Leader id, needs to be unique
35+
id, err := os.Hostname()
36+
if err != nil {
37+
return nil, err
38+
}
39+
id = id + "_" + string(uuid.NewUUID())
40+
41+
return &FakeResourceLock{
42+
id: id,
43+
record: resourcelock.LeaderElectionRecord{
44+
HolderIdentity: id,
45+
LeaseDurationSeconds: 15,
46+
AcquireTime: metav1.NewTime(time.Now()),
47+
RenewTime: metav1.NewTime(time.Now().Add(15 * time.Second)),
48+
LeaderTransitions: 1,
49+
},
50+
}, nil
51+
}
52+
53+
// FakeResourceLock implements the ResourceLockInterface.
54+
// By default returns that the current identity holds the lock.
55+
type FakeResourceLock struct {
56+
id string
57+
record resourcelock.LeaderElectionRecord
58+
}
59+
60+
// Get implements the ResourceLockInterface.
61+
func (f *FakeResourceLock) Get() (*resourcelock.LeaderElectionRecord, error) {
62+
return &f.record, nil
63+
}
64+
65+
// Create implements the ResourceLockInterface.
66+
func (f *FakeResourceLock) Create(ler resourcelock.LeaderElectionRecord) error {
67+
f.record = ler
68+
return nil
69+
}
70+
71+
// Update implements the ResourceLockInterface.
72+
func (f *FakeResourceLock) Update(ler resourcelock.LeaderElectionRecord) error {
73+
f.record = ler
74+
return nil
75+
}
76+
77+
// RecordEvent implements the ResourceLockInterface.
78+
func (f *FakeResourceLock) RecordEvent(s string) {
79+
return
80+
}
81+
82+
// Idenity implements the ResourceLockInterface.
83+
func (f *FakeResourceLock) Identity() string {
84+
return f.id
85+
}
86+
87+
// Describe implements the ResourceLockInterface.
88+
func (f *FakeResourceLock) Describe() string {
89+
return f.id
90+
}

pkg/manager/manager_test.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"sigs.k8s.io/controller-runtime/pkg/cache"
2929
"sigs.k8s.io/controller-runtime/pkg/cache/informertest"
3030
"sigs.k8s.io/controller-runtime/pkg/client"
31+
fakeleaderelection "sigs.k8s.io/controller-runtime/pkg/leaderelection/fake"
3132
"sigs.k8s.io/controller-runtime/pkg/reconcile"
3233
"sigs.k8s.io/controller-runtime/pkg/recorder"
3334
"sigs.k8s.io/controller-runtime/pkg/runtime/inject"
@@ -197,6 +198,99 @@ var _ = Describe("manger.Manager", func() {
197198
<-c2
198199
<-c3
199200
})
201+
202+
Context("with leaderelection enabled", func() {
203+
opts := Options{
204+
LeaderElection: true,
205+
LeaderElectionID: "controller-runtime",
206+
LeaderElectionNamespace: "default",
207+
newResourceLock: fakeleaderelection.NewFakeResourceLock,
208+
}
209+
210+
It("should Start each Component", func(done Done) {
211+
m, err := New(cfg, opts)
212+
Expect(err).NotTo(HaveOccurred())
213+
c1 := make(chan struct{})
214+
m.Add(RunnableFunc(func(s <-chan struct{}) error {
215+
defer close(c1)
216+
defer GinkgoRecover()
217+
return nil
218+
}))
219+
220+
c2 := make(chan struct{})
221+
m.Add(RunnableFunc(func(s <-chan struct{}) error {
222+
defer close(c2)
223+
defer GinkgoRecover()
224+
return nil
225+
}))
226+
227+
go func() {
228+
defer GinkgoRecover()
229+
Expect(m.Start(stop)).NotTo(HaveOccurred())
230+
}()
231+
<-c1
232+
<-c2
233+
234+
close(done)
235+
})
236+
237+
It("should stop when stop is called", func(done Done) {
238+
m, err := New(cfg, opts)
239+
Expect(err).NotTo(HaveOccurred())
240+
s := make(chan struct{})
241+
close(s)
242+
Expect(m.Start(s)).NotTo(HaveOccurred())
243+
244+
close(done)
245+
})
246+
247+
It("should return an error if it can't start the cache", func(done Done) {
248+
m, err := New(cfg, opts)
249+
Expect(err).NotTo(HaveOccurred())
250+
mgr, ok := m.(*controllerManager)
251+
Expect(ok).To(BeTrue())
252+
mgr.startCache = func(stop <-chan struct{}) error {
253+
return fmt.Errorf("expected error")
254+
}
255+
Expect(m.Start(stop).Error()).To(ContainSubstring("expected error"))
256+
257+
close(done)
258+
}, 5)
259+
260+
It("should return an error if any Components fail to Start", func(done Done) {
261+
m, err := New(cfg, opts)
262+
Expect(err).NotTo(HaveOccurred())
263+
c1 := make(chan struct{})
264+
m.Add(RunnableFunc(func(s <-chan struct{}) error {
265+
defer GinkgoRecover()
266+
defer close(c1)
267+
return nil
268+
}))
269+
270+
c2 := make(chan struct{})
271+
m.Add(RunnableFunc(func(s <-chan struct{}) error {
272+
defer GinkgoRecover()
273+
defer close(c2)
274+
return fmt.Errorf("expected error")
275+
}))
276+
277+
c3 := make(chan struct{})
278+
m.Add(RunnableFunc(func(s <-chan struct{}) error {
279+
defer GinkgoRecover()
280+
defer close(c3)
281+
return nil
282+
}))
283+
284+
go func() {
285+
defer GinkgoRecover()
286+
Expect(m.Start(stop)).NotTo(HaveOccurred())
287+
close(done)
288+
}()
289+
<-c1
290+
<-c2
291+
<-c3
292+
})
293+
})
200294
})
201295

202296
Describe("Add", func() {

0 commit comments

Comments
 (0)