Skip to content

Commit d458ef0

Browse files
committed
Test leaderelection code path with fake resource lock
1 parent 8381e45 commit d458ef0

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

201295
Describe("Add", func() {

0 commit comments

Comments
 (0)