@@ -110,7 +110,7 @@ func TestAllocRunner_TaskLeader_KillTG(t *testing.T) {
110
110
found := false
111
111
killingMsg := ""
112
112
for _ , e := range state1 .Events {
113
- if e .Type ! = structs .TaskLeaderDead {
113
+ if e .Type = = structs .TaskLeaderDead {
114
114
found = true
115
115
}
116
116
if e .Type == structs .TaskKilling {
@@ -142,6 +142,127 @@ func TestAllocRunner_TaskLeader_KillTG(t *testing.T) {
142
142
})
143
143
}
144
144
145
+ // TestAllocRunner_TaskMain_KillTG asserts that when main tasks die the
146
+ // entire task group is killed.
147
+ func TestAllocRunner_TaskMain_KillTG (t * testing.T ) {
148
+ t .Parallel ()
149
+
150
+ alloc := mock .BatchAlloc ()
151
+ tr := alloc .AllocatedResources .Tasks [alloc .Job .TaskGroups [0 ].Tasks [0 ].Name ]
152
+ alloc .Job .TaskGroups [0 ].RestartPolicy .Attempts = 0
153
+ alloc .Job .TaskGroups [0 ].Tasks [0 ].RestartPolicy .Attempts = 0
154
+
155
+ // Create three tasks in the task group
156
+ sidecar := alloc .Job .TaskGroups [0 ].Tasks [0 ].Copy ()
157
+ sidecar .Name = "sidecar"
158
+ sidecar .Driver = "mock_driver"
159
+ sidecar .KillTimeout = 10 * time .Millisecond
160
+ sidecar .Lifecycle = & structs.TaskLifecycleConfig {
161
+ Hook : structs .TaskLifecycleHookPrestart ,
162
+ Sidecar : true ,
163
+ }
164
+
165
+ sidecar .Config = map [string ]interface {}{
166
+ "run_for" : "100s" ,
167
+ }
168
+
169
+ main1 := alloc .Job .TaskGroups [0 ].Tasks [0 ].Copy ()
170
+ main1 .Name = "task2"
171
+ main1 .Driver = "mock_driver"
172
+ main1 .Config = map [string ]interface {}{
173
+ "run_for" : "1s" ,
174
+ }
175
+
176
+ main2 := alloc .Job .TaskGroups [0 ].Tasks [0 ].Copy ()
177
+ main2 .Name = "task2"
178
+ main2 .Driver = "mock_driver"
179
+ main2 .Config = map [string ]interface {}{
180
+ "run_for" : "2s" ,
181
+ }
182
+
183
+ alloc .Job .TaskGroups [0 ].Tasks = []* structs.Task {sidecar , main1 , main2 }
184
+ alloc .AllocatedResources .Tasks = map [string ]* structs.AllocatedTaskResources {
185
+ sidecar .Name : tr ,
186
+ main1 .Name : tr ,
187
+ main2 .Name : tr ,
188
+ }
189
+
190
+ conf , cleanup := testAllocRunnerConfig (t , alloc )
191
+ defer cleanup ()
192
+ ar , err := NewAllocRunner (conf )
193
+ require .NoError (t , err )
194
+ defer destroy (ar )
195
+ go ar .Run ()
196
+
197
+ hasTaskMainEvent := func (state * structs.TaskState ) bool {
198
+ for _ , e := range state .Events {
199
+ if e .Type == structs .TaskMainDead {
200
+ return true
201
+ }
202
+ }
203
+
204
+ return false
205
+ }
206
+
207
+ // Wait for all tasks to be killed
208
+ upd := conf .StateUpdater .(* MockStateUpdater )
209
+ testutil .WaitForResult (func () (bool , error ) {
210
+ last := upd .Last ()
211
+ if last == nil {
212
+ return false , fmt .Errorf ("No updates" )
213
+ }
214
+ if last .ClientStatus != structs .AllocClientStatusComplete {
215
+ return false , fmt .Errorf ("got status %v; want %v" , last .ClientStatus , structs .AllocClientStatusComplete )
216
+ }
217
+
218
+ var state * structs.TaskState
219
+
220
+ // Task1 should be killed because Task2 exited
221
+ state = last .TaskStates [sidecar .Name ]
222
+ if state .State != structs .TaskStateDead {
223
+ return false , fmt .Errorf ("got state %v; want %v" , state .State , structs .TaskStateDead )
224
+ }
225
+ if state .FinishedAt .IsZero () || state .StartedAt .IsZero () {
226
+ return false , fmt .Errorf ("expected to have a start and finish time" )
227
+ }
228
+ if len (state .Events ) < 2 {
229
+ // At least have a received and destroyed
230
+ return false , fmt .Errorf ("Unexpected number of events" )
231
+ }
232
+
233
+ if ! hasTaskMainEvent (state ) {
234
+ return false , fmt .Errorf ("Did not find event %v: %#+v" , structs .TaskMainDead , state .Events )
235
+ }
236
+
237
+ // main tasks should die naturely
238
+ state = last .TaskStates [main1 .Name ]
239
+ if state .State != structs .TaskStateDead {
240
+ return false , fmt .Errorf ("got state %v; want %v" , state .State , structs .TaskStateDead )
241
+ }
242
+ if state .FinishedAt .IsZero () || state .StartedAt .IsZero () {
243
+ return false , fmt .Errorf ("expected to have a start and finish time" )
244
+ }
245
+ if hasTaskMainEvent (state ) {
246
+ return false , fmt .Errorf ("unexpected event %#+v in %v" , structs .TaskMainDead , state .Events )
247
+ }
248
+
249
+ state = last .TaskStates [main2 .Name ]
250
+ if state .State != structs .TaskStateDead {
251
+ return false , fmt .Errorf ("got state %v; want %v" , state .State , structs .TaskStateDead )
252
+ }
253
+ if state .FinishedAt .IsZero () || state .StartedAt .IsZero () {
254
+ return false , fmt .Errorf ("expected to have a start and finish time" )
255
+ }
256
+ if hasTaskMainEvent (state ) {
257
+ return false , fmt .Errorf ("unexpected event %v in %#+v" , structs .TaskMainDead , state .Events )
258
+ }
259
+
260
+ return true , nil
261
+ }, func (err error ) {
262
+ t .Fatalf ("err: %v" , err )
263
+ })
264
+ }
265
+
145
266
func TestAllocRunner_TaskGroup_ShutdownDelay (t * testing.T ) {
146
267
t .Parallel ()
147
268
0 commit comments