github.com/zoomfoo/nomad@v0.8.5-0.20180907175415-f28fd3a1a056/nomad/eval_endpoint_test.go (about) 1 package nomad 2 3 import ( 4 "fmt" 5 "reflect" 6 "strings" 7 "testing" 8 "time" 9 10 memdb "github.com/hashicorp/go-memdb" 11 "github.com/hashicorp/net-rpc-msgpackrpc" 12 "github.com/hashicorp/nomad/acl" 13 "github.com/hashicorp/nomad/helper/uuid" 14 "github.com/hashicorp/nomad/nomad/mock" 15 "github.com/hashicorp/nomad/nomad/structs" 16 "github.com/hashicorp/nomad/scheduler" 17 "github.com/hashicorp/nomad/testutil" 18 "github.com/stretchr/testify/assert" 19 ) 20 21 func TestEvalEndpoint_GetEval(t *testing.T) { 22 t.Parallel() 23 s1 := TestServer(t, nil) 24 defer s1.Shutdown() 25 codec := rpcClient(t, s1) 26 testutil.WaitForLeader(t, s1.RPC) 27 28 // Create the register request 29 eval1 := mock.Eval() 30 s1.fsm.State().UpsertEvals(1000, []*structs.Evaluation{eval1}) 31 32 // Lookup the eval 33 get := &structs.EvalSpecificRequest{ 34 EvalID: eval1.ID, 35 QueryOptions: structs.QueryOptions{Region: "global"}, 36 } 37 var resp structs.SingleEvalResponse 38 if err := msgpackrpc.CallWithCodec(codec, "Eval.GetEval", get, &resp); err != nil { 39 t.Fatalf("err: %v", err) 40 } 41 if resp.Index != 1000 { 42 t.Fatalf("Bad index: %d %d", resp.Index, 1000) 43 } 44 45 if !reflect.DeepEqual(eval1, resp.Eval) { 46 t.Fatalf("bad: %#v %#v", eval1, resp.Eval) 47 } 48 49 // Lookup non-existing node 50 get.EvalID = uuid.Generate() 51 if err := msgpackrpc.CallWithCodec(codec, "Eval.GetEval", get, &resp); err != nil { 52 t.Fatalf("err: %v", err) 53 } 54 if resp.Index != 1000 { 55 t.Fatalf("Bad index: %d %d", resp.Index, 1000) 56 } 57 if resp.Eval != nil { 58 t.Fatalf("unexpected eval") 59 } 60 } 61 62 func TestEvalEndpoint_GetEval_ACL(t *testing.T) { 63 t.Parallel() 64 s1, root := TestACLServer(t, nil) 65 defer s1.Shutdown() 66 codec := rpcClient(t, s1) 67 testutil.WaitForLeader(t, s1.RPC) 68 assert := assert.New(t) 69 70 // Create the register request 71 eval1 := mock.Eval() 72 state := s1.fsm.State() 73 state.UpsertEvals(1000, []*structs.Evaluation{eval1}) 74 75 // Create ACL tokens 76 validToken := mock.CreatePolicyAndToken(t, state, 1003, "test-valid", 77 mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityReadJob})) 78 invalidToken := mock.CreatePolicyAndToken(t, state, 1001, "test-invalid", 79 mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityListJobs})) 80 81 get := &structs.EvalSpecificRequest{ 82 EvalID: eval1.ID, 83 QueryOptions: structs.QueryOptions{Region: "global"}, 84 } 85 86 // Try with no token and expect permission denied 87 { 88 var resp structs.SingleEvalResponse 89 err := msgpackrpc.CallWithCodec(codec, "Eval.GetEval", get, &resp) 90 assert.NotNil(err) 91 assert.Contains(err.Error(), structs.ErrPermissionDenied.Error()) 92 } 93 94 // Try with an invalid token and expect permission denied 95 { 96 get.AuthToken = invalidToken.SecretID 97 var resp structs.SingleEvalResponse 98 err := msgpackrpc.CallWithCodec(codec, "Eval.GetEval", get, &resp) 99 assert.NotNil(err) 100 assert.Contains(err.Error(), structs.ErrPermissionDenied.Error()) 101 } 102 103 // Lookup the eval using a valid token 104 { 105 get.AuthToken = validToken.SecretID 106 var resp structs.SingleEvalResponse 107 assert.Nil(msgpackrpc.CallWithCodec(codec, "Eval.GetEval", get, &resp)) 108 assert.Equal(uint64(1000), resp.Index, "Bad index: %d %d", resp.Index, 1000) 109 assert.Equal(eval1, resp.Eval) 110 } 111 112 // Lookup the eval using a root token 113 { 114 get.AuthToken = root.SecretID 115 var resp structs.SingleEvalResponse 116 assert.Nil(msgpackrpc.CallWithCodec(codec, "Eval.GetEval", get, &resp)) 117 assert.Equal(uint64(1000), resp.Index, "Bad index: %d %d", resp.Index, 1000) 118 assert.Equal(eval1, resp.Eval) 119 } 120 } 121 122 func TestEvalEndpoint_GetEval_Blocking(t *testing.T) { 123 t.Parallel() 124 s1 := TestServer(t, nil) 125 defer s1.Shutdown() 126 state := s1.fsm.State() 127 codec := rpcClient(t, s1) 128 testutil.WaitForLeader(t, s1.RPC) 129 130 // Create the evals 131 eval1 := mock.Eval() 132 eval2 := mock.Eval() 133 134 // First create an unrelated eval 135 time.AfterFunc(100*time.Millisecond, func() { 136 err := state.UpsertEvals(100, []*structs.Evaluation{eval1}) 137 if err != nil { 138 t.Fatalf("err: %v", err) 139 } 140 }) 141 142 // Upsert the eval we are watching later 143 time.AfterFunc(200*time.Millisecond, func() { 144 err := state.UpsertEvals(200, []*structs.Evaluation{eval2}) 145 if err != nil { 146 t.Fatalf("err: %v", err) 147 } 148 }) 149 150 // Lookup the eval 151 req := &structs.EvalSpecificRequest{ 152 EvalID: eval2.ID, 153 QueryOptions: structs.QueryOptions{ 154 Region: "global", 155 MinQueryIndex: 150, 156 }, 157 } 158 var resp structs.SingleEvalResponse 159 start := time.Now() 160 if err := msgpackrpc.CallWithCodec(codec, "Eval.GetEval", req, &resp); err != nil { 161 t.Fatalf("err: %v", err) 162 } 163 164 if elapsed := time.Since(start); elapsed < 200*time.Millisecond { 165 t.Fatalf("should block (returned in %s) %#v", elapsed, resp) 166 } 167 if resp.Index != 200 { 168 t.Fatalf("Bad index: %d %d", resp.Index, 200) 169 } 170 if resp.Eval == nil || resp.Eval.ID != eval2.ID { 171 t.Fatalf("bad: %#v", resp.Eval) 172 } 173 174 // Eval delete triggers watches 175 time.AfterFunc(100*time.Millisecond, func() { 176 err := state.DeleteEval(300, []string{eval2.ID}, []string{}) 177 if err != nil { 178 t.Fatalf("err: %v", err) 179 } 180 }) 181 182 req.QueryOptions.MinQueryIndex = 250 183 var resp2 structs.SingleEvalResponse 184 start = time.Now() 185 if err := msgpackrpc.CallWithCodec(codec, "Eval.GetEval", req, &resp2); err != nil { 186 t.Fatalf("err: %v", err) 187 } 188 189 if elapsed := time.Since(start); elapsed < 100*time.Millisecond { 190 t.Fatalf("should block (returned in %s) %#v", elapsed, resp2) 191 } 192 if resp2.Index != 300 { 193 t.Fatalf("Bad index: %d %d", resp2.Index, 300) 194 } 195 if resp2.Eval != nil { 196 t.Fatalf("bad: %#v", resp2.Eval) 197 } 198 } 199 200 func TestEvalEndpoint_Dequeue(t *testing.T) { 201 t.Parallel() 202 s1 := TestServer(t, func(c *Config) { 203 c.NumSchedulers = 0 // Prevent automatic dequeue 204 }) 205 defer s1.Shutdown() 206 codec := rpcClient(t, s1) 207 testutil.WaitForLeader(t, s1.RPC) 208 209 // Create the register request 210 eval1 := mock.Eval() 211 s1.evalBroker.Enqueue(eval1) 212 213 // Dequeue the eval 214 get := &structs.EvalDequeueRequest{ 215 Schedulers: defaultSched, 216 SchedulerVersion: scheduler.SchedulerVersion, 217 WriteRequest: structs.WriteRequest{Region: "global"}, 218 } 219 var resp structs.EvalDequeueResponse 220 if err := msgpackrpc.CallWithCodec(codec, "Eval.Dequeue", get, &resp); err != nil { 221 t.Fatalf("err: %v", err) 222 } 223 224 if !reflect.DeepEqual(eval1, resp.Eval) { 225 t.Fatalf("bad: %v %v", eval1, resp.Eval) 226 } 227 228 // Ensure outstanding 229 token, ok := s1.evalBroker.Outstanding(eval1.ID) 230 if !ok { 231 t.Fatalf("should be outstanding") 232 } 233 if token != resp.Token { 234 t.Fatalf("bad token: %#v %#v", token, resp.Token) 235 } 236 237 if resp.WaitIndex != eval1.ModifyIndex { 238 t.Fatalf("bad wait index; got %d; want %d", resp.WaitIndex, eval1.ModifyIndex) 239 } 240 } 241 242 func TestEvalEndpoint_Dequeue_WaitIndex(t *testing.T) { 243 t.Parallel() 244 s1 := TestServer(t, func(c *Config) { 245 c.NumSchedulers = 0 // Prevent automatic dequeue 246 }) 247 defer s1.Shutdown() 248 codec := rpcClient(t, s1) 249 testutil.WaitForLeader(t, s1.RPC) 250 251 // Create the register request 252 eval1 := mock.Eval() 253 eval2 := mock.Eval() 254 eval2.JobID = eval1.JobID 255 s1.fsm.State().UpsertEvals(1000, []*structs.Evaluation{eval1}) 256 s1.evalBroker.Enqueue(eval1) 257 s1.fsm.State().UpsertEvals(1001, []*structs.Evaluation{eval2}) 258 259 // Dequeue the eval 260 get := &structs.EvalDequeueRequest{ 261 Schedulers: defaultSched, 262 SchedulerVersion: scheduler.SchedulerVersion, 263 WriteRequest: structs.WriteRequest{Region: "global"}, 264 } 265 var resp structs.EvalDequeueResponse 266 if err := msgpackrpc.CallWithCodec(codec, "Eval.Dequeue", get, &resp); err != nil { 267 t.Fatalf("err: %v", err) 268 } 269 270 if !reflect.DeepEqual(eval1, resp.Eval) { 271 t.Fatalf("bad: %v %v", eval1, resp.Eval) 272 } 273 274 // Ensure outstanding 275 token, ok := s1.evalBroker.Outstanding(eval1.ID) 276 if !ok { 277 t.Fatalf("should be outstanding") 278 } 279 if token != resp.Token { 280 t.Fatalf("bad token: %#v %#v", token, resp.Token) 281 } 282 283 if resp.WaitIndex != 1001 { 284 t.Fatalf("bad wait index; got %d; want %d", resp.WaitIndex, 1001) 285 } 286 } 287 288 func TestEvalEndpoint_Dequeue_UpdateWaitIndex(t *testing.T) { 289 // test enqueuing an eval, updating a plan result for the same eval and de-queueing the eval 290 t.Parallel() 291 s1 := TestServer(t, func(c *Config) { 292 c.NumSchedulers = 0 // Prevent automatic dequeue 293 }) 294 defer s1.Shutdown() 295 codec := rpcClient(t, s1) 296 testutil.WaitForLeader(t, s1.RPC) 297 298 alloc := mock.Alloc() 299 job := alloc.Job 300 alloc.Job = nil 301 302 state := s1.fsm.State() 303 304 if err := state.UpsertJob(999, job); err != nil { 305 t.Fatalf("err: %v", err) 306 } 307 308 eval := mock.Eval() 309 eval.JobID = job.ID 310 311 // Create an eval 312 if err := state.UpsertEvals(1, []*structs.Evaluation{eval}); err != nil { 313 t.Fatalf("err: %v", err) 314 } 315 316 s1.evalBroker.Enqueue(eval) 317 318 // Create a plan result and apply it with a later index 319 res := structs.ApplyPlanResultsRequest{ 320 AllocUpdateRequest: structs.AllocUpdateRequest{ 321 Alloc: []*structs.Allocation{alloc}, 322 Job: job, 323 }, 324 EvalID: eval.ID, 325 } 326 assert := assert.New(t) 327 err := state.UpsertPlanResults(1000, &res) 328 assert.Nil(err) 329 330 // Dequeue the eval 331 get := &structs.EvalDequeueRequest{ 332 Schedulers: defaultSched, 333 SchedulerVersion: scheduler.SchedulerVersion, 334 WriteRequest: structs.WriteRequest{Region: "global"}, 335 } 336 var resp structs.EvalDequeueResponse 337 if err := msgpackrpc.CallWithCodec(codec, "Eval.Dequeue", get, &resp); err != nil { 338 t.Fatalf("err: %v", err) 339 } 340 341 // Ensure outstanding 342 token, ok := s1.evalBroker.Outstanding(eval.ID) 343 if !ok { 344 t.Fatalf("should be outstanding") 345 } 346 if token != resp.Token { 347 t.Fatalf("bad token: %#v %#v", token, resp.Token) 348 } 349 350 if resp.WaitIndex != 1000 { 351 t.Fatalf("bad wait index; got %d; want %d", resp.WaitIndex, 1000) 352 } 353 } 354 355 func TestEvalEndpoint_Dequeue_Version_Mismatch(t *testing.T) { 356 t.Parallel() 357 s1 := TestServer(t, func(c *Config) { 358 c.NumSchedulers = 0 // Prevent automatic dequeue 359 }) 360 defer s1.Shutdown() 361 codec := rpcClient(t, s1) 362 testutil.WaitForLeader(t, s1.RPC) 363 364 // Create the register request 365 eval1 := mock.Eval() 366 s1.evalBroker.Enqueue(eval1) 367 368 // Dequeue the eval 369 get := &structs.EvalDequeueRequest{ 370 Schedulers: defaultSched, 371 SchedulerVersion: 0, 372 WriteRequest: structs.WriteRequest{Region: "global"}, 373 } 374 var resp structs.EvalDequeueResponse 375 err := msgpackrpc.CallWithCodec(codec, "Eval.Dequeue", get, &resp) 376 if err == nil || !strings.Contains(err.Error(), "scheduler version is 0") { 377 t.Fatalf("err: %v", err) 378 } 379 } 380 381 func TestEvalEndpoint_Ack(t *testing.T) { 382 t.Parallel() 383 s1 := TestServer(t, nil) 384 defer s1.Shutdown() 385 codec := rpcClient(t, s1) 386 387 testutil.WaitForResult(func() (bool, error) { 388 return s1.evalBroker.Enabled(), nil 389 }, func(err error) { 390 t.Fatalf("should enable eval broker") 391 }) 392 393 // Create the register request 394 eval1 := mock.Eval() 395 s1.evalBroker.Enqueue(eval1) 396 out, token, err := s1.evalBroker.Dequeue(defaultSched, time.Second) 397 if err != nil { 398 t.Fatalf("err: %v", err) 399 } 400 if out == nil { 401 t.Fatalf("missing eval") 402 } 403 404 // Ack the eval 405 get := &structs.EvalAckRequest{ 406 EvalID: out.ID, 407 Token: token, 408 WriteRequest: structs.WriteRequest{Region: "global"}, 409 } 410 var resp structs.GenericResponse 411 if err := msgpackrpc.CallWithCodec(codec, "Eval.Ack", get, &resp); err != nil { 412 t.Fatalf("err: %v", err) 413 } 414 415 // Ensure outstanding 416 if _, ok := s1.evalBroker.Outstanding(eval1.ID); ok { 417 t.Fatalf("should not be outstanding") 418 } 419 } 420 421 func TestEvalEndpoint_Nack(t *testing.T) { 422 t.Parallel() 423 s1 := TestServer(t, func(c *Config) { 424 // Disable all of the schedulers so we can manually dequeue 425 // evals and check the queue status 426 c.NumSchedulers = 0 427 }) 428 defer s1.Shutdown() 429 codec := rpcClient(t, s1) 430 431 testutil.WaitForResult(func() (bool, error) { 432 return s1.evalBroker.Enabled(), nil 433 }, func(err error) { 434 t.Fatalf("should enable eval broker") 435 }) 436 437 // Create the register request 438 eval1 := mock.Eval() 439 s1.evalBroker.Enqueue(eval1) 440 out, token, _ := s1.evalBroker.Dequeue(defaultSched, time.Second) 441 if out == nil { 442 t.Fatalf("missing eval") 443 } 444 445 // Nack the eval 446 get := &structs.EvalAckRequest{ 447 EvalID: out.ID, 448 Token: token, 449 WriteRequest: structs.WriteRequest{Region: "global"}, 450 } 451 var resp structs.GenericResponse 452 if err := msgpackrpc.CallWithCodec(codec, "Eval.Nack", get, &resp); err != nil { 453 t.Fatalf("err: %v", err) 454 } 455 456 // Ensure outstanding 457 if _, ok := s1.evalBroker.Outstanding(eval1.ID); ok { 458 t.Fatalf("should not be outstanding") 459 } 460 461 // Should get it back 462 testutil.WaitForResult(func() (bool, error) { 463 out2, _, _ := s1.evalBroker.Dequeue(defaultSched, time.Second) 464 if out2 != out { 465 return false, fmt.Errorf("nack failed") 466 } 467 468 return true, nil 469 }, func(err error) { 470 t.Fatal(err) 471 }) 472 } 473 474 func TestEvalEndpoint_Update(t *testing.T) { 475 t.Parallel() 476 s1 := TestServer(t, nil) 477 defer s1.Shutdown() 478 codec := rpcClient(t, s1) 479 480 testutil.WaitForResult(func() (bool, error) { 481 return s1.evalBroker.Enabled(), nil 482 }, func(err error) { 483 t.Fatalf("should enable eval broker") 484 }) 485 486 // Create the register request 487 eval1 := mock.Eval() 488 s1.evalBroker.Enqueue(eval1) 489 out, token, err := s1.evalBroker.Dequeue(defaultSched, time.Second) 490 if err != nil { 491 t.Fatalf("err: %v", err) 492 } 493 if out == nil { 494 t.Fatalf("missing eval") 495 } 496 497 // Update the eval 498 eval2 := eval1.Copy() 499 eval2.Status = structs.EvalStatusComplete 500 501 get := &structs.EvalUpdateRequest{ 502 Evals: []*structs.Evaluation{eval2}, 503 EvalToken: token, 504 WriteRequest: structs.WriteRequest{Region: "global"}, 505 } 506 var resp structs.GenericResponse 507 if err := msgpackrpc.CallWithCodec(codec, "Eval.Update", get, &resp); err != nil { 508 t.Fatalf("err: %v", err) 509 } 510 511 // Ensure updated 512 ws := memdb.NewWatchSet() 513 outE, err := s1.fsm.State().EvalByID(ws, eval2.ID) 514 if err != nil { 515 t.Fatalf("err: %v", err) 516 } 517 if outE.Status != structs.EvalStatusComplete { 518 t.Fatalf("Bad: %#v", out) 519 } 520 } 521 522 func TestEvalEndpoint_Create(t *testing.T) { 523 t.Parallel() 524 s1 := TestServer(t, func(c *Config) { 525 c.NumSchedulers = 0 // Prevent automatic dequeue 526 }) 527 defer s1.Shutdown() 528 codec := rpcClient(t, s1) 529 530 testutil.WaitForResult(func() (bool, error) { 531 return s1.evalBroker.Enabled(), nil 532 }, func(err error) { 533 t.Fatalf("should enable eval broker") 534 }) 535 536 // Create the register request 537 prev := mock.Eval() 538 s1.evalBroker.Enqueue(prev) 539 out, token, err := s1.evalBroker.Dequeue(defaultSched, time.Second) 540 if err != nil { 541 t.Fatalf("err: %v", err) 542 } 543 if out == nil { 544 t.Fatalf("missing eval") 545 } 546 547 // Create the register request 548 eval1 := mock.Eval() 549 eval1.PreviousEval = prev.ID 550 get := &structs.EvalUpdateRequest{ 551 Evals: []*structs.Evaluation{eval1}, 552 EvalToken: token, 553 WriteRequest: structs.WriteRequest{Region: "global"}, 554 } 555 var resp structs.GenericResponse 556 if err := msgpackrpc.CallWithCodec(codec, "Eval.Create", get, &resp); err != nil { 557 t.Fatalf("err: %v", err) 558 } 559 560 // Ensure created 561 ws := memdb.NewWatchSet() 562 outE, err := s1.fsm.State().EvalByID(ws, eval1.ID) 563 if err != nil { 564 t.Fatalf("err: %v", err) 565 } 566 567 eval1.CreateIndex = resp.Index 568 eval1.ModifyIndex = resp.Index 569 if !reflect.DeepEqual(eval1, outE) { 570 t.Fatalf("Bad: %#v %#v", outE, eval1) 571 } 572 } 573 574 func TestEvalEndpoint_Reap(t *testing.T) { 575 t.Parallel() 576 s1 := TestServer(t, nil) 577 defer s1.Shutdown() 578 codec := rpcClient(t, s1) 579 testutil.WaitForLeader(t, s1.RPC) 580 581 // Create the register request 582 eval1 := mock.Eval() 583 s1.fsm.State().UpsertEvals(1000, []*structs.Evaluation{eval1}) 584 585 // Reap the eval 586 get := &structs.EvalDeleteRequest{ 587 Evals: []string{eval1.ID}, 588 WriteRequest: structs.WriteRequest{Region: "global"}, 589 } 590 var resp structs.GenericResponse 591 if err := msgpackrpc.CallWithCodec(codec, "Eval.Reap", get, &resp); err != nil { 592 t.Fatalf("err: %v", err) 593 } 594 if resp.Index == 0 { 595 t.Fatalf("Bad index: %d", resp.Index) 596 } 597 598 // Ensure deleted 599 ws := memdb.NewWatchSet() 600 outE, err := s1.fsm.State().EvalByID(ws, eval1.ID) 601 if err != nil { 602 t.Fatalf("err: %v", err) 603 } 604 if outE != nil { 605 t.Fatalf("Bad: %#v", outE) 606 } 607 } 608 609 func TestEvalEndpoint_List(t *testing.T) { 610 t.Parallel() 611 s1 := TestServer(t, nil) 612 defer s1.Shutdown() 613 codec := rpcClient(t, s1) 614 testutil.WaitForLeader(t, s1.RPC) 615 616 // Create the register request 617 eval1 := mock.Eval() 618 eval1.ID = "aaaaaaaa-3350-4b4b-d185-0e1992ed43e9" 619 eval2 := mock.Eval() 620 eval2.ID = "aaaabbbb-3350-4b4b-d185-0e1992ed43e9" 621 s1.fsm.State().UpsertEvals(1000, []*structs.Evaluation{eval1, eval2}) 622 623 // Lookup the eval 624 get := &structs.EvalListRequest{ 625 QueryOptions: structs.QueryOptions{ 626 Region: "global", 627 Namespace: structs.DefaultNamespace, 628 }, 629 } 630 var resp structs.EvalListResponse 631 if err := msgpackrpc.CallWithCodec(codec, "Eval.List", get, &resp); err != nil { 632 t.Fatalf("err: %v", err) 633 } 634 if resp.Index != 1000 { 635 t.Fatalf("Bad index: %d %d", resp.Index, 1000) 636 } 637 638 if len(resp.Evaluations) != 2 { 639 t.Fatalf("bad: %#v", resp.Evaluations) 640 } 641 642 // Lookup the eval by prefix 643 get = &structs.EvalListRequest{ 644 QueryOptions: structs.QueryOptions{ 645 Region: "global", 646 Namespace: structs.DefaultNamespace, 647 Prefix: "aaaabb", 648 }, 649 } 650 var resp2 structs.EvalListResponse 651 if err := msgpackrpc.CallWithCodec(codec, "Eval.List", get, &resp2); err != nil { 652 t.Fatalf("err: %v", err) 653 } 654 if resp2.Index != 1000 { 655 t.Fatalf("Bad index: %d %d", resp2.Index, 1000) 656 } 657 658 if len(resp2.Evaluations) != 1 { 659 t.Fatalf("bad: %#v", resp2.Evaluations) 660 } 661 662 } 663 664 func TestEvalEndpoint_List_ACL(t *testing.T) { 665 t.Parallel() 666 s1, root := TestACLServer(t, nil) 667 defer s1.Shutdown() 668 codec := rpcClient(t, s1) 669 testutil.WaitForLeader(t, s1.RPC) 670 assert := assert.New(t) 671 672 // Create the register request 673 eval1 := mock.Eval() 674 eval1.ID = "aaaaaaaa-3350-4b4b-d185-0e1992ed43e9" 675 eval2 := mock.Eval() 676 eval2.ID = "aaaabbbb-3350-4b4b-d185-0e1992ed43e9" 677 state := s1.fsm.State() 678 assert.Nil(state.UpsertEvals(1000, []*structs.Evaluation{eval1, eval2})) 679 680 // Create ACL tokens 681 validToken := mock.CreatePolicyAndToken(t, state, 1003, "test-valid", 682 mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityReadJob})) 683 invalidToken := mock.CreatePolicyAndToken(t, state, 1001, "test-invalid", 684 mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityListJobs})) 685 686 get := &structs.EvalListRequest{ 687 QueryOptions: structs.QueryOptions{ 688 Region: "global", 689 Namespace: structs.DefaultNamespace, 690 }, 691 } 692 693 // Try without a token and expect permission denied 694 { 695 var resp structs.EvalListResponse 696 err := msgpackrpc.CallWithCodec(codec, "Eval.List", get, &resp) 697 assert.NotNil(err) 698 assert.Contains(err.Error(), structs.ErrPermissionDenied.Error()) 699 } 700 701 // Try with an invalid token and expect permission denied 702 { 703 get.AuthToken = invalidToken.SecretID 704 var resp structs.EvalListResponse 705 err := msgpackrpc.CallWithCodec(codec, "Eval.List", get, &resp) 706 assert.NotNil(err) 707 assert.Contains(err.Error(), structs.ErrPermissionDenied.Error()) 708 } 709 710 // List evals with a valid token 711 { 712 get.AuthToken = validToken.SecretID 713 var resp structs.EvalListResponse 714 assert.Nil(msgpackrpc.CallWithCodec(codec, "Eval.List", get, &resp)) 715 assert.Equal(uint64(1000), resp.Index, "Bad index: %d %d", resp.Index, 1000) 716 assert.Lenf(resp.Evaluations, 2, "bad: %#v", resp.Evaluations) 717 } 718 719 // List evals with a root token 720 { 721 get.AuthToken = root.SecretID 722 var resp structs.EvalListResponse 723 assert.Nil(msgpackrpc.CallWithCodec(codec, "Eval.List", get, &resp)) 724 assert.Equal(uint64(1000), resp.Index, "Bad index: %d %d", resp.Index, 1000) 725 assert.Lenf(resp.Evaluations, 2, "bad: %#v", resp.Evaluations) 726 } 727 } 728 729 func TestEvalEndpoint_List_Blocking(t *testing.T) { 730 t.Parallel() 731 s1 := TestServer(t, nil) 732 defer s1.Shutdown() 733 state := s1.fsm.State() 734 codec := rpcClient(t, s1) 735 testutil.WaitForLeader(t, s1.RPC) 736 737 // Create the ieval 738 eval := mock.Eval() 739 740 // Upsert eval triggers watches 741 time.AfterFunc(100*time.Millisecond, func() { 742 if err := state.UpsertEvals(2, []*structs.Evaluation{eval}); err != nil { 743 t.Fatalf("err: %v", err) 744 } 745 }) 746 747 req := &structs.EvalListRequest{ 748 QueryOptions: structs.QueryOptions{ 749 Region: "global", 750 Namespace: structs.DefaultNamespace, 751 MinQueryIndex: 1, 752 }, 753 } 754 start := time.Now() 755 var resp structs.EvalListResponse 756 if err := msgpackrpc.CallWithCodec(codec, "Eval.List", req, &resp); err != nil { 757 t.Fatalf("err: %v", err) 758 } 759 760 if elapsed := time.Since(start); elapsed < 100*time.Millisecond { 761 t.Fatalf("should block (returned in %s) %#v", elapsed, resp) 762 } 763 if resp.Index != 2 { 764 t.Fatalf("Bad index: %d %d", resp.Index, 2) 765 } 766 if len(resp.Evaluations) != 1 || resp.Evaluations[0].ID != eval.ID { 767 t.Fatalf("bad: %#v", resp.Evaluations) 768 } 769 770 // Eval deletion triggers watches 771 time.AfterFunc(100*time.Millisecond, func() { 772 if err := state.DeleteEval(3, []string{eval.ID}, nil); err != nil { 773 t.Fatalf("err: %v", err) 774 } 775 }) 776 777 req.MinQueryIndex = 2 778 start = time.Now() 779 var resp2 structs.EvalListResponse 780 if err := msgpackrpc.CallWithCodec(codec, "Eval.List", req, &resp2); err != nil { 781 t.Fatalf("err: %v", err) 782 } 783 784 if elapsed := time.Since(start); elapsed < 100*time.Millisecond { 785 t.Fatalf("should block (returned in %s) %#v", elapsed, resp2) 786 } 787 if resp2.Index != 3 { 788 t.Fatalf("Bad index: %d %d", resp2.Index, 3) 789 } 790 if len(resp2.Evaluations) != 0 { 791 t.Fatalf("bad: %#v", resp2.Evaluations) 792 } 793 } 794 795 func TestEvalEndpoint_Allocations(t *testing.T) { 796 t.Parallel() 797 s1 := TestServer(t, nil) 798 defer s1.Shutdown() 799 codec := rpcClient(t, s1) 800 testutil.WaitForLeader(t, s1.RPC) 801 802 // Create the register request 803 alloc1 := mock.Alloc() 804 alloc2 := mock.Alloc() 805 alloc2.EvalID = alloc1.EvalID 806 state := s1.fsm.State() 807 state.UpsertJobSummary(998, mock.JobSummary(alloc1.JobID)) 808 state.UpsertJobSummary(999, mock.JobSummary(alloc2.JobID)) 809 err := state.UpsertAllocs(1000, 810 []*structs.Allocation{alloc1, alloc2}) 811 if err != nil { 812 t.Fatalf("err: %v", err) 813 } 814 815 // Lookup the eval 816 get := &structs.EvalSpecificRequest{ 817 EvalID: alloc1.EvalID, 818 QueryOptions: structs.QueryOptions{Region: "global"}, 819 } 820 var resp structs.EvalAllocationsResponse 821 if err := msgpackrpc.CallWithCodec(codec, "Eval.Allocations", get, &resp); err != nil { 822 t.Fatalf("err: %v", err) 823 } 824 if resp.Index != 1000 { 825 t.Fatalf("Bad index: %d %d", resp.Index, 1000) 826 } 827 828 if len(resp.Allocations) != 2 { 829 t.Fatalf("bad: %#v", resp.Allocations) 830 } 831 } 832 833 func TestEvalEndpoint_Allocations_ACL(t *testing.T) { 834 t.Parallel() 835 s1, root := TestACLServer(t, nil) 836 defer s1.Shutdown() 837 codec := rpcClient(t, s1) 838 testutil.WaitForLeader(t, s1.RPC) 839 assert := assert.New(t) 840 841 // Create the register request 842 alloc1 := mock.Alloc() 843 alloc2 := mock.Alloc() 844 alloc2.EvalID = alloc1.EvalID 845 state := s1.fsm.State() 846 assert.Nil(state.UpsertJobSummary(998, mock.JobSummary(alloc1.JobID))) 847 assert.Nil(state.UpsertJobSummary(999, mock.JobSummary(alloc2.JobID))) 848 assert.Nil(state.UpsertAllocs(1000, []*structs.Allocation{alloc1, alloc2})) 849 850 // Create ACL tokens 851 validToken := mock.CreatePolicyAndToken(t, state, 1003, "test-valid", 852 mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityReadJob})) 853 invalidToken := mock.CreatePolicyAndToken(t, state, 1001, "test-invalid", 854 mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityListJobs})) 855 856 get := &structs.EvalSpecificRequest{ 857 EvalID: alloc1.EvalID, 858 QueryOptions: structs.QueryOptions{Region: "global"}, 859 } 860 861 // Try with no token and expect permission denied 862 { 863 var resp structs.EvalAllocationsResponse 864 err := msgpackrpc.CallWithCodec(codec, "Eval.Allocations", get, &resp) 865 assert.NotNil(err) 866 assert.Contains(err.Error(), structs.ErrPermissionDenied.Error()) 867 } 868 869 // Try with an invalid token and expect permission denied 870 { 871 get.AuthToken = invalidToken.SecretID 872 var resp structs.EvalAllocationsResponse 873 err := msgpackrpc.CallWithCodec(codec, "Eval.Allocations", get, &resp) 874 assert.NotNil(err) 875 assert.Contains(err.Error(), structs.ErrPermissionDenied.Error()) 876 } 877 878 // Lookup the eval with a valid token 879 { 880 get.AuthToken = validToken.SecretID 881 var resp structs.EvalAllocationsResponse 882 assert.Nil(msgpackrpc.CallWithCodec(codec, "Eval.Allocations", get, &resp)) 883 assert.Equal(uint64(1000), resp.Index, "Bad index: %d %d", resp.Index, 1000) 884 assert.Lenf(resp.Allocations, 2, "bad: %#v", resp.Allocations) 885 } 886 887 // Lookup the eval with a root token 888 { 889 get.AuthToken = root.SecretID 890 var resp structs.EvalAllocationsResponse 891 assert.Nil(msgpackrpc.CallWithCodec(codec, "Eval.Allocations", get, &resp)) 892 assert.Equal(uint64(1000), resp.Index, "Bad index: %d %d", resp.Index, 1000) 893 assert.Lenf(resp.Allocations, 2, "bad: %#v", resp.Allocations) 894 } 895 } 896 897 func TestEvalEndpoint_Allocations_Blocking(t *testing.T) { 898 t.Parallel() 899 s1 := TestServer(t, nil) 900 defer s1.Shutdown() 901 state := s1.fsm.State() 902 codec := rpcClient(t, s1) 903 testutil.WaitForLeader(t, s1.RPC) 904 905 // Create the allocs 906 alloc1 := mock.Alloc() 907 alloc2 := mock.Alloc() 908 909 // Upsert an unrelated alloc first 910 time.AfterFunc(100*time.Millisecond, func() { 911 state.UpsertJobSummary(99, mock.JobSummary(alloc1.JobID)) 912 err := state.UpsertAllocs(100, []*structs.Allocation{alloc1}) 913 if err != nil { 914 t.Fatalf("err: %v", err) 915 } 916 }) 917 918 // Upsert an alloc which will trigger the watch later 919 time.AfterFunc(200*time.Millisecond, func() { 920 state.UpsertJobSummary(199, mock.JobSummary(alloc2.JobID)) 921 err := state.UpsertAllocs(200, []*structs.Allocation{alloc2}) 922 if err != nil { 923 t.Fatalf("err: %v", err) 924 } 925 }) 926 927 // Lookup the eval 928 get := &structs.EvalSpecificRequest{ 929 EvalID: alloc2.EvalID, 930 QueryOptions: structs.QueryOptions{ 931 Region: "global", 932 MinQueryIndex: 150, 933 }, 934 } 935 var resp structs.EvalAllocationsResponse 936 start := time.Now() 937 if err := msgpackrpc.CallWithCodec(codec, "Eval.Allocations", get, &resp); err != nil { 938 t.Fatalf("err: %v", err) 939 } 940 941 if elapsed := time.Since(start); elapsed < 200*time.Millisecond { 942 t.Fatalf("should block (returned in %s) %#v", elapsed, resp) 943 } 944 if resp.Index != 200 { 945 t.Fatalf("Bad index: %d %d", resp.Index, 200) 946 } 947 if len(resp.Allocations) != 1 || resp.Allocations[0].ID != alloc2.ID { 948 t.Fatalf("bad: %#v", resp.Allocations) 949 } 950 } 951 952 func TestEvalEndpoint_Reblock_Nonexistent(t *testing.T) { 953 t.Parallel() 954 s1 := TestServer(t, func(c *Config) { 955 c.NumSchedulers = 0 // Prevent automatic dequeue 956 }) 957 defer s1.Shutdown() 958 codec := rpcClient(t, s1) 959 960 testutil.WaitForResult(func() (bool, error) { 961 return s1.evalBroker.Enabled(), nil 962 }, func(err error) { 963 t.Fatalf("should enable eval broker") 964 }) 965 966 // Create the register request 967 eval1 := mock.Eval() 968 s1.evalBroker.Enqueue(eval1) 969 out, token, err := s1.evalBroker.Dequeue(defaultSched, time.Second) 970 if err != nil { 971 t.Fatalf("err: %v", err) 972 } 973 if out == nil { 974 t.Fatalf("missing eval") 975 } 976 977 get := &structs.EvalUpdateRequest{ 978 Evals: []*structs.Evaluation{eval1}, 979 EvalToken: token, 980 WriteRequest: structs.WriteRequest{Region: "global"}, 981 } 982 var resp structs.GenericResponse 983 if err := msgpackrpc.CallWithCodec(codec, "Eval.Reblock", get, &resp); err == nil { 984 t.Fatalf("expect error since eval does not exist") 985 } 986 } 987 988 func TestEvalEndpoint_Reblock_NonBlocked(t *testing.T) { 989 t.Parallel() 990 s1 := TestServer(t, func(c *Config) { 991 c.NumSchedulers = 0 // Prevent automatic dequeue 992 }) 993 defer s1.Shutdown() 994 codec := rpcClient(t, s1) 995 996 testutil.WaitForResult(func() (bool, error) { 997 return s1.evalBroker.Enabled(), nil 998 }, func(err error) { 999 t.Fatalf("should enable eval broker") 1000 }) 1001 1002 // Create the eval 1003 eval1 := mock.Eval() 1004 s1.evalBroker.Enqueue(eval1) 1005 1006 // Insert it into the state store 1007 if err := s1.fsm.State().UpsertEvals(1000, []*structs.Evaluation{eval1}); err != nil { 1008 t.Fatal(err) 1009 } 1010 1011 out, token, err := s1.evalBroker.Dequeue(defaultSched, 2*time.Second) 1012 if err != nil { 1013 t.Fatalf("err: %v", err) 1014 } 1015 if out == nil { 1016 t.Fatalf("missing eval") 1017 } 1018 1019 get := &structs.EvalUpdateRequest{ 1020 Evals: []*structs.Evaluation{eval1}, 1021 EvalToken: token, 1022 WriteRequest: structs.WriteRequest{Region: "global"}, 1023 } 1024 var resp structs.GenericResponse 1025 if err := msgpackrpc.CallWithCodec(codec, "Eval.Reblock", get, &resp); err == nil { 1026 t.Fatalf("should error since eval was not in blocked state: %v", err) 1027 } 1028 } 1029 1030 func TestEvalEndpoint_Reblock(t *testing.T) { 1031 t.Parallel() 1032 s1 := TestServer(t, func(c *Config) { 1033 c.NumSchedulers = 0 // Prevent automatic dequeue 1034 }) 1035 defer s1.Shutdown() 1036 codec := rpcClient(t, s1) 1037 1038 testutil.WaitForResult(func() (bool, error) { 1039 return s1.evalBroker.Enabled(), nil 1040 }, func(err error) { 1041 t.Fatalf("should enable eval broker") 1042 }) 1043 1044 // Create the eval 1045 eval1 := mock.Eval() 1046 eval1.Status = structs.EvalStatusBlocked 1047 s1.evalBroker.Enqueue(eval1) 1048 1049 // Insert it into the state store 1050 if err := s1.fsm.State().UpsertEvals(1000, []*structs.Evaluation{eval1}); err != nil { 1051 t.Fatal(err) 1052 } 1053 1054 out, token, err := s1.evalBroker.Dequeue(defaultSched, 7*time.Second) 1055 if err != nil { 1056 t.Fatalf("err: %v", err) 1057 } 1058 if out == nil { 1059 t.Fatalf("bad: %v", out) 1060 } 1061 1062 get := &structs.EvalUpdateRequest{ 1063 Evals: []*structs.Evaluation{eval1}, 1064 EvalToken: token, 1065 WriteRequest: structs.WriteRequest{Region: "global"}, 1066 } 1067 var resp structs.GenericResponse 1068 if err := msgpackrpc.CallWithCodec(codec, "Eval.Reblock", get, &resp); err != nil { 1069 t.Fatalf("err: %v", err) 1070 } 1071 1072 // Check that it is blocked 1073 bStats := s1.blockedEvals.Stats() 1074 if bStats.TotalBlocked+bStats.TotalEscaped == 0 { 1075 t.Fatalf("ReblockEval didn't insert eval into the blocked eval tracker") 1076 } 1077 }