github.com/bigcommerce/nomad@v0.9.3-bc/client/allocwatcher/group_alloc_watcher_test.go (about) 1 package allocwatcher 2 3 import ( 4 "context" 5 "fmt" 6 "testing" 7 8 "github.com/hashicorp/nomad/nomad/structs" 9 "github.com/hashicorp/nomad/testutil" 10 "github.com/stretchr/testify/require" 11 ) 12 13 // TestPrevAlloc_GroupPrevAllocWatcher_Block asserts that when there are 14 // prevAllocs is set a groupPrevAllocWatcher will block on them 15 func TestPrevAlloc_GroupPrevAllocWatcher_Block(t *testing.T) { 16 t.Parallel() 17 conf, cleanup := newConfig(t) 18 19 defer cleanup() 20 21 conf.Alloc.Job.TaskGroups[0].Tasks[0].Config = map[string]interface{}{ 22 "run_for": "500ms", 23 } 24 25 waiter, _ := NewAllocWatcher(conf) 26 27 groupWaiter := &groupPrevAllocWatcher{prevAllocs: []PrevAllocWatcher{waiter}} 28 29 // Wait in a goroutine with a context to make sure it exits at the right time 30 ctx, cancel := context.WithCancel(context.Background()) 31 defer cancel() 32 go func() { 33 defer cancel() 34 groupWaiter.Wait(ctx) 35 }() 36 37 // Assert watcher is waiting 38 testutil.WaitForResult(func() (bool, error) { 39 return groupWaiter.IsWaiting(), fmt.Errorf("expected watcher to be waiting") 40 }, func(err error) { 41 t.Fatalf("error: %v", err) 42 }) 43 44 // Broadcast a non-terminal alloc update to assert only terminal 45 // updates break out of waiting. 46 update := conf.PreviousRunner.Alloc().Copy() 47 update.DesiredStatus = structs.AllocDesiredStatusStop 48 update.ModifyIndex++ 49 update.AllocModifyIndex++ 50 51 broadcaster := conf.PreviousRunner.(*fakeAllocRunner).Broadcaster 52 err := broadcaster.Send(update) 53 require.NoError(t, err) 54 55 // Assert watcher is still waiting because alloc isn't terminal 56 testutil.WaitForResult(func() (bool, error) { 57 return groupWaiter.IsWaiting(), fmt.Errorf("expected watcher to be waiting") 58 }, func(err error) { 59 t.Fatalf("error: %v", err) 60 }) 61 62 // Stop the previous alloc and assert watcher stops blocking 63 update = update.Copy() 64 update.DesiredStatus = structs.AllocDesiredStatusStop 65 update.ClientStatus = structs.AllocClientStatusComplete 66 update.ModifyIndex++ 67 update.AllocModifyIndex++ 68 69 err = broadcaster.Send(update) 70 require.NoError(t, err) 71 72 testutil.WaitForResult(func() (bool, error) { 73 return !groupWaiter.IsWaiting(), fmt.Errorf("did not expect watcher to be waiting") 74 }, func(err error) { 75 t.Fatalf("error: %v", err) 76 }) 77 } 78 79 // TestPrevAlloc_GroupPrevAllocWatcher_BlockMulti asserts that when there are 80 // multiple prevAllocs is set a groupPrevAllocWatcher will block until all 81 // are complete 82 func TestPrevAlloc_GroupPrevAllocWatcher_BlockMulti(t *testing.T) { 83 t.Parallel() 84 conf1, cleanup1 := newConfig(t) 85 defer cleanup1() 86 conf1.Alloc.Job.TaskGroups[0].Tasks[0].Config = map[string]interface{}{ 87 "run_for": "500ms", 88 } 89 90 conf2, cleanup2 := newConfig(t) 91 defer cleanup2() 92 conf2.Alloc.Job.TaskGroups[0].Tasks[0].Config = map[string]interface{}{ 93 "run_for": "500ms", 94 } 95 96 waiter1, _ := NewAllocWatcher(conf1) 97 waiter2, _ := NewAllocWatcher(conf2) 98 99 groupWaiter := &groupPrevAllocWatcher{ 100 prevAllocs: []PrevAllocWatcher{ 101 waiter1, 102 waiter2, 103 }, 104 } 105 106 terminalBroadcastFn := func(cfg Config) { 107 update := cfg.PreviousRunner.Alloc().Copy() 108 update.DesiredStatus = structs.AllocDesiredStatusStop 109 update.ClientStatus = structs.AllocClientStatusComplete 110 update.ModifyIndex++ 111 update.AllocModifyIndex++ 112 113 broadcaster := cfg.PreviousRunner.(*fakeAllocRunner).Broadcaster 114 err := broadcaster.Send(update) 115 require.NoError(t, err) 116 } 117 118 // Wait in a goroutine with a context to make sure it exits at the right time 119 ctx, cancel := context.WithCancel(context.Background()) 120 defer cancel() 121 go func() { 122 defer cancel() 123 groupWaiter.Wait(ctx) 124 }() 125 126 // Assert watcher is waiting 127 testutil.WaitForResult(func() (bool, error) { 128 return groupWaiter.IsWaiting(), fmt.Errorf("expected watcher to be waiting") 129 }, func(err error) { 130 t.Fatalf("error: %v", err) 131 }) 132 133 // Broadcast a terminal alloc update to the first watcher 134 terminalBroadcastFn(conf1) 135 136 // Assert watcher is still waiting because alloc isn't terminal 137 testutil.WaitForResult(func() (bool, error) { 138 return groupWaiter.IsWaiting(), fmt.Errorf("expected watcher to be waiting") 139 }, func(err error) { 140 t.Fatalf("error: %v", err) 141 }) 142 143 // Broadcast a terminal alloc update to the second watcher 144 terminalBroadcastFn(conf2) 145 146 testutil.WaitForResult(func() (bool, error) { 147 return !groupWaiter.IsWaiting(), fmt.Errorf("did not expect watcher to be waiting") 148 }, func(err error) { 149 t.Fatalf("error: %v", err) 150 }) 151 }