Browse code

Add LeaveAll support

Currently container can join one endpoint when it is started.
More endpoints can be attached at a later point in time. But
when that happens this attachment should only have meaning
only as long as the container is alive. The attachment should
lose it's meaning when the container goes away. Cuurently there
is no way for the container management code to tell libnetwork
to detach the container from all attached endpoints. This PR
provides an additional API `LeaveAll` which adds this
functionality,

To facilitate this and make the sanbox lifecycle consistent
some slight changes have been made to the behavior of sandbox
management code. The sandbox is no longer destroyed when the
last endpoint is detached from the container. Instead the sandbox
ie kept alive and can only be destroyed with a `LeaveAll` call.
This gives better control of sandbox lifecycle by the container
management code and the sandbox doesn't get destroyed from under
the carpet while the container is still using it.

Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>

Jana Radhakrishnan authored on 2015/06/20 10:41:31
Showing 7 changed files
... ...
@@ -87,6 +87,9 @@ type NetworkController interface {
87 87
 	// NetworkByID returns the Network which has the passed id. If not found, the error ErrNoSuchNetwork is returned.
88 88
 	NetworkByID(id string) (Network, error)
89 89
 
90
+	// LeaveAll accepts a container id and attempts to leave all endpoints that the container has joined
91
+	LeaveAll(id string) error
92
+
90 93
 	// GC triggers immediate garbage collection of resources which are garbage collected.
91 94
 	GC()
92 95
 }
