github.com/hspak/nomad@v0.7.2-0.20180309000617-bc4ae22a39a5/api/tasks_test.go (about) 1 package api 2 3 import ( 4 "reflect" 5 "testing" 6 "time" 7 8 "github.com/hashicorp/nomad/helper" 9 "github.com/hashicorp/nomad/nomad/structs" 10 "github.com/stretchr/testify/assert" 11 ) 12 13 func TestTaskGroup_NewTaskGroup(t *testing.T) { 14 t.Parallel() 15 grp := NewTaskGroup("grp1", 2) 16 expect := &TaskGroup{ 17 Name: helper.StringToPtr("grp1"), 18 Count: helper.IntToPtr(2), 19 } 20 if !reflect.DeepEqual(grp, expect) { 21 t.Fatalf("expect: %#v, got: %#v", expect, grp) 22 } 23 } 24 25 func TestTaskGroup_Constrain(t *testing.T) { 26 t.Parallel() 27 grp := NewTaskGroup("grp1", 1) 28 29 // Add a constraint to the group 30 out := grp.Constrain(NewConstraint("kernel.name", "=", "darwin")) 31 if n := len(grp.Constraints); n != 1 { 32 t.Fatalf("expected 1 constraint, got: %d", n) 33 } 34 35 // Check that the group was returned 36 if out != grp { 37 t.Fatalf("expected: %#v, got: %#v", grp, out) 38 } 39 40 // Add a second constraint 41 grp.Constrain(NewConstraint("memory.totalbytes", ">=", "128000000")) 42 expect := []*Constraint{ 43 { 44 LTarget: "kernel.name", 45 RTarget: "darwin", 46 Operand: "=", 47 }, 48 { 49 LTarget: "memory.totalbytes", 50 RTarget: "128000000", 51 Operand: ">=", 52 }, 53 } 54 if !reflect.DeepEqual(grp.Constraints, expect) { 55 t.Fatalf("expect: %#v, got: %#v", expect, grp.Constraints) 56 } 57 } 58 59 func TestTaskGroup_SetMeta(t *testing.T) { 60 t.Parallel() 61 grp := NewTaskGroup("grp1", 1) 62 63 // Initializes an empty map 64 out := grp.SetMeta("foo", "bar") 65 if grp.Meta == nil { 66 t.Fatalf("should be initialized") 67 } 68 69 // Check that we returned the group 70 if out != grp { 71 t.Fatalf("expect: %#v, got: %#v", grp, out) 72 } 73 74 // Add a second meta k/v 75 grp.SetMeta("baz", "zip") 76 expect := map[string]string{"foo": "bar", "baz": "zip"} 77 if !reflect.DeepEqual(grp.Meta, expect) { 78 t.Fatalf("expect: %#v, got: %#v", expect, grp.Meta) 79 } 80 } 81 82 func TestTaskGroup_AddTask(t *testing.T) { 83 t.Parallel() 84 grp := NewTaskGroup("grp1", 1) 85 86 // Add the task to the task group 87 out := grp.AddTask(NewTask("task1", "java")) 88 if n := len(grp.Tasks); n != 1 { 89 t.Fatalf("expected 1 task, got: %d", n) 90 } 91 92 // Check that we returned the group 93 if out != grp { 94 t.Fatalf("expect: %#v, got: %#v", grp, out) 95 } 96 97 // Add a second task 98 grp.AddTask(NewTask("task2", "exec")) 99 expect := []*Task{ 100 { 101 Name: "task1", 102 Driver: "java", 103 }, 104 { 105 Name: "task2", 106 Driver: "exec", 107 }, 108 } 109 if !reflect.DeepEqual(grp.Tasks, expect) { 110 t.Fatalf("expect: %#v, got: %#v", expect, grp.Tasks) 111 } 112 } 113 114 func TestTask_NewTask(t *testing.T) { 115 t.Parallel() 116 task := NewTask("task1", "exec") 117 expect := &Task{ 118 Name: "task1", 119 Driver: "exec", 120 } 121 if !reflect.DeepEqual(task, expect) { 122 t.Fatalf("expect: %#v, got: %#v", expect, task) 123 } 124 } 125 126 func TestTask_SetConfig(t *testing.T) { 127 t.Parallel() 128 task := NewTask("task1", "exec") 129 130 // Initializes an empty map 131 out := task.SetConfig("foo", "bar") 132 if task.Config == nil { 133 t.Fatalf("should be initialized") 134 } 135 136 // Check that we returned the task 137 if out != task { 138 t.Fatalf("expect: %#v, got: %#v", task, out) 139 } 140 141 // Set another config value 142 task.SetConfig("baz", "zip") 143 expect := map[string]interface{}{"foo": "bar", "baz": "zip"} 144 if !reflect.DeepEqual(task.Config, expect) { 145 t.Fatalf("expect: %#v, got: %#v", expect, task.Config) 146 } 147 } 148 149 func TestTask_SetMeta(t *testing.T) { 150 t.Parallel() 151 task := NewTask("task1", "exec") 152 153 // Initializes an empty map 154 out := task.SetMeta("foo", "bar") 155 if task.Meta == nil { 156 t.Fatalf("should be initialized") 157 } 158 159 // Check that we returned the task 160 if out != task { 161 t.Fatalf("expect: %#v, got: %#v", task, out) 162 } 163 164 // Set another meta k/v 165 task.SetMeta("baz", "zip") 166 expect := map[string]string{"foo": "bar", "baz": "zip"} 167 if !reflect.DeepEqual(task.Meta, expect) { 168 t.Fatalf("expect: %#v, got: %#v", expect, task.Meta) 169 } 170 } 171 172 func TestTask_Require(t *testing.T) { 173 t.Parallel() 174 task := NewTask("task1", "exec") 175 176 // Create some require resources 177 resources := &Resources{ 178 CPU: helper.IntToPtr(1250), 179 MemoryMB: helper.IntToPtr(128), 180 DiskMB: helper.IntToPtr(2048), 181 IOPS: helper.IntToPtr(500), 182 Networks: []*NetworkResource{ 183 { 184 CIDR: "0.0.0.0/0", 185 MBits: helper.IntToPtr(100), 186 ReservedPorts: []Port{{"", 80}, {"", 443}}, 187 }, 188 }, 189 } 190 out := task.Require(resources) 191 if !reflect.DeepEqual(task.Resources, resources) { 192 t.Fatalf("expect: %#v, got: %#v", resources, task.Resources) 193 } 194 195 // Check that we returned the task 196 if out != task { 197 t.Fatalf("expect: %#v, got: %#v", task, out) 198 } 199 } 200 201 func TestTask_Constrain(t *testing.T) { 202 t.Parallel() 203 task := NewTask("task1", "exec") 204 205 // Add a constraint to the task 206 out := task.Constrain(NewConstraint("kernel.name", "=", "darwin")) 207 if n := len(task.Constraints); n != 1 { 208 t.Fatalf("expected 1 constraint, got: %d", n) 209 } 210 211 // Check that the task was returned 212 if out != task { 213 t.Fatalf("expected: %#v, got: %#v", task, out) 214 } 215 216 // Add a second constraint 217 task.Constrain(NewConstraint("memory.totalbytes", ">=", "128000000")) 218 expect := []*Constraint{ 219 { 220 LTarget: "kernel.name", 221 RTarget: "darwin", 222 Operand: "=", 223 }, 224 { 225 LTarget: "memory.totalbytes", 226 RTarget: "128000000", 227 Operand: ">=", 228 }, 229 } 230 if !reflect.DeepEqual(task.Constraints, expect) { 231 t.Fatalf("expect: %#v, got: %#v", expect, task.Constraints) 232 } 233 } 234 235 func TestTask_Artifact(t *testing.T) { 236 t.Parallel() 237 a := TaskArtifact{ 238 GetterSource: helper.StringToPtr("http://localhost/foo.txt"), 239 GetterMode: helper.StringToPtr("file"), 240 } 241 a.Canonicalize() 242 if *a.GetterMode != "file" { 243 t.Errorf("expected file but found %q", *a.GetterMode) 244 } 245 if *a.RelativeDest != "local/foo.txt" { 246 t.Errorf("expected local/foo.txt but found %q", *a.RelativeDest) 247 } 248 } 249 250 // Ensures no regression on https://github.com/hashicorp/nomad/issues/3132 251 func TestTaskGroup_Canonicalize_Update(t *testing.T) { 252 job := &Job{ 253 ID: helper.StringToPtr("test"), 254 Update: &UpdateStrategy{ 255 AutoRevert: helper.BoolToPtr(false), 256 Canary: helper.IntToPtr(0), 257 HealthCheck: helper.StringToPtr(""), 258 HealthyDeadline: helper.TimeToPtr(0), 259 MaxParallel: helper.IntToPtr(0), 260 MinHealthyTime: helper.TimeToPtr(0), 261 Stagger: helper.TimeToPtr(0), 262 }, 263 } 264 job.Canonicalize() 265 tg := &TaskGroup{ 266 Name: helper.StringToPtr("foo"), 267 } 268 tg.Canonicalize(job) 269 assert.Nil(t, tg.Update) 270 } 271 272 // Verifies that reschedule policy is merged correctly 273 func TestTaskGroup_Canonicalize_ReschedulePolicy(t *testing.T) { 274 type testCase struct { 275 desc string 276 jobReschedulePolicy *ReschedulePolicy 277 taskReschedulePolicy *ReschedulePolicy 278 expected *ReschedulePolicy 279 } 280 281 testCases := []testCase{ 282 { 283 desc: "Default", 284 jobReschedulePolicy: nil, 285 taskReschedulePolicy: nil, 286 expected: &ReschedulePolicy{ 287 Attempts: helper.IntToPtr(structs.DefaultBatchJobReschedulePolicy.Attempts), 288 Interval: helper.TimeToPtr(structs.DefaultBatchJobReschedulePolicy.Interval), 289 }, 290 }, 291 { 292 desc: "Empty job reschedule policy", 293 jobReschedulePolicy: &ReschedulePolicy{ 294 Attempts: helper.IntToPtr(0), 295 Interval: helper.TimeToPtr(0), 296 }, 297 taskReschedulePolicy: nil, 298 expected: &ReschedulePolicy{ 299 Attempts: helper.IntToPtr(0), 300 Interval: helper.TimeToPtr(0), 301 }, 302 }, 303 { 304 desc: "Inherit from job", 305 jobReschedulePolicy: &ReschedulePolicy{ 306 Attempts: helper.IntToPtr(1), 307 Interval: helper.TimeToPtr(20 * time.Second), 308 }, 309 taskReschedulePolicy: nil, 310 expected: &ReschedulePolicy{ 311 Attempts: helper.IntToPtr(1), 312 Interval: helper.TimeToPtr(20 * time.Second), 313 }, 314 }, 315 { 316 desc: "Set in task", 317 jobReschedulePolicy: nil, 318 taskReschedulePolicy: &ReschedulePolicy{ 319 Attempts: helper.IntToPtr(5), 320 Interval: helper.TimeToPtr(2 * time.Minute), 321 }, 322 expected: &ReschedulePolicy{ 323 Attempts: helper.IntToPtr(5), 324 Interval: helper.TimeToPtr(2 * time.Minute), 325 }, 326 }, 327 { 328 desc: "Merge from job", 329 jobReschedulePolicy: &ReschedulePolicy{ 330 Attempts: helper.IntToPtr(1), 331 }, 332 taskReschedulePolicy: &ReschedulePolicy{ 333 Interval: helper.TimeToPtr(5 * time.Minute), 334 }, 335 expected: &ReschedulePolicy{ 336 Attempts: helper.IntToPtr(1), 337 Interval: helper.TimeToPtr(5 * time.Minute), 338 }, 339 }, 340 { 341 desc: "Override from group", 342 jobReschedulePolicy: &ReschedulePolicy{ 343 Attempts: helper.IntToPtr(1), 344 }, 345 taskReschedulePolicy: &ReschedulePolicy{ 346 Attempts: helper.IntToPtr(5), 347 }, 348 expected: &ReschedulePolicy{ 349 Attempts: helper.IntToPtr(5), 350 Interval: helper.TimeToPtr(structs.DefaultBatchJobReschedulePolicy.Interval), 351 }, 352 }, 353 { 354 desc: "Attempts from job, default interval", 355 jobReschedulePolicy: &ReschedulePolicy{ 356 Attempts: helper.IntToPtr(1), 357 }, 358 taskReschedulePolicy: nil, 359 expected: &ReschedulePolicy{ 360 Attempts: helper.IntToPtr(1), 361 Interval: helper.TimeToPtr(structs.DefaultBatchJobReschedulePolicy.Interval), 362 }, 363 }, 364 } 365 366 for _, tc := range testCases { 367 t.Run(tc.desc, func(t *testing.T) { 368 job := &Job{ 369 ID: helper.StringToPtr("test"), 370 Reschedule: tc.jobReschedulePolicy, 371 Type: helper.StringToPtr(JobTypeBatch), 372 } 373 job.Canonicalize() 374 tg := &TaskGroup{ 375 Name: helper.StringToPtr("foo"), 376 ReschedulePolicy: tc.taskReschedulePolicy, 377 } 378 tg.Canonicalize(job) 379 assert.Equal(t, tc.expected, tg.ReschedulePolicy) 380 }) 381 } 382 } 383 384 // TestService_CheckRestart asserts Service.CheckRestart settings are properly 385 // inherited by Checks. 386 func TestService_CheckRestart(t *testing.T) { 387 job := &Job{Name: helper.StringToPtr("job")} 388 tg := &TaskGroup{Name: helper.StringToPtr("group")} 389 task := &Task{Name: "task"} 390 service := &Service{ 391 CheckRestart: &CheckRestart{ 392 Limit: 11, 393 Grace: helper.TimeToPtr(11 * time.Second), 394 IgnoreWarnings: true, 395 }, 396 Checks: []ServiceCheck{ 397 { 398 Name: "all-set", 399 CheckRestart: &CheckRestart{ 400 Limit: 22, 401 Grace: helper.TimeToPtr(22 * time.Second), 402 IgnoreWarnings: true, 403 }, 404 }, 405 { 406 Name: "some-set", 407 CheckRestart: &CheckRestart{ 408 Limit: 33, 409 Grace: helper.TimeToPtr(33 * time.Second), 410 }, 411 }, 412 { 413 Name: "unset", 414 }, 415 }, 416 } 417 418 service.Canonicalize(task, tg, job) 419 assert.Equal(t, service.Checks[0].CheckRestart.Limit, 22) 420 assert.Equal(t, *service.Checks[0].CheckRestart.Grace, 22*time.Second) 421 assert.True(t, service.Checks[0].CheckRestart.IgnoreWarnings) 422 423 assert.Equal(t, service.Checks[1].CheckRestart.Limit, 33) 424 assert.Equal(t, *service.Checks[1].CheckRestart.Grace, 33*time.Second) 425 assert.True(t, service.Checks[1].CheckRestart.IgnoreWarnings) 426 427 assert.Equal(t, service.Checks[2].CheckRestart.Limit, 11) 428 assert.Equal(t, *service.Checks[2].CheckRestart.Grace, 11*time.Second) 429 assert.True(t, service.Checks[2].CheckRestart.IgnoreWarnings) 430 }