Skip to content

Commit a6f2a46

Browse files
committed
Add framework for api validation tests using envtest
1 parent a4e2079 commit a6f2a46

File tree

4 files changed

+454
-0
lines changed

4 files changed

+454
-0
lines changed
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
/*
2+
Copyright 2024 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 apivalidations
18+
19+
import (
20+
. "github.com/onsi/ginkgo/v2"
21+
. "github.com/onsi/gomega"
22+
corev1 "k8s.io/api/core/v1"
23+
24+
infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1"
25+
)
26+
27+
var _ = Describe("Neutron filter API validations", func() {
28+
var (
29+
namespace *corev1.Namespace
30+
cluster *infrav1.OpenStackCluster
31+
machine *infrav1.OpenStackMachine
32+
)
33+
34+
BeforeEach(func() {
35+
namespace = createNamespace()
36+
37+
// Initialise a basic machine object in the correct namespace
38+
machine = &infrav1.OpenStackMachine{}
39+
machine.Namespace = namespace.Name
40+
machine.GenerateName = "machine-"
41+
42+
// Initialise a basic cluster object in the correct namespace
43+
cluster = &infrav1.OpenStackCluster{}
44+
cluster.Namespace = namespace.Name
45+
cluster.GenerateName = "cluster-"
46+
})
47+
48+
DescribeTable("Allow valid neutron filter tags", func(tags []infrav1.FilterByNeutronTags) {
49+
// Specify the given neutron tags in every filter it is
50+
// possible to specify them in, then create the
51+
// resulting object. It should be valid.
52+
53+
securityGroups := make([]infrav1.SecurityGroupFilter, len(tags))
54+
for i := range tags {
55+
securityGroups[i].FilterByNeutronTags = tags[i]
56+
}
57+
machine.Spec.SecurityGroups = securityGroups
58+
59+
ports := make([]infrav1.PortOpts, len(tags))
60+
for i := range tags {
61+
port := &ports[i]
62+
port.Network = &infrav1.NetworkFilter{FilterByNeutronTags: tags[i]}
63+
port.FixedIPs = []infrav1.FixedIP{{Subnet: &infrav1.SubnetFilter{FilterByNeutronTags: tags[i]}}}
64+
port.SecurityGroups = []infrav1.SecurityGroupFilter{{FilterByNeutronTags: tags[i]}}
65+
}
66+
Expect(k8sClient.Create(ctx, machine)).To(Succeed(), "OpenStackMachine creation should succeed")
67+
68+
// Maximum of 2 subnets are supported
69+
nSubnets := min(len(tags), 2)
70+
subnets := make([]infrav1.SubnetFilter, nSubnets)
71+
for i := 0; i < nSubnets; i++ {
72+
subnets[i].FilterByNeutronTags = tags[i]
73+
}
74+
cluster.Spec.Subnets = subnets
75+
if len(tags) > 0 {
76+
cluster.Spec.Network = infrav1.NetworkFilter{FilterByNeutronTags: tags[0]}
77+
cluster.Spec.ExternalNetwork = infrav1.NetworkFilter{FilterByNeutronTags: tags[0]}
78+
cluster.Spec.Router = &infrav1.RouterFilter{FilterByNeutronTags: tags[0]}
79+
}
80+
Expect(k8sClient.Create(ctx, cluster)).To(Succeed(), "OpenStackCluster creation should succeed")
81+
},
82+
Entry("empty list", nil),
83+
Entry("single tag", []infrav1.FilterByNeutronTags{
84+
{Tags: []infrav1.NeutronTag{"foo"}},
85+
}),
86+
Entry("multiple filters, multiple tags", []infrav1.FilterByNeutronTags{
87+
{Tags: []infrav1.NeutronTag{"foo", "bar"}},
88+
{TagsAny: []infrav1.NeutronTag{"foo", "bar"}},
89+
{NotTags: []infrav1.NeutronTag{"foo", "bar"}},
90+
{NotTagsAny: []infrav1.NeutronTag{"foo", "bar"}},
91+
}),
92+
)
93+
94+
DescribeTable("Disallow invalid neutron filter tags", func(tags []infrav1.FilterByNeutronTags) {
95+
{
96+
machine := machine.DeepCopy()
97+
securityGroups := make([]infrav1.SecurityGroupFilter, len(tags))
98+
for i := range tags {
99+
securityGroups[i].FilterByNeutronTags = tags[i]
100+
}
101+
machine.Spec.SecurityGroups = securityGroups
102+
Expect(k8sClient.Create(ctx, machine)).NotTo(Succeed(), "OpenStackMachine creation should fail with invalid security group neutron tags")
103+
}
104+
105+
for i := range tags {
106+
{
107+
machine := machine.DeepCopy()
108+
machine.Spec.Ports = []infrav1.PortOpts{
109+
{Network: &infrav1.NetworkFilter{FilterByNeutronTags: tags[i]}},
110+
}
111+
Expect(k8sClient.Create(ctx, machine)).NotTo(Succeed(), "OpenStackMachine creation should fail with invalid port network neutron tags")
112+
}
113+
{
114+
machine := machine.DeepCopy()
115+
machine.Spec.Ports = []infrav1.PortOpts{
116+
{FixedIPs: []infrav1.FixedIP{{Subnet: &infrav1.SubnetFilter{FilterByNeutronTags: tags[i]}}}},
117+
}
118+
Expect(k8sClient.Create(ctx, machine)).NotTo(Succeed(), "OpenStackMachine creation should fail with invalid port subnet neutron tags")
119+
}
120+
{
121+
machine := machine.DeepCopy()
122+
machine.Spec.Ports = []infrav1.PortOpts{
123+
{SecurityGroups: []infrav1.SecurityGroupFilter{{FilterByNeutronTags: tags[i]}}},
124+
}
125+
Expect(k8sClient.Create(ctx, machine)).NotTo(Succeed(), "OpenStackMachine creation should fail with invalid port security group neutron tags")
126+
}
127+
}
128+
129+
if len(tags) > 0 {
130+
tag := tags[0]
131+
132+
{
133+
cluster := cluster.DeepCopy()
134+
cluster.Spec.Subnets = []infrav1.SubnetFilter{{FilterByNeutronTags: tag}}
135+
Expect(k8sClient.Create(ctx, cluster)).NotTo(Succeed(), "OpenStackCluster creation should fail with invalid subnet neutron tags")
136+
}
137+
138+
{
139+
cluster := cluster.DeepCopy()
140+
cluster.Spec.Network = infrav1.NetworkFilter{FilterByNeutronTags: tag}
141+
Expect(k8sClient.Create(ctx, cluster)).NotTo(Succeed(), "OpenStackCluster creation should fail with invalid network neutron tags")
142+
}
143+
144+
{
145+
cluster := cluster.DeepCopy()
146+
cluster.Spec.ExternalNetwork = infrav1.NetworkFilter{FilterByNeutronTags: tag}
147+
Expect(k8sClient.Create(ctx, cluster)).NotTo(Succeed(), "OpenStackCluster creation should fail with invalid external network neutron tags")
148+
}
149+
150+
{
151+
cluster := cluster.DeepCopy()
152+
cluster.Spec.Router = &infrav1.RouterFilter{FilterByNeutronTags: tag}
153+
Expect(k8sClient.Create(ctx, cluster)).NotTo(Succeed(), "OpenStackCluster creation should fail with invalid router neutron tags")
154+
}
155+
}
156+
},
157+
Entry("contains leading comma", []infrav1.FilterByNeutronTags{
158+
{Tags: []infrav1.NeutronTag{",foo"}},
159+
}),
160+
Entry("contains trailing comma", []infrav1.FilterByNeutronTags{
161+
{Tags: []infrav1.NeutronTag{"foo,"}},
162+
}),
163+
Entry("contains comma in middle", []infrav1.FilterByNeutronTags{
164+
{Tags: []infrav1.NeutronTag{"foo,bar"}},
165+
}),
166+
Entry("contains multiple commas", []infrav1.FilterByNeutronTags{
167+
{Tags: []infrav1.NeutronTag{"foo,,bar"}},
168+
}),
169+
Entry("empty tag", []infrav1.FilterByNeutronTags{
170+
{Tags: []infrav1.NeutronTag{""}},
171+
}),
172+
Entry("second tag is invalid", []infrav1.FilterByNeutronTags{
173+
{Tags: []infrav1.NeutronTag{"foo", ""}},
174+
}),
175+
)
176+
})
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
Copyright 2024 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 apivalidations
18+
19+
import (
20+
. "github.com/onsi/ginkgo/v2"
21+
. "github.com/onsi/gomega"
22+
corev1 "k8s.io/api/core/v1"
23+
24+
infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1"
25+
)
26+
27+
var _ = Describe("OpenStackCluster API validations", func() {
28+
var cluster *infrav1.OpenStackCluster
29+
var namespace *corev1.Namespace
30+
31+
BeforeEach(func() {
32+
namespace = createNamespace()
33+
34+
// Initialise a basic cluster object in the correct namespace
35+
cluster = &infrav1.OpenStackCluster{}
36+
cluster.Namespace = namespace.Name
37+
cluster.GenerateName = "cluster-"
38+
})
39+
40+
It("should allow the smallest permissible cluster spec", func() {
41+
Expect(k8sClient.Create(ctx, cluster)).To(Succeed(), "OpenStackCluster creation should succeed")
42+
})
43+
44+
It("should only allow controlPlaneEndpoint to be set once", func() {
45+
By("Creating a bare cluster")
46+
Expect(k8sClient.Create(ctx, cluster)).To(Succeed(), "OpenStackCluster creation should succeed")
47+
48+
By("Setting the control plane endpoint")
49+
cluster.Spec.ControlPlaneEndpoint.Host = "foo"
50+
cluster.Spec.ControlPlaneEndpoint.Port = 1234
51+
Expect(k8sClient.Update(ctx, cluster)).To(Succeed(), "Setting control plane endpoint should succeed")
52+
53+
By("Modifying the control plane endpoint")
54+
cluster.Spec.ControlPlaneEndpoint.Host = "bar"
55+
Expect(k8sClient.Update(ctx, cluster)).NotTo(Succeed(), "Updating control plane endpoint should fail")
56+
})
57+
58+
It("should allow an empty managed security groups definition", func() {
59+
cluster.Spec.ManagedSecurityGroups = &infrav1.ManagedSecurityGroups{}
60+
Expect(k8sClient.Create(ctx, cluster)).To(Succeed(), "OpenStackCluster creation should succeed")
61+
})
62+
})
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
Copyright 2024 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 apivalidations
18+
19+
import (
20+
. "github.com/onsi/ginkgo/v2"
21+
. "github.com/onsi/gomega"
22+
corev1 "k8s.io/api/core/v1"
23+
"k8s.io/utils/pointer"
24+
25+
infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1"
26+
)
27+
28+
var _ = Describe("OpenStackMachine API validations", func() {
29+
var namespace *corev1.Namespace
30+
var machine *infrav1.OpenStackMachine
31+
32+
BeforeEach(func() {
33+
namespace = createNamespace()
34+
35+
// Initialise a basic machine object in the correct namespace
36+
machine = &infrav1.OpenStackMachine{}
37+
machine.Namespace = namespace.Name
38+
machine.GenerateName = "machine-"
39+
})
40+
41+
It("should allow the smallest permissible machine spec", func() {
42+
Expect(k8sClient.Create(ctx, machine)).To(Succeed(), "OpenStackMachine creation should succeed")
43+
})
44+
45+
It("should only allow the providerID to be set once", func() {
46+
By("Creating a bare machine")
47+
Expect(k8sClient.Create(ctx, machine)).To(Succeed(), "OpenStackMachine creation should succeed")
48+
49+
By("Setting the providerID")
50+
machine.Spec.ProviderID = pointer.String("foo")
51+
Expect(k8sClient.Update(ctx, machine)).To(Succeed(), "Setting providerID should succeed")
52+
53+
By("Modifying the providerID")
54+
machine.Spec.ProviderID = pointer.String("bar")
55+
Expect(k8sClient.Update(ctx, machine)).NotTo(Succeed(), "Updating providerID should fail")
56+
})
57+
})

0 commit comments

Comments
 (0)