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