@@ -3,6 +3,7 @@ package allocator
33import (
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
553672func isValidNetwork (t assert.TestingT , n * api.Network ) bool {
0 commit comments