Skip to content

Commit 342a97a

Browse files
authored
Add integration test for shardlease controller (#488)
* Update `controllerring` integration test * Add integration test for `shardlease` controller
1 parent 22c8fc2 commit 342a97a

File tree

7 files changed

+323
-20
lines changed

7 files changed

+323
-20
lines changed

pkg/controller/shardlease/add.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@ package shardlease
1818

1919
import (
2020
coordinationv1 "k8s.io/api/coordination/v1"
21+
"k8s.io/client-go/util/workqueue"
2122
"k8s.io/utils/clock"
2223
"sigs.k8s.io/controller-runtime/pkg/builder"
2324
"sigs.k8s.io/controller-runtime/pkg/controller"
2425
"sigs.k8s.io/controller-runtime/pkg/event"
2526
"sigs.k8s.io/controller-runtime/pkg/handler"
2627
"sigs.k8s.io/controller-runtime/pkg/manager"
2728
"sigs.k8s.io/controller-runtime/pkg/predicate"
29+
"sigs.k8s.io/controller-runtime/pkg/reconcile"
2830

2931
shardingv1alpha1 "github.com/timebertt/kubernetes-controller-sharding/pkg/apis/sharding/v1alpha1"
3032
shardinghandler "github.com/timebertt/kubernetes-controller-sharding/pkg/sharding/handler"
@@ -54,6 +56,12 @@ func (r *Reconciler) AddToManager(mgr manager.Manager) error {
5456
).
5557
WithOptions(controller.Options{
5658
MaxConcurrentReconciles: 5,
59+
NewQueue: func(controllerName string, rateLimiter workqueue.TypedRateLimiter[reconcile.Request]) workqueue.TypedRateLimitingInterface[reconcile.Request] {
60+
return workqueue.NewTypedRateLimitingQueueWithConfig(rateLimiter, workqueue.TypedRateLimitingQueueConfig[reconcile.Request]{
61+
Name: controllerName,
62+
Clock: r.Clock,
63+
})
64+
},
5765
}).
5866
Complete(r)
5967
}

pkg/controller/shardlease/add_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,11 @@ var _ = Describe("Reconciler", func() {
4444
p predicate.Predicate
4545
obj, objOld *coordinationv1.Lease
4646

47-
fakeClock *testing.FakePassiveClock
47+
fakeClock *testing.FakeClock
4848
)
4949

5050
BeforeEach(func() {
51-
fakeClock = testing.NewFakePassiveClock(time.Now())
51+
fakeClock = testing.NewFakeClock(time.Now())
5252
r.Clock = fakeClock
5353

5454
p = r.LeasePredicate()

pkg/controller/shardlease/reconciler.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ import (
3939
// Reconciler reconciles shard leases.
4040
type Reconciler struct {
4141
Client client.Client
42-
Clock clock.PassiveClock
42+
Clock clock.WithTicker
4343
}
4444

4545
// Reconcile reconciles a Lease object.

test/integration/sharder/controller/controllerring/controllerring_suite_test.go

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import (
2424
"github.com/go-logr/logr"
2525
. "github.com/onsi/ginkgo/v2"
2626
. "github.com/onsi/gomega"
27-
coordinationv1 "k8s.io/api/coordination/v1"
2827
corev1 "k8s.io/api/core/v1"
2928
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3029
"k8s.io/apimachinery/pkg/labels"
@@ -51,7 +50,7 @@ func TestControllerRing(t *testing.T) {
5150
RunSpecs(t, "Sharder ControllerRing Controller Integration Test Suite")
5251
}
5352

54-
const testID = "controllerring-test"
53+
const testID = "controllerring-controller-test"
5554

5655
var (
5756
log logr.Logger
@@ -60,7 +59,6 @@ var (
6059

6160
clock *testclock.FakePassiveClock
6261

63-
testNamespace *corev1.Namespace
6462
testRunID string
6563
testRunLabels map[string]string
6664
)
@@ -97,7 +95,7 @@ var _ = BeforeSuite(func(ctx SpecContext) {
9795
DeferCleanup(clientCancel)
9896

9997
By("Create test Namespace")
100-
testNamespace = &corev1.Namespace{
98+
testNamespace := &corev1.Namespace{
10199
ObjectMeta: metav1.ObjectMeta{
102100
GenerateName: testID + "-",
103101
},
@@ -123,9 +121,6 @@ var _ = BeforeSuite(func(ctx SpecContext) {
123121
&shardingv1alpha1.ControllerRing{}: {
124122
Label: labels.SelectorFromSet(testRunLabels),
125123
},
126-
&coordinationv1.Lease{}: {
127-
Label: labels.SelectorFromSet(testRunLabels),
128-
},
129124
},
130125
},
131126
})

test/integration/sharder/controller/controllerring/controllerring_test.go

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -102,38 +102,38 @@ var _ = Describe("ControllerRing controller", func() {
102102
Describe("should reflect the shard leases in the status", Ordered, func() {
103103
var lease *coordinationv1.Lease
104104

105-
It("Create available shard lease", func(ctx SpecContext) {
105+
It("create available shard lease", func(ctx SpecContext) {
106106
lease = newLease(controllerRing.Name)
107107
Expect(testClient.Create(ctx, lease)).To(Succeed())
108108

109109
Eventually(ctx, Object(controllerRing)).Should(haveStatusShards(1, 1))
110110
}, SpecTimeout(time.Minute))
111111

112-
It("Create orphaned shard lease", func(ctx SpecContext) {
112+
It("create orphaned shard lease", func(ctx SpecContext) {
113113
lease = newLease(controllerRing.Name)
114114
lease.Spec.HolderIdentity = nil
115115
Expect(testClient.Create(ctx, lease)).To(Succeed())
116116

117117
Eventually(ctx, Object(controllerRing)).Should(haveStatusShards(1, 2))
118118
}, SpecTimeout(time.Minute))
119119

120-
It("Make lease healthy", func(ctx SpecContext) {
120+
It("make lease healthy", func(ctx SpecContext) {
121121
Eventually(ctx, Update(lease, func() {
122122
lease.Spec.HolderIdentity = ptr.To(lease.Name)
123123
})).Should(Succeed())
124124

125125
Eventually(ctx, Object(controllerRing)).Should(haveStatusShards(2, 2))
126126
}, SpecTimeout(time.Minute))
127127

128-
It("Make lease unhealthy", func(ctx SpecContext) {
128+
It("make lease unhealthy", func(ctx SpecContext) {
129129
Eventually(ctx, Update(lease, func() {
130130
lease.Spec.HolderIdentity = nil
131131
})).Should(Succeed())
132132

133133
Eventually(ctx, Object(controllerRing)).Should(haveStatusShards(1, 2))
134134
}, SpecTimeout(time.Minute))
135135

136-
It("Delete unhealthy lease", func(ctx SpecContext) {
136+
It("delete unhealthy lease", func(ctx SpecContext) {
137137
Expect(testClient.Delete(ctx, lease)).To(Succeed())
138138

139139
Eventually(ctx, Object(controllerRing)).Should(haveStatusShards(1, 1))
@@ -144,11 +144,13 @@ var _ = Describe("ControllerRing controller", func() {
144144
func newLease(controllerRingName string) *coordinationv1.Lease {
145145
name := testRunID + "-" + test.RandomSuffix()
146146

147-
lease := &coordinationv1.Lease{
147+
return &coordinationv1.Lease{
148148
ObjectMeta: metav1.ObjectMeta{
149149
Name: name,
150150
Namespace: testRunID,
151-
Labels: maps.Clone(testRunLabels),
151+
Labels: map[string]string{
152+
shardingv1alpha1.LabelControllerRing: controllerRingName,
153+
},
152154
},
153155
Spec: coordinationv1.LeaseSpec{
154156
HolderIdentity: ptr.To(name),
@@ -157,9 +159,6 @@ func newLease(controllerRingName string) *coordinationv1.Lease {
157159
RenewTime: ptr.To(metav1.NewMicroTime(clock.Now().Add(-2 * time.Second))),
158160
},
159161
}
160-
metav1.SetMetaDataLabel(&lease.ObjectMeta, shardingv1alpha1.LabelControllerRing, controllerRingName)
161-
162-
return lease
163162
}
164163

165164
func haveStatusShards(availableShards, shards int32) gomegatypes.GomegaMatcher {
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
/*
2+
Copyright 2025 Tim Ebert.
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 shardlease_test
18+
19+
import (
20+
"context"
21+
"testing"
22+
"time"
23+
24+
"github.com/go-logr/logr"
25+
. "github.com/onsi/ginkgo/v2"
26+
. "github.com/onsi/gomega"
27+
corev1 "k8s.io/api/core/v1"
28+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29+
"k8s.io/apimachinery/pkg/labels"
30+
testclock "k8s.io/utils/clock/testing"
31+
"sigs.k8s.io/controller-runtime/pkg/cache"
32+
"sigs.k8s.io/controller-runtime/pkg/client"
33+
"sigs.k8s.io/controller-runtime/pkg/envtest"
34+
"sigs.k8s.io/controller-runtime/pkg/envtest/komega"
35+
logf "sigs.k8s.io/controller-runtime/pkg/log"
36+
"sigs.k8s.io/controller-runtime/pkg/log/zap"
37+
"sigs.k8s.io/controller-runtime/pkg/manager"
38+
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
39+
40+
configv1alpha1 "github.com/timebertt/kubernetes-controller-sharding/pkg/apis/config/v1alpha1"
41+
shardingv1alpha1 "github.com/timebertt/kubernetes-controller-sharding/pkg/apis/sharding/v1alpha1"
42+
"github.com/timebertt/kubernetes-controller-sharding/pkg/controller/shardlease"
43+
utilclient "github.com/timebertt/kubernetes-controller-sharding/pkg/utils/client"
44+
"github.com/timebertt/kubernetes-controller-sharding/pkg/utils/test"
45+
. "github.com/timebertt/kubernetes-controller-sharding/pkg/utils/test/matchers"
46+
)
47+
48+
func TestShardLease(t *testing.T) {
49+
RegisterFailHandler(Fail)
50+
RunSpecs(t, "Sharder Shard Lease Controller Integration Test Suite")
51+
}
52+
53+
const testID = "shardlease-controller-test"
54+
55+
var (
56+
log logr.Logger
57+
58+
testClient client.Client
59+
60+
clock *testclock.FakeClock
61+
62+
testRunID string
63+
testRunLabels map[string]string
64+
)
65+
66+
var _ = BeforeSuite(func(ctx SpecContext) {
67+
logf.SetLogger(zap.New(zap.UseDevMode(true), zap.WriteTo(GinkgoWriter)))
68+
log = logf.Log.WithName(testID)
69+
70+
By("Start test environment")
71+
testEnv := &envtest.Environment{
72+
CRDInstallOptions: envtest.CRDInstallOptions{
73+
Paths: []string{test.PathShardingCRDs()},
74+
},
75+
ErrorIfCRDPathMissing: true,
76+
}
77+
78+
restConfig, err := testEnv.Start()
79+
Expect(err).NotTo(HaveOccurred())
80+
Expect(restConfig).NotTo(BeNil())
81+
82+
DeferCleanup(func() {
83+
By("Stop test environment")
84+
Expect(testEnv.Stop()).To(Succeed())
85+
})
86+
87+
By("Create test clients")
88+
testClient, err = client.New(restConfig, client.Options{Scheme: utilclient.SharderScheme})
89+
Expect(err).NotTo(HaveOccurred())
90+
Expect(testClient).NotTo(BeNil())
91+
92+
clientContext, clientCancel := context.WithCancel(context.Background())
93+
komega.SetClient(testClient)
94+
komega.SetContext(clientContext)
95+
DeferCleanup(clientCancel)
96+
97+
By("Create test Namespace")
98+
testNamespace := &corev1.Namespace{
99+
ObjectMeta: metav1.ObjectMeta{
100+
GenerateName: testID + "-",
101+
},
102+
}
103+
Expect(testClient.Create(ctx, testNamespace)).To(Succeed())
104+
log.Info("Created Namespace for test", "namespaceName", testNamespace.Name)
105+
testRunID = testNamespace.Name
106+
log = log.WithValues("testRunID", testRunID)
107+
testRunLabels = map[string]string{testID: testRunID}
108+
109+
DeferCleanup(func(ctx SpecContext) {
110+
By("Delete test Namespace")
111+
Expect(testClient.Delete(ctx, testNamespace)).To(Or(Succeed(), BeNotFoundError()))
112+
}, NodeTimeout(time.Minute))
113+
114+
By("Setup manager")
115+
mgr, err := manager.New(restConfig, manager.Options{
116+
Scheme: utilclient.SharderScheme,
117+
Metrics: metricsserver.Options{BindAddress: "0"},
118+
Cache: cache.Options{
119+
DefaultNamespaces: map[string]cache.Config{testNamespace.Name: {}},
120+
ByObject: map[client.Object]cache.ByObject{
121+
&shardingv1alpha1.ControllerRing{}: {
122+
Label: labels.SelectorFromSet(testRunLabels),
123+
},
124+
},
125+
},
126+
})
127+
Expect(err).NotTo(HaveOccurred())
128+
129+
By("Register controller")
130+
config := &configv1alpha1.SharderConfig{}
131+
mgr.GetScheme().Default(config)
132+
133+
clock = testclock.NewFakeClock(time.Now())
134+
135+
Expect((&shardlease.Reconciler{
136+
Clock: clock,
137+
}).AddToManager(mgr)).To(Succeed())
138+
139+
By("Start manager")
140+
mgrContext, mgrCancel := context.WithCancel(context.Background())
141+
142+
go func() {
143+
defer GinkgoRecover()
144+
Expect(mgr.Start(mgrContext)).To(Succeed())
145+
}()
146+
147+
DeferCleanup(func() {
148+
By("Stop manager")
149+
mgrCancel()
150+
})
151+
}, NodeTimeout(time.Minute))

0 commit comments

Comments
 (0)