github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/client/allocrunner/task_hook_coordinator_test.go (about) 1 package allocrunner 2 3 import ( 4 "fmt" 5 "testing" 6 "time" 7 8 "github.com/stretchr/testify/require" 9 10 "github.com/hashicorp/nomad/client/allocrunner/taskrunner" 11 "github.com/hashicorp/nomad/nomad/structs" 12 13 "github.com/hashicorp/nomad/helper/testlog" 14 "github.com/hashicorp/nomad/nomad/mock" 15 ) 16 17 func TestTaskHookCoordinator_OnlyMainApp(t *testing.T) { 18 alloc := mock.Alloc() 19 tasks := alloc.Job.TaskGroups[0].Tasks 20 task := tasks[0] 21 logger := testlog.HCLogger(t) 22 23 coord := newTaskHookCoordinator(logger, tasks) 24 25 ch := coord.startConditionForTask(task) 26 27 require.Truef(t, isChannelClosed(ch), "%s channel was open, should be closed", task.Name) 28 } 29 30 func TestTaskHookCoordinator_PrestartRunsBeforeMain(t *testing.T) { 31 logger := testlog.HCLogger(t) 32 33 alloc := mock.LifecycleAlloc() 34 tasks := alloc.Job.TaskGroups[0].Tasks 35 36 mainTask := tasks[0] 37 sideTask := tasks[1] 38 initTask := tasks[2] 39 40 coord := newTaskHookCoordinator(logger, tasks) 41 initCh := coord.startConditionForTask(initTask) 42 sideCh := coord.startConditionForTask(sideTask) 43 mainCh := coord.startConditionForTask(mainTask) 44 45 require.Truef(t, isChannelClosed(initCh), "%s channel was open, should be closed", initTask.Name) 46 require.Truef(t, isChannelClosed(sideCh), "%s channel was open, should be closed", sideTask.Name) 47 require.Falsef(t, isChannelClosed(mainCh), "%s channel was closed, should be open", mainTask.Name) 48 } 49 50 func TestTaskHookCoordinator_MainRunsAfterPrestart(t *testing.T) { 51 logger := testlog.HCLogger(t) 52 53 alloc := mock.LifecycleAlloc() 54 tasks := alloc.Job.TaskGroups[0].Tasks 55 56 mainTask := tasks[0] 57 sideTask := tasks[1] 58 initTask := tasks[2] 59 60 coord := newTaskHookCoordinator(logger, tasks) 61 initCh := coord.startConditionForTask(initTask) 62 sideCh := coord.startConditionForTask(sideTask) 63 mainCh := coord.startConditionForTask(mainTask) 64 65 require.Truef(t, isChannelClosed(initCh), "%s channel was open, should be closed", initTask.Name) 66 require.Truef(t, isChannelClosed(sideCh), "%s channel was open, should be closed", sideTask.Name) 67 require.Falsef(t, isChannelClosed(mainCh), "%s channel was closed, should be open", mainTask.Name) 68 69 states := map[string]*structs.TaskState{ 70 mainTask.Name: { 71 State: structs.TaskStatePending, 72 Failed: false, 73 }, 74 initTask.Name: { 75 State: structs.TaskStateDead, 76 Failed: false, 77 StartedAt: time.Now(), 78 FinishedAt: time.Now(), 79 }, 80 sideTask.Name: { 81 State: structs.TaskStateRunning, 82 Failed: false, 83 StartedAt: time.Now(), 84 }, 85 } 86 87 coord.taskStateUpdated(states) 88 89 require.Truef(t, isChannelClosed(initCh), "%s channel was open, should be closed", initTask.Name) 90 require.Truef(t, isChannelClosed(sideCh), "%s channel was open, should be closed", sideTask.Name) 91 require.Truef(t, isChannelClosed(mainCh), "%s channel was open, should be closed", mainTask.Name) 92 } 93 94 func TestTaskHookCoordinator_MainRunsAfterManyInitTasks(t *testing.T) { 95 logger := testlog.HCLogger(t) 96 97 alloc := mock.LifecycleAlloc() 98 alloc.Job = mock.VariableLifecycleJob(structs.Resources{CPU: 100, MemoryMB: 256}, 1, 2, 0) 99 tasks := alloc.Job.TaskGroups[0].Tasks 100 101 mainTask := tasks[0] 102 init1Task := tasks[1] 103 init2Task := tasks[2] 104 105 coord := newTaskHookCoordinator(logger, tasks) 106 mainCh := coord.startConditionForTask(mainTask) 107 init1Ch := coord.startConditionForTask(init1Task) 108 init2Ch := coord.startConditionForTask(init2Task) 109 110 require.Truef(t, isChannelClosed(init1Ch), "%s channel was open, should be closed", init1Task.Name) 111 require.Truef(t, isChannelClosed(init2Ch), "%s channel was open, should be closed", init2Task.Name) 112 require.Falsef(t, isChannelClosed(mainCh), "%s channel was closed, should be open", mainTask.Name) 113 114 states := map[string]*structs.TaskState{ 115 mainTask.Name: { 116 State: structs.TaskStatePending, 117 Failed: false, 118 }, 119 init1Task.Name: { 120 State: structs.TaskStateDead, 121 Failed: false, 122 StartedAt: time.Now(), 123 FinishedAt: time.Now(), 124 }, 125 init2Task.Name: { 126 State: structs.TaskStateDead, 127 Failed: false, 128 StartedAt: time.Now(), 129 }, 130 } 131 132 coord.taskStateUpdated(states) 133 134 require.Truef(t, isChannelClosed(init1Ch), "%s channel was open, should be closed", init1Task.Name) 135 require.Truef(t, isChannelClosed(init2Ch), "%s channel was open, should be closed", init2Task.Name) 136 require.Truef(t, isChannelClosed(mainCh), "%s channel was open, should be closed", mainTask.Name) 137 } 138 139 func TestTaskHookCoordinator_FailedInitTask(t *testing.T) { 140 logger := testlog.HCLogger(t) 141 142 alloc := mock.LifecycleAlloc() 143 alloc.Job = mock.VariableLifecycleJob(structs.Resources{CPU: 100, MemoryMB: 256}, 1, 2, 0) 144 tasks := alloc.Job.TaskGroups[0].Tasks 145 146 mainTask := tasks[0] 147 init1Task := tasks[1] 148 init2Task := tasks[2] 149 150 coord := newTaskHookCoordinator(logger, tasks) 151 mainCh := coord.startConditionForTask(mainTask) 152 init1Ch := coord.startConditionForTask(init1Task) 153 init2Ch := coord.startConditionForTask(init2Task) 154 155 require.Truef(t, isChannelClosed(init1Ch), "%s channel was open, should be closed", init1Task.Name) 156 require.Truef(t, isChannelClosed(init2Ch), "%s channel was open, should be closed", init2Task.Name) 157 require.Falsef(t, isChannelClosed(mainCh), "%s channel was closed, should be open", mainTask.Name) 158 159 states := map[string]*structs.TaskState{ 160 mainTask.Name: { 161 State: structs.TaskStatePending, 162 Failed: false, 163 }, 164 init1Task.Name: { 165 State: structs.TaskStateDead, 166 Failed: false, 167 StartedAt: time.Now(), 168 FinishedAt: time.Now(), 169 }, 170 init2Task.Name: { 171 State: structs.TaskStateDead, 172 Failed: true, 173 StartedAt: time.Now(), 174 }, 175 } 176 177 coord.taskStateUpdated(states) 178 179 require.Truef(t, isChannelClosed(init1Ch), "%s channel was open, should be closed", init1Task.Name) 180 require.Truef(t, isChannelClosed(init2Ch), "%s channel was open, should be closed", init2Task.Name) 181 require.Falsef(t, isChannelClosed(mainCh), "%s channel was closed, should be open", mainTask.Name) 182 } 183 184 func TestTaskHookCoordinator_SidecarNeverStarts(t *testing.T) { 185 logger := testlog.HCLogger(t) 186 187 alloc := mock.LifecycleAlloc() 188 tasks := alloc.Job.TaskGroups[0].Tasks 189 190 mainTask := tasks[0] 191 sideTask := tasks[1] 192 initTask := tasks[2] 193 194 coord := newTaskHookCoordinator(logger, tasks) 195 initCh := coord.startConditionForTask(initTask) 196 sideCh := coord.startConditionForTask(sideTask) 197 mainCh := coord.startConditionForTask(mainTask) 198 199 require.Truef(t, isChannelClosed(initCh), "%s channel was open, should be closed", initTask.Name) 200 require.Truef(t, isChannelClosed(sideCh), "%s channel was open, should be closed", sideTask.Name) 201 require.Falsef(t, isChannelClosed(mainCh), "%s channel was closed, should be open", mainTask.Name) 202 203 states := map[string]*structs.TaskState{ 204 mainTask.Name: { 205 State: structs.TaskStatePending, 206 Failed: false, 207 }, 208 initTask.Name: { 209 State: structs.TaskStateDead, 210 Failed: false, 211 StartedAt: time.Now(), 212 FinishedAt: time.Now(), 213 }, 214 sideTask.Name: { 215 State: structs.TaskStatePending, 216 Failed: false, 217 }, 218 } 219 220 coord.taskStateUpdated(states) 221 222 require.Truef(t, isChannelClosed(initCh), "%s channel was open, should be closed", initTask.Name) 223 require.Truef(t, isChannelClosed(sideCh), "%s channel was open, should be closed", sideTask.Name) 224 require.Falsef(t, isChannelClosed(mainCh), "%s channel was closed, should be open", mainTask.Name) 225 } 226 227 func TestTaskHookCoordinator_PoststartStartsAfterMain(t *testing.T) { 228 logger := testlog.HCLogger(t) 229 230 alloc := mock.LifecycleAlloc() 231 tasks := alloc.Job.TaskGroups[0].Tasks 232 233 mainTask := tasks[0] 234 sideTask := tasks[1] 235 postTask := tasks[2] 236 237 // Make the the third task a poststart hook 238 postTask.Lifecycle.Hook = structs.TaskLifecycleHookPoststart 239 240 coord := newTaskHookCoordinator(logger, tasks) 241 postCh := coord.startConditionForTask(postTask) 242 sideCh := coord.startConditionForTask(sideTask) 243 mainCh := coord.startConditionForTask(mainTask) 244 245 require.Truef(t, isChannelClosed(sideCh), "%s channel was open, should be closed", sideTask.Name) 246 require.Falsef(t, isChannelClosed(mainCh), "%s channel was closed, should be open", mainTask.Name) 247 require.Falsef(t, isChannelClosed(mainCh), "%s channel was closed, should be open", postTask.Name) 248 249 states := map[string]*structs.TaskState{ 250 postTask.Name: { 251 State: structs.TaskStatePending, 252 Failed: false, 253 }, 254 mainTask.Name: { 255 State: structs.TaskStateRunning, 256 Failed: false, 257 StartedAt: time.Now(), 258 }, 259 sideTask.Name: { 260 State: structs.TaskStateRunning, 261 Failed: false, 262 StartedAt: time.Now(), 263 }, 264 } 265 266 coord.taskStateUpdated(states) 267 268 require.Truef(t, isChannelClosed(postCh), "%s channel was open, should be closed", postTask.Name) 269 require.Truef(t, isChannelClosed(sideCh), "%s channel was open, should be closed", sideTask.Name) 270 require.Truef(t, isChannelClosed(mainCh), "%s channel was open, should be closed", mainTask.Name) 271 } 272 273 func isChannelClosed(ch <-chan struct{}) bool { 274 select { 275 case <-ch: 276 return true 277 default: 278 return false 279 } 280 } 281 282 func TestHasSidecarTasks(t *testing.T) { 283 284 falseV, trueV := false, true 285 286 cases := []struct { 287 name string 288 // nil if main task, false if non-sidecar hook, true if sidecar hook 289 indicators []*bool 290 291 hasSidecars bool 292 hasNonsidecars bool 293 }{ 294 { 295 name: "all sidecar - one", 296 indicators: []*bool{&trueV}, 297 hasSidecars: true, 298 hasNonsidecars: false, 299 }, 300 { 301 name: "all sidecar - multiple", 302 indicators: []*bool{&trueV, &trueV, &trueV}, 303 hasSidecars: true, 304 hasNonsidecars: false, 305 }, 306 { 307 name: "some sidecars, some others", 308 indicators: []*bool{nil, &falseV, &trueV}, 309 hasSidecars: true, 310 hasNonsidecars: true, 311 }, 312 { 313 name: "no sidecars", 314 indicators: []*bool{nil, &falseV, nil}, 315 hasSidecars: false, 316 hasNonsidecars: true, 317 }, 318 } 319 320 for _, c := range cases { 321 t.Run(c.name, func(t *testing.T) { 322 alloc := allocWithSidecarIndicators(c.indicators) 323 arConf, cleanup := testAllocRunnerConfig(t, alloc) 324 defer cleanup() 325 326 ar, err := NewAllocRunner(arConf) 327 require.NoError(t, err) 328 329 require.Equal(t, c.hasSidecars, hasSidecarTasks(ar.tasks), "sidecars") 330 331 runners := []*taskrunner.TaskRunner{} 332 for _, r := range ar.tasks { 333 runners = append(runners, r) 334 } 335 require.Equal(t, c.hasNonsidecars, hasNonSidecarTasks(runners), "non-sidecars") 336 337 }) 338 } 339 } 340 341 func allocWithSidecarIndicators(indicators []*bool) *structs.Allocation { 342 alloc := mock.BatchAlloc() 343 344 tasks := []*structs.Task{} 345 resources := map[string]*structs.AllocatedTaskResources{} 346 347 tr := alloc.AllocatedResources.Tasks[alloc.Job.TaskGroups[0].Tasks[0].Name] 348 349 for i, indicator := range indicators { 350 task := alloc.Job.TaskGroups[0].Tasks[0].Copy() 351 task.Name = fmt.Sprintf("task%d", i) 352 if indicator != nil { 353 task.Lifecycle = &structs.TaskLifecycleConfig{ 354 Hook: structs.TaskLifecycleHookPrestart, 355 Sidecar: *indicator, 356 } 357 } 358 tasks = append(tasks, task) 359 resources[task.Name] = tr 360 } 361 362 alloc.Job.TaskGroups[0].Tasks = tasks 363 364 alloc.AllocatedResources.Tasks = resources 365 return alloc 366 367 }