diff --git a/pkg/route/manager.go b/pkg/route/manager.go index 26c0f193..cc3800db 100644 --- a/pkg/route/manager.go +++ b/pkg/route/manager.go @@ -49,9 +49,6 @@ func (m *Manager) Add(object string, r route, precheck, update bool) error { itm, exists := m.tracker[key] if !exists { - m.tracker[key] = newItem(r) - itm = m.tracker[key] - added, err := r.AddRoute(precheck) if err != nil { if update && errors.Is(err, syscall.EEXIST) && update { @@ -70,6 +67,9 @@ func (m *Manager) Add(object string, r route, precheck, update bool) error { } } + itm = newItem(r) + m.tracker[key] = itm + if added { log.Debug("[RT] added route", "path", key, "object", object) } diff --git a/pkg/route/manager_atomic_test.go b/pkg/route/manager_atomic_test.go new file mode 100644 index 00000000..32070c0b --- /dev/null +++ b/pkg/route/manager_atomic_test.go @@ -0,0 +1,29 @@ +package route + +import ( + "errors" + "testing" +) + +var errTransientRouteAdd = errors.New("transient route add failure") + +func TestManagerRetriesRouteAfterInitialAddFailure(t *testing.T) { + m := NewManager() + r := &mockRoute{hash: "retry", addErr: errTransientRouteAdd} + + if err := m.Add("service", r, false, true); !errors.Is(err, errTransientRouteAdd) { + t.Fatalf("first add error = %v, want %v", err, errTransientRouteAdd) + } + + // A transient netlink failure (for example while the link is being recreated) + // must not poison the in-memory tracker. The next reconciliation has to retry + // the kernel operation. + r.addErr = nil + r.added = true + if err := m.Add("service", r, false, true); err != nil { + t.Fatalf("retry add failed: %v", err) + } + if r.addCalls != 2 { + t.Fatalf("AddRoute called %d times, want 2", r.addCalls) + } +} diff --git a/pkg/route/manager_test.go b/pkg/route/manager_test.go index b387e79e..12ca2f18 100644 --- a/pkg/route/manager_test.go +++ b/pkg/route/manager_test.go @@ -186,6 +186,7 @@ func Test_MultipleRoutesAddDel(t *testing.T) { type mockRoute struct { added bool + addCalls int addErr error updated bool updateErr error @@ -195,6 +196,7 @@ type mockRoute struct { } func (mr *mockRoute) AddRoute(_ bool) (bool, error) { + mr.addCalls++ return mr.added, mr.addErr }