Skip to content

Commit c8e4966

Browse files
committed
Add unit tests for compute.getServerNetworks()
1 parent 6aa7494 commit c8e4966

File tree

2 files changed

+372
-0
lines changed

2 files changed

+372
-0
lines changed

pkg/cloud/services/compute/instance_test.go

Lines changed: 363 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,16 @@ package compute
1919
import (
2020
"testing"
2121

22+
"github.com/go-logr/logr"
23+
"github.com/golang/mock/gomock"
24+
"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
25+
"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
26+
. "github.com/onsi/gomega"
2227
"k8s.io/utils/pointer"
2328

2429
infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha4"
30+
"sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/networking"
31+
"sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/networking/mock_networking"
2532
)
2633

2734
func Test_getPortName(t *testing.T) {
@@ -64,3 +71,359 @@ func Test_getPortName(t *testing.T) {
6471
})
6572
}
6673
}
74+
75+
func TestService_getServerNetworks(t *testing.T) {
76+
const testClusterTag = "cluster=mycluster"
77+
78+
// Network A:
79+
// Network is tagged
80+
// Has 3 subnets
81+
// Subnets A1 and A2 are tagged
82+
// Subnet A3 is not tagged
83+
// Network B:
84+
// Network is tagged
85+
// Has 1 subnet, B1, which is also tagged
86+
// Network C:
87+
// Network is not tagged
88+
// Has 1 subnet, C1, which is also not tagged
89+
90+
networkAUUID := "7f0a7cc9-d7c8-41d2-87a2-2fc7f5ec544e"
91+
networkBUUID := "607559d9-a5a4-4a0b-a92d-75eba89e3343"
92+
networkCUUID := "9d7b0284-b22e-4bc7-b90e-28a652cac7cc"
93+
subnetA1UUID := "869f6790-17a9-44d5-83a1-89e180514515"
94+
subnetA2UUID := "bd926900-5277-47a5-bd71-c6f713165dbd"
95+
subnetA3UUID := "79dfde1b-07f1-48a0-97fd-07e2f6018c46"
96+
subnetB1UUID := "efc2cc7d-c6e0-45c6-8147-0e08b8530664"
97+
subnetC1UUID := "b33271f4-6bb1-430a-88bf-789394815aaf"
98+
99+
testNetworkA := networks.Network{
100+
ID: networkAUUID,
101+
Name: "network-a",
102+
Subnets: []string{subnetA1UUID, subnetA2UUID},
103+
Tags: []string{testClusterTag},
104+
}
105+
testNetworkB := networks.Network{
106+
ID: networkBUUID,
107+
Name: "network-b",
108+
Subnets: []string{subnetB1UUID},
109+
Tags: []string{testClusterTag},
110+
}
111+
testNetworkC := networks.Network{
112+
ID: networkCUUID,
113+
Name: "network-c",
114+
Subnets: []string{subnetC1UUID},
115+
}
116+
117+
testSubnetA1 := subnets.Subnet{
118+
ID: subnetA1UUID,
119+
Name: "subnet-a1",
120+
NetworkID: networkAUUID,
121+
Tags: []string{testClusterTag},
122+
}
123+
testSubnetA2 := subnets.Subnet{
124+
ID: subnetA2UUID,
125+
Name: "subnet-a2",
126+
NetworkID: networkAUUID,
127+
Tags: []string{testClusterTag},
128+
}
129+
testSubnetA3 := subnets.Subnet{
130+
ID: subnetA3UUID,
131+
Name: "subnet-a3",
132+
NetworkID: networkAUUID,
133+
}
134+
testSubnetB1 := subnets.Subnet{
135+
ID: subnetB1UUID,
136+
Name: "subnet-b1",
137+
NetworkID: networkBUUID,
138+
Tags: []string{testClusterTag},
139+
}
140+
testSubnetC1 := subnets.Subnet{
141+
ID: subnetC1UUID,
142+
Name: "subnet-c1",
143+
NetworkID: networkCUUID,
144+
}
145+
146+
// Define arbitrary test network and subnet filters for use in multiple tests,
147+
// the gophercloud ListOpts they should translate to, and the arbitrary returned networks/subnets.
148+
testNetworkFilter := infrav1.Filter{Tags: testClusterTag}
149+
testNetworkListOpts := networks.ListOpts{Tags: testClusterTag}
150+
testSubnetFilter := infrav1.SubnetFilter{Tags: testClusterTag}
151+
testSubnetListOpts := subnets.ListOpts{Tags: testClusterTag}
152+
153+
// Expect a list query by network UUID which returns the network with the same UUID
154+
expectNetworkListByUUID := func(m *mock_networking.MockNetworkClientMockRecorder, network *networks.Network) {
155+
m.ListNetwork(&networks.ListOpts{ID: network.ID}).
156+
Return([]networks.Network{*network}, nil)
157+
}
158+
159+
// Expect a list query by subnet and network UUID which returns the subnet with the same UUID
160+
expectSubnetListByUUID := func(m *mock_networking.MockNetworkClientMockRecorder, subnet *subnets.Subnet) {
161+
m.ListSubnet(&subnets.ListOpts{ID: subnet.ID, NetworkID: subnet.NetworkID}).
162+
Return([]subnets.Subnet{*subnet}, nil)
163+
}
164+
165+
tests := []struct {
166+
name string
167+
networkParams []infrav1.NetworkParam
168+
want []infrav1.Network
169+
expect func(m *mock_networking.MockNetworkClientMockRecorder)
170+
wantErr bool
171+
}{
172+
{
173+
name: "Network UUID without subnet",
174+
networkParams: []infrav1.NetworkParam{
175+
{UUID: networkAUUID},
176+
},
177+
want: []infrav1.Network{
178+
{ID: networkAUUID, Subnet: &infrav1.Subnet{}},
179+
},
180+
expect: func(m *mock_networking.MockNetworkClientMockRecorder) {
181+
expectNetworkListByUUID(m, &testNetworkA)
182+
},
183+
wantErr: false,
184+
},
185+
{
186+
name: "Network filter without subnet",
187+
networkParams: []infrav1.NetworkParam{
188+
{Filter: testNetworkFilter},
189+
},
190+
want: []infrav1.Network{
191+
{ID: networkAUUID, Subnet: &infrav1.Subnet{}},
192+
{ID: networkBUUID, Subnet: &infrav1.Subnet{}},
193+
},
194+
expect: func(m *mock_networking.MockNetworkClientMockRecorder) {
195+
// List tagged networks (A & B)
196+
m.ListNetwork(&testNetworkListOpts).
197+
Return([]networks.Network{testNetworkA, testNetworkB}, nil)
198+
},
199+
wantErr: false,
200+
},
201+
{
202+
name: "Subnet by filter without network",
203+
networkParams: []infrav1.NetworkParam{
204+
{
205+
Subnets: []infrav1.SubnetParam{{Filter: testSubnetFilter}},
206+
},
207+
},
208+
want: nil,
209+
/* We expect this to return all tagged subnets in any network, but it returns error
210+
211+
[]infrav1.Network{
212+
{ID: networkAUUID, Subnet: &infrav1.Subnet{ID: subnetA1UUID}},
213+
{ID: networkAUUID, Subnet: &infrav1.Subnet{ID: subnetA2UUID}},
214+
{ID: networkAUUID, Subnet: &infrav1.Subnet{ID: subnetB1UUID}},
215+
},
216+
*/
217+
expect: func(m *mock_networking.MockNetworkClientMockRecorder) {
218+
// Empty list query returns all networks
219+
m.ListNetwork(&networks.ListOpts{}).
220+
Return([]networks.Network{testNetworkA, testNetworkB, testNetworkC}, nil)
221+
222+
// List tagged subnets in network A (A1 & A2)
223+
networkAFilter := testSubnetListOpts
224+
networkAFilter.NetworkID = networkAUUID
225+
m.ListSubnet(&networkAFilter).
226+
Return([]subnets.Subnet{testSubnetA1, testSubnetA2}, nil)
227+
228+
// List tagged subnets in network B (B1)
229+
networkBFilter := testSubnetListOpts
230+
networkBFilter.NetworkID = networkBUUID
231+
m.ListSubnet(&networkBFilter).
232+
Return([]subnets.Subnet{testSubnetB1}, nil)
233+
234+
// List tagged subnets in network C (none)
235+
networkCFilter := testSubnetListOpts
236+
networkCFilter.NetworkID = networkCUUID
237+
m.ListSubnet(&networkCFilter).
238+
Return([]subnets.Subnet{}, nil)
239+
},
240+
wantErr: true,
241+
},
242+
{
243+
name: "Network UUID and subnet filter",
244+
networkParams: []infrav1.NetworkParam{
245+
{
246+
UUID: networkAUUID,
247+
Subnets: []infrav1.SubnetParam{
248+
{Filter: testSubnetFilter},
249+
},
250+
},
251+
},
252+
want: []infrav1.Network{
253+
{ID: networkAUUID, Subnet: &infrav1.Subnet{ID: subnetA1UUID}},
254+
{ID: networkAUUID, Subnet: &infrav1.Subnet{ID: subnetA2UUID}},
255+
},
256+
expect: func(m *mock_networking.MockNetworkClientMockRecorder) {
257+
// List network A by UUID
258+
expectNetworkListByUUID(m, &testNetworkA)
259+
260+
// List tagged subnets in network A (A1 & A2)
261+
networkAFilter := testSubnetListOpts
262+
networkAFilter.NetworkID = networkAUUID
263+
m.ListSubnet(&networkAFilter).
264+
Return([]subnets.Subnet{testSubnetA1, testSubnetA2}, nil)
265+
},
266+
wantErr: false,
267+
},
268+
{
269+
name: "Network UUID and subnet UUID",
270+
networkParams: []infrav1.NetworkParam{
271+
{
272+
UUID: networkAUUID,
273+
Subnets: []infrav1.SubnetParam{
274+
{UUID: subnetA1UUID},
275+
},
276+
},
277+
},
278+
want: []infrav1.Network{
279+
{ID: networkAUUID, Subnet: &infrav1.Subnet{ID: subnetA1UUID}},
280+
},
281+
expect: func(m *mock_networking.MockNetworkClientMockRecorder) {
282+
// List network A by uuid
283+
expectNetworkListByUUID(m, &testNetworkA)
284+
285+
// List subnet A1 by uuid
286+
expectSubnetListByUUID(m, &testSubnetA1)
287+
},
288+
wantErr: false,
289+
},
290+
{
291+
name: "Network UUID and multiple subnet params",
292+
networkParams: []infrav1.NetworkParam{
293+
{
294+
UUID: networkAUUID,
295+
Subnets: []infrav1.SubnetParam{
296+
{UUID: subnetA3UUID},
297+
{Filter: testSubnetFilter},
298+
},
299+
},
300+
},
301+
want: []infrav1.Network{
302+
{ID: networkAUUID, Subnet: &infrav1.Subnet{ID: subnetA3UUID}},
303+
{ID: networkAUUID, Subnet: &infrav1.Subnet{ID: subnetA1UUID}},
304+
{ID: networkAUUID, Subnet: &infrav1.Subnet{ID: subnetA2UUID}},
305+
},
306+
expect: func(m *mock_networking.MockNetworkClientMockRecorder) {
307+
// List network A by uuid
308+
expectNetworkListByUUID(m, &testNetworkA)
309+
310+
// List subnet A3 by uuid
311+
expectSubnetListByUUID(m, &testSubnetA3)
312+
313+
// List tagged subnets in network A
314+
networkAFilter := testSubnetListOpts
315+
networkAFilter.NetworkID = networkAUUID
316+
m.ListSubnet(&networkAFilter).
317+
Return([]subnets.Subnet{testSubnetA1, testSubnetA2}, nil)
318+
},
319+
wantErr: false,
320+
},
321+
{
322+
name: "Multiple network params",
323+
networkParams: []infrav1.NetworkParam{
324+
{
325+
UUID: networkCUUID,
326+
Subnets: []infrav1.SubnetParam{
327+
{UUID: subnetC1UUID},
328+
},
329+
},
330+
{
331+
Filter: testNetworkFilter,
332+
Subnets: []infrav1.SubnetParam{
333+
{Filter: testSubnetFilter},
334+
},
335+
},
336+
},
337+
want: []infrav1.Network{
338+
{ID: networkCUUID, Subnet: &infrav1.Subnet{ID: subnetC1UUID}},
339+
{ID: networkAUUID, Subnet: &infrav1.Subnet{ID: subnetA1UUID}},
340+
{ID: networkAUUID, Subnet: &infrav1.Subnet{ID: subnetA2UUID}},
341+
{ID: networkBUUID, Subnet: &infrav1.Subnet{ID: subnetB1UUID}},
342+
},
343+
expect: func(m *mock_networking.MockNetworkClientMockRecorder) {
344+
// List network C by uuid
345+
expectNetworkListByUUID(m, &testNetworkC)
346+
347+
// List subnet C1 by uuid
348+
expectSubnetListByUUID(m, &testSubnetC1)
349+
350+
// List tagged networks (A & B)
351+
m.ListNetwork(&testNetworkListOpts).
352+
Return([]networks.Network{testNetworkA, testNetworkB}, nil)
353+
354+
// List tagged subnets in network A (A1 & A2)
355+
networkAFilter := testSubnetListOpts
356+
networkAFilter.NetworkID = networkAUUID
357+
m.ListSubnet(&networkAFilter).
358+
Return([]subnets.Subnet{testSubnetA1, testSubnetA2}, nil)
359+
360+
// List tagged subnets in network B (B1)
361+
networkBFilter := testSubnetListOpts
362+
networkBFilter.NetworkID = networkBUUID
363+
m.ListSubnet(&networkBFilter).
364+
Return([]subnets.Subnet{testSubnetB1}, nil)
365+
},
366+
wantErr: false,
367+
},
368+
{
369+
// Expect an error if a network filter doesn't match any networks
370+
name: "Network filter matches no networks",
371+
networkParams: []infrav1.NetworkParam{
372+
{Filter: testNetworkFilter},
373+
},
374+
want: nil,
375+
expect: func(m *mock_networking.MockNetworkClientMockRecorder) {
376+
// List tagged networks (none for this test)
377+
m.ListNetwork(&testNetworkListOpts).Return([]networks.Network{}, nil)
378+
},
379+
wantErr: true,
380+
},
381+
{
382+
// Expect an error if a subnet filter doesn't match any subnets
383+
name: "Subnet filter matches no subnets",
384+
networkParams: []infrav1.NetworkParam{
385+
{
386+
UUID: networkAUUID,
387+
Subnets: []infrav1.SubnetParam{
388+
{Filter: testSubnetFilter},
389+
},
390+
},
391+
},
392+
want: nil,
393+
expect: func(m *mock_networking.MockNetworkClientMockRecorder) {
394+
// List network A by UUID
395+
expectNetworkListByUUID(m, &testNetworkA)
396+
397+
// List tagged subnets in network A
398+
networkAFilter := testSubnetListOpts
399+
networkAFilter.NetworkID = networkAUUID
400+
m.ListSubnet(&networkAFilter).Return([]subnets.Subnet{}, nil)
401+
},
402+
wantErr: true,
403+
},
404+
}
405+
406+
for _, tt := range tests {
407+
t.Run(tt.name, func(t *testing.T) {
408+
mockCtrl := gomock.NewController(t)
409+
mockNetworkClient := mock_networking.NewMockNetworkClient(mockCtrl)
410+
tt.expect(mockNetworkClient.EXPECT())
411+
412+
networkingService := networking.NewTestService(
413+
"", mockNetworkClient, logr.Discard(),
414+
)
415+
s := &Service{
416+
networkingService: networkingService,
417+
}
418+
419+
got, err := s.getServerNetworks(tt.networkParams)
420+
g := NewWithT(t)
421+
if tt.wantErr {
422+
g.Expect(err).To(HaveOccurred())
423+
} else {
424+
g.Expect(err).NotTo(HaveOccurred())
425+
}
426+
g.Expect(got).To(Equal(tt.want))
427+
})
428+
}
429+
}

pkg/cloud/services/networking/service.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,12 @@ func NewService(providerClient *gophercloud.ProviderClient, clientOpts *clientco
6969
logger: logger,
7070
}, nil
7171
}
72+
73+
// NewTestService returns a Service with no initialisation. It should only be used by tests.
74+
func NewTestService(projectID string, client NetworkClient, logger logr.Logger) *Service {
75+
return &Service{
76+
projectID: projectID,
77+
client: client,
78+
logger: logger,
79+
}
80+
}

0 commit comments

Comments
 (0)