github.com/ryanslade/nomad@v0.2.4-0.20160128061903-fc95782f2089/nomad/eval_endpoint_test.go (about) 1 package nomad 2 3 import ( 4 "reflect" 5 "testing" 6 "time" 7 8 "github.com/hashicorp/net-rpc-msgpackrpc" 9 "github.com/hashicorp/nomad/nomad/mock" 10 "github.com/hashicorp/nomad/nomad/structs" 11 "github.com/hashicorp/nomad/testutil" 12 ) 13 14 func TestEvalEndpoint_GetEval(t *testing.T) { 15 s1 := testServer(t, nil) 16 defer s1.Shutdown() 17 codec := rpcClient(t, s1) 18 testutil.WaitForLeader(t, s1.RPC) 19 20 // Create the register request 21 eval1 := mock.Eval() 22 s1.fsm.State().UpsertEvals(1000, []*structs.Evaluation{eval1}) 23 24 // Lookup the eval 25 get := &structs.EvalSpecificRequest{ 26 EvalID: eval1.ID, 27 QueryOptions: structs.QueryOptions{Region: "global"}, 28 } 29 var resp structs.SingleEvalResponse 30 if err := msgpackrpc.CallWithCodec(codec, "Eval.GetEval", get, &resp); err != nil { 31 t.Fatalf("err: %v", err) 32 } 33 if resp.Index != 1000 { 34 t.Fatalf("Bad index: %d %d", resp.Index, 1000) 35 } 36 37 if !reflect.DeepEqual(eval1, resp.Eval) { 38 t.Fatalf("bad: %#v %#v", eval1, resp.Eval) 39 } 40 41 // Lookup non-existing node 42 get.EvalID = structs.GenerateUUID() 43 if err := msgpackrpc.CallWithCodec(codec, "Eval.GetEval", get, &resp); err != nil { 44 t.Fatalf("err: %v", err) 45 } 46 if resp.Index != 1000 { 47 t.Fatalf("Bad index: %d %d", resp.Index, 1000) 48 } 49 if resp.Eval != nil { 50 t.Fatalf("unexpected eval") 51 } 52 } 53 54 func TestEvalEndpoint_GetEval_Blocking(t *testing.T) { 55 s1 := testServer(t, nil) 56 defer s1.Shutdown() 57 state := s1.fsm.State() 58 codec := rpcClient(t, s1) 59 testutil.WaitForLeader(t, s1.RPC) 60 61 // Create the evals 62 eval1 := mock.Eval() 63 eval2 := mock.Eval() 64 65 // First create an unrelated eval 66 time.AfterFunc(100*time.Millisecond, func() { 67 err := state.UpsertEvals(100, []*structs.Evaluation{eval1}) 68 if err != nil { 69 t.Fatalf("err: %v", err) 70 } 71 }) 72 73 // Upsert the eval we are watching later 74 time.AfterFunc(200*time.Millisecond, func() { 75 err := state.UpsertEvals(200, []*structs.Evaluation{eval2}) 76 if err != nil { 77 t.Fatalf("err: %v", err) 78 } 79 }) 80 81 // Lookup the eval 82 req := &structs.EvalSpecificRequest{ 83 EvalID: eval2.ID, 84 QueryOptions: structs.QueryOptions{ 85 Region: "global", 86 MinQueryIndex: 50, 87 }, 88 } 89 var resp structs.SingleEvalResponse 90 start := time.Now() 91 if err := msgpackrpc.CallWithCodec(codec, "Eval.GetEval", req, &resp); err != nil { 92 t.Fatalf("err: %v", err) 93 } 94 95 if elapsed := time.Since(start); elapsed < 200*time.Millisecond { 96 t.Fatalf("should block (returned in %s) %#v", elapsed, resp) 97 } 98 if resp.Index != 200 { 99 t.Fatalf("Bad index: %d %d", resp.Index, 200) 100 } 101 if resp.Eval == nil || resp.Eval.ID != eval2.ID { 102 t.Fatalf("bad: %#v", resp.Eval) 103 } 104 105 // Eval delete triggers watches 106 time.AfterFunc(100*time.Millisecond, func() { 107 err := state.DeleteEval(300, []string{eval2.ID}, []string{}) 108 if err != nil { 109 t.Fatalf("err: %v", err) 110 } 111 }) 112 113 req.QueryOptions.MinQueryIndex = 250 114 var resp2 structs.SingleEvalResponse 115 start = time.Now() 116 if err := msgpackrpc.CallWithCodec(codec, "Eval.GetEval", req, &resp2); err != nil { 117 t.Fatalf("err: %v", err) 118 } 119 120 if elapsed := time.Since(start); elapsed < 100*time.Millisecond { 121 t.Fatalf("should block (returned in %s) %#v", elapsed, resp2) 122 } 123 if resp2.Index != 300 { 124 t.Fatalf("Bad index: %d %d", resp2.Index, 300) 125 } 126 if resp2.Eval != nil { 127 t.Fatalf("bad: %#v", resp2.Eval) 128 } 129 } 130 131 func TestEvalEndpoint_Dequeue(t *testing.T) { 132 s1 := testServer(t, func(c *Config) { 133 c.NumSchedulers = 0 // Prevent automatic dequeue 134 }) 135 defer s1.Shutdown() 136 codec := rpcClient(t, s1) 137 testutil.WaitForLeader(t, s1.RPC) 138 139 // Create the register request 140 eval1 := mock.Eval() 141 testutil.WaitForResult(func() (bool, error) { 142 err := s1.evalBroker.Enqueue(eval1) 143 return err == nil, err 144 }, func(err error) { 145 t.Fatalf("err: %v", err) 146 }) 147 148 // Dequeue the eval 149 get := &structs.EvalDequeueRequest{ 150 Schedulers: defaultSched, 151 WriteRequest: structs.WriteRequest{Region: "global"}, 152 } 153 var resp structs.EvalDequeueResponse 154 if err := msgpackrpc.CallWithCodec(codec, "Eval.Dequeue", get, &resp); err != nil { 155 t.Fatalf("err: %v", err) 156 } 157 158 if !reflect.DeepEqual(eval1, resp.Eval) { 159 t.Fatalf("bad: %v %v", eval1, resp.Eval) 160 } 161 162 // Ensure outstanding 163 token, ok := s1.evalBroker.Outstanding(eval1.ID) 164 if !ok { 165 t.Fatalf("should be outstanding") 166 } 167 if token != resp.Token { 168 t.Fatalf("bad token: %#v %#v", token, resp.Token) 169 } 170 } 171 172 func TestEvalEndpoint_Ack(t *testing.T) { 173 s1 := testServer(t, nil) 174 defer s1.Shutdown() 175 codec := rpcClient(t, s1) 176 177 testutil.WaitForResult(func() (bool, error) { 178 return s1.evalBroker.Enabled(), nil 179 }, func(err error) { 180 t.Fatalf("should enable eval broker") 181 }) 182 183 // Create the register request 184 eval1 := mock.Eval() 185 s1.evalBroker.Enqueue(eval1) 186 out, token, err := s1.evalBroker.Dequeue(defaultSched, time.Second) 187 if err != nil { 188 t.Fatalf("err: %v", err) 189 } 190 if out == nil { 191 t.Fatalf("missing eval") 192 } 193 194 // Ack the eval 195 get := &structs.EvalAckRequest{ 196 EvalID: out.ID, 197 Token: token, 198 WriteRequest: structs.WriteRequest{Region: "global"}, 199 } 200 var resp structs.GenericResponse 201 if err := msgpackrpc.CallWithCodec(codec, "Eval.Ack", get, &resp); err != nil { 202 t.Fatalf("err: %v", err) 203 } 204 205 // Ensure outstanding 206 if _, ok := s1.evalBroker.Outstanding(eval1.ID); ok { 207 t.Fatalf("should not be outstanding") 208 } 209 } 210 211 func TestEvalEndpoint_Nack(t *testing.T) { 212 s1 := testServer(t, func(c *Config) { 213 // Disable all of the schedulers so we can manually dequeue 214 // evals and check the queue status 215 c.NumSchedulers = 0 216 }) 217 defer s1.Shutdown() 218 codec := rpcClient(t, s1) 219 220 testutil.WaitForResult(func() (bool, error) { 221 return s1.evalBroker.Enabled(), nil 222 }, func(err error) { 223 t.Fatalf("should enable eval broker") 224 }) 225 226 // Create the register request 227 eval1 := mock.Eval() 228 s1.evalBroker.Enqueue(eval1) 229 out, token, _ := s1.evalBroker.Dequeue(defaultSched, time.Second) 230 if out == nil { 231 t.Fatalf("missing eval") 232 } 233 234 // Nack the eval 235 get := &structs.EvalAckRequest{ 236 EvalID: out.ID, 237 Token: token, 238 WriteRequest: structs.WriteRequest{Region: "global"}, 239 } 240 var resp structs.GenericResponse 241 if err := msgpackrpc.CallWithCodec(codec, "Eval.Nack", get, &resp); err != nil { 242 t.Fatalf("err: %v", err) 243 } 244 245 // Ensure outstanding 246 if _, ok := s1.evalBroker.Outstanding(eval1.ID); ok { 247 t.Fatalf("should not be outstanding") 248 } 249 250 // Should get it back 251 out2, _, _ := s1.evalBroker.Dequeue(defaultSched, time.Second) 252 if out2 != out { 253 t.Fatalf("nack failed") 254 } 255 } 256 257 func TestEvalEndpoint_Update(t *testing.T) { 258 s1 := testServer(t, nil) 259 defer s1.Shutdown() 260 codec := rpcClient(t, s1) 261 262 testutil.WaitForResult(func() (bool, error) { 263 return s1.evalBroker.Enabled(), nil 264 }, func(err error) { 265 t.Fatalf("should enable eval broker") 266 }) 267 268 // Create the register request 269 eval1 := mock.Eval() 270 s1.evalBroker.Enqueue(eval1) 271 out, token, err := s1.evalBroker.Dequeue(defaultSched, time.Second) 272 if err != nil { 273 t.Fatalf("err: %v", err) 274 } 275 if out == nil { 276 t.Fatalf("missing eval") 277 } 278 279 // Update the eval 280 eval2 := eval1.Copy() 281 eval2.Status = structs.EvalStatusComplete 282 283 get := &structs.EvalUpdateRequest{ 284 Evals: []*structs.Evaluation{eval2}, 285 EvalToken: token, 286 WriteRequest: structs.WriteRequest{Region: "global"}, 287 } 288 var resp structs.GenericResponse 289 if err := msgpackrpc.CallWithCodec(codec, "Eval.Update", get, &resp); err != nil { 290 t.Fatalf("err: %v", err) 291 } 292 293 // Ensure updated 294 outE, err := s1.fsm.State().EvalByID(eval2.ID) 295 if err != nil { 296 t.Fatalf("err: %v", err) 297 } 298 if outE.Status != structs.EvalStatusComplete { 299 t.Fatalf("Bad: %#v", out) 300 } 301 } 302 303 func TestEvalEndpoint_Create(t *testing.T) { 304 s1 := testServer(t, func(c *Config) { 305 c.NumSchedulers = 0 // Prevent automatic dequeue 306 }) 307 defer s1.Shutdown() 308 codec := rpcClient(t, s1) 309 310 testutil.WaitForResult(func() (bool, error) { 311 return s1.evalBroker.Enabled(), nil 312 }, func(err error) { 313 t.Fatalf("should enable eval broker") 314 }) 315 316 // Create the register request 317 prev := mock.Eval() 318 s1.evalBroker.Enqueue(prev) 319 out, token, err := s1.evalBroker.Dequeue(defaultSched, time.Second) 320 if err != nil { 321 t.Fatalf("err: %v", err) 322 } 323 if out == nil { 324 t.Fatalf("missing eval") 325 } 326 327 // Create the register request 328 eval1 := mock.Eval() 329 eval1.PreviousEval = prev.ID 330 get := &structs.EvalUpdateRequest{ 331 Evals: []*structs.Evaluation{eval1}, 332 EvalToken: token, 333 WriteRequest: structs.WriteRequest{Region: "global"}, 334 } 335 var resp structs.GenericResponse 336 if err := msgpackrpc.CallWithCodec(codec, "Eval.Create", get, &resp); err != nil { 337 t.Fatalf("err: %v", err) 338 } 339 340 // Ensure created 341 outE, err := s1.fsm.State().EvalByID(eval1.ID) 342 if err != nil { 343 t.Fatalf("err: %v", err) 344 } 345 346 eval1.CreateIndex = resp.Index 347 eval1.ModifyIndex = resp.Index 348 if !reflect.DeepEqual(eval1, outE) { 349 t.Fatalf("Bad: %#v %#v", outE, eval1) 350 } 351 } 352 353 func TestEvalEndpoint_Reap(t *testing.T) { 354 s1 := testServer(t, nil) 355 defer s1.Shutdown() 356 codec := rpcClient(t, s1) 357 testutil.WaitForLeader(t, s1.RPC) 358 359 // Create the register request 360 eval1 := mock.Eval() 361 s1.fsm.State().UpsertEvals(1000, []*structs.Evaluation{eval1}) 362 363 // Reap the eval 364 get := &structs.EvalDeleteRequest{ 365 Evals: []string{eval1.ID}, 366 WriteRequest: structs.WriteRequest{Region: "global"}, 367 } 368 var resp structs.GenericResponse 369 if err := msgpackrpc.CallWithCodec(codec, "Eval.Reap", get, &resp); err != nil { 370 t.Fatalf("err: %v", err) 371 } 372 if resp.Index == 0 { 373 t.Fatalf("Bad index: %d", resp.Index) 374 } 375 376 // Ensure deleted 377 outE, err := s1.fsm.State().EvalByID(eval1.ID) 378 if err != nil { 379 t.Fatalf("err: %v", err) 380 } 381 if outE != nil { 382 t.Fatalf("Bad: %#v", outE) 383 } 384 } 385 386 func TestEvalEndpoint_List(t *testing.T) { 387 s1 := testServer(t, nil) 388 defer s1.Shutdown() 389 codec := rpcClient(t, s1) 390 testutil.WaitForLeader(t, s1.RPC) 391 392 // Create the register request 393 eval1 := mock.Eval() 394 eval1.ID = "aaaaaaaa-3350-4b4b-d185-0e1992ed43e9" 395 eval2 := mock.Eval() 396 eval2.ID = "aaaabbbb-3350-4b4b-d185-0e1992ed43e9" 397 s1.fsm.State().UpsertEvals(1000, []*structs.Evaluation{eval1, eval2}) 398 399 // Lookup the eval 400 get := &structs.EvalListRequest{ 401 QueryOptions: structs.QueryOptions{Region: "global"}, 402 } 403 var resp structs.EvalListResponse 404 if err := msgpackrpc.CallWithCodec(codec, "Eval.List", get, &resp); err != nil { 405 t.Fatalf("err: %v", err) 406 } 407 if resp.Index != 1000 { 408 t.Fatalf("Bad index: %d %d", resp.Index, 1000) 409 } 410 411 if len(resp.Evaluations) != 2 { 412 t.Fatalf("bad: %#v", resp.Evaluations) 413 } 414 415 // Lookup the eval by prefix 416 get = &structs.EvalListRequest{ 417 QueryOptions: structs.QueryOptions{Region: "global", Prefix: "aaaabb"}, 418 } 419 var resp2 structs.EvalListResponse 420 if err := msgpackrpc.CallWithCodec(codec, "Eval.List", get, &resp2); err != nil { 421 t.Fatalf("err: %v", err) 422 } 423 if resp2.Index != 1000 { 424 t.Fatalf("Bad index: %d %d", resp2.Index, 1000) 425 } 426 427 if len(resp2.Evaluations) != 1 { 428 t.Fatalf("bad: %#v", resp2.Evaluations) 429 } 430 431 } 432 433 func TestEvalEndpoint_List_Blocking(t *testing.T) { 434 s1 := testServer(t, nil) 435 defer s1.Shutdown() 436 state := s1.fsm.State() 437 codec := rpcClient(t, s1) 438 testutil.WaitForLeader(t, s1.RPC) 439 440 // Create the ieval 441 eval := mock.Eval() 442 443 // Upsert eval triggers watches 444 time.AfterFunc(100*time.Millisecond, func() { 445 if err := state.UpsertEvals(2, []*structs.Evaluation{eval}); err != nil { 446 t.Fatalf("err: %v", err) 447 } 448 }) 449 450 req := &structs.EvalListRequest{ 451 QueryOptions: structs.QueryOptions{ 452 Region: "global", 453 MinQueryIndex: 1, 454 }, 455 } 456 start := time.Now() 457 var resp structs.EvalListResponse 458 if err := msgpackrpc.CallWithCodec(codec, "Eval.List", req, &resp); err != nil { 459 t.Fatalf("err: %v", err) 460 } 461 462 if elapsed := time.Since(start); elapsed < 100*time.Millisecond { 463 t.Fatalf("should block (returned in %s) %#v", elapsed, resp) 464 } 465 if resp.Index != 2 { 466 t.Fatalf("Bad index: %d %d", resp.Index, 2) 467 } 468 if len(resp.Evaluations) != 1 || resp.Evaluations[0].ID != eval.ID { 469 t.Fatalf("bad: %#v", resp.Evaluations) 470 } 471 472 // Eval deletion triggers watches 473 time.AfterFunc(100*time.Millisecond, func() { 474 if err := state.DeleteEval(3, []string{eval.ID}, nil); err != nil { 475 t.Fatalf("err: %v", err) 476 } 477 }) 478 479 req.MinQueryIndex = 2 480 start = time.Now() 481 var resp2 structs.EvalListResponse 482 if err := msgpackrpc.CallWithCodec(codec, "Eval.List", req, &resp2); err != nil { 483 t.Fatalf("err: %v", err) 484 } 485 486 if elapsed := time.Since(start); elapsed < 100*time.Millisecond { 487 t.Fatalf("should block (returned in %s) %#v", elapsed, resp2) 488 } 489 if resp2.Index != 3 { 490 t.Fatalf("Bad index: %d %d", resp2.Index, 3) 491 } 492 if len(resp2.Evaluations) != 0 { 493 t.Fatalf("bad: %#v", resp2.Evaluations) 494 } 495 } 496 497 func TestEvalEndpoint_Allocations(t *testing.T) { 498 s1 := testServer(t, nil) 499 defer s1.Shutdown() 500 codec := rpcClient(t, s1) 501 testutil.WaitForLeader(t, s1.RPC) 502 503 // Create the register request 504 alloc1 := mock.Alloc() 505 alloc2 := mock.Alloc() 506 alloc2.EvalID = alloc1.EvalID 507 state := s1.fsm.State() 508 err := state.UpsertAllocs(1000, 509 []*structs.Allocation{alloc1, alloc2}) 510 if err != nil { 511 t.Fatalf("err: %v", err) 512 } 513 514 // Lookup the eval 515 get := &structs.EvalSpecificRequest{ 516 EvalID: alloc1.EvalID, 517 QueryOptions: structs.QueryOptions{Region: "global"}, 518 } 519 var resp structs.EvalAllocationsResponse 520 if err := msgpackrpc.CallWithCodec(codec, "Eval.Allocations", get, &resp); err != nil { 521 t.Fatalf("err: %v", err) 522 } 523 if resp.Index != 1000 { 524 t.Fatalf("Bad index: %d %d", resp.Index, 1000) 525 } 526 527 if len(resp.Allocations) != 2 { 528 t.Fatalf("bad: %#v", resp.Allocations) 529 } 530 } 531 532 func TestEvalEndpoint_Allocations_Blocking(t *testing.T) { 533 s1 := testServer(t, nil) 534 defer s1.Shutdown() 535 state := s1.fsm.State() 536 codec := rpcClient(t, s1) 537 testutil.WaitForLeader(t, s1.RPC) 538 539 // Create the allocs 540 alloc1 := mock.Alloc() 541 alloc2 := mock.Alloc() 542 543 // Upsert an unrelated alloc first 544 time.AfterFunc(100*time.Millisecond, func() { 545 err := state.UpsertAllocs(100, []*structs.Allocation{alloc1}) 546 if err != nil { 547 t.Fatalf("err: %v", err) 548 } 549 }) 550 551 // Upsert an alloc which will trigger the watch later 552 time.AfterFunc(200*time.Millisecond, func() { 553 err := state.UpsertAllocs(200, []*structs.Allocation{alloc2}) 554 if err != nil { 555 t.Fatalf("err: %v", err) 556 } 557 }) 558 559 // Lookup the eval 560 get := &structs.EvalSpecificRequest{ 561 EvalID: alloc2.EvalID, 562 QueryOptions: structs.QueryOptions{ 563 Region: "global", 564 MinQueryIndex: 50, 565 }, 566 } 567 var resp structs.EvalAllocationsResponse 568 start := time.Now() 569 if err := msgpackrpc.CallWithCodec(codec, "Eval.Allocations", get, &resp); err != nil { 570 t.Fatalf("err: %v", err) 571 } 572 573 if elapsed := time.Since(start); elapsed < 200*time.Millisecond { 574 t.Fatalf("should block (returned in %s) %#v", elapsed, resp) 575 } 576 if resp.Index != 200 { 577 t.Fatalf("Bad index: %d %d", resp.Index, 200) 578 } 579 if len(resp.Allocations) != 1 || resp.Allocations[0].ID != alloc2.ID { 580 t.Fatalf("bad: %#v", resp.Allocations) 581 } 582 }