Skip to content

Commit dbe5adf

Browse files
Merge pull request #557 from fmount/topology_envtest
Improve topology testing coverage
2 parents c3a9cfb + 16991cf commit dbe5adf

File tree

2 files changed

+100
-13
lines changed

2 files changed

+100
-13
lines changed

tests/functional/base_test.go

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,11 @@ import (
2626
"k8s.io/apimachinery/pkg/types"
2727
"sigs.k8s.io/controller-runtime/pkg/client"
2828

29+
topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1"
2930
keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1"
3031
keystone_base "github.com/openstack-k8s-operators/keystone-operator/pkg/keystone"
3132
condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition"
33+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3234
)
3335

3436
func GetKeystoneAPISpec(fernetMaxKeys int32) map[string]interface{} {
@@ -121,14 +123,14 @@ func CreateKeystoneMessageBusSecret(namespace string, name string) *corev1.Secre
121123
}
122124

123125
// GetSampleTopologySpec - A sample (and opinionated) Topology Spec used to
124-
// test Keystone
126+
// test KeystoneAPI
125127
// Note this is just an example that should not be used in production for
126128
// multiple reasons:
127129
// 1. It uses ScheduleAnyway as strategy, which is something we might
128130
// want to avoid by default
129131
// 2. Usually a topologySpreadConstraints is used to take care about
130132
// multi AZ, which is not applicable in this context
131-
func GetSampleTopologySpec() map[string]interface{} {
133+
func GetSampleTopologySpec(label string) (map[string]interface{}, []corev1.TopologySpreadConstraint) {
132134
// Build the topology Spec
133135
topologySpec := map[string]interface{}{
134136
"topologySpreadConstraints": []map[string]interface{}{
@@ -138,17 +140,35 @@ func GetSampleTopologySpec() map[string]interface{} {
138140
"whenUnsatisfiable": "ScheduleAnyway",
139141
"labelSelector": map[string]interface{}{
140142
"matchLabels": map[string]interface{}{
141-
"service": keystone_base.ServiceName,
143+
"service": keystone_base.ServiceName,
144+
"component": label,
142145
},
143146
},
144147
},
145148
},
146149
}
147-
return topologySpec
150+
// Build the topologyObj representation
151+
topologySpecObj := []corev1.TopologySpreadConstraint{
152+
{
153+
MaxSkew: 1,
154+
TopologyKey: corev1.LabelHostname,
155+
WhenUnsatisfiable: corev1.ScheduleAnyway,
156+
LabelSelector: &metav1.LabelSelector{
157+
MatchLabels: map[string]string{
158+
"service": keystone_base.ServiceName,
159+
"component": label,
160+
},
161+
},
162+
},
163+
}
164+
return topologySpec, topologySpecObj
148165
}
149166

150167
// CreateTopology - Creates a Topology CR based on the spec passed as input
151-
func CreateTopology(topology types.NamespacedName, spec map[string]interface{}) client.Object {
168+
func CreateTopology(
169+
topology types.NamespacedName,
170+
spec map[string]interface{},
171+
) (client.Object, topologyv1.TopoRef) {
152172
raw := map[string]interface{}{
153173
"apiVersion": "topology.openstack.org/v1beta1",
154174
"kind": "Topology",
@@ -158,5 +178,20 @@ func CreateTopology(topology types.NamespacedName, spec map[string]interface{})
158178
},
159179
"spec": spec,
160180
}
161-
return th.CreateUnstructured(raw)
181+
// other than creating the topology based on the raw spec, we return the
182+
// TopoRef that can be referenced
183+
topologyRef := topologyv1.TopoRef{
184+
Name: topology.Name,
185+
Namespace: topology.Namespace,
186+
}
187+
return th.CreateUnstructured(raw), topologyRef
188+
}
189+
190+
// GetTopology - Returns the referenced Topology
191+
func GetTopology(name types.NamespacedName) *topologyv1.Topology {
192+
instance := &topologyv1.Topology{}
193+
Eventually(func(g Gomega) {
194+
g.Expect(k8sClient.Get(ctx, name, instance)).Should(Succeed())
195+
}, timeout, interval).Should(Succeed())
196+
return instance
162197
}

tests/functional/keystoneapi_controller_test.go

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
. "github.com/openstack-k8s-operators/lib-common/modules/common/test/helpers"
3232

3333
memcachedv1 "github.com/openstack-k8s-operators/infra-operator/apis/memcached/v1beta1"
34+
topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1"
3435
condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition"
3536
mariadb_test "github.com/openstack-k8s-operators/mariadb-operator/api/test/helpers"
3637
mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1"
@@ -1454,16 +1455,28 @@ var _ = Describe("Keystone controller", func() {
14541455
})
14551456

14561457
When("Topology is referenced", func() {
1458+
var topologyRef, topologyRefAlt *topologyv1.TopoRef
14571459
BeforeEach(func() {
1458-
// Build the topology Spec
1459-
topologySpec := GetSampleTopologySpec()
1460+
1461+
// Define the two topology references used in this test
1462+
topologyRef = &topologyv1.TopoRef{
1463+
Name: keystoneAPITopologies[0].Name,
1464+
Namespace: keystoneAPITopologies[0].Namespace,
1465+
}
1466+
topologyRefAlt = &topologyv1.TopoRef{
1467+
Name: keystoneAPITopologies[1].Name,
1468+
Namespace: keystoneAPITopologies[1].Namespace,
1469+
}
1470+
14601471
// Create Test Topologies
14611472
for _, t := range keystoneAPITopologies {
1473+
// Build the topology Spec
1474+
topologySpec, _ := GetSampleTopologySpec(t.Name)
14621475
CreateTopology(t, topologySpec)
14631476
}
14641477
spec := GetDefaultKeystoneAPISpec()
14651478
spec["topologyRef"] = map[string]interface{}{
1466-
"name": keystoneAPITopologies[0].Name,
1479+
"name": topologyRef.Name,
14671480
}
14681481
DeferCleanup(
14691482
k8sClient.Delete, ctx, CreateKeystoneMessageBusSecret(namespace, "rabbitmq-secret"))
@@ -1499,28 +1512,55 @@ var _ = Describe("Keystone controller", func() {
14991512

15001513
It("check topology has been applied", func() {
15011514
Eventually(func(g Gomega) {
1515+
tp := GetTopology(types.NamespacedName{
1516+
Name: topologyRef.Name,
1517+
Namespace: topologyRef.Namespace,
1518+
})
1519+
finalizers := tp.GetFinalizers()
1520+
g.Expect(finalizers).To(HaveLen(1))
15021521
keystoneAPI := GetKeystoneAPI(keystoneAPIName)
15031522
g.Expect(keystoneAPI.Status.LastAppliedTopology).ToNot(BeNil())
1504-
g.Expect(keystoneAPI.Status.LastAppliedTopology.Name).To(Equal(keystoneAPITopologies[0].Name))
1523+
g.Expect(keystoneAPI.Status.LastAppliedTopology).To(Equal(topologyRef))
1524+
g.Expect(finalizers).To(ContainElement(
1525+
fmt.Sprintf("openstack.org/keystoneapi-%s", keystoneAPIName.Name)))
15051526
}, timeout, interval).Should(Succeed())
15061527
})
15071528
It("sets topology in resource specs", func() {
15081529
Eventually(func(g Gomega) {
1509-
g.Expect(th.GetDeployment(deploymentName).Spec.Template.Spec.TopologySpreadConstraints).ToNot(BeNil())
1530+
_, topologySpecObj := GetSampleTopologySpec(topologyRef.Name)
15101531
g.Expect(th.GetDeployment(deploymentName).Spec.Template.Spec.Affinity).To(BeNil())
1532+
g.Expect(th.GetDeployment(deploymentName).Spec.Template.Spec.TopologySpreadConstraints).ToNot(BeNil())
1533+
g.Expect(th.GetDeployment(deploymentName).Spec.Template.Spec.TopologySpreadConstraints).To(Equal(topologySpecObj))
15111534
}, timeout, interval).Should(Succeed())
15121535
})
15131536
It("updates topology when the reference changes", func() {
15141537
Eventually(func(g Gomega) {
15151538
keystoneAPI := GetKeystoneAPI(keystoneAPIName)
1516-
keystoneAPI.Spec.TopologyRef.Name = keystoneAPITopologies[1].Name
1539+
keystoneAPI.Spec.TopologyRef.Name = topologyRefAlt.Name
15171540
g.Expect(k8sClient.Update(ctx, keystoneAPI)).To(Succeed())
15181541
}, timeout, interval).Should(Succeed())
15191542

15201543
Eventually(func(g Gomega) {
1544+
tp := GetTopology(types.NamespacedName{
1545+
Name: topologyRefAlt.Name,
1546+
Namespace: topologyRefAlt.Namespace,
1547+
})
1548+
finalizers := tp.GetFinalizers()
1549+
g.Expect(finalizers).To(HaveLen(1))
1550+
15211551
keystoneAPI := GetKeystoneAPI(keystoneAPIName)
15221552
g.Expect(keystoneAPI.Status.LastAppliedTopology).ToNot(BeNil())
1523-
g.Expect(keystoneAPI.Status.LastAppliedTopology.Name).To(Equal(keystoneAPITopologies[1].Name))
1553+
g.Expect(keystoneAPI.Status.LastAppliedTopology).To(Equal(topologyRefAlt))
1554+
g.Expect(finalizers).To(ContainElement(
1555+
fmt.Sprintf("openstack.org/keystoneapi-%s", keystoneAPIName.Name)))
1556+
1557+
// Verify the previous referenced topology has no finalizers
1558+
tp = GetTopology(types.NamespacedName{
1559+
Name: topologyRef.Name,
1560+
Namespace: topologyRef.Namespace,
1561+
})
1562+
finalizers = tp.GetFinalizers()
1563+
g.Expect(finalizers).To(BeEmpty())
15241564
}, timeout, interval).Should(Succeed())
15251565
})
15261566
It("removes topologyRef from the spec", func() {
@@ -1540,6 +1580,18 @@ var _ = Describe("Keystone controller", func() {
15401580
g.Expect(th.GetDeployment(deploymentName).Spec.Template.Spec.TopologySpreadConstraints).To(BeNil())
15411581
g.Expect(th.GetDeployment(deploymentName).Spec.Template.Spec.Affinity).ToNot(BeNil())
15421582
}, timeout, interval).Should(Succeed())
1583+
1584+
// Verify the existing topologies have no finalizer anymore
1585+
Eventually(func(g Gomega) {
1586+
for _, topology := range keystoneAPITopologies {
1587+
tp := GetTopology(types.NamespacedName{
1588+
Name: topology.Name,
1589+
Namespace: topology.Namespace,
1590+
})
1591+
finalizers := tp.GetFinalizers()
1592+
g.Expect(finalizers).To(BeEmpty())
1593+
}
1594+
}, timeout, interval).Should(Succeed())
15431595
})
15441596
})
15451597

0 commit comments

Comments
 (0)