master
go 203 lines 4.93 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package l2topology
4
5 import (
6 "errors"
7 "strconv"
8 "sync/atomic"
9 "testing"
10 "time"
11
12 "github.com/stretchr/testify/require"
13 )
14
15 // Port of TopologyUpdaterIT.verifyGetTopologyAccessWhileDiscoveryInProgressDoesNotBlock.
16 func TestTopologyUpdater_GetTopologyDoesNotBlockDuringDiscovery(t *testing.T) {
17 var buildCalls atomic.Int64
18 updater := NewTopologyUpdater(func() (NetworkRouterTopology, error) {
19 buildCalls.Add(1)
20 time.Sleep(1 * time.Second)
21 return NetworkRouterTopology{
22 Vertices: []NetworkRouterVertex{
23 {ID: "v1", Label: "Vertex 1"},
24 {ID: "v2", Label: "Vertex 2"},
25 },
26 }, nil
27 }, nil, nil)
28
29 discoveryDone := make(chan struct{})
30 go func() {
31 updater.RunSchedulable()
32 close(discoveryDone)
33 }()
34
35 type topoResult struct {
36 vertices int
37 edges int
38 }
39
40 getCurrent := func() <-chan topoResult {
41 done := make(chan topoResult, 1)
42 go func() {
43 current := updater.GetTopology()
44 done <- topoResult{
45 vertices: len(current.Vertices),
46 edges: len(current.Edges),
47 }
48 }()
49 return done
50 }
51
52 select {
53 case <-time.After(1 * time.Second):
54 t.Fatalf("get topology timed out while discovery was running")
55 case result := <-getCurrent():
56 require.Equal(t, 0, result.vertices)
57 require.Equal(t, 0, result.edges)
58 }
59
60 select {
61 case <-time.After(3 * time.Second):
62 t.Fatalf("discovery did not complete")
63 case <-discoveryDone:
64 }
65
66 select {
67 case <-time.After(1 * time.Second):
68 t.Fatalf("get topology timed out after discovery")
69 case result := <-getCurrent():
70 require.Equal(t, 2, result.vertices)
71 require.Equal(t, 0, result.edges)
72 }
73
74 require.Equal(t, int64(1), buildCalls.Load())
75 }
76
77 func TestTopologyUpdaterFirstRunFailureAndRecovery(t *testing.T) {
78 var calls int
79 boom := errors.New("boom")
80
81 updater := NewTopologyUpdater(func() (NetworkRouterTopology, error) {
82 calls++
83 if calls == 1 {
84 return NetworkRouterTopology{}, boom
85 }
86 return NetworkRouterTopology{
87 Vertices: []NetworkRouterVertex{{ID: "node-1", Label: "node-1"}},
88 DefaultVertex: "node-1",
89 }, nil
90 }, nil, nil)
91
92 require.False(t, updater.HasRun())
93 require.Nil(t, updater.LastError())
94
95 updater.RunSchedulable()
96
97 require.False(t, updater.HasRun())
98 require.ErrorIs(t, updater.LastError(), boom)
99 require.Empty(t, updater.GetTopology().Vertices)
100
101 updater.RunSchedulable()
102
103 require.True(t, updater.HasRun())
104 require.NoError(t, updater.LastError())
105 topology := updater.GetTopology()
106 require.Equal(t, "node-1", topology.DefaultVertex)
107 require.Len(t, topology.Vertices, 1)
108
109 topology.Vertices[0].ID = "mutated"
110 require.Equal(t, "node-1", updater.GetTopology().Vertices[0].ID)
111 }
112
113 func TestTopologyUpdaterForceRunAndParseUpdatesTriggerRefresh(t *testing.T) {
114 var (
115 builderCalls int
116 parseCalls int
117 refreshCalls int
118 parseUpdates bool
119 )
120
121 updater := NewTopologyUpdater(
122 func() (NetworkRouterTopology, error) {
123 builderCalls++
124 vertexID := strconv.Itoa(builderCalls)
125 return NetworkRouterTopology{
126 Vertices: []NetworkRouterVertex{{ID: vertexID, Label: "node-" + vertexID}},
127 DefaultVertex: vertexID,
128 }, nil
129 },
130 func() bool {
131 parseCalls++
132 return parseUpdates
133 },
134 func() {
135 refreshCalls++
136 },
137 )
138
139 updater.RunSchedulable()
140 require.True(t, updater.HasRun())
141 require.Equal(t, 1, builderCalls)
142 require.Equal(t, 0, parseCalls)
143 require.Equal(t, 0, refreshCalls)
144 require.Equal(t, "1", updater.GetTopology().DefaultVertex)
145
146 updater.RunSchedulable()
147 require.Equal(t, 1, builderCalls)
148 require.Equal(t, 1, parseCalls)
149 require.Equal(t, 0, refreshCalls)
150 require.Equal(t, "1", updater.GetTopology().DefaultVertex)
151
152 updater.ForceRun()
153 updater.RunSchedulable()
154 require.Equal(t, 2, builderCalls)
155 require.Equal(t, 2, parseCalls)
156 require.Equal(t, 1, refreshCalls)
157 require.Equal(t, "2", updater.GetTopology().DefaultVertex)
158
159 parseUpdates = true
160 updater.RunSchedulable()
161 require.Equal(t, 3, builderCalls)
162 require.Equal(t, 3, parseCalls)
163 require.Equal(t, 2, refreshCalls)
164 require.Equal(t, "3", updater.GetTopology().DefaultVertex)
165 require.NoError(t, updater.LastError())
166 }
167
168 func TestTopologyUpdaterRefreshFailureKeepsLastSuccessfulTopology(t *testing.T) {
169 var (
170 builderCalls int
171 parseUpdates bool
172 )
173 boom := errors.New("refresh failed")
174
175 updater := NewTopologyUpdater(
176 func() (NetworkRouterTopology, error) {
177 builderCalls++
178 if builderCalls == 2 {
179 return NetworkRouterTopology{}, boom
180 }
181 vertexID := strconv.Itoa(builderCalls)
182 return NetworkRouterTopology{
183 Vertices: []NetworkRouterVertex{{ID: vertexID, Label: "node-" + vertexID}},
184 DefaultVertex: vertexID,
185 }, nil
186 },
187 func() bool {
188 return parseUpdates
189 },
190 nil,
191 )
192
193 updater.RunSchedulable()
194 require.True(t, updater.HasRun())
195 require.Equal(t, "1", updater.GetTopology().DefaultVertex)
196 require.NoError(t, updater.LastError())
197
198 parseUpdates = true
199 updater.RunSchedulable()
200 require.True(t, updater.HasRun())
201 require.ErrorIs(t, updater.LastError(), boom)
202 require.Equal(t, "1", updater.GetTopology().DefaultVertex)
203 }