Skip to content

Commit 878f4d6

Browse files
committed
imp: flow controller flows cleanup
using external id on the bridge flow controller will remove flows once pod is deleted cleanup is done as best effort and will only log an error Signed-off-by: Michael Filanov <[email protected]>
1 parent 3e8cd91 commit 878f4d6

File tree

5 files changed

+118
-40
lines changed

5 files changed

+118
-40
lines changed

internal/controller/flow_controller.go

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,14 @@ func (r *FlowReconciler) Reconcile(ctx context.Context, pod *corev1.Pod) (ctrl.R
6767

6868
logr.Info("reconcile", "namespace", pod.Namespace, "name", pod.Name)
6969

70+
if pod.DeletionTimestamp != nil {
71+
logr.Info("pod is being deleted, deleting flows if needed")
72+
if err := r.Flows.DeletePodRailFlows(GenerateUint64FromString(string(pod.UID)), string(pod.UID)); err != nil {
73+
logr.Error(err, "failed to delete flows")
74+
}
75+
return ctrl.Result{}, nil
76+
}
77+
7078
if pod.Annotations == nil {
7179
return reconcile.Result{}, nil
7280
}
@@ -100,10 +108,6 @@ func (r *FlowReconciler) handlePodFlows(ctx context.Context, pod *corev1.Pod, re
100108

101109
cookie := GenerateUint64FromString(string(pod.UID))
102110

103-
if pod.DeletionTimestamp != nil {
104-
logr.Info(fmt.Sprintf("pod %s/%s is being deleted, deleting flows", pod.Namespace, pod.Name))
105-
}
106-
107111
var errs error
108112

109113
for _, ns := range relevantNetworkStatus {
@@ -121,13 +125,6 @@ func (r *FlowReconciler) handlePodFlows(ctx context.Context, pod *corev1.Pod, re
121125
continue
122126
}
123127

124-
if pod.DeletionTimestamp != nil {
125-
if err = r.Flows.DeletePodRailFlows(cookie, bridge); err != nil {
126-
logr.Error(err, fmt.Sprintf("failed to delete flows for rail %s", bridge))
127-
}
128-
continue
129-
}
130-
131128
if err = r.Flows.AddPodRailFlows(cookie, rep, bridge, ns.IPs[0], ns.Mac); err != nil {
132129
logr.Error(err, fmt.Sprintf("failed to add flows to rail %s", ns.Interface))
133130
errs = multierr.Append(errs, err)

internal/controller/flow_controller_test.go

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -167,22 +167,10 @@ var _ = Describe("Pod Controller", func() {
167167
Expect(k8sClient.Create(ctx, pod)).Should(Succeed())
168168

169169
pod.DeletionTimestamp = &metav1.Time{Time: time.Now()}
170-
171-
// rail1
172-
execMock.EXPECT().
173-
Execute("ovs-vsctl --no-heading --columns=name find Port external_ids:contIface=net1 external_ids:contPodUid="+string(pod.UID)).
174-
Return("pod-vf-1", nil).Times(1)
175-
execMock.EXPECT().Execute("ovs-vsctl iface-to-br pod-vf-1").Return("br-rail1", nil).Times(1)
176-
177-
// rail2
178-
execMock.EXPECT().
179-
Execute("ovs-vsctl --no-heading --columns=name find Port external_ids:contIface=net2 external_ids:contPodUid="+string(pod.UID)).
180-
Return("pod-vf-2", nil)
181-
execMock.EXPECT().Execute("ovs-vsctl iface-to-br pod-vf-2").Return("br-rail2", nil).Times(1)
182170
})
183171

184172
It("delete flows on pod deletion", func() {
185-
flowsMock.EXPECT().DeletePodRailFlows(gomock.Any(), gomock.Any()).Return(nil).Times(2)
173+
flowsMock.EXPECT().DeletePodRailFlows(gomock.Any(), gomock.Any()).Return(nil).Times(1)
186174
pod.DeletionTimestamp = &metav1.Time{Time: time.Now()}
187175
result, err := flowController.Reconcile(ctx, pod)
188176
Expect(err).Should(Succeed())
@@ -191,7 +179,6 @@ var _ = Describe("Pod Controller", func() {
191179

192180
It("failed to delete flows on pod deletion - do not fail reconciliation", func() {
193181
flowsMock.EXPECT().DeletePodRailFlows(gomock.Any(), gomock.Any()).Return(fmt.Errorf("failed to delete flows"))
194-
flowsMock.EXPECT().DeletePodRailFlows(gomock.Any(), gomock.Any()).Return(nil)
195182
pod.DeletionTimestamp = &metav1.Time{Time: time.Now()}
196183
result, err := flowController.Reconcile(ctx, pod)
197184
Expect(err).Should(Succeed())

internal/controller/flows.go

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,26 @@ package controller
1717

1818
import (
1919
"fmt"
20+
"strings"
2021

2122
"github.com/Mellanox/spectrum-x-operator/pkg/exec"
2223
libnetlink "github.com/Mellanox/spectrum-x-operator/pkg/lib/netlink"
24+
25+
"go.uber.org/multierr"
2326
)
2427

2528
const (
2629
railPeerIP = "rail_peer_ip"
2730
railUplink = "rail_uplink"
31+
RailPodID = "rail_pod_id"
2832
defaultPriority = 32768
2933
)
3034

3135
//go:generate ../../bin/mockgen -destination mock_flows.go -source flows.go -package controller
3236

3337
type FlowsAPI interface {
3438
AddPodRailFlows(cookie uint64, vf, bridge, podIP, podMAC string) error
35-
DeletePodRailFlows(cookie uint64, bridge string) error
39+
DeletePodRailFlows(cookie uint64, podID string) error
3640
}
3741

3842
var _ FlowsAPI = &Flows{}
@@ -102,10 +106,39 @@ func (f *Flows) AddPodRailFlows(cookie uint64, vf, bridge, podIP, podMAC string)
102106
return nil
103107
}
104108

105-
func (f *Flows) DeletePodRailFlows(cookie uint64, bridge string) error {
106-
flow := fmt.Sprintf(`ovs-ofctl del-flows %s cookie=0x%x/-1`, bridge, cookie)
107-
_, err := f.Exec.Execute(flow)
108-
return err
109+
func (f *Flows) DeletePodRailFlows(cookie uint64, podID string) error {
110+
out, err := f.Exec.Execute("ovs-vsctl list-br")
111+
if err != nil {
112+
return fmt.Errorf("failed to list bridges: %v", err)
113+
}
114+
115+
bridges := strings.Split(out, "\n")
116+
117+
var errs error
118+
119+
for _, bridge := range bridges {
120+
val, err := f.Exec.Execute(fmt.Sprintf("ovs-vsctl br-get-external-id %s %s", bridge, podID))
121+
if err != nil {
122+
errs = multierr.Append(errs, fmt.Errorf("failed to get external id for bridge %s: %v", bridge, err))
123+
continue
124+
}
125+
126+
if val == RailPodID {
127+
flow := fmt.Sprintf(`ovs-ofctl del-flows %s cookie=0x%x/-1`, bridge, cookie)
128+
out, err := f.Exec.Execute(flow)
129+
if err != nil {
130+
errs = multierr.Append(errs, fmt.Errorf("failed to delete flows for bridge %s: %v, output: %s", bridge, err, out))
131+
continue
132+
}
133+
// clear external id
134+
_, err = f.Exec.Execute(fmt.Sprintf(`ovs-vsctl br-set-external-id %s %s ""`, bridge, podID))
135+
if err != nil {
136+
errs = multierr.Append(errs, fmt.Errorf("failed to clear external id for bridge %s: %v", bridge, err))
137+
continue
138+
}
139+
}
140+
}
141+
return errs
109142
}
110143

111144
func (f *Flows) getTorMac(torIP string) (string, error) {

internal/controller/flows_test.go

Lines changed: 67 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ import (
2323

2424
"github.com/Mellanox/spectrum-x-operator/pkg/exec"
2525
mock_netlink "github.com/Mellanox/spectrum-x-operator/pkg/lib/netlink/mocks"
26-
gomock "github.com/golang/mock/gomock"
27-
vishvananda_netlink "github.com/vishvananda/netlink"
2826

27+
gomock "github.com/golang/mock/gomock"
2928
. "github.com/onsi/ginkgo/v2"
3029
. "github.com/onsi/gomega"
30+
vishvananda_netlink "github.com/vishvananda/netlink"
3131
)
3232

3333
type containsSubstringMatcher struct {
@@ -223,15 +223,76 @@ var _ = Describe("Flows", func() {
223223

224224
Context("DeletePodRailFlows", func() {
225225
It("should delete flows on pod deletion", func() {
226+
execMock.EXPECT().Execute("ovs-vsctl list-br").Return("br-rail1\nbr-rail2", nil)
227+
execMock.EXPECT().Execute("ovs-vsctl br-get-external-id br-rail1 test-pod-uid").
228+
Return("rail_pod_id", nil)
229+
execMock.EXPECT().Execute("ovs-vsctl br-get-external-id br-rail2 test-pod-uid").
230+
Return("rail_pod_id", nil)
226231
execMock.EXPECT().Execute("ovs-ofctl del-flows br-rail1 cookie=0x5/-1").Return("", nil)
227-
err := flows.DeletePodRailFlows(0x5, "br-rail1")
232+
execMock.EXPECT().Execute("ovs-ofctl del-flows br-rail2 cookie=0x5/-1").Return("", nil)
233+
execMock.EXPECT().Execute(`ovs-vsctl br-set-external-id br-rail1 test-pod-uid ""`).Return("", nil)
234+
execMock.EXPECT().Execute(`ovs-vsctl br-set-external-id br-rail2 test-pod-uid ""`).Return("", nil)
235+
err := flows.DeletePodRailFlows(0x5, "test-pod-uid")
228236
Expect(err).Should(Succeed())
229237
})
230238

231-
It("should return error if ovs-ofctl fails", func() {
232-
execMock.EXPECT().Execute("ovs-ofctl del-flows br-rail1 cookie=0x5/-1").Return("", fmt.Errorf("failed to delete flows"))
233-
err := flows.DeletePodRailFlows(0x5, "br-rail1")
239+
It("should return error if failed to list bridges", func() {
240+
execMock.EXPECT().Execute("ovs-vsctl list-br").Return("", fmt.Errorf("failed to list bridges"))
241+
err := flows.DeletePodRailFlows(0x5, "test-pod-uid")
242+
Expect(err).Should(HaveOccurred())
243+
})
244+
245+
It("should return error if failed to get external id", func() {
246+
execMock.EXPECT().Execute("ovs-vsctl list-br").Return("br-rail1", nil)
247+
execMock.EXPECT().
248+
Execute("ovs-vsctl br-get-external-id br-rail1 test-pod-uid").
249+
Return("", fmt.Errorf("failed to get external id"))
250+
251+
err := flows.DeletePodRailFlows(0x5, "test-pod-uid")
252+
Expect(err).Should(HaveOccurred())
253+
})
254+
255+
It("should return error if failed to delete flows", func() {
256+
execMock.EXPECT().Execute("ovs-vsctl list-br").Return("br-rail1", nil)
257+
258+
execMock.EXPECT().
259+
Execute("ovs-vsctl br-get-external-id br-rail1 test-pod-uid").
260+
Return("rail_pod_id", nil)
261+
262+
execMock.EXPECT().Execute("ovs-ofctl del-flows br-rail1 cookie=0x5/-1").
263+
Return("", fmt.Errorf("failed to delete flows"))
264+
265+
err := flows.DeletePodRailFlows(0x5, "test-pod-uid")
266+
Expect(err).Should(HaveOccurred())
267+
})
268+
269+
It("should return error if failed to clear external id", func() {
270+
execMock.EXPECT().Execute("ovs-vsctl list-br").Return("br-rail1", nil)
271+
execMock.EXPECT().
272+
Execute("ovs-vsctl br-get-external-id br-rail1 test-pod-uid").
273+
Return("rail_pod_id", nil)
274+
execMock.EXPECT().Execute("ovs-ofctl del-flows br-rail1 cookie=0x5/-1").Return("", nil)
275+
execMock.EXPECT().
276+
Execute(`ovs-vsctl br-set-external-id br-rail1 test-pod-uid ""`).
277+
Return("", fmt.Errorf("failed to clear external id"))
278+
279+
err := flows.DeletePodRailFlows(0x5, "test-pod-uid")
280+
Expect(err).Should(HaveOccurred())
281+
Expect(err.Error()).Should(ContainSubstring("failed to clear external id"))
282+
})
283+
284+
It("should try to delete all flows before returning error", func() {
285+
execMock.EXPECT().Execute("ovs-vsctl list-br").Return("br-rail1\nbr-rail2", nil)
286+
execMock.EXPECT().Execute("ovs-vsctl br-get-external-id br-rail1 test-pod-uid").
287+
Return("rail_pod_id", fmt.Errorf("failed to get external id"))
288+
execMock.EXPECT().Execute("ovs-vsctl br-get-external-id br-rail2 test-pod-uid").
289+
Return("rail_pod_id", nil)
290+
execMock.EXPECT().Execute("ovs-ofctl del-flows br-rail2 cookie=0x5/-1").Return("", fmt.Errorf("failed to delete flows"))
291+
292+
err := flows.DeletePodRailFlows(0x5, "test-pod-uid")
234293
Expect(err).Should(HaveOccurred())
294+
Expect(err.Error()).Should(ContainSubstring("failed to get external id"))
295+
Expect(err.Error()).Should(ContainSubstring("failed to delete flows"))
235296
})
236297
})
237298
})

internal/controller/mock_flows.go

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)