Skip to content

Commit 027261f

Browse files
authored
Add integration test for sharder controller (#506)
* Simplify existing integration tests * Add integration test for `sharder` controller
1 parent 68aeb3e commit 027261f

File tree

4 files changed

+457
-6
lines changed

4 files changed

+457
-6
lines changed

pkg/controller/sharder/reconciler.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,13 @@ func (r *Reconciler) resyncResource(
189189
return allErrs.ErrorOrNil()
190190
}
191191

192+
var (
193+
// KeyForObject is an alias for key.ForObject, exposed for testing.
194+
KeyForObject = key.ForObject
195+
// KeyForController is an alias for key.ForController, exposed for testing.
196+
KeyForController = key.ForController
197+
)
198+
192199
func (r *Reconciler) resyncObject(
193200
ctx context.Context,
194201
log logr.Logger,
@@ -201,9 +208,9 @@ func (r *Reconciler) resyncObject(
201208
) error {
202209
log = log.WithValues("object", client.ObjectKeyFromObject(obj))
203210

204-
keyFunc := key.ForObject
211+
keyFunc := KeyForObject
205212
if controlled {
206-
keyFunc = key.ForController
213+
keyFunc = KeyForController
207214
}
208215

209216
hashKey, err := keyFunc(obj)
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
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 sharder_test
18+
19+
import (
20+
"context"
21+
"maps"
22+
"testing"
23+
"time"
24+
25+
"github.com/go-logr/logr"
26+
. "github.com/onsi/ginkgo/v2"
27+
. "github.com/onsi/gomega"
28+
corev1 "k8s.io/api/core/v1"
29+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30+
"k8s.io/apimachinery/pkg/labels"
31+
testclock "k8s.io/utils/clock/testing"
32+
"sigs.k8s.io/controller-runtime/pkg/cache"
33+
"sigs.k8s.io/controller-runtime/pkg/client"
34+
"sigs.k8s.io/controller-runtime/pkg/envtest"
35+
"sigs.k8s.io/controller-runtime/pkg/envtest/komega"
36+
logf "sigs.k8s.io/controller-runtime/pkg/log"
37+
"sigs.k8s.io/controller-runtime/pkg/log/zap"
38+
"sigs.k8s.io/controller-runtime/pkg/manager"
39+
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
40+
41+
configv1alpha1 "github.com/timebertt/kubernetes-controller-sharding/pkg/apis/config/v1alpha1"
42+
shardingv1alpha1 "github.com/timebertt/kubernetes-controller-sharding/pkg/apis/sharding/v1alpha1"
43+
"github.com/timebertt/kubernetes-controller-sharding/pkg/controller/sharder"
44+
utilclient "github.com/timebertt/kubernetes-controller-sharding/pkg/utils/client"
45+
"github.com/timebertt/kubernetes-controller-sharding/pkg/utils/test"
46+
. "github.com/timebertt/kubernetes-controller-sharding/pkg/utils/test/matchers"
47+
)
48+
49+
func TestSharder(t *testing.T) {
50+
RegisterFailHandler(Fail)
51+
RunSpecs(t, "Sharder Controller Integration Test Suite")
52+
}
53+
54+
const testID = "sharder-controller-test"
55+
56+
var (
57+
log logr.Logger
58+
59+
testClient client.Client
60+
mgrClient client.Client
61+
62+
clock *testclock.FakePassiveClock
63+
64+
testRunID string
65+
testRunLabels map[string]string
66+
67+
controllerRing *shardingv1alpha1.ControllerRing
68+
)
69+
70+
var _ = BeforeSuite(func(ctx SpecContext) {
71+
logf.SetLogger(zap.New(zap.UseDevMode(true), zap.WriteTo(GinkgoWriter)))
72+
log = logf.Log.WithName(testID)
73+
74+
By("Start test environment")
75+
testEnv := &envtest.Environment{
76+
CRDInstallOptions: envtest.CRDInstallOptions{
77+
Paths: []string{test.PathShardingCRDs()},
78+
},
79+
ErrorIfCRDPathMissing: true,
80+
}
81+
82+
restConfig, err := testEnv.Start()
83+
Expect(err).NotTo(HaveOccurred())
84+
Expect(restConfig).NotTo(BeNil())
85+
86+
DeferCleanup(func() {
87+
By("Stop test environment")
88+
Expect(testEnv.Stop()).To(Succeed())
89+
})
90+
91+
By("Create test clients")
92+
testClient, err = client.New(restConfig, client.Options{Scheme: utilclient.SharderScheme})
93+
Expect(err).NotTo(HaveOccurred())
94+
Expect(testClient).NotTo(BeNil())
95+
96+
clientContext, clientCancel := context.WithCancel(context.Background())
97+
komega.SetClient(testClient)
98+
komega.SetContext(clientContext)
99+
DeferCleanup(clientCancel)
100+
101+
By("Create test Namespace")
102+
testNamespace := &corev1.Namespace{
103+
ObjectMeta: metav1.ObjectMeta{
104+
GenerateName: testID + "-",
105+
},
106+
}
107+
Expect(testClient.Create(ctx, testNamespace)).To(Succeed())
108+
log.Info("Created Namespace for test", "namespaceName", testNamespace.Name)
109+
testRunID = testNamespace.Name
110+
log = log.WithValues("testRunID", testRunID)
111+
testRunLabels = map[string]string{testID: testRunID}
112+
113+
DeferCleanup(func(ctx SpecContext) {
114+
By("Delete test Namespace")
115+
Expect(testClient.Delete(ctx, testNamespace)).To(Or(Succeed(), BeNotFoundError()))
116+
}, NodeTimeout(time.Minute))
117+
118+
By("Create test ControllerRing")
119+
controllerRing = &shardingv1alpha1.ControllerRing{
120+
ObjectMeta: metav1.ObjectMeta{
121+
Name: testRunID,
122+
Labels: maps.Clone(testRunLabels),
123+
},
124+
Spec: shardingv1alpha1.ControllerRingSpec{
125+
Resources: []shardingv1alpha1.RingResource{{
126+
GroupResource: metav1.GroupResource{Group: "", Resource: "secrets"},
127+
ControlledResources: []metav1.GroupResource{{Group: "", Resource: "configmaps"}},
128+
}},
129+
NamespaceSelector: &metav1.LabelSelector{
130+
MatchExpressions: []metav1.LabelSelectorRequirement{{
131+
Key: corev1.LabelMetadataName,
132+
Operator: metav1.LabelSelectorOpIn,
133+
Values: []string{testRunID},
134+
}},
135+
},
136+
},
137+
}
138+
Expect(testClient.Create(ctx, controllerRing)).To(Succeed())
139+
log.Info("Created ControllerRing for test", "controllerRingName", controllerRing.Name)
140+
141+
DeferCleanup(func(ctx SpecContext) {
142+
By("Delete test ControllerRing")
143+
Expect(testClient.Delete(ctx, controllerRing)).To(Or(Succeed(), BeNotFoundError()))
144+
}, NodeTimeout(time.Minute))
145+
146+
By("Setup manager")
147+
mgr, err := manager.New(restConfig, manager.Options{
148+
Scheme: utilclient.SharderScheme,
149+
Metrics: metricsserver.Options{BindAddress: "0"},
150+
Cache: cache.Options{
151+
DefaultNamespaces: map[string]cache.Config{
152+
testNamespace.Name: {},
153+
metav1.NamespaceDefault: {},
154+
},
155+
ByObject: map[client.Object]cache.ByObject{
156+
&shardingv1alpha1.ControllerRing{}: {
157+
Label: labels.SelectorFromSet(testRunLabels),
158+
},
159+
},
160+
},
161+
})
162+
Expect(err).NotTo(HaveOccurred())
163+
mgrClient = mgr.GetClient()
164+
165+
By("Register controller")
166+
config := &configv1alpha1.SharderConfig{}
167+
mgr.GetScheme().Default(config)
168+
169+
clock = testclock.NewFakePassiveClock(time.Now())
170+
171+
Expect((&sharder.Reconciler{
172+
Clock: clock,
173+
Config: config,
174+
}).AddToManager(mgr)).To(Succeed())
175+
176+
By("Start manager")
177+
mgrContext, mgrCancel := context.WithCancel(context.Background())
178+
179+
go func() {
180+
defer GinkgoRecover()
181+
Expect(mgr.Start(mgrContext)).To(Succeed())
182+
}()
183+
184+
DeferCleanup(func() {
185+
By("Stop manager")
186+
mgrCancel()
187+
})
188+
}, NodeTimeout(time.Minute))

0 commit comments

Comments
 (0)