Skip to content

Commit a4bf013

Browse files
author
Aaron Lehmann
authored
Merge pull request moby#2237 from aaronlehmann/duplicate-ips
allocator: Avoid assigning duplicate IPs during initialization
2 parents ea13d2b + 61483fb commit a4bf013

2 files changed

Lines changed: 273 additions & 97 deletions

File tree

manager/allocator/allocator_test.go

Lines changed: 120 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package allocator
33
import (
44
"net"
55
"runtime/debug"
6+
"strconv"
67
"testing"
78
"time"
89

@@ -212,6 +213,7 @@ func TestAllocator(t *testing.T) {
212213
go func() {
213214
assert.NoError(t, a.Run(context.Background()))
214215
}()
216+
defer a.Stop()
215217

216218
// Now verify if we get network and tasks updated properly
217219
watchNetwork(t, netWatch, false, isValidNetwork)
@@ -546,8 +548,125 @@ func TestAllocator(t *testing.T) {
546548
}))
547549
watchService(t, serviceWatch, false, nil)
548550
watchTask(t, s, taskWatch, false, isValidTask)
551+
}
552+
553+
func TestNoDuplicateIPs(t *testing.T) {
554+
s := store.NewMemoryStore(nil)
555+
assert.NotNil(t, s)
556+
defer s.Close()
557+
558+
// Try adding some objects to store before allocator is started
559+
assert.NoError(t, s.Update(func(tx store.Tx) error {
560+
// populate ingress network
561+
in := &api.Network{
562+
ID: "ingress-nw-id",
563+
Spec: api.NetworkSpec{
564+
Annotations: api.Annotations{
565+
Name: "default-ingress",
566+
},
567+
Ingress: true,
568+
},
569+
}
570+
assert.NoError(t, store.CreateNetwork(tx, in))
571+
572+
n1 := &api.Network{
573+
ID: "testID1",
574+
Spec: api.NetworkSpec{
575+
Annotations: api.Annotations{
576+
Name: "test1",
577+
},
578+
},
579+
}
580+
assert.NoError(t, store.CreateNetwork(tx, n1))
581+
582+
s1 := &api.Service{
583+
ID: "testServiceID1",
584+
Spec: api.ServiceSpec{
585+
Annotations: api.Annotations{
586+
Name: "service1",
587+
},
588+
Task: api.TaskSpec{
589+
Networks: []*api.NetworkAttachmentConfig{
590+
{
591+
Target: "testID1",
592+
},
593+
},
594+
},
595+
Endpoint: &api.EndpointSpec{
596+
Mode: api.ResolutionModeVirtualIP,
597+
Ports: []*api.PortConfig{
598+
{
599+
Name: "portName",
600+
Protocol: api.ProtocolTCP,
601+
TargetPort: 8000,
602+
PublishedPort: 8001,
603+
},
604+
},
605+
},
606+
},
607+
}
608+
assert.NoError(t, store.CreateService(tx, s1))
549609

550-
a.Stop()
610+
return nil
611+
}))
612+
613+
taskWatch, cancel := state.Watch(s.WatchQueue(), api.EventUpdateTask{}, api.EventDeleteTask{})
614+
defer cancel()
615+
616+
assignedIPs := make(map[string]string)
617+
hasUniqueIP := func(fakeT assert.TestingT, s *store.MemoryStore, task *api.Task) bool {
618+
if len(task.Networks) == 0 {
619+
panic("missing networks")
620+
}
621+
if len(task.Networks[0].Addresses) == 0 {
622+
panic("missing network address")
623+
}
624+
625+
assignedIP := task.Networks[0].Addresses[0]
626+
oldTaskID, present := assignedIPs[assignedIP]
627+
if present && task.ID != oldTaskID {
628+
t.Fatalf("task %s assigned duplicate IP %s, previously assigned to task %s", task.ID, assignedIP, oldTaskID)
629+
}
630+
assignedIPs[assignedIP] = task.ID
631+
return true
632+
}
633+
634+
reps := 100
635+
for i := 0; i != reps; i++ {
636+
assert.NoError(t, s.Update(func(tx store.Tx) error {
637+
t2 := &api.Task{
638+
// The allocator iterates over the tasks in
639+
// lexical order, so number tasks in descending
640+
// order. Note that the problem this test was
641+
// meant to trigger also showed up with tasks
642+
// numbered in ascending order, but it took
643+
// until the 52nd task.
644+
ID: "testTaskID" + strconv.Itoa(reps-i),
645+
Status: api.TaskStatus{
646+
State: api.TaskStateNew,
647+
},
648+
ServiceID: "testServiceID1",
649+
DesiredState: api.TaskStateRunning,
650+
}
651+
assert.NoError(t, store.CreateTask(tx, t2))
652+
653+
return nil
654+
}))
655+
656+
a, err := New(s, nil)
657+
assert.NoError(t, err)
658+
assert.NotNil(t, a)
659+
660+
// Start allocator
661+
go func() {
662+
assert.NoError(t, a.Run(context.Background()))
663+
}()
664+
665+
// Confirm task gets a unique IP
666+
watchTask(t, s, taskWatch, false, hasUniqueIP)
667+
668+
a.Stop()
669+
}
551670
}
552671

553672
func isValidNetwork(t assert.TestingT, n *api.Network) bool {

0 commit comments

Comments
 (0)