1package structs
2
3import (
4	"fmt"
5	"net"
6	"reflect"
7	"testing"
8
9	"github.com/stretchr/testify/require"
10)
11
12func TestNetworkIndex_Overcommitted(t *testing.T) {
13	t.Skip()
14	idx := NewNetworkIndex()
15
16	// Consume some network
17	reserved := &NetworkResource{
18		Device:        "eth0",
19		IP:            "192.168.0.100",
20		MBits:         505,
21		ReservedPorts: []Port{{"one", 8000, 0, ""}, {"two", 9000, 0, ""}},
22	}
23	collide := idx.AddReserved(reserved)
24	if collide {
25		t.Fatalf("bad")
26	}
27	if !idx.Overcommitted() {
28		t.Fatalf("have no resources")
29	}
30
31	// Add resources
32	n := &Node{
33		NodeResources: &NodeResources{
34			Networks: []*NetworkResource{
35				{
36					Device: "eth0",
37					CIDR:   "192.168.0.100/32",
38					MBits:  1000,
39				},
40			},
41		},
42	}
43	idx.SetNode(n)
44	if idx.Overcommitted() {
45		t.Fatalf("have resources")
46	}
47
48	// Double up our usage
49	idx.AddReserved(reserved)
50	if !idx.Overcommitted() {
51		t.Fatalf("should be overcommitted")
52	}
53}
54
55func TestNetworkIndex_SetNode(t *testing.T) {
56	idx := NewNetworkIndex()
57	n := &Node{
58		NodeResources: &NodeResources{
59			Networks: []*NetworkResource{
60				{
61					Device: "eth0",
62					CIDR:   "192.168.0.100/32",
63					IP:     "192.168.0.100",
64					MBits:  1000,
65				},
66			},
67		},
68		ReservedResources: &NodeReservedResources{
69			Networks: NodeReservedNetworkResources{
70				ReservedHostPorts: "22",
71			},
72		},
73	}
74	collide := idx.SetNode(n)
75	if collide {
76		t.Fatalf("bad")
77	}
78
79	if len(idx.AvailNetworks) != 1 {
80		t.Fatalf("Bad")
81	}
82	if idx.AvailBandwidth["eth0"] != 1000 {
83		t.Fatalf("Bad")
84	}
85	if !idx.UsedPorts["192.168.0.100"].Check(22) {
86		t.Fatalf("Bad")
87	}
88}
89
90func TestNetworkIndex_AddAllocs(t *testing.T) {
91	idx := NewNetworkIndex()
92	allocs := []*Allocation{
93		{
94			AllocatedResources: &AllocatedResources{
95				Tasks: map[string]*AllocatedTaskResources{
96					"web": {
97						Networks: []*NetworkResource{
98							{
99								Device:        "eth0",
100								IP:            "192.168.0.100",
101								MBits:         20,
102								ReservedPorts: []Port{{"one", 8000, 0, ""}, {"two", 9000, 0, ""}},
103							},
104						},
105					},
106				},
107			},
108		},
109		{
110			AllocatedResources: &AllocatedResources{
111				Tasks: map[string]*AllocatedTaskResources{
112					"api": {
113						Networks: []*NetworkResource{
114							{
115								Device:        "eth0",
116								IP:            "192.168.0.100",
117								MBits:         50,
118								ReservedPorts: []Port{{"one", 10000, 0, ""}},
119							},
120						},
121					},
122				},
123			},
124		},
125	}
126	collide := idx.AddAllocs(allocs)
127	if collide {
128		t.Fatalf("bad")
129	}
130
131	if idx.UsedBandwidth["eth0"] != 70 {
132		t.Fatalf("Bad")
133	}
134	if !idx.UsedPorts["192.168.0.100"].Check(8000) {
135		t.Fatalf("Bad")
136	}
137	if !idx.UsedPorts["192.168.0.100"].Check(9000) {
138		t.Fatalf("Bad")
139	}
140	if !idx.UsedPorts["192.168.0.100"].Check(10000) {
141		t.Fatalf("Bad")
142	}
143}
144
145func TestNetworkIndex_AddReserved(t *testing.T) {
146	idx := NewNetworkIndex()
147
148	reserved := &NetworkResource{
149		Device:        "eth0",
150		IP:            "192.168.0.100",
151		MBits:         20,
152		ReservedPorts: []Port{{"one", 8000, 0, ""}, {"two", 9000, 0, ""}},
153	}
154	collide := idx.AddReserved(reserved)
155	if collide {
156		t.Fatalf("bad")
157	}
158
159	if idx.UsedBandwidth["eth0"] != 20 {
160		t.Fatalf("Bad")
161	}
162	if !idx.UsedPorts["192.168.0.100"].Check(8000) {
163		t.Fatalf("Bad")
164	}
165	if !idx.UsedPorts["192.168.0.100"].Check(9000) {
166		t.Fatalf("Bad")
167	}
168
169	// Try to reserve the same network
170	collide = idx.AddReserved(reserved)
171	if !collide {
172		t.Fatalf("bad")
173	}
174}
175
176// XXX Reserving ports doesn't work when yielding from a CIDR block. This is
177// okay for now since we do not actually fingerprint CIDR blocks.
178func TestNetworkIndex_yieldIP(t *testing.T) {
179	idx := NewNetworkIndex()
180	n := &Node{
181		NodeResources: &NodeResources{
182			Networks: []*NetworkResource{
183				{
184					Device: "eth0",
185					CIDR:   "192.168.0.100/30",
186					MBits:  1000,
187				},
188			},
189		},
190	}
191	idx.SetNode(n)
192
193	var out []string
194	idx.yieldIP(func(n *NetworkResource, ip net.IP) (stop bool) {
195		out = append(out, ip.String())
196		return
197	})
198
199	expect := []string{"192.168.0.100", "192.168.0.101",
200		"192.168.0.102", "192.168.0.103"}
201	if !reflect.DeepEqual(out, expect) {
202		t.Fatalf("bad: %v", out)
203	}
204}
205
206func TestNetworkIndex_AssignNetwork(t *testing.T) {
207	idx := NewNetworkIndex()
208	n := &Node{
209		NodeResources: &NodeResources{
210			Networks: []*NetworkResource{
211				{
212					Device: "eth0",
213					CIDR:   "192.168.0.100/30",
214					MBits:  1000,
215				},
216			},
217		},
218	}
219	idx.SetNode(n)
220
221	allocs := []*Allocation{
222		{
223			TaskResources: map[string]*Resources{
224				"web": {
225					Networks: []*NetworkResource{
226						{
227							Device:        "eth0",
228							IP:            "192.168.0.100",
229							MBits:         20,
230							ReservedPorts: []Port{{"one", 8000, 0, ""}, {"two", 9000, 0, ""}},
231						},
232					},
233				},
234			},
235		},
236		{
237			TaskResources: map[string]*Resources{
238				"api": {
239					Networks: []*NetworkResource{
240						{
241							Device:        "eth0",
242							IP:            "192.168.0.100",
243							MBits:         50,
244							ReservedPorts: []Port{{"main", 10000, 0, ""}},
245						},
246					},
247				},
248			},
249		},
250	}
251	idx.AddAllocs(allocs)
252
253	// Ask for a reserved port
254	ask := &NetworkResource{
255		ReservedPorts: []Port{{"main", 8000, 0, ""}},
256	}
257	offer, err := idx.AssignNetwork(ask)
258	require.NoError(t, err)
259	require.NotNil(t, offer)
260	require.Equal(t, "192.168.0.101", offer.IP)
261	rp := Port{"main", 8000, 0, ""}
262	require.Len(t, offer.ReservedPorts, 1)
263	require.Exactly(t, rp, offer.ReservedPorts[0])
264
265	// Ask for dynamic ports
266	ask = &NetworkResource{
267		DynamicPorts: []Port{{"http", 0, 80, ""}, {"https", 0, 443, ""}, {"admin", 0, -1, ""}},
268	}
269	offer, err = idx.AssignNetwork(ask)
270	require.NoError(t, err)
271	require.NotNil(t, offer)
272	require.Equal(t, "192.168.0.100", offer.IP)
273	require.Len(t, offer.DynamicPorts, 3)
274	var adminPort Port
275	for _, port := range offer.DynamicPorts {
276		require.NotZero(t, port.Value)
277		if port.Label == "admin" {
278			adminPort = port
279		}
280	}
281	require.Equal(t, adminPort.Value, adminPort.To)
282
283	// Ask for reserved + dynamic ports
284	ask = &NetworkResource{
285		ReservedPorts: []Port{{"main", 2345, 0, ""}},
286		DynamicPorts:  []Port{{"http", 0, 80, ""}, {"https", 0, 443, ""}, {"admin", 0, 8080, ""}},
287	}
288	offer, err = idx.AssignNetwork(ask)
289	require.NoError(t, err)
290	require.NotNil(t, offer)
291	require.Equal(t, "192.168.0.100", offer.IP)
292
293	rp = Port{"main", 2345, 0, ""}
294	require.Len(t, offer.ReservedPorts, 1)
295	require.Exactly(t, rp, offer.ReservedPorts[0])
296
297	// Ask for too much bandwidth
298	ask = &NetworkResource{
299		MBits: 1000,
300	}
301	offer, err = idx.AssignNetwork(ask)
302	require.Error(t, err)
303	require.Equal(t, "bandwidth exceeded", err.Error())
304	require.Nil(t, offer)
305}
306
307// This test ensures that even with a small domain of available ports we are
308// able to make a dynamic port allocation.
309func TestNetworkIndex_AssignNetwork_Dynamic_Contention(t *testing.T) {
310
311	// Create a node that only has one free port
312	idx := NewNetworkIndex()
313	n := &Node{
314		NodeResources: &NodeResources{
315			Networks: []*NetworkResource{
316				{
317					Device: "eth0",
318					CIDR:   "192.168.0.100/32",
319					IP:     "192.168.0.100",
320					MBits:  1000,
321				},
322			},
323		},
324		ReservedResources: &NodeReservedResources{
325			Networks: NodeReservedNetworkResources{
326				ReservedHostPorts: fmt.Sprintf("%d-%d", MinDynamicPort, MaxDynamicPort-1),
327			},
328		},
329	}
330	idx.SetNode(n)
331
332	// Ask for dynamic ports
333	ask := &NetworkResource{
334		DynamicPorts: []Port{{"http", 0, 80, ""}},
335	}
336	offer, err := idx.AssignNetwork(ask)
337	if err != nil {
338		t.Fatalf("err: %v", err)
339	}
340	if offer == nil {
341		t.Fatalf("bad")
342	}
343	if offer.IP != "192.168.0.100" {
344		t.Fatalf("bad: %#v", offer)
345	}
346	if len(offer.DynamicPorts) != 1 {
347		t.Fatalf("There should be one dynamic ports")
348	}
349	if p := offer.DynamicPorts[0].Value; p != MaxDynamicPort {
350		t.Fatalf("Dynamic Port: should have been assigned %d; got %d", p, MaxDynamicPort)
351	}
352}
353
354// COMPAT(0.11): Remove in 0.11
355func TestNetworkIndex_SetNode_Old(t *testing.T) {
356	idx := NewNetworkIndex()
357	n := &Node{
358		Resources: &Resources{
359			Networks: []*NetworkResource{
360				{
361					Device: "eth0",
362					CIDR:   "192.168.0.100/32",
363					MBits:  1000,
364				},
365			},
366		},
367		Reserved: &Resources{
368			Networks: []*NetworkResource{
369				{
370					Device:        "eth0",
371					IP:            "192.168.0.100",
372					ReservedPorts: []Port{{"ssh", 22, 0, ""}},
373					MBits:         1,
374				},
375			},
376		},
377	}
378	collide := idx.SetNode(n)
379	if collide {
380		t.Fatalf("bad")
381	}
382
383	if len(idx.AvailNetworks) != 1 {
384		t.Fatalf("Bad")
385	}
386	if idx.AvailBandwidth["eth0"] != 1000 {
387		t.Fatalf("Bad")
388	}
389	if idx.UsedBandwidth["eth0"] != 1 {
390		t.Fatalf("Bad")
391	}
392	if !idx.UsedPorts["192.168.0.100"].Check(22) {
393		t.Fatalf("Bad")
394	}
395}
396
397// COMPAT(0.11): Remove in 0.11
398func TestNetworkIndex_AddAllocs_Old(t *testing.T) {
399	idx := NewNetworkIndex()
400	allocs := []*Allocation{
401		{
402			TaskResources: map[string]*Resources{
403				"web": {
404					Networks: []*NetworkResource{
405						{
406							Device:        "eth0",
407							IP:            "192.168.0.100",
408							MBits:         20,
409							ReservedPorts: []Port{{"one", 8000, 0, ""}, {"two", 9000, 0, ""}},
410						},
411					},
412				},
413			},
414		},
415		{
416			TaskResources: map[string]*Resources{
417				"api": {
418					Networks: []*NetworkResource{
419						{
420							Device:        "eth0",
421							IP:            "192.168.0.100",
422							MBits:         50,
423							ReservedPorts: []Port{{"one", 10000, 0, ""}},
424						},
425					},
426				},
427			},
428		},
429	}
430	collide := idx.AddAllocs(allocs)
431	if collide {
432		t.Fatalf("bad")
433	}
434
435	if idx.UsedBandwidth["eth0"] != 70 {
436		t.Fatalf("Bad")
437	}
438	if !idx.UsedPorts["192.168.0.100"].Check(8000) {
439		t.Fatalf("Bad")
440	}
441	if !idx.UsedPorts["192.168.0.100"].Check(9000) {
442		t.Fatalf("Bad")
443	}
444	if !idx.UsedPorts["192.168.0.100"].Check(10000) {
445		t.Fatalf("Bad")
446	}
447}
448
449// COMPAT(0.11): Remove in 0.11
450func TestNetworkIndex_yieldIP_Old(t *testing.T) {
451	idx := NewNetworkIndex()
452	n := &Node{
453		Resources: &Resources{
454			Networks: []*NetworkResource{
455				{
456					Device: "eth0",
457					CIDR:   "192.168.0.100/30",
458					MBits:  1000,
459				},
460			},
461		},
462		Reserved: &Resources{
463			Networks: []*NetworkResource{
464				{
465					Device:        "eth0",
466					IP:            "192.168.0.100",
467					ReservedPorts: []Port{{"ssh", 22, 0, ""}},
468					MBits:         1,
469				},
470			},
471		},
472	}
473	idx.SetNode(n)
474
475	var out []string
476	idx.yieldIP(func(n *NetworkResource, ip net.IP) (stop bool) {
477		out = append(out, ip.String())
478		return
479	})
480
481	expect := []string{"192.168.0.100", "192.168.0.101",
482		"192.168.0.102", "192.168.0.103"}
483	if !reflect.DeepEqual(out, expect) {
484		t.Fatalf("bad: %v", out)
485	}
486}
487
488// COMPAT(0.11): Remove in 0.11
489func TestNetworkIndex_AssignNetwork_Old(t *testing.T) {
490	idx := NewNetworkIndex()
491	n := &Node{
492		Resources: &Resources{
493			Networks: []*NetworkResource{
494				{
495					Device: "eth0",
496					CIDR:   "192.168.0.100/30",
497					MBits:  1000,
498				},
499			},
500		},
501		Reserved: &Resources{
502			Networks: []*NetworkResource{
503				{
504					Device:        "eth0",
505					IP:            "192.168.0.100",
506					ReservedPorts: []Port{{"ssh", 22, 0, ""}},
507					MBits:         1,
508				},
509			},
510		},
511	}
512	idx.SetNode(n)
513
514	allocs := []*Allocation{
515		{
516			TaskResources: map[string]*Resources{
517				"web": {
518					Networks: []*NetworkResource{
519						{
520							Device:        "eth0",
521							IP:            "192.168.0.100",
522							MBits:         20,
523							ReservedPorts: []Port{{"one", 8000, 0, ""}, {"two", 9000, 0, ""}},
524						},
525					},
526				},
527			},
528		},
529		{
530			TaskResources: map[string]*Resources{
531				"api": {
532					Networks: []*NetworkResource{
533						{
534							Device:        "eth0",
535							IP:            "192.168.0.100",
536							MBits:         50,
537							ReservedPorts: []Port{{"main", 10000, 0, ""}},
538						},
539					},
540				},
541			},
542		},
543	}
544	idx.AddAllocs(allocs)
545
546	// Ask for a reserved port
547	ask := &NetworkResource{
548		ReservedPorts: []Port{{"main", 8000, 0, ""}},
549	}
550	offer, err := idx.AssignNetwork(ask)
551	if err != nil {
552		t.Fatalf("err: %v", err)
553	}
554	if offer == nil {
555		t.Fatalf("bad")
556	}
557	if offer.IP != "192.168.0.101" {
558		t.Fatalf("bad: %#v", offer)
559	}
560	rp := Port{"main", 8000, 0, ""}
561	if len(offer.ReservedPorts) != 1 || offer.ReservedPorts[0] != rp {
562		t.Fatalf("bad: %#v", offer)
563	}
564
565	// Ask for dynamic ports
566	ask = &NetworkResource{
567		DynamicPorts: []Port{{"http", 0, 80, ""}, {"https", 0, 443, ""}, {"admin", 0, 8080, ""}},
568	}
569	offer, err = idx.AssignNetwork(ask)
570	if err != nil {
571		t.Fatalf("err: %v", err)
572	}
573	if offer == nil {
574		t.Fatalf("bad")
575	}
576	if offer.IP != "192.168.0.100" {
577		t.Fatalf("bad: %#v", offer)
578	}
579	if len(offer.DynamicPorts) != 3 {
580		t.Fatalf("There should be three dynamic ports")
581	}
582	for _, port := range offer.DynamicPorts {
583		if port.Value == 0 {
584			t.Fatalf("Dynamic Port: %v should have been assigned a host port", port.Label)
585		}
586	}
587
588	// Ask for reserved + dynamic ports
589	ask = &NetworkResource{
590		ReservedPorts: []Port{{"main", 2345, 0, ""}},
591		DynamicPorts:  []Port{{"http", 0, 80, ""}, {"https", 0, 443, ""}, {"admin", 0, 8080, ""}},
592	}
593	offer, err = idx.AssignNetwork(ask)
594	if err != nil {
595		t.Fatalf("err: %v", err)
596	}
597	if offer == nil {
598		t.Fatalf("bad")
599	}
600	if offer.IP != "192.168.0.100" {
601		t.Fatalf("bad: %#v", offer)
602	}
603
604	rp = Port{"main", 2345, 0, ""}
605	if len(offer.ReservedPorts) != 1 || offer.ReservedPorts[0] != rp {
606		t.Fatalf("bad: %#v", offer)
607	}
608
609	// Ask for too much bandwidth
610	ask = &NetworkResource{
611		MBits: 1000,
612	}
613	offer, err = idx.AssignNetwork(ask)
614	if err.Error() != "bandwidth exceeded" {
615		t.Fatalf("err: %v", err)
616	}
617	if offer != nil {
618		t.Fatalf("bad")
619	}
620}
621
622// COMPAT(0.11): Remove in 0.11
623// This test ensures that even with a small domain of available ports we are
624// able to make a dynamic port allocation.
625func TestNetworkIndex_AssignNetwork_Dynamic_Contention_Old(t *testing.T) {
626
627	// Create a node that only has one free port
628	idx := NewNetworkIndex()
629	n := &Node{
630		Resources: &Resources{
631			Networks: []*NetworkResource{
632				{
633					Device: "eth0",
634					CIDR:   "192.168.0.100/32",
635					MBits:  1000,
636				},
637			},
638		},
639		Reserved: &Resources{
640			Networks: []*NetworkResource{
641				{
642					Device: "eth0",
643					IP:     "192.168.0.100",
644					MBits:  1,
645				},
646			},
647		},
648	}
649	for i := MinDynamicPort; i < MaxDynamicPort; i++ {
650		n.Reserved.Networks[0].ReservedPorts = append(n.Reserved.Networks[0].ReservedPorts, Port{Value: i})
651	}
652
653	idx.SetNode(n)
654
655	// Ask for dynamic ports
656	ask := &NetworkResource{
657		DynamicPorts: []Port{{"http", 0, 80, ""}},
658	}
659	offer, err := idx.AssignNetwork(ask)
660	if err != nil {
661		t.Fatalf("err: %v", err)
662	}
663	if offer == nil {
664		t.Fatalf("bad")
665	}
666	if offer.IP != "192.168.0.100" {
667		t.Fatalf("bad: %#v", offer)
668	}
669	if len(offer.DynamicPorts) != 1 {
670		t.Fatalf("There should be three dynamic ports")
671	}
672	if p := offer.DynamicPorts[0].Value; p != MaxDynamicPort {
673		t.Fatalf("Dynamic Port: should have been assigned %d; got %d", p, MaxDynamicPort)
674	}
675}
676
677func TestIntContains(t *testing.T) {
678	l := []int{1, 2, 10, 20}
679	if isPortReserved(l, 50) {
680		t.Fatalf("bad")
681	}
682	if !isPortReserved(l, 20) {
683		t.Fatalf("bad")
684	}
685	if !isPortReserved(l, 1) {
686		t.Fatalf("bad")
687	}
688}
689