-
Notifications
You must be signed in to change notification settings - Fork 254
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
🐛 Fix Port Leaks #1648
🐛 Fix Port Leaks #1648
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -289,18 +289,38 @@ func (s *Service) DeletePorts(openStackCluster *infrav1.OpenStackCluster) error | |
return nil | ||
} | ||
|
||
func (s *Service) GarbageCollectErrorInstancesPort(eventObject runtime.Object, instanceName string) error { | ||
portList, err := s.client.ListPort(ports.ListOpts{ | ||
Name: instanceName, | ||
}) | ||
if err != nil { | ||
return err | ||
} | ||
for _, p := range portList { | ||
if err := s.DeletePort(eventObject, p.ID); err != nil { | ||
func (s *Service) GarbageCollectErrorInstancesPort(eventObject runtime.Object, instanceName string, portOpts []infrav1.PortOpts) error { | ||
for i := range portOpts { | ||
portOpt := &portOpts[i] | ||
|
||
portName := GetPortName(instanceName, portOpt, i) | ||
|
||
// TODO: whould be nice if gophercloud could be persuaded to accept multiple | ||
// names as is allowed by the API in order to reduce API traffic. | ||
Comment on lines
+298
to
+299
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ping @pierreprinetti If you'd like to resurrect that effort? The use cases are mounting. |
||
portList, err := s.client.ListPort(ports.ListOpts{Name: portName}) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// NOTE: https://github.com/kubernetes-sigs/cluster-api-provider-openstack/issues/1476 | ||
// It is up to the end user to specify a UNIQUE cluster name when provisioning in the | ||
// same project, otherwise things will alias and we could delete more than we should. | ||
if len(portList) > 1 { | ||
return fmt.Errorf("garbage collection of port %s failed, found %d ports with the same name", portName, len(portList)) | ||
} | ||
|
||
if err := s.DeletePort(eventObject, portList[0].ID); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// GetPortName appends a suffix to an instance name in order to try and get a unique name per port. | ||
func GetPortName(instanceName string, opts *infrav1.PortOpts, netIndex int) string { | ||
if opts != nil && opts.NameSuffix != "" { | ||
return fmt.Sprintf("%s-%s", instanceName, opts.NameSuffix) | ||
} | ||
return fmt.Sprintf("%s-%d", instanceName, netIndex) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -535,6 +535,85 @@ func Test_GetOrCreatePort(t *testing.T) { | |
} | ||
} | ||
|
||
func Test_GarbageCollectErrorInstancesPort(t *testing.T) { | ||
mockCtrl := gomock.NewController(t) | ||
defer mockCtrl.Finish() | ||
|
||
instanceName := "foo" | ||
portID1 := "dc6e0ae3-dad6-4240-a9cb-e541916f20d3" | ||
portID2 := "a38ab1cb-c2cc-4c1b-9d1d-696ec73356d2" | ||
portName1 := GetPortName(instanceName, nil, 0) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. RE: above comment, I wanted to be absolutely sure that things generated with |
||
portName2 := GetPortName(instanceName, nil, 1) | ||
|
||
tests := []struct { | ||
// man is the name of the test. | ||
name string | ||
// expect allows definition of any expected calls to the mock. | ||
expect func(m *mock.MockNetworkClientMockRecorder) | ||
// portOpts defines the instance ports as defined in the OSM spec. | ||
portOpts []infrav1.PortOpts | ||
// wantErr defines whether the test is supposed to fail. | ||
wantErr bool | ||
}{ | ||
{ | ||
name: "garbage collects all ports for an instance", | ||
expect: func(m *mock.MockNetworkClientMockRecorder) { | ||
o1 := ports.ListOpts{ | ||
Name: portName1, | ||
} | ||
p1 := []ports.Port{ | ||
{ | ||
ID: portID1, | ||
Name: portName1, | ||
}, | ||
} | ||
m.ListPort(o1).Return(p1, nil) | ||
m.DeletePort(portID1) | ||
o2 := ports.ListOpts{ | ||
Name: portName2, | ||
} | ||
p2 := []ports.Port{ | ||
{ | ||
ID: portID2, | ||
Name: portName2, | ||
}, | ||
} | ||
|
||
m.ListPort(o2).Return(p2, nil) | ||
m.DeletePort(portID2) | ||
}, | ||
portOpts: []infrav1.PortOpts{ | ||
{}, | ||
{}, | ||
}, | ||
wantErr: false, | ||
}, | ||
} | ||
|
||
eventObject := &infrav1.OpenStackMachine{} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
g := NewWithT(t) | ||
mockClient := mock.NewMockNetworkClient(mockCtrl) | ||
tt.expect(mockClient.EXPECT()) | ||
s := Service{ | ||
client: mockClient, | ||
} | ||
err := s.GarbageCollectErrorInstancesPort( | ||
eventObject, | ||
instanceName, | ||
tt.portOpts, | ||
) | ||
if tt.wantErr { | ||
g.Expect(err).To(HaveOccurred()) | ||
} else { | ||
g.Expect(err).NotTo(HaveOccurred()) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func pointerTo(b bool) *bool { | ||
return &b | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did I move this you will inevitably ask? Well, problem is I want to associate the test with the function that causes the issue in the first place. Problem is I cannot just export this and include it because of circular dependencies. That led me to going, well this should be a
networking_test
package anyway, which then led to to not being able to create the networking service due to the client field not being exported, which then became ugly because of a hackyNewWithClientForTestOnly()
function in the production code. Moving it was by far the simplest solution.