Skip to content

Commit 8b17f0e

Browse files
authored
Merge pull request #1020 from Nordix/ports-unit-tests
🏃 Add unit tests for getOrCreatePort func
2 parents eb83033 + 43df21f commit 8b17f0e

File tree

1 file changed

+371
-0
lines changed

1 file changed

+371
-0
lines changed
Lines changed: 371 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,371 @@
1+
/*
2+
Copyright 2021 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 networking
18+
19+
import (
20+
"testing"
21+
22+
"github.com/go-logr/logr"
23+
"github.com/golang/mock/gomock"
24+
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/attributestags"
25+
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsbinding"
26+
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsecurity"
27+
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/trunks"
28+
"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
29+
. "github.com/onsi/gomega"
30+
31+
infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha4"
32+
"sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/networking/mock_networking"
33+
)
34+
35+
func Test_GetOrCreatePort(t *testing.T) {
36+
mockCtrl := gomock.NewController(t)
37+
defer mockCtrl.Finish()
38+
39+
// Arbitrary GUIDs used in the tests
40+
netID := "7fd24ceb-788a-441f-ad0a-d8e2f5d31a1d"
41+
subnetID := "d9c88a6d-0b8c-48ff-8f0e-8d85a078c194"
42+
portID1 := "50214c48-c09e-4a54-914f-97b40fd22802"
43+
portID2 := "4c096384-f0a5-466d-9534-06a7ed281a79"
44+
hostID := "825c1b11-3dca-4bfe-a2d8-a3cc1964c8d5"
45+
tenantID := "62b523a7-f838-45fd-904f-d2db2bb58e04"
46+
projectID := "063171b1-0595-4882-98cd-3ee79676ff87"
47+
trunkID := "eb7541fa-5e2a-4cca-b2c3-dfa409b917ce"
48+
49+
// Other arbitrary variables passed in to the tests
50+
instanceSecurityGroups := []string{"instance-secgroup"}
51+
portSecurityGroups := []string{"port-secgroup"}
52+
53+
pointerToTrue := pointerTo(true)
54+
pointerToFalse := pointerTo(false)
55+
56+
tests := []struct {
57+
name string
58+
portName string
59+
net infrav1.Network
60+
instanceSecurityGroups *[]string
61+
tags []string
62+
expect func(m *mock_networking.MockNetworkClientMockRecorder)
63+
// Note the 'wanted' port isn't so important, since it will be whatever we tell ListPort or CreatePort to return.
64+
// Mostly in this test suite, we're checking that ListPort/CreatePort is called with the expected port opts.
65+
want *ports.Port
66+
wantErr bool
67+
}{
68+
{
69+
"gets and returns existing port if name matches",
70+
"foo-port-1",
71+
infrav1.Network{
72+
ID: netID,
73+
Subnet: &infrav1.Subnet{},
74+
},
75+
nil,
76+
[]string{},
77+
func(m *mock_networking.MockNetworkClientMockRecorder) {
78+
m.
79+
ListPort(ports.ListOpts{
80+
Name: "foo-port-1",
81+
NetworkID: netID,
82+
}).Return([]ports.Port{{
83+
ID: portID1,
84+
}}, nil)
85+
},
86+
&ports.Port{
87+
ID: portID1,
88+
},
89+
false,
90+
},
91+
{
92+
"errors if multiple matching ports are found",
93+
"foo-port-1",
94+
infrav1.Network{
95+
ID: netID,
96+
Subnet: &infrav1.Subnet{},
97+
},
98+
nil,
99+
[]string{},
100+
func(m *mock_networking.MockNetworkClientMockRecorder) {
101+
m.
102+
ListPort(ports.ListOpts{
103+
Name: "foo-port-1",
104+
NetworkID: netID,
105+
}).Return([]ports.Port{
106+
{
107+
ID: portID1,
108+
NetworkID: netID,
109+
Name: "foo-port-1",
110+
},
111+
{
112+
ID: portID2,
113+
NetworkID: netID,
114+
Name: "foo-port-2",
115+
},
116+
}, nil)
117+
},
118+
nil,
119+
true,
120+
},
121+
{
122+
"creates port with defaults (description and secgroups) if not specified in portOpts",
123+
"foo-port-1",
124+
infrav1.Network{
125+
ID: netID,
126+
PortOpts: &infrav1.PortOpts{},
127+
},
128+
&instanceSecurityGroups,
129+
[]string{},
130+
func(m *mock_networking.MockNetworkClientMockRecorder) {
131+
// No ports found
132+
m.
133+
ListPort(ports.ListOpts{
134+
Name: "foo-port-1",
135+
NetworkID: netID,
136+
}).Return([]ports.Port{}, nil)
137+
m.
138+
CreatePort(portsbinding.CreateOptsExt{
139+
CreateOptsBuilder: ports.CreateOpts{
140+
Name: "foo-port-1",
141+
Description: "Created by cluster-api-provider-openstack cluster test-cluster",
142+
SecurityGroups: &instanceSecurityGroups,
143+
NetworkID: netID,
144+
AllowedAddressPairs: []ports.AddressPair{},
145+
},
146+
}).Return(&ports.Port{ID: portID1}, nil)
147+
},
148+
&ports.Port{ID: portID1},
149+
false,
150+
},
151+
{
152+
"creates port with specified portOpts if no matching port exists",
153+
"foo-port-bar",
154+
infrav1.Network{
155+
ID: netID,
156+
Subnet: &infrav1.Subnet{},
157+
PortOpts: &infrav1.PortOpts{
158+
NameSuffix: "bar",
159+
Description: "this is a test port",
160+
MACAddress: "fe:fe:fe:fe:fe:fe",
161+
AdminStateUp: pointerToTrue,
162+
FixedIPs: []infrav1.FixedIP{{
163+
SubnetID: subnetID,
164+
IPAddress: "192.168.0.50",
165+
}, {IPAddress: "192.168.1.50"}},
166+
TenantID: tenantID,
167+
ProjectID: projectID,
168+
SecurityGroups: &portSecurityGroups,
169+
AllowedAddressPairs: []infrav1.AddressPair{{
170+
IPAddress: "10.10.10.10",
171+
MACAddress: "f1:f1:f1:f1:f1:f1",
172+
}},
173+
HostID: hostID,
174+
VNICType: "direct",
175+
Profile: map[string]string{"interface_name": "eno1"},
176+
DisablePortSecurity: pointerToFalse,
177+
},
178+
},
179+
nil,
180+
nil,
181+
func(m *mock_networking.MockNetworkClientMockRecorder) {
182+
portCreateOpts := ports.CreateOpts{
183+
NetworkID: netID,
184+
Name: "foo-port-bar",
185+
Description: "this is a test port",
186+
AdminStateUp: pointerToTrue,
187+
MACAddress: "fe:fe:fe:fe:fe:fe",
188+
FixedIPs: []ports.IP{
189+
{
190+
SubnetID: subnetID,
191+
IPAddress: "192.168.0.50",
192+
}, {
193+
IPAddress: "192.168.1.50",
194+
},
195+
},
196+
TenantID: tenantID,
197+
ProjectID: projectID,
198+
SecurityGroups: &portSecurityGroups,
199+
AllowedAddressPairs: []ports.AddressPair{{
200+
IPAddress: "10.10.10.10",
201+
MACAddress: "f1:f1:f1:f1:f1:f1",
202+
}},
203+
}
204+
portsecurityCreateOptsExt := portsecurity.PortCreateOptsExt{
205+
CreateOptsBuilder: portCreateOpts,
206+
PortSecurityEnabled: pointerToTrue,
207+
}
208+
portbindingCreateOptsExt := portsbinding.CreateOptsExt{
209+
// Note for the test matching, the order in which the builders are composed
210+
// must be the same as in the function we are testing.
211+
CreateOptsBuilder: portsecurityCreateOptsExt,
212+
HostID: hostID,
213+
VNICType: "direct",
214+
Profile: map[string]interface{}{"interface_name": "eno1"},
215+
}
216+
m.
217+
ListPort(ports.ListOpts{
218+
Name: "foo-port-bar",
219+
NetworkID: netID,
220+
}).Return([]ports.Port{}, nil)
221+
m.
222+
CreatePort(portbindingCreateOptsExt).
223+
Return(&ports.Port{
224+
ID: portID1,
225+
}, nil)
226+
},
227+
&ports.Port{
228+
ID: portID1,
229+
},
230+
false,
231+
},
232+
{
233+
"overrides default (instance) security groups if port security groups are specified",
234+
"foo-port-1",
235+
infrav1.Network{
236+
ID: netID,
237+
PortOpts: &infrav1.PortOpts{
238+
SecurityGroups: &portSecurityGroups,
239+
},
240+
},
241+
&instanceSecurityGroups,
242+
[]string{},
243+
func(m *mock_networking.MockNetworkClientMockRecorder) {
244+
// No ports found
245+
m.
246+
ListPort(ports.ListOpts{
247+
Name: "foo-port-1",
248+
NetworkID: netID,
249+
}).Return([]ports.Port{}, nil)
250+
m.
251+
CreatePort(portsbinding.CreateOptsExt{
252+
CreateOptsBuilder: ports.CreateOpts{
253+
Name: "foo-port-1",
254+
Description: "Created by cluster-api-provider-openstack cluster test-cluster",
255+
SecurityGroups: &portSecurityGroups,
256+
NetworkID: netID,
257+
AllowedAddressPairs: []ports.AddressPair{},
258+
},
259+
},
260+
).Return(&ports.Port{ID: portID1}, nil)
261+
},
262+
&ports.Port{ID: portID1},
263+
false,
264+
},
265+
{
266+
"creates port with tags passed to function",
267+
"foo-port-1",
268+
infrav1.Network{
269+
ID: netID,
270+
PortOpts: &infrav1.PortOpts{},
271+
},
272+
nil,
273+
[]string{"my-tag"},
274+
func(m *mock_networking.MockNetworkClientMockRecorder) {
275+
// No ports found
276+
m.
277+
ListPort(ports.ListOpts{
278+
Name: "foo-port-1",
279+
NetworkID: netID,
280+
}).Return([]ports.Port{}, nil)
281+
m.CreatePort(portsbinding.CreateOptsExt{
282+
CreateOptsBuilder: ports.CreateOpts{
283+
Name: "foo-port-1",
284+
Description: "Created by cluster-api-provider-openstack cluster test-cluster",
285+
NetworkID: netID,
286+
AllowedAddressPairs: []ports.AddressPair{},
287+
},
288+
}).Return(&ports.Port{ID: portID1}, nil)
289+
m.ReplaceAllAttributesTags("ports", portID1, attributestags.ReplaceAllOpts{Tags: []string{"my-tag"}}).Return([]string{"my-tag"}, nil)
290+
},
291+
&ports.Port{ID: portID1},
292+
false,
293+
},
294+
{
295+
"creates port and trunk (with tags) if they aren't found",
296+
"foo-port-1",
297+
infrav1.Network{
298+
ID: netID,
299+
PortOpts: &infrav1.PortOpts{
300+
Trunk: pointerToTrue,
301+
},
302+
},
303+
nil,
304+
[]string{"my-tag"},
305+
func(m *mock_networking.MockNetworkClientMockRecorder) {
306+
// No ports found
307+
m.
308+
ListPort(ports.ListOpts{
309+
Name: "foo-port-1",
310+
NetworkID: netID,
311+
}).Return([]ports.Port{}, nil)
312+
m.
313+
CreatePort(portsbinding.CreateOptsExt{
314+
CreateOptsBuilder: ports.CreateOpts{
315+
Name: "foo-port-1",
316+
Description: "Created by cluster-api-provider-openstack cluster test-cluster",
317+
NetworkID: netID,
318+
AllowedAddressPairs: []ports.AddressPair{},
319+
},
320+
}).Return(&ports.Port{Name: "foo-port-1", ID: portID1}, nil)
321+
m.
322+
ListTrunk(trunks.ListOpts{
323+
Name: "foo-port-1",
324+
PortID: portID1,
325+
}).Return([]trunks.Trunk{}, nil)
326+
m.
327+
CreateTrunk(trunks.CreateOpts{
328+
Name: "foo-port-1",
329+
PortID: portID1,
330+
Description: "Created by cluster-api-provider-openstack cluster test-cluster",
331+
}).Return(&trunks.Trunk{ID: trunkID}, nil)
332+
333+
m.ReplaceAllAttributesTags("ports", portID1, attributestags.ReplaceAllOpts{Tags: []string{"my-tag"}}).Return([]string{"my-tag"}, nil)
334+
m.ReplaceAllAttributesTags("trunks", trunkID, attributestags.ReplaceAllOpts{Tags: []string{"my-tag"}}).Return([]string{"my-tag"}, nil)
335+
},
336+
&ports.Port{Name: "foo-port-1", ID: portID1},
337+
false,
338+
},
339+
}
340+
341+
eventObject := &infrav1.OpenStackMachine{}
342+
for _, tt := range tests {
343+
t.Run(tt.name, func(t *testing.T) {
344+
g := NewWithT(t)
345+
mockClient := mock_networking.NewMockNetworkClient(mockCtrl)
346+
tt.expect(mockClient.EXPECT())
347+
s := Service{
348+
client: mockClient,
349+
logger: logr.DiscardLogger{},
350+
}
351+
got, err := s.GetOrCreatePort(
352+
eventObject,
353+
"test-cluster",
354+
tt.portName,
355+
tt.net,
356+
tt.instanceSecurityGroups,
357+
tt.tags,
358+
)
359+
if tt.wantErr {
360+
g.Expect(err).To(HaveOccurred())
361+
} else {
362+
g.Expect(err).NotTo(HaveOccurred())
363+
}
364+
g.Expect(got).To(Equal(tt.want))
365+
})
366+
}
367+
}
368+
369+
func pointerTo(b bool) *bool {
370+
return &b
371+
}

0 commit comments

Comments
 (0)