Skip to content
This repository was archived by the owner on May 18, 2026. It is now read-only.

Commit 9240aa6

Browse files
committed
Add overlay index selection tests
Tests the production scenario: multiple overlay containers on the same node get different designated IPs, including simulation of the run() loop's index counting with mixed overlay and non-overlay containers.
1 parent e6c5543 commit 9240aa6

1 file changed

Lines changed: 154 additions & 0 deletions

File tree

main_test.go

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,3 +526,157 @@ func TestComputeIPCandidates(t *testing.T) {
526526
}
527527
})
528528
}
529+
530+
// candidateForOverlayIndex mirrors the indexing logic in highIPCandidates:
531+
// compute the full band, then return only the IP for the given overlay index.
532+
func candidateForOverlayIndex(ipNet *net.IPNet, peerIPs []string, nodeAddr string, overlayContainers int, overlayIndex int) net.IP {
533+
candidates := computeIPCandidates(ipNet, peerIPs, nodeAddr, overlayContainers)
534+
if overlayIndex >= len(candidates) {
535+
return nil
536+
}
537+
return candidates[overlayIndex]
538+
}
539+
540+
func TestOverlayIndexSelection(t *testing.T) {
541+
t.Run("two containers on same node get different IPs", func(t *testing.T) {
542+
ipNet := mustParseCIDR(t, "10.0.64.0/21")
543+
peers := []string{"10.0.0.1", "10.0.0.2", "10.0.0.3"}
544+
545+
ip0 := candidateForOverlayIndex(ipNet, peers, "10.0.0.1", 2, 0)
546+
ip1 := candidateForOverlayIndex(ipNet, peers, "10.0.0.1", 2, 1)
547+
548+
if ip0 == nil || ip1 == nil {
549+
t.Fatalf("got nil: ip0=%v ip1=%v", ip0, ip1)
550+
}
551+
if ip0.Equal(ip1) {
552+
t.Errorf("both containers got same IP: %s", ip0)
553+
}
554+
if ip0.String() != "10.0.71.254" {
555+
t.Errorf("container 0: got %s, want 10.0.71.254", ip0)
556+
}
557+
if ip1.String() != "10.0.71.253" {
558+
t.Errorf("container 1: got %s, want 10.0.71.253", ip1)
559+
}
560+
})
561+
562+
t.Run("three containers on same node all get unique IPs", func(t *testing.T) {
563+
ipNet := mustParseCIDR(t, "10.0.1.0/24")
564+
peers := []string{"10.0.0.1", "10.0.0.2"}
565+
566+
seen := map[string]int{}
567+
for i := range 3 {
568+
ip := candidateForOverlayIndex(ipNet, peers, "10.0.0.1", 3, i)
569+
if ip == nil {
570+
t.Fatalf("container %d got nil", i)
571+
}
572+
if prev, exists := seen[ip.String()]; exists {
573+
t.Errorf("container %d and %d both got %s", prev, i, ip)
574+
}
575+
seen[ip.String()] = i
576+
}
577+
})
578+
579+
t.Run("overlay index out of bounds returns nil", func(t *testing.T) {
580+
ipNet := mustParseCIDR(t, "10.0.1.0/24")
581+
peers := []string{"10.0.0.1"}
582+
583+
ip := candidateForOverlayIndex(ipNet, peers, "10.0.0.1", 2, 2)
584+
if ip != nil {
585+
t.Errorf("index 2 with 2 containers: got %s, want nil", ip)
586+
}
587+
588+
ip = candidateForOverlayIndex(ipNet, peers, "10.0.0.1", 2, 99)
589+
if ip != nil {
590+
t.Errorf("index 99 with 2 containers: got %s, want nil", ip)
591+
}
592+
})
593+
594+
t.Run("containers on different nodes never collide", func(t *testing.T) {
595+
ipNet := mustParseCIDR(t, "10.0.64.0/21")
596+
peers := []string{"10.0.0.1", "10.0.0.2", "10.0.0.3", "10.0.0.4"}
597+
overlayContainers := 3
598+
599+
seen := map[string]string{}
600+
for _, peer := range peers {
601+
for i := range overlayContainers {
602+
ip := candidateForOverlayIndex(ipNet, peers, peer, overlayContainers, i)
603+
if ip == nil {
604+
continue
605+
}
606+
key := ip.String()
607+
label := fmt.Sprintf("%s[%d]", peer, i)
608+
if prev, exists := seen[key]; exists {
609+
t.Errorf("IP %s assigned to %s and %s", key, prev, label)
610+
}
611+
seen[key] = label
612+
}
613+
}
614+
})
615+
616+
t.Run("simulates run() overlay index counting", func(t *testing.T) {
617+
// Config: [non-overlay, heartbeat (overlay), non-overlay, metricbeat (overlay)]
618+
type fakeContainer struct {
619+
name string
620+
overlay bool
621+
}
622+
containers := []fakeContainer{
623+
{"logger", false},
624+
{"heartbeat", true},
625+
{"proxy", false},
626+
{"metricbeat", true},
627+
}
628+
629+
ipNet := mustParseCIDR(t, "10.0.1.0/24")
630+
peers := []string{"10.0.0.1", "10.0.0.2"}
631+
632+
overlayIndex := 0
633+
results := map[string]net.IP{}
634+
for _, c := range containers {
635+
currentIndex := overlayIndex
636+
if c.overlay {
637+
overlayIndex++
638+
}
639+
if c.overlay {
640+
ip := candidateForOverlayIndex(ipNet, peers, "10.0.0.1", 2, currentIndex)
641+
results[c.name] = ip
642+
}
643+
}
644+
645+
if results["heartbeat"].String() != "10.0.1.254" {
646+
t.Errorf("heartbeat: got %s, want 10.0.1.254", results["heartbeat"])
647+
}
648+
if results["metricbeat"].String() != "10.0.1.253" {
649+
t.Errorf("metricbeat: got %s, want 10.0.1.253", results["metricbeat"])
650+
}
651+
if results["heartbeat"].Equal(results["metricbeat"]) {
652+
t.Errorf("heartbeat and metricbeat got same IP: %s", results["heartbeat"])
653+
}
654+
})
655+
656+
t.Run("/30 with 2 containers — second has no room on second peer", func(t *testing.T) {
657+
// /30: 2 usable IPs. Peer 0 band = [.2, .1]. Peer 1 band = [.0] but .0 = network → empty.
658+
// So peer 1 container 0 gets nil, container 1 also nil.
659+
ipNet := mustParseCIDR(t, "10.0.0.0/30")
660+
peers := []string{"10.0.0.1", "10.0.0.2"}
661+
662+
// Peer 0: band has 1 IP (.2), container 0 gets it, container 1 gets nil
663+
ip := candidateForOverlayIndex(ipNet, peers, "10.0.0.1", 2, 0)
664+
if ip == nil || ip.String() != "10.0.0.2" {
665+
t.Errorf("peer0 container0: got %v, want 10.0.0.2", ip)
666+
}
667+
ip = candidateForOverlayIndex(ipNet, peers, "10.0.0.1", 2, 1)
668+
if ip == nil || ip.String() != "10.0.0.1" {
669+
t.Errorf("peer0 container1: got %v, want 10.0.0.1", ip)
670+
}
671+
672+
// Peer 1: startOffset = 1+1*2 = 3, broadcast-3 = .0 = network → empty band
673+
ip = candidateForOverlayIndex(ipNet, peers, "10.0.0.2", 2, 0)
674+
if ip != nil {
675+
t.Errorf("peer1 container0: got %s, want nil", ip)
676+
}
677+
ip = candidateForOverlayIndex(ipNet, peers, "10.0.0.2", 2, 1)
678+
if ip != nil {
679+
t.Errorf("peer1 container1: got %s, want nil", ip)
680+
}
681+
})
682+
}

0 commit comments

Comments
 (0)