1
2
3 package main
4
5 import (
6 "context"
7 "net"
8 "path/filepath"
9 "testing"
10 "time"
11
12 computeagentMock "github.com/Microsoft/hcsshim/internal/computeagent/mock"
13 ncproxystore "github.com/Microsoft/hcsshim/internal/ncproxy/store"
14 "github.com/Microsoft/hcsshim/internal/ncproxyttrpc"
15 nodenetsvcV0 "github.com/Microsoft/hcsshim/pkg/ncproxy/nodenetsvc/v0"
16 nodenetsvcMockV0 "github.com/Microsoft/hcsshim/pkg/ncproxy/nodenetsvc/v0/mock"
17 nodenetsvc "github.com/Microsoft/hcsshim/pkg/ncproxy/nodenetsvc/v1"
18 nodenetsvcMock "github.com/Microsoft/hcsshim/pkg/ncproxy/nodenetsvc/v1/mock"
19 "github.com/containerd/ttrpc"
20 "github.com/golang/mock/gomock"
21 "github.com/pkg/errors"
22 bolt "go.etcd.io/bbolt"
23 "google.golang.org/grpc/codes"
24 "google.golang.org/grpc/status"
25 )
26
27 func TestRegisterComputeAgent(t *testing.T) {
28 ctx := context.Background()
29
30
31 tempDir := t.TempDir()
32
33 db, err := bolt.Open(filepath.Join(tempDir, "networkproxy.db.test"), 0600, nil)
34 if err != nil {
35 t.Fatal(err)
36 }
37 defer db.Close()
38
39
40 store := ncproxystore.NewComputeAgentStore(db)
41 agentCache := newComputeAgentCache()
42 tService := newTTRPCService(ctx, agentCache, store)
43
44
45 winioDialPipe = func(path string, timeout *time.Duration) (net.Conn, error) {
46 rPipe, _ := net.Pipe()
47 return rPipe, nil
48 }
49 ttrpcNewClient = func(conn net.Conn, opts ...ttrpc.ClientOpts) *ttrpc.Client {
50 return &ttrpc.Client{}
51 }
52
53 containerID := t.Name() + "-containerID"
54 req := &ncproxyttrpc.RegisterComputeAgentRequest{
55 AgentAddress: t.Name() + "-agent-address",
56 ContainerID: containerID,
57 }
58 if _, err := tService.RegisterComputeAgent(ctx, req); err != nil {
59 t.Fatalf("expected to get no error, instead got %v", err)
60 }
61
62
63 actual, err := agentCache.get(containerID)
64 if err != nil {
65 t.Fatalf("failed to get the agent entry %v", err)
66 }
67 if actual == nil {
68 t.Fatal("compute agent client was not put into agent cache")
69 }
70 }
71
72 func TestConfigureNetworking_V1(t *testing.T) {
73 ctx := context.Background()
74
75
76 tempDir := t.TempDir()
77
78 db, err := bolt.Open(filepath.Join(tempDir, "networkproxy.db.test"), 0600, nil)
79 if err != nil {
80 t.Fatal(err)
81 }
82 defer db.Close()
83
84
85 store := ncproxystore.NewComputeAgentStore(db)
86 agentCache := newComputeAgentCache()
87 tService := newTTRPCService(ctx, agentCache, store)
88
89
90 nodeNetCtrl := gomock.NewController(t)
91 defer nodeNetCtrl.Finish()
92 mockedClient := nodenetsvcMock.NewMockNodeNetworkServiceClient(nodeNetCtrl)
93 mockedClientV0 := nodenetsvcMockV0.NewMockNodeNetworkServiceClient(nodeNetCtrl)
94 nodeNetSvcClient = &nodeNetSvcConn{
95 addr: "",
96 client: mockedClient,
97 v0Client: mockedClientV0,
98 }
99
100
101 mockedClient.EXPECT().ConfigureNetworking(gomock.Any(), gomock.Any()).Return(&nodenetsvc.ConfigureNetworkingResponse{}, nil).AnyTimes()
102
103 type config struct {
104 name string
105 containerID string
106 requestType ncproxyttrpc.RequestTypeInternal
107 errorExpected bool
108 }
109 containerID := t.Name() + "-containerID"
110 tests := []config{
111 {
112 name: "Configure Networking setup returns no error",
113 containerID: containerID,
114 requestType: ncproxyttrpc.RequestTypeInternal_Setup,
115 errorExpected: false,
116 },
117 {
118 name: "Configure Networking teardown returns no error",
119 containerID: containerID,
120 requestType: ncproxyttrpc.RequestTypeInternal_Teardown,
121 errorExpected: false,
122 },
123 {
124 name: "Configure Networking setup returns error when container ID is empty",
125 containerID: "",
126 requestType: ncproxyttrpc.RequestTypeInternal_Setup,
127 errorExpected: true,
128 },
129 {
130 name: "Configure Networking setup returns error when request type is not supported",
131 containerID: containerID,
132 requestType: 3,
133 errorExpected: true,
134 },
135 }
136
137 for _, test := range tests {
138 t.Run(test.name, func(_ *testing.T) {
139 req := &ncproxyttrpc.ConfigureNetworkingInternalRequest{
140 ContainerID: test.containerID,
141 RequestType: test.requestType,
142 }
143 _, err := tService.ConfigureNetworking(ctx, req)
144 if test.errorExpected && err == nil {
145 t.Fatalf("expected ConfigureNetworking to return an error")
146 }
147 if !test.errorExpected && err != nil {
148 t.Fatalf("expected ConfigureNetworking to return no error, instead got %v", err)
149 }
150 })
151 }
152 }
153
154 func TestConfigureNetworking_V0(t *testing.T) {
155 ctx := context.Background()
156
157
158 tempDir := t.TempDir()
159
160 db, err := bolt.Open(filepath.Join(tempDir, "networkproxy.db.test"), 0600, nil)
161 if err != nil {
162 t.Fatal(err)
163 }
164 defer db.Close()
165
166
167 store := ncproxystore.NewComputeAgentStore(db)
168 agentCache := newComputeAgentCache()
169 tService := newTTRPCService(ctx, agentCache, store)
170
171
172 nodeNetCtrl := gomock.NewController(t)
173 defer nodeNetCtrl.Finish()
174 mockedClient := nodenetsvcMock.NewMockNodeNetworkServiceClient(nodeNetCtrl)
175 mockedClientV0 := nodenetsvcMockV0.NewMockNodeNetworkServiceClient(nodeNetCtrl)
176 nodeNetSvcClient = &nodeNetSvcConn{
177 addr: "",
178 client: mockedClient,
179 v0Client: mockedClientV0,
180 }
181
182
183
184 mockedClientV0.EXPECT().ConfigureNetworking(gomock.Any(), gomock.Any()).Return(&nodenetsvcV0.ConfigureNetworkingResponse{}, nil).AnyTimes()
185 mockedClient.EXPECT().ConfigureNetworking(gomock.Any(), gomock.Any()).Return(nil, status.Error(codes.Unimplemented, "mock the v1 api not implemented")).AnyTimes()
186
187 type config struct {
188 name string
189 containerID string
190 requestType ncproxyttrpc.RequestTypeInternal
191 errorExpected bool
192 }
193 containerID := t.Name() + "-containerID"
194 tests := []config{
195 {
196 name: "Configure Networking setup returns no error",
197 containerID: containerID,
198 requestType: ncproxyttrpc.RequestTypeInternal_Setup,
199 errorExpected: false,
200 },
201 {
202 name: "Configure Networking teardown returns no error",
203 containerID: containerID,
204 requestType: ncproxyttrpc.RequestTypeInternal_Teardown,
205 errorExpected: false,
206 },
207 {
208 name: "Configure Networking setup returns error when container ID is empty",
209 containerID: "",
210 requestType: ncproxyttrpc.RequestTypeInternal_Setup,
211 errorExpected: true,
212 },
213 {
214 name: "Configure Networking setup returns error when request type is not supported",
215 containerID: containerID,
216 requestType: 3,
217 errorExpected: true,
218 },
219 }
220
221 for _, test := range tests {
222 t.Run(test.name, func(_ *testing.T) {
223 req := &ncproxyttrpc.ConfigureNetworkingInternalRequest{
224 ContainerID: test.containerID,
225 RequestType: test.requestType,
226 }
227 _, err := tService.ConfigureNetworking(ctx, req)
228 if test.errorExpected && err == nil {
229 t.Fatalf("expected ConfigureNetworking to return an error")
230 }
231 if !test.errorExpected && err != nil {
232 t.Fatalf("expected ConfigureNetworking to return no error, instead got %v", err)
233 }
234 })
235 }
236 }
237
238 func TestReconnectComputeAgents_Success(t *testing.T) {
239 ctx := context.Background()
240
241
242 tempDir := t.TempDir()
243
244 db, err := bolt.Open(filepath.Join(tempDir, "networkproxy.db.test"), 0600, nil)
245 if err != nil {
246 t.Fatal(err)
247 }
248 defer db.Close()
249
250
251 store := ncproxystore.NewComputeAgentStore(db)
252 agentCache := newComputeAgentCache()
253
254
255 winioDialPipe = func(path string, timeout *time.Duration) (net.Conn, error) {
256 rPipe, _ := net.Pipe()
257 return rPipe, nil
258 }
259 ttrpcNewClient = func(conn net.Conn, opts ...ttrpc.ClientOpts) *ttrpc.Client {
260 return &ttrpc.Client{}
261 }
262
263
264 containerID := "fake-container-id"
265 address := "123412341234"
266
267 if err := store.UpdateComputeAgent(ctx, containerID, address); err != nil {
268 t.Fatal(err)
269 }
270
271 reconnectComputeAgents(ctx, store, agentCache)
272
273
274 actualClient, err := agentCache.get(containerID)
275 if err != nil {
276 t.Fatal(err)
277 }
278 if actualClient == nil {
279 t.Fatal("no entry added on reconnect to agent client cache")
280 }
281 }
282
283 func TestReconnectComputeAgents_Failure(t *testing.T) {
284 ctx := context.Background()
285
286
287 tempDir := t.TempDir()
288
289 db, err := bolt.Open(filepath.Join(tempDir, "networkproxy.db.test"), 0600, nil)
290 if err != nil {
291 t.Fatal(err)
292 }
293 defer db.Close()
294
295
296 store := ncproxystore.NewComputeAgentStore(db)
297 agentCache := newComputeAgentCache()
298
299
300 winioDialPipe = func(path string, timeout *time.Duration) (net.Conn, error) {
301
302
303 return nil, errors.New("fake error")
304 }
305 ttrpcNewClient = func(conn net.Conn, opts ...ttrpc.ClientOpts) *ttrpc.Client {
306 return &ttrpc.Client{}
307 }
308
309
310 containerID := "fake-container-id"
311 address := "123412341234"
312
313 if err := store.UpdateComputeAgent(ctx, containerID, address); err != nil {
314 t.Fatal(err)
315 }
316
317 reconnectComputeAgents(ctx, store, agentCache)
318
319
320 actualClient, err := agentCache.get(containerID)
321 if err != nil {
322 t.Fatal(err)
323 }
324 if actualClient != nil {
325 t.Fatalf("expected no entry on failure, instead found %v", actualClient)
326 }
327
328
329 value, err := store.GetComputeAgent(ctx, containerID)
330 if err == nil {
331 t.Fatalf("expected an error, instead found value %s", value)
332 }
333 }
334
335 func TestDisconnectComputeAgents(t *testing.T) {
336 ctx := context.Background()
337 containerID := "fake-container-id"
338
339 agentCache := newComputeAgentCache()
340
341
342 computeAgentCtrl := gomock.NewController(t)
343 defer computeAgentCtrl.Finish()
344 mockedService := computeagentMock.NewMockComputeAgentService(computeAgentCtrl)
345 mockedAgentClient := &computeAgentClient{nil, mockedService}
346
347
348 if err := agentCache.put(containerID, mockedAgentClient); err != nil {
349 t.Fatal(err)
350 }
351
352 if err := disconnectComputeAgents(ctx, agentCache); err != nil {
353 t.Fatal(err)
354 }
355
356
357 actual, err := agentCache.get(containerID)
358 if err == nil {
359 t.Fatalf("expected to find the cache empty, instead found %v", actual)
360 }
361 }
362
View as plain text