Skip to content

Commit af698ed

Browse files
committed
test: adapt shared TLS coverage for release-1.1
1 parent 198b000 commit af698ed

2 files changed

Lines changed: 282 additions & 0 deletions

File tree

controllers/apps/cluster/cluster_controller_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -827,6 +827,7 @@ var _ = Describe("Cluster Controller", func() {
827827
It("creates one KubeBlocks TLS source shared by every shard", func() {
828828
shardingDef := testapps.NewShardingDefinitionFactory("shared-tls-sharding", compDefObj.Name).
829829
WithRandomName()
830+
shardingDef.AddAnnotations(constant.CRDAPIVersionAnnotationKey, appsv1.GroupVersion.String())
830831
shardingDef.Get().Spec.TLS = &appsv1.ShardingTLS{Shared: ptr.To(true)}
831832
shardingDefObj := shardingDef.Create(&testCtx).GetObject()
832833
Eventually(testapps.CheckObj(&testCtx, client.ObjectKeyFromObject(shardingDefObj),
@@ -893,6 +894,7 @@ var _ = Describe("Cluster Controller", func() {
893894
It("applies shared TLS per shard template ShardingDefinition", func() {
894895
createShardingDef := func(prefix string, shared bool) *appsv1.ShardingDefinition {
895896
factory := testapps.NewShardingDefinitionFactory(prefix, compDefObj.Name).WithRandomName()
897+
factory.AddAnnotations(constant.CRDAPIVersionAnnotationKey, appsv1.GroupVersion.String())
896898
factory.Get().Spec.TLS = &appsv1.ShardingTLS{Shared: ptr.To(shared)}
897899
obj := factory.Create(&testCtx).GetObject()
898900
Eventually(testapps.CheckObj(&testCtx, client.ObjectKeyFromObject(obj),
Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
/*
2+
Copyright (C) 2022-2026 ApeCloud Co., Ltd
3+
4+
This file is part of KubeBlocks project
5+
6+
This program is free software: you can redistribute it and/or modify
7+
it under the terms of the GNU Affero General Public License as published by
8+
the Free Software Foundation, either version 3 of the License, or
9+
(at your option) any later version.
10+
11+
This program is distributed in the hope that it will be useful
12+
but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
GNU Affero General Public License for more details.
15+
16+
You should have received a copy of the GNU Affero General Public License
17+
along with this program. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
package cluster
21+
22+
import (
23+
"context"
24+
25+
. "github.com/onsi/ginkgo/v2"
26+
. "github.com/onsi/gomega"
27+
28+
corev1 "k8s.io/api/core/v1"
29+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30+
"k8s.io/apimachinery/pkg/runtime"
31+
"k8s.io/apimachinery/pkg/util/sets"
32+
"k8s.io/utils/ptr"
33+
"sigs.k8s.io/controller-runtime/pkg/client"
34+
"sigs.k8s.io/controller-runtime/pkg/client/fake"
35+
36+
appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
37+
"github.com/apecloud/kubeblocks/pkg/constant"
38+
)
39+
40+
var _ = Describe("cluster sharding shared TLS transformer", func() {
41+
const (
42+
namespace = "default"
43+
clusterName = "cluster"
44+
shardingName = "shard"
45+
compDefName = "compdef"
46+
)
47+
48+
newTestScheme := func() *runtime.Scheme {
49+
scheme := runtime.NewScheme()
50+
Expect(corev1.AddToScheme(scheme)).Should(Succeed())
51+
Expect(appsv1.AddToScheme(scheme)).Should(Succeed())
52+
return scheme
53+
}
54+
newObjectMeta := func(name, namespace string) metav1.ObjectMeta {
55+
return metav1.ObjectMeta{Name: name, Namespace: namespace}
56+
}
57+
58+
newTransformContext := func(objects ...client.Object) *clusterTransformContext {
59+
return &clusterTransformContext{
60+
Context: context.Background(),
61+
Client: fake.NewClientBuilder().
62+
WithScheme(newTestScheme()).
63+
WithObjects(objects...).
64+
Build(),
65+
Cluster: &appsv1.Cluster{
66+
ObjectMeta: newObjectMeta(clusterName, namespace),
67+
},
68+
OrigCluster: &appsv1.Cluster{
69+
ObjectMeta: newObjectMeta(clusterName, namespace),
70+
},
71+
}
72+
}
73+
74+
newComponentDefinition := func() *appsv1.ComponentDefinition {
75+
caFile := "ca.pem"
76+
certFile := "tls.crt"
77+
keyFile := "tls.key"
78+
return &appsv1.ComponentDefinition{
79+
ObjectMeta: newObjectMeta(compDefName, ""),
80+
Spec: appsv1.ComponentDefinitionSpec{
81+
Labels: map[string]string{"def-label": "yes"},
82+
Annotations: map[string]string{"def-annotation": "yes"},
83+
TLS: &appsv1.TLS{
84+
CAFile: &caFile,
85+
CertFile: &certFile,
86+
KeyFile: &keyFile,
87+
},
88+
},
89+
}
90+
}
91+
92+
newSharding := func() *appsv1.ClusterSharding {
93+
return &appsv1.ClusterSharding{
94+
Name: shardingName,
95+
ShardingDef: "sharddef",
96+
Template: appsv1.ClusterComponentSpec{
97+
ComponentDef: compDefName,
98+
Labels: map[string]string{"template-label": "yes"},
99+
Annotations: map[string]string{"template-annotation": "yes"},
100+
TLS: true,
101+
Issuer: &appsv1.Issuer{Name: appsv1.IssuerKubeBlocks},
102+
},
103+
}
104+
}
105+
106+
newShardingDefinition := func() *appsv1.ShardingDefinition {
107+
return &appsv1.ShardingDefinition{
108+
ObjectMeta: newObjectMeta("sharddef", ""),
109+
Spec: appsv1.ShardingDefinitionSpec{
110+
TLS: &appsv1.ShardingTLS{Shared: ptr.To(true)},
111+
},
112+
}
113+
}
114+
115+
It("builds deterministic shared TLS secret metadata and rewrites expanded shard specs", func() {
116+
transformer := &clusterShardingTLSTransformer{}
117+
transCtx := newTransformContext()
118+
sharding := newSharding()
119+
flattenedComp := sharding.Template.DeepCopy()
120+
flattenedComp.Name = "default-shard"
121+
templatedComp := sharding.Template.DeepCopy()
122+
templatedComp.Name = "templated-shard"
123+
transCtx.shardingComps = map[string][]*appsv1.ClusterComponentSpec{
124+
sharding.Name: {flattenedComp, templatedComp},
125+
}
126+
transCtx.shardingCompsWithTpl = map[string]map[string][]*appsv1.ClusterComponentSpec{
127+
sharding.Name: {"": {flattenedComp}, "template": {templatedComp}},
128+
}
129+
130+
secret := transformer.newTLSSecret(transCtx, sharding)
131+
Expect(secret.Namespace).Should(Equal(namespace))
132+
Expect(secret.Name).Should(Equal(shardingTLSSecretName(clusterName, shardingName)))
133+
Expect(secret.Labels).Should(HaveKeyWithValue(constant.AppInstanceLabelKey, clusterName))
134+
Expect(secret.Labels).Should(HaveKeyWithValue(constant.KBAppShardingNameLabelKey, shardingName))
135+
Expect(secret.Labels).Should(HaveKeyWithValue("template-label", "yes"))
136+
Expect(secret.Annotations).Should(HaveKeyWithValue("template-annotation", "yes"))
137+
Expect(secret.Data).Should(BeEmpty())
138+
139+
transformer.rewriteTLSConfig(transCtx, sharding, sets.New("", "template"))
140+
Expect(sharding.Template.Issuer.Name).Should(Equal(appsv1.IssuerKubeBlocks))
141+
for _, comp := range []*appsv1.ClusterComponentSpec{flattenedComp, templatedComp} {
142+
Expect(comp.Issuer.Name).Should(Equal(appsv1.IssuerUserProvided))
143+
Expect(comp.Issuer.SecretRef.Name).Should(Equal(shardingTLSSecretName(clusterName, shardingName)))
144+
Expect(comp.Issuer.SecretRef.CA).Should(Equal(shardingTLSCAKey))
145+
Expect(comp.Issuer.SecretRef.Cert).Should(Equal(shardingTLSCertKey))
146+
Expect(comp.Issuer.SecretRef.Key).Should(Equal(shardingTLSKeyKey))
147+
}
148+
})
149+
150+
It("keeps managed labels authoritative on the shared TLS secret", func() {
151+
transformer := &clusterShardingTLSTransformer{}
152+
transCtx := newTransformContext()
153+
sharding := newSharding()
154+
sharding.Template.Labels[constant.AppInstanceLabelKey] = "other-cluster"
155+
sharding.Template.Labels[constant.KBAppShardingNameLabelKey] = "other-sharding"
156+
157+
secret := transformer.newTLSSecret(transCtx, sharding)
158+
Expect(secret.Labels).Should(HaveKeyWithValue(constant.AppInstanceLabelKey, clusterName))
159+
Expect(secret.Labels).Should(HaveKeyWithValue(constant.KBAppShardingNameLabelKey, shardingName))
160+
})
161+
162+
It("handles stable shared TLS reconciliation guard branches", func() {
163+
transformer := &clusterShardingTLSTransformer{}
164+
transCtx := newTransformContext()
165+
sharding := newSharding()
166+
167+
sharding.Template.TLS = false
168+
sharding.Template.Issuer = nil
169+
Expect(transformer.reconcileShardingTLS(transCtx, nil, nil, sharding, sets.New(""))).Should(Succeed())
170+
171+
sharding.Template.TLS = true
172+
Expect(transformer.reconcileShardingTLS(transCtx, nil, nil, sharding, sets.New(""))).Should(
173+
MatchError("issuer shouldn't be nil when tls enabled"))
174+
175+
sharding.Template.Issuer = &appsv1.Issuer{Name: appsv1.IssuerUserProvided}
176+
Expect(transformer.reconcileShardingTLS(transCtx, nil, nil, sharding, sets.New(""))).Should(Succeed())
177+
})
178+
179+
It("honors TLS sharing overrides from each shard template's ShardingDefinition", func() {
180+
transformer := &clusterShardingTLSTransformer{}
181+
transCtx := newTransformContext()
182+
sharding := newSharding()
183+
sharding.ShardTemplates = []appsv1.ShardTemplate{
184+
{Name: "private", ShardingDef: ptr.To("plain-def")},
185+
{Name: "shared", ShardingDef: ptr.To("shared-def")},
186+
}
187+
transCtx.shardingDefs = map[string]*appsv1.ShardingDefinition{
188+
"sharddef": newShardingDefinition(),
189+
"shared-def": newShardingDefinition(),
190+
"plain-def": {Spec: appsv1.ShardingDefinitionSpec{TLS: &appsv1.ShardingTLS{Shared: ptr.To(false)}}},
191+
}
192+
193+
defaultComp := sharding.Template.DeepCopy()
194+
defaultComp.Name = "default"
195+
privateComp := sharding.Template.DeepCopy()
196+
privateComp.Name = "private"
197+
sharedComp := sharding.Template.DeepCopy()
198+
sharedComp.Name = "shared"
199+
transCtx.shardingComps = map[string][]*appsv1.ClusterComponentSpec{
200+
sharding.Name: {defaultComp, privateComp, sharedComp},
201+
}
202+
transCtx.shardingCompsWithTpl = map[string]map[string][]*appsv1.ClusterComponentSpec{
203+
sharding.Name: {"": {defaultComp}, "private": {privateComp}, "shared": {sharedComp}},
204+
}
205+
206+
sharedTemplates := transformer.sharedShardTemplates(transCtx, sharding)
207+
Expect(sharedTemplates).Should(Equal(sets.New("", "shared")))
208+
transformer.rewriteTLSConfig(transCtx, sharding, sharedTemplates)
209+
Expect(defaultComp.Issuer.Name).Should(Equal(appsv1.IssuerUserProvided))
210+
Expect(sharedComp.Issuer.Name).Should(Equal(appsv1.IssuerUserProvided))
211+
Expect(privateComp.Issuer.Name).Should(Equal(appsv1.IssuerKubeBlocks))
212+
Expect(sharding.Template.Issuer.Name).Should(Equal(appsv1.IssuerKubeBlocks))
213+
})
214+
215+
It("uses fixed source keys when ComponentDefinition TLS file names are omitted", func() {
216+
transformer := &clusterShardingTLSTransformer{}
217+
transCtx := newTransformContext()
218+
sharding := newSharding()
219+
compDef := newComponentDefinition()
220+
compDef.Spec.TLS.CAFile = nil
221+
compDef.Spec.TLS.CertFile = nil
222+
compDef.Spec.TLS.KeyFile = nil
223+
transCtx.componentDefs = map[string]*appsv1.ComponentDefinition{compDef.Name: compDef}
224+
225+
secret, err := transformer.buildTLSSecret(transCtx, sharding)
226+
Expect(err).ShouldNot(HaveOccurred())
227+
Expect(secret.Data).Should(HaveKey(shardingTLSCAKey))
228+
Expect(secret.Data).Should(HaveKey(shardingTLSCertKey))
229+
Expect(secret.Data).Should(HaveKey(shardingTLSKeyKey))
230+
231+
comp := sharding.Template.DeepCopy()
232+
transCtx.shardingComps = map[string][]*appsv1.ClusterComponentSpec{sharding.Name: {comp}}
233+
transCtx.shardingCompsWithTpl = map[string]map[string][]*appsv1.ClusterComponentSpec{
234+
sharding.Name: {"": {comp}},
235+
}
236+
transformer.rewriteTLSConfig(transCtx, sharding, sets.New(""))
237+
Expect(comp.Issuer.SecretRef.CA).Should(Equal(shardingTLSCAKey))
238+
Expect(comp.Issuer.SecretRef.Cert).Should(Equal(shardingTLSCertKey))
239+
Expect(comp.Issuer.SecretRef.Key).Should(Equal(shardingTLSKeyKey))
240+
})
241+
242+
It("checks shared TLS secret existence with a fake client", func() {
243+
transformer := &clusterShardingTLSTransformer{}
244+
sharding := newSharding()
245+
246+
secret, err := transformer.checkTLSSecret(newTransformContext(), sharding)
247+
Expect(err).ShouldNot(HaveOccurred())
248+
Expect(secret).Should(BeNil())
249+
250+
existing := &corev1.Secret{ObjectMeta: newObjectMeta(shardingTLSSecretName(clusterName, shardingName), namespace)}
251+
existing.Labels = constant.GetClusterLabels(clusterName, map[string]string{
252+
constant.KBAppShardingNameLabelKey: shardingName,
253+
})
254+
secret, err = transformer.checkTLSSecret(newTransformContext(existing), sharding)
255+
Expect(err).ShouldNot(HaveOccurred())
256+
Expect(secret).ShouldNot(BeNil())
257+
258+
unmanaged := existing.DeepCopy()
259+
unmanaged.Labels = nil
260+
secret, err = transformer.checkTLSSecret(newTransformContext(unmanaged), sharding)
261+
Expect(err).Should(MatchError(ContainSubstring("is not managed by sharding")))
262+
Expect(secret).Should(BeNil())
263+
})
264+
265+
It("rejects shared TLS before dereferencing an unavailable TLS definition", func() {
266+
transformer := &clusterShardingTLSTransformer{}
267+
sharding := newSharding()
268+
transCtx := newTransformContext()
269+
transCtx.componentDefs = map[string]*appsv1.ComponentDefinition{}
270+
271+
Expect(transformer.reconcileShardingTLS(transCtx, nil, nil, sharding, sets.New(""))).Should(
272+
MatchError(ContainSubstring("component definition \"compdef\" not found")))
273+
274+
compDef := newComponentDefinition()
275+
compDef.Spec.TLS = nil
276+
transCtx.componentDefs[compDefName] = compDef
277+
Expect(transformer.reconcileShardingTLS(transCtx, nil, nil, sharding, sets.New(""))).Should(
278+
MatchError(ContainSubstring("doesn't support it")))
279+
})
280+
})

0 commit comments

Comments
 (0)