... ...
@@ -349,11 +349,6 @@ func (ep *endpoint) Join(containerID string, options ...EndpointOption) error {
349 349
 	ep.joinLeaveStart()
350 350
 	defer func() {
351 351
 		ep.joinLeaveEnd()
352
-		if err != nil {
353
-			if e := ep.Leave(containerID); e != nil {
354
-				log.Warnf("couldnt leave endpoint : %v", ep.name, err)
355
-			}
356
-		}
357 352
 	}()
358 353
 
359 354
 	ep.Lock()
... ...
@@ -403,6 +398,13 @@ func (ep *endpoint) Join(containerID string, options ...EndpointOption) error {
403 403
 	if err != nil {
404 404
 		return err
405 405
 	}
406
+	defer func() {
407
+		if err != nil {
408
+			if err = driver.Leave(nid, epid); err != nil {
409
+				log.Warnf("driver leave failed while rolling back join: %v", err)
410
+			}
411
+		}
412
+	}()
406 413
 
407 414
 	err = ep.buildHostsFiles()
408 415
 	if err != nil {
... ...
@@ -421,7 +423,7 @@ func (ep *endpoint) Join(containerID string, options ...EndpointOption) error {
421 421
 
422 422
 	sb, err := ctrlr.sandboxAdd(sboxKey, !container.config.useDefaultSandBox, ep)
423 423
 	if err != nil {
424
-		return err
424
+		return fmt.Errorf("failed sandbox add: %v", err)
425 425
 	}
426 426
 	defer func() {
427 427
 		if err != nil {
... ...
@@ -51,7 +51,7 @@ func (ij ErrInvalidJoin) BadRequest() {}
51 51
 type ErrNoContainer struct{}
52 52
 
53 53
 func (nc ErrNoContainer) Error() string {
54
-	return "a container has already joined the endpoint"
54
+	return "no container is attached to the endpoint"
55 55
 }
56 56
 
57 57
 // Maskable denotes the type of this error
... ...
@@ -1009,6 +1009,8 @@ func TestEndpointJoin(t *testing.T) {
1009 1009
 		t.Fatalf("Expected an empty sandbox key for an empty endpoint. Instead found a non-empty sandbox key: %s", info.SandboxKey())
1010 1010
 	}
1011 1011
 
1012
+	defer controller.LeaveAll(containerID)
1013
+
1012 1014
 	err = ep1.Join(containerID,
1013 1015
 		libnetwork.JoinOptionHostname("test"),
1014 1016
 		libnetwork.JoinOptionDomainname("docker.io"),
... ...
@@ -1017,7 +1019,6 @@ func TestEndpointJoin(t *testing.T) {
1017 1017
 	if err != nil {
1018 1018
 		t.Fatal(err)
1019 1019
 	}
1020
-
1021 1020
 	defer func() {
1022 1021
 		err = ep1.Leave(containerID)
1023 1022
 		runtime.LockOSThread()
... ...
@@ -1072,19 +1073,21 @@ func TestEndpointJoin(t *testing.T) {
1072 1072
 	if err != nil {
1073 1073
 		t.Fatal(err)
1074 1074
 	}
1075
-
1076
-	if ep1.ContainerInfo().ID() != ep2.ContainerInfo().ID() {
1077
-		t.Fatalf("ep1 and ep2 returned different container info")
1078
-	}
1079
-
1075
+	runtime.LockOSThread()
1080 1076
 	defer func() {
1081 1077
 		err = ep2.Leave(containerID)
1078
+		runtime.LockOSThread()
1082 1079
 		if err != nil {
1083 1080
 			t.Fatal(err)
1084 1081
 		}
1085 1082
 	}()
1086 1083
 
1084
+	if ep1.ContainerInfo().ID() != ep2.ContainerInfo().ID() {
1085
+		t.Fatalf("ep1 and ep2 returned different container info")
1086
+	}
1087
+
1087 1088
 	checkSandbox(t, info)
1089
+
1088 1090
 }
1089 1091
 
1090 1092
 func TestEndpointJoinInvalidContainerId(t *testing.T) {
... ...
@@ -1151,6 +1154,14 @@ func TestEndpointDeleteWithActiveContainer(t *testing.T) {
1151 1151
 	if err != nil {
1152 1152
 		t.Fatal(err)
1153 1153
 	}
1154
+	defer func() {
1155
+		err = ep.Delete()
1156
+		if err != nil {
1157
+			t.Fatal(err)
1158
+		}
1159
+	}()
1160
+
1161
+	defer controller.LeaveAll(containerID)
1154 1162
 
1155 1163
 	err = ep.Join(containerID,
1156 1164
 		libnetwork.JoinOptionHostname("test"),
... ...
@@ -1166,11 +1177,6 @@ func TestEndpointDeleteWithActiveContainer(t *testing.T) {
1166 1166
 		if err != nil {
1167 1167
 			t.Fatal(err)
1168 1168
 		}
1169
-
1170
-		err = ep.Delete()
1171
-		if err != nil {
1172
-			t.Fatal(err)
1173
-		}
1174 1169
 	}()
1175 1170
 
1176 1171
 	err = ep.Delete()
... ...
@@ -1213,6 +1219,8 @@ func TestEndpointMultipleJoins(t *testing.T) {
1213 1213
 		}
1214 1214
 	}()
1215 1215
 
1216
+	defer controller.LeaveAll(containerID)
1217
+
1216 1218
 	err = ep.Join(containerID,
1217 1219
 		libnetwork.JoinOptionHostname("test"),
1218 1220
 		libnetwork.JoinOptionDomainname("docker.io"),
... ...
@@ -1239,6 +1247,71 @@ func TestEndpointMultipleJoins(t *testing.T) {
1239 1239
 	}
1240 1240
 }
1241 1241
 
1242
+func TestLeaveAll(t *testing.T) {
1243
+	if !netutils.IsRunningInContainer() {
1244
+		defer netutils.SetupTestNetNS(t)()
1245
+	}
1246
+
1247
+	n, err := createTestNetwork(bridgeNetType, "testnetwork", options.Generic{
1248
+		netlabel.GenericData: options.Generic{
1249
+			"BridgeName":            "testnetwork",
1250
+			"AllowNonDefaultBridge": true,
1251
+		},
1252
+	})
1253
+	if err != nil {
1254
+		t.Fatal(err)
1255
+	}
1256
+	defer func() {
1257
+		if err := n.Delete(); err != nil {
1258
+			t.Fatal(err)
1259
+		}
1260
+	}()
1261
+
1262
+	ep1, err := n.CreateEndpoint("ep1")
1263
+	if err != nil {
1264
+		t.Fatal(err)
1265
+	}
1266
+	defer func() {
1267
+		if err := ep1.Delete(); err != nil {
1268
+			t.Fatal(err)
1269
+		}
1270
+	}()
1271
+
1272
+	ep2, err := n.CreateEndpoint("ep2")
1273
+	if err != nil {
1274
+		t.Fatal(err)
1275
+	}
1276
+	defer func() {
1277
+		if err := ep2.Delete(); err != nil {
1278
+			t.Fatal(err)
1279
+		}
1280
+	}()
1281
+
1282
+	err = ep1.Join("leaveall")
1283
+	if err != nil {
1284
+		t.Fatalf("Failed to join ep1: %v", err)
1285
+	}
1286
+	runtime.LockOSThread()
1287
+
1288
+	err = ep2.Join("leaveall")
1289
+	if err != nil {
1290
+		t.Fatalf("Failed to join ep2: %v", err)
1291
+	}
1292
+	runtime.LockOSThread()
1293
+
1294
+	err = ep1.Leave("leaveall")
1295
+	if err != nil {
1296
+		t.Fatalf("Failed to leave ep1: %v", err)
1297
+	}
1298
+	runtime.LockOSThread()
1299
+
1300
+	err = controller.LeaveAll("leaveall")
1301
+	if err != nil {
1302
+		t.Fatal(err)
1303
+	}
1304
+	runtime.LockOSThread()
1305
+}
1306
+
1242 1307
 func TestEndpointInvalidLeave(t *testing.T) {
1243 1308
 	if !netutils.IsRunningInContainer() {
1244 1309
 		defer netutils.SetupTestNetNS(t)()
... ...
@@ -1280,6 +1353,8 @@ func TestEndpointInvalidLeave(t *testing.T) {
1280 1280
 		}
1281 1281
 	}
1282 1282
 
1283
+	defer controller.LeaveAll(containerID)
1284
+
1283 1285
 	err = ep.Join(containerID,
1284 1286
 		libnetwork.JoinOptionHostname("test"),
1285 1287
 		libnetwork.JoinOptionDomainname("docker.io"),
... ...
@@ -1313,7 +1388,6 @@ func TestEndpointInvalidLeave(t *testing.T) {
1313 1313
 	if _, ok := err.(libnetwork.InvalidContainerIDError); !ok {
1314 1314
 		t.Fatalf("Failed for unexpected reason: %v", err)
1315 1315
 	}
1316
-
1317 1316
 }
1318 1317
 
1319 1318
 func TestEndpointUpdateParent(t *testing.T) {
... ...
@@ -1346,6 +1420,7 @@ func TestEndpointUpdateParent(t *testing.T) {
1346 1346
 		}
1347 1347
 	}()
1348 1348
 
1349
+	defer controller.LeaveAll(containerID)
1349 1350
 	err = ep1.Join(containerID,
1350 1351
 		libnetwork.JoinOptionHostname("test1"),
1351 1352
 		libnetwork.JoinOptionDomainname("docker.io"),
... ...
@@ -1372,6 +1447,7 @@ func TestEndpointUpdateParent(t *testing.T) {
1372 1372
 		}
1373 1373
 	}()
1374 1374
 
1375
+	defer controller.LeaveAll("container2")
1375 1376
 	err = ep2.Join("container2",
1376 1377
 		libnetwork.JoinOptionHostname("test2"),
1377 1378
 		libnetwork.JoinOptionDomainname("docker.io"),
... ...
@@ -1382,13 +1458,11 @@ func TestEndpointUpdateParent(t *testing.T) {
1382 1382
 		t.Fatal(err)
1383 1383
 	}
1384 1384
 
1385
-	defer func() {
1386
-		err = ep2.Leave("container2")
1387
-		runtime.LockOSThread()
1388
-		if err != nil {
1389
-			t.Fatal(err)
1390
-		}
1391
-	}()
1385
+	err = ep2.Leave("container2")
1386
+	runtime.LockOSThread()
1387
+	if err != nil {
1388
+		t.Fatal(err)
1389
+	}
1392 1390
 
1393 1391
 }
1394 1392
 
... ...
@@ -1452,6 +1526,7 @@ func TestEnableIPv6(t *testing.T) {
1452 1452
 	resolvConfPath := "/tmp/libnetwork_test/resolv.conf"
1453 1453
 	defer os.Remove(resolvConfPath)
1454 1454
 
1455
+	defer controller.LeaveAll(containerID)
1455 1456
 	err = ep1.Join(containerID,
1456 1457
 		libnetwork.JoinOptionResolvConfPath(resolvConfPath))
1457 1458
 	runtime.LockOSThread()
... ...
@@ -1536,6 +1611,7 @@ func TestResolvConf(t *testing.T) {
1536 1536
 	resolvConfPath := "/tmp/libnetwork_test/resolv.conf"
1537 1537
 	defer os.Remove(resolvConfPath)
1538 1538
 
1539
+	defer controller.LeaveAll(containerID)
1539 1540
 	err = ep1.Join(containerID,
1540 1541
 		libnetwork.JoinOptionResolvConfPath(resolvConfPath))
1541 1542
 	runtime.LockOSThread()
... ...
@@ -81,7 +81,7 @@ func programGateway(path string, gw net.IP, isAdd bool) error {
81 81
 	return nsInvoke(path, func(nsFD int) error { return nil }, func(callerFD int) error {
82 82
 		gwRoutes, err := netlink.RouteGet(gw)
83 83
 		if err != nil {
84
-			return fmt.Errorf("route for the gateway could not be found: %v", err)
84
+			return fmt.Errorf("route for the gateway %s could not be found: %v", gw, err)
85 85
 		}
86 86
 
87 87
 		if isAdd {
... ...
@@ -105,7 +105,7 @@ func programRoute(path string, dest *net.IPNet, nh net.IP) error {
105 105
 	return nsInvoke(path, func(nsFD int) error { return nil }, func(callerFD int) error {
106 106
 		gwRoutes, err := netlink.RouteGet(nh)
107 107
 		if err != nil {
108
-			return fmt.Errorf("route for the next hop could not be found: %v", err)
108
+			return fmt.Errorf("route for the next hop %s could not be found: %v", nh, err)
109 109
 		}
110 110
 
111 111
 		return netlink.RouteAdd(&netlink.Route{
... ...
@@ -2,6 +2,7 @@ package libnetwork
2 2
 
3 3
 import (
4 4
 	"container/heap"
5
+	"fmt"
5 6
 	"sync"
6 7
 
7 8
 	"github.com/Sirupsen/logrus"
... ...
@@ -48,13 +49,9 @@ func (eh *epHeap) Pop() interface{} {
48 48
 
49 49
 func (s *sandboxData) updateGateway(ep *endpoint) error {
50 50
 	sb := s.sandbox()
51
-	if err := sb.UnsetGateway(); err != nil {
52
-		return err
53
-	}
54 51
 
55
-	if err := sb.UnsetGatewayIPv6(); err != nil {
56
-		return err
57
-	}
52
+	sb.UnsetGateway()
53
+	sb.UnsetGatewayIPv6()
58 54
 
59 55
 	if ep == nil {
60 56
 		return nil
... ...
@@ -65,11 +62,11 @@ func (s *sandboxData) updateGateway(ep *endpoint) error {
65 65
 	ep.Unlock()
66 66
 
67 67
 	if err := sb.SetGateway(joinInfo.gw); err != nil {
68
-		return err
68
+		return fmt.Errorf("failed to set gateway while updating gateway: %v", err)
69 69
 	}
70 70
 
71 71
 	if err := sb.SetGatewayIPv6(joinInfo.gw6); err != nil {
72
-		return err
72
+		return fmt.Errorf("failed to set IPv6 gateway while updating gateway: %v", err)
73 73
 	}
74 74
 
75 75
 	return nil
... ...
@@ -93,7 +90,7 @@ func (s *sandboxData) addEndpoint(ep *endpoint) error {
93 93
 		}
94 94
 
95 95
 		if err := sb.AddInterface(i.srcName, i.dstPrefix, ifaceOptions...); err != nil {
96
-			return err
96
+			return fmt.Errorf("failed to add interface %s to sandbox: %v", i.srcName, err)
97 97
 		}
98 98
 	}
99 99
 
... ...
@@ -101,7 +98,7 @@ func (s *sandboxData) addEndpoint(ep *endpoint) error {
101 101
 		// Set up non-interface routes.
102 102
 		for _, r := range ep.joinInfo.StaticRoutes {
103 103
 			if err := sb.AddStaticRoute(r); err != nil {
104
-				return err
104
+				return fmt.Errorf("failed to add static route %s: %v", r.Destination.String(), err)
105 105
 			}
106 106
 		}
107 107
 	}
... ...
@@ -117,14 +114,10 @@ func (s *sandboxData) addEndpoint(ep *endpoint) error {
117 117
 		}
118 118
 	}
119 119
 
120
-	s.Lock()
121
-	s.refCnt++
122
-	s.Unlock()
123
-
124 120
 	return nil
125 121
 }
126 122
 
127
-func (s *sandboxData) rmEndpoint(ep *endpoint) int {
123
+func (s *sandboxData) rmEndpoint(ep *endpoint) {
128 124
 	ep.Lock()
129 125
 	joinInfo := ep.joinInfo
130 126
 	ep.Unlock()
... ...
@@ -171,17 +164,6 @@ func (s *sandboxData) rmEndpoint(ep *endpoint) int {
171 171
 	if highEpBefore != highEpAfter {
172 172
 		s.updateGateway(highEpAfter)
173 173
 	}
174
-
175
-	s.Lock()
176
-	s.refCnt--
177
-	refCnt := s.refCnt
178
-	s.Unlock()
179
-
180
-	if refCnt == 0 {
181
-		s.sandbox().Destroy()
182
-	}
183
-
184
-	return refCnt
185 174
 }
186 175
 
187 176
 func (s *sandboxData) sandbox() sandbox.Sandbox {
... ...
@@ -199,7 +181,7 @@ func (c *controller) sandboxAdd(key string, create bool, ep *endpoint) (sandbox.
199 199
 	if !ok {
200 200
 		sb, err := sandbox.NewSandbox(key, create)
201 201
 		if err != nil {
202
-			return nil, err
202
+			return nil, fmt.Errorf("failed to create new sandbox: %v", err)
203 203
 		}
204 204
 
205 205
 		sData = &sandboxData{
... ...
@@ -225,11 +207,7 @@ func (c *controller) sandboxRm(key string, ep *endpoint) {
225 225
 	sData := c.sandboxes[key]
226 226
 	c.Unlock()
227 227
 
228
-	if sData.rmEndpoint(ep) == 0 {
229
-		c.Lock()
230
-		delete(c.sandboxes, key)
231
-		c.Unlock()
232
-	}
228
+	sData.rmEndpoint(ep)
233 229
 }
234 230
 
235 231
 func (c *controller) sandboxGet(key string) sandbox.Sandbox {
... ...
@@ -243,3 +221,32 @@ func (c *controller) sandboxGet(key string) sandbox.Sandbox {
243 243
 
244 244
 	return sData.sandbox()
245 245
 }
246
+
247
+func (c *controller) LeaveAll(id string) error {
248
+	c.Lock()
249
+	sData, ok := c.sandboxes[sandbox.GenerateKey(id)]
250
+	c.Unlock()
251
+
252
+	if !ok {
253
+		c.Unlock()
254
+		return fmt.Errorf("could not find sandbox for container id %s", id)
255
+	}
256
+
257
+	sData.Lock()
258
+	eps := make([]*endpoint, len(sData.endpoints))
259
+	for i, ep := range sData.endpoints {
260
+		eps[i] = ep
261
+	}
262
+	sData.Unlock()
263
+
264
+	for _, ep := range eps {
265
+		if err := ep.Leave(id); err != nil {
266
+			logrus.Warnf("Failed leaving endpoint id %s: %v\n", ep.ID(), err)
267
+		}
268
+	}
269
+
270
+	sData.sandbox().Destroy()
271
+	delete(c.sandboxes, sandbox.GenerateKey(id))
272
+
273
+	return nil
274
+}
... ...
@@ -22,16 +22,13 @@ func TestSandboxAddEmpty(t *testing.T) {
22 22
 	ctrlr := createEmptyCtrlr()
23 23
 	ep := createEmptyEndpoint()
24 24
 
25
-	if _, err := ctrlr.sandboxAdd("sandbox1", true, ep); err != nil {
25
+	if _, err := ctrlr.sandboxAdd(sandbox.GenerateKey("sandbox1"), true, ep); err != nil {
26 26
 		t.Fatal(err)
27 27
 	}
28 28
 
29
-	if ctrlr.sandboxes["sandbox1"].refCnt != 1 {
30
-		t.Fatalf("Unexpected sandbox ref count. Expected 1, got %d",
31
-			ctrlr.sandboxes["sandbox1"].refCnt)
32
-	}
29
+	ctrlr.sandboxRm(sandbox.GenerateKey("sandbox1"), ep)
33 30
 
34
-	ctrlr.sandboxRm("sandbox1", ep)
31
+	ctrlr.LeaveAll("sandbox1")
35 32
 	if len(ctrlr.sandboxes) != 0 {
36 33
 		t.Fatalf("controller sandboxes is not empty. len = %d", len(ctrlr.sandboxes))
37 34
 	}
... ...
@@ -49,50 +46,52 @@ func TestSandboxAddMultiPrio(t *testing.T) {
49 49
 	ep2.container.config.prio = 2
50 50
 	ep3.container.config.prio = 3
51 51
 
52
-	if _, err := ctrlr.sandboxAdd("sandbox1", true, ep1); err != nil {
53
-		t.Fatal(err)
54
-	}
52
+	sKey := sandbox.GenerateKey("sandbox1")
55 53
 
56
-	if _, err := ctrlr.sandboxAdd("sandbox1", true, ep2); err != nil {
54
+	if _, err := ctrlr.sandboxAdd(sKey, true, ep1); err != nil {
57 55
 		t.Fatal(err)
58 56
 	}
59 57
 
60
-	if _, err := ctrlr.sandboxAdd("sandbox1", true, ep3); err != nil {
58
+	if _, err := ctrlr.sandboxAdd(sKey, true, ep2); err != nil {
61 59
 		t.Fatal(err)
62 60
 	}
63 61
 
64
-	if ctrlr.sandboxes["sandbox1"].refCnt != 3 {
65
-		t.Fatalf("Unexpected sandbox ref count. Expected 3, got %d",
66
-			ctrlr.sandboxes["sandbox1"].refCnt)
62
+	if _, err := ctrlr.sandboxAdd(sKey, true, ep3); err != nil {
63
+		t.Fatal(err)
67 64
 	}
68 65
 
69
-	if ctrlr.sandboxes["sandbox1"].endpoints[0] != ep3 {
66
+	if ctrlr.sandboxes[sKey].endpoints[0] != ep3 {
70 67
 		t.Fatal("Expected ep3 to be at the top of the heap. But did not find ep3 at the top of the heap")
71 68
 	}
72 69
 
73
-	ctrlr.sandboxRm("sandbox1", ep3)
70
+	ctrlr.sandboxRm(sKey, ep3)
74 71
 
75
-	if ctrlr.sandboxes["sandbox1"].endpoints[0] != ep2 {
72
+	if ctrlr.sandboxes[sKey].endpoints[0] != ep2 {
76 73
 		t.Fatal("Expected ep2 to be at the top of the heap after removing ep3. But did not find ep2 at the top of the heap")
77 74
 	}
78 75
 
79
-	ctrlr.sandboxRm("sandbox1", ep2)
76
+	ctrlr.sandboxRm(sKey, ep2)
80 77
 
81
-	if ctrlr.sandboxes["sandbox1"].endpoints[0] != ep1 {
78
+	if ctrlr.sandboxes[sKey].endpoints[0] != ep1 {
82 79
 		t.Fatal("Expected ep1 to be at the top of the heap after removing ep2. But did not find ep1 at the top of the heap")
83 80
 	}
84 81
 
85 82
 	// Re-add ep3 back
86
-	if _, err := ctrlr.sandboxAdd("sandbox1", true, ep3); err != nil {
83
+	if _, err := ctrlr.sandboxAdd(sKey, true, ep3); err != nil {
87 84
 		t.Fatal(err)
88 85
 	}
89 86
 
90
-	if ctrlr.sandboxes["sandbox1"].endpoints[0] != ep3 {
87
+	if ctrlr.sandboxes[sKey].endpoints[0] != ep3 {
91 88
 		t.Fatal("Expected ep3 to be at the top of the heap after adding ep3 back. But did not find ep3 at the top of the heap")
92 89
 	}
93 90
 
94
-	ctrlr.sandboxRm("sandbox1", ep3)
95
-	ctrlr.sandboxRm("sandbox1", ep1)
91
+	ctrlr.sandboxRm(sKey, ep3)
92
+	ctrlr.sandboxRm(sKey, ep1)
93
+
94
+	if err := ctrlr.LeaveAll("sandbox1"); err != nil {
95
+		t.Fatal(err)
96
+	}
97
+
96 98
 	if len(ctrlr.sandboxes) != 0 {
97 99
 		t.Fatalf("controller sandboxes is not empty. len = %d", len(ctrlr.sandboxes))
98 100
 	}
... ...
@@ -108,30 +107,32 @@ func TestSandboxAddSamePrio(t *testing.T) {
108 108
 	ep1.network = &network{name: "aaa"}
109 109
 	ep2.network = &network{name: "bbb"}
110 110
 
111
-	if _, err := ctrlr.sandboxAdd("sandbox1", true, ep1); err != nil {
112
-		t.Fatal(err)
113
-	}
111
+	sKey := sandbox.GenerateKey("sandbox1")
114 112
 
115
-	if _, err := ctrlr.sandboxAdd("sandbox1", true, ep2); err != nil {
113
+	if _, err := ctrlr.sandboxAdd(sKey, true, ep1); err != nil {
116 114
 		t.Fatal(err)
117 115
 	}
118 116
 
119
-	if ctrlr.sandboxes["sandbox1"].refCnt != 2 {
120
-		t.Fatalf("Unexpected sandbox ref count. Expected 2, got %d",
121
-			ctrlr.sandboxes["sandbox1"].refCnt)
117
+	if _, err := ctrlr.sandboxAdd(sKey, true, ep2); err != nil {
118
+		t.Fatal(err)
122 119
 	}
123 120
 
124
-	if ctrlr.sandboxes["sandbox1"].endpoints[0] != ep1 {
121
+	if ctrlr.sandboxes[sKey].endpoints[0] != ep1 {
125 122
 		t.Fatal("Expected ep1 to be at the top of the heap. But did not find ep1 at the top of the heap")
126 123
 	}
127 124
 
128
-	ctrlr.sandboxRm("sandbox1", ep1)
125
+	ctrlr.sandboxRm(sKey, ep1)
129 126
 
130
-	if ctrlr.sandboxes["sandbox1"].endpoints[0] != ep2 {
127
+	if ctrlr.sandboxes[sKey].endpoints[0] != ep2 {
131 128
 		t.Fatal("Expected ep2 to be at the top of the heap after removing ep3. But did not find ep2 at the top of the heap")
132 129
 	}
133 130
 
134
-	ctrlr.sandboxRm("sandbox1", ep2)
131
+	ctrlr.sandboxRm(sKey, ep2)
132
+
133
+	if err := ctrlr.LeaveAll("sandbox1"); err != nil {
134
+		t.Fatal(err)
135
+	}
136
+
135 137
 	if len(ctrlr.sandboxes) != 0 {
136 138
 		t.Fatalf("controller sandboxes is not empty. len = %d", len(ctrlr.sandboxes))
137 139
 	}