github.com/manicqin/nomad@v0.9.5/nomad/alloc_endpoint_test.go (about) 1 package nomad 2 3 import ( 4 "reflect" 5 "testing" 6 "time" 7 8 msgpackrpc "github.com/hashicorp/net-rpc-msgpackrpc" 9 "github.com/hashicorp/nomad/acl" 10 "github.com/hashicorp/nomad/helper" 11 "github.com/hashicorp/nomad/helper/uuid" 12 "github.com/hashicorp/nomad/nomad/mock" 13 "github.com/hashicorp/nomad/nomad/structs" 14 "github.com/hashicorp/nomad/testutil" 15 "github.com/stretchr/testify/assert" 16 "github.com/stretchr/testify/require" 17 ) 18 19 func TestAllocEndpoint_List(t *testing.T) { 20 t.Parallel() 21 22 s1, cleanupS1 := TestServer(t, nil) 23 defer cleanupS1() 24 25 codec := rpcClient(t, s1) 26 testutil.WaitForLeader(t, s1.RPC) 27 28 // Create the register request 29 alloc := mock.Alloc() 30 summary := mock.JobSummary(alloc.JobID) 31 state := s1.fsm.State() 32 33 if err := state.UpsertJobSummary(999, summary); err != nil { 34 t.Fatalf("err: %v", err) 35 } 36 if err := state.UpsertAllocs(1000, []*structs.Allocation{alloc}); err != nil { 37 t.Fatalf("err: %v", err) 38 } 39 40 // Lookup the allocations 41 get := &structs.AllocListRequest{ 42 QueryOptions: structs.QueryOptions{ 43 Region: "global", 44 Namespace: structs.DefaultNamespace, 45 }, 46 } 47 var resp structs.AllocListResponse 48 if err := msgpackrpc.CallWithCodec(codec, "Alloc.List", get, &resp); err != nil { 49 t.Fatalf("err: %v", err) 50 } 51 if resp.Index != 1000 { 52 t.Fatalf("Bad index: %d %d", resp.Index, 1000) 53 } 54 55 if len(resp.Allocations) != 1 { 56 t.Fatalf("bad: %#v", resp.Allocations) 57 } 58 if resp.Allocations[0].ID != alloc.ID { 59 t.Fatalf("bad: %#v", resp.Allocations[0]) 60 } 61 62 // Lookup the allocations by prefix 63 get = &structs.AllocListRequest{ 64 QueryOptions: structs.QueryOptions{ 65 Region: "global", 66 Namespace: structs.DefaultNamespace, 67 Prefix: alloc.ID[:4], 68 }, 69 } 70 71 var resp2 structs.AllocListResponse 72 if err := msgpackrpc.CallWithCodec(codec, "Alloc.List", get, &resp2); err != nil { 73 t.Fatalf("err: %v", err) 74 } 75 if resp2.Index != 1000 { 76 t.Fatalf("Bad index: %d %d", resp2.Index, 1000) 77 } 78 79 if len(resp2.Allocations) != 1 { 80 t.Fatalf("bad: %#v", resp2.Allocations) 81 } 82 if resp2.Allocations[0].ID != alloc.ID { 83 t.Fatalf("bad: %#v", resp2.Allocations[0]) 84 } 85 } 86 87 func TestAllocEndpoint_List_ACL(t *testing.T) { 88 t.Parallel() 89 90 s1, root, cleanupS1 := TestACLServer(t, nil) 91 defer cleanupS1() 92 codec := rpcClient(t, s1) 93 testutil.WaitForLeader(t, s1.RPC) 94 assert := assert.New(t) 95 96 // Create the alloc 97 alloc := mock.Alloc() 98 allocs := []*structs.Allocation{alloc} 99 summary := mock.JobSummary(alloc.JobID) 100 state := s1.fsm.State() 101 102 assert.Nil(state.UpsertJobSummary(999, summary), "UpsertJobSummary") 103 assert.Nil(state.UpsertAllocs(1000, allocs), "UpsertAllocs") 104 105 stubAllocs := []*structs.AllocListStub{alloc.Stub()} 106 stubAllocs[0].CreateIndex = 1000 107 stubAllocs[0].ModifyIndex = 1000 108 109 // Create the namespace policy and tokens 110 validToken := mock.CreatePolicyAndToken(t, state, 1001, "test-valid", 111 mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityReadJob})) 112 invalidToken := mock.CreatePolicyAndToken(t, state, 1003, "test-invalid", 113 mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityListJobs})) 114 115 // Lookup the allocs without a token and expect failure 116 get := &structs.AllocListRequest{ 117 QueryOptions: structs.QueryOptions{ 118 Region: "global", 119 Namespace: structs.DefaultNamespace, 120 }, 121 } 122 var resp structs.AllocListResponse 123 assert.NotNil(msgpackrpc.CallWithCodec(codec, "Alloc.List", get, &resp), "RPC") 124 125 // Try with a valid token 126 get.AuthToken = validToken.SecretID 127 assert.Nil(msgpackrpc.CallWithCodec(codec, "Alloc.List", get, &resp), "RPC") 128 assert.EqualValues(resp.Index, 1000, "resp.Index") 129 assert.Equal(stubAllocs, resp.Allocations, "Returned alloc list not equal") 130 131 // Try with a invalid token 132 get.AuthToken = invalidToken.SecretID 133 err := msgpackrpc.CallWithCodec(codec, "Alloc.List", get, &resp) 134 assert.NotNil(err, "RPC") 135 assert.Equal(err.Error(), structs.ErrPermissionDenied.Error()) 136 137 // Try with a root token 138 get.AuthToken = root.SecretID 139 assert.Nil(msgpackrpc.CallWithCodec(codec, "Alloc.List", get, &resp), "RPC") 140 assert.EqualValues(resp.Index, 1000, "resp.Index") 141 assert.Equal(stubAllocs, resp.Allocations, "Returned alloc list not equal") 142 } 143 144 func TestAllocEndpoint_List_Blocking(t *testing.T) { 145 t.Parallel() 146 147 s1, cleanupS1 := TestServer(t, nil) 148 defer cleanupS1() 149 state := s1.fsm.State() 150 codec := rpcClient(t, s1) 151 testutil.WaitForLeader(t, s1.RPC) 152 153 // Create the alloc 154 alloc := mock.Alloc() 155 156 summary := mock.JobSummary(alloc.JobID) 157 if err := state.UpsertJobSummary(1, summary); err != nil { 158 t.Fatalf("err: %v", err) 159 } 160 // Upsert alloc triggers watches 161 time.AfterFunc(100*time.Millisecond, func() { 162 if err := state.UpsertAllocs(2, []*structs.Allocation{alloc}); err != nil { 163 t.Fatalf("err: %v", err) 164 } 165 }) 166 167 req := &structs.AllocListRequest{ 168 QueryOptions: structs.QueryOptions{ 169 Region: "global", 170 Namespace: structs.DefaultNamespace, 171 MinQueryIndex: 1, 172 }, 173 } 174 start := time.Now() 175 var resp structs.AllocListResponse 176 if err := msgpackrpc.CallWithCodec(codec, "Alloc.List", req, &resp); err != nil { 177 t.Fatalf("err: %v", err) 178 } 179 180 if elapsed := time.Since(start); elapsed < 100*time.Millisecond { 181 t.Fatalf("should block (returned in %s) %#v", elapsed, resp) 182 } 183 if resp.Index != 2 { 184 t.Fatalf("Bad index: %d %d", resp.Index, 2) 185 } 186 if len(resp.Allocations) != 1 || resp.Allocations[0].ID != alloc.ID { 187 t.Fatalf("bad: %#v", resp.Allocations) 188 } 189 190 // Client updates trigger watches 191 alloc2 := mock.Alloc() 192 alloc2.ID = alloc.ID 193 alloc2.ClientStatus = structs.AllocClientStatusRunning 194 time.AfterFunc(100*time.Millisecond, func() { 195 state.UpsertJobSummary(3, mock.JobSummary(alloc2.JobID)) 196 if err := state.UpdateAllocsFromClient(4, []*structs.Allocation{alloc2}); err != nil { 197 t.Fatalf("err: %v", err) 198 } 199 }) 200 201 req.MinQueryIndex = 3 202 start = time.Now() 203 var resp2 structs.AllocListResponse 204 if err := msgpackrpc.CallWithCodec(codec, "Alloc.List", req, &resp2); err != nil { 205 t.Fatalf("err: %v", err) 206 } 207 208 if elapsed := time.Since(start); elapsed < 100*time.Millisecond { 209 t.Fatalf("should block (returned in %s) %#v", elapsed, resp2) 210 } 211 if resp2.Index != 4 { 212 t.Fatalf("Bad index: %d %d", resp2.Index, 4) 213 } 214 if len(resp2.Allocations) != 1 || resp.Allocations[0].ID != alloc.ID || 215 resp2.Allocations[0].ClientStatus != structs.AllocClientStatusRunning { 216 t.Fatalf("bad: %#v", resp2.Allocations) 217 } 218 } 219 220 func TestAllocEndpoint_GetAlloc(t *testing.T) { 221 t.Parallel() 222 223 s1, cleanupS1 := TestServer(t, nil) 224 defer cleanupS1() 225 codec := rpcClient(t, s1) 226 testutil.WaitForLeader(t, s1.RPC) 227 228 // Create the register request 229 prevAllocID := uuid.Generate() 230 alloc := mock.Alloc() 231 alloc.RescheduleTracker = &structs.RescheduleTracker{ 232 Events: []*structs.RescheduleEvent{ 233 {RescheduleTime: time.Now().UTC().UnixNano(), PrevNodeID: "boom", PrevAllocID: prevAllocID}, 234 }, 235 } 236 state := s1.fsm.State() 237 state.UpsertJobSummary(999, mock.JobSummary(alloc.JobID)) 238 err := state.UpsertAllocs(1000, []*structs.Allocation{alloc}) 239 if err != nil { 240 t.Fatalf("err: %v", err) 241 } 242 243 // Lookup the alloc 244 get := &structs.AllocSpecificRequest{ 245 AllocID: alloc.ID, 246 QueryOptions: structs.QueryOptions{Region: "global"}, 247 } 248 var resp structs.SingleAllocResponse 249 if err := msgpackrpc.CallWithCodec(codec, "Alloc.GetAlloc", get, &resp); err != nil { 250 t.Fatalf("err: %v", err) 251 } 252 if resp.Index != 1000 { 253 t.Fatalf("Bad index: %d %d", resp.Index, 1000) 254 } 255 256 if !reflect.DeepEqual(alloc, resp.Alloc) { 257 t.Fatalf("bad: %#v", resp.Alloc) 258 } 259 } 260 261 func TestAllocEndpoint_GetAlloc_ACL(t *testing.T) { 262 t.Parallel() 263 264 s1, root, cleanupS1 := TestACLServer(t, nil) 265 defer cleanupS1() 266 codec := rpcClient(t, s1) 267 testutil.WaitForLeader(t, s1.RPC) 268 assert := assert.New(t) 269 270 // Create the alloc 271 alloc := mock.Alloc() 272 allocs := []*structs.Allocation{alloc} 273 summary := mock.JobSummary(alloc.JobID) 274 state := s1.fsm.State() 275 276 assert.Nil(state.UpsertJobSummary(999, summary), "UpsertJobSummary") 277 assert.Nil(state.UpsertAllocs(1000, allocs), "UpsertAllocs") 278 279 // Create the namespace policy and tokens 280 validToken := mock.CreatePolicyAndToken(t, state, 1001, "test-valid", 281 mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityReadJob})) 282 invalidToken := mock.CreatePolicyAndToken(t, state, 1003, "test-invalid", 283 mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityListJobs})) 284 285 getReq := func() *structs.AllocSpecificRequest { 286 return &structs.AllocSpecificRequest{ 287 AllocID: alloc.ID, 288 QueryOptions: structs.QueryOptions{ 289 Region: "global", 290 }, 291 } 292 } 293 294 cases := []struct { 295 Name string 296 F func(t *testing.T) 297 }{ 298 // Lookup the alloc without a token and expect failure 299 { 300 Name: "no-token", 301 F: func(t *testing.T) { 302 var resp structs.SingleAllocResponse 303 err := msgpackrpc.CallWithCodec(codec, "Alloc.GetAlloc", getReq(), &resp) 304 require.True(t, structs.IsErrUnknownAllocation(err), "expected unknown alloc but found: %v", err) 305 }, 306 }, 307 308 // Try with a valid ACL token 309 { 310 Name: "valid-token", 311 F: func(t *testing.T) { 312 get := getReq() 313 get.AuthToken = validToken.SecretID 314 get.AllocID = alloc.ID 315 var resp structs.SingleAllocResponse 316 require.NoError(t, msgpackrpc.CallWithCodec(codec, "Alloc.GetAlloc", get, &resp), "RPC") 317 require.EqualValues(t, resp.Index, 1000, "resp.Index") 318 require.Equal(t, alloc, resp.Alloc, "Returned alloc not equal") 319 }, 320 }, 321 322 // Try with a valid Node.SecretID 323 { 324 Name: "valid-node-secret", 325 F: func(t *testing.T) { 326 node := mock.Node() 327 assert.Nil(state.UpsertNode(1005, node)) 328 get := getReq() 329 get.AuthToken = node.SecretID 330 get.AllocID = alloc.ID 331 var resp structs.SingleAllocResponse 332 require.NoError(t, msgpackrpc.CallWithCodec(codec, "Alloc.GetAlloc", get, &resp), "RPC") 333 require.EqualValues(t, resp.Index, 1000, "resp.Index") 334 require.Equal(t, alloc, resp.Alloc, "Returned alloc not equal") 335 }, 336 }, 337 338 // Try with a invalid token 339 { 340 Name: "invalid-token", 341 F: func(t *testing.T) { 342 get := getReq() 343 get.AuthToken = invalidToken.SecretID 344 get.AllocID = alloc.ID 345 var resp structs.SingleAllocResponse 346 err := msgpackrpc.CallWithCodec(codec, "Alloc.GetAlloc", get, &resp) 347 require.NotNil(t, err, "RPC") 348 require.True(t, structs.IsErrUnknownAllocation(err), "expected unknown alloc but found: %v", err) 349 }, 350 }, 351 352 // Try with a root token 353 { 354 Name: "root-token", 355 F: func(t *testing.T) { 356 get := getReq() 357 get.AuthToken = root.SecretID 358 get.AllocID = alloc.ID 359 var resp structs.SingleAllocResponse 360 require.NoError(t, msgpackrpc.CallWithCodec(codec, "Alloc.GetAlloc", get, &resp), "RPC") 361 require.EqualValues(t, resp.Index, 1000, "resp.Index") 362 require.Equal(t, alloc, resp.Alloc, "Returned alloc not equal") 363 }, 364 }, 365 } 366 367 for _, tc := range cases { 368 t.Run(tc.Name, tc.F) 369 } 370 } 371 372 func TestAllocEndpoint_GetAlloc_Blocking(t *testing.T) { 373 t.Parallel() 374 375 s1, cleanupS1 := TestServer(t, nil) 376 defer cleanupS1() 377 state := s1.fsm.State() 378 codec := rpcClient(t, s1) 379 testutil.WaitForLeader(t, s1.RPC) 380 381 // Create the allocs 382 alloc1 := mock.Alloc() 383 alloc2 := mock.Alloc() 384 385 // First create an unrelated alloc 386 time.AfterFunc(100*time.Millisecond, func() { 387 state.UpsertJobSummary(99, mock.JobSummary(alloc1.JobID)) 388 err := state.UpsertAllocs(100, []*structs.Allocation{alloc1}) 389 if err != nil { 390 t.Fatalf("err: %v", err) 391 } 392 }) 393 394 // Create the alloc we are watching later 395 time.AfterFunc(200*time.Millisecond, func() { 396 state.UpsertJobSummary(199, mock.JobSummary(alloc2.JobID)) 397 err := state.UpsertAllocs(200, []*structs.Allocation{alloc2}) 398 if err != nil { 399 t.Fatalf("err: %v", err) 400 } 401 }) 402 403 // Lookup the allocs 404 get := &structs.AllocSpecificRequest{ 405 AllocID: alloc2.ID, 406 QueryOptions: structs.QueryOptions{ 407 Region: "global", 408 MinQueryIndex: 150, 409 }, 410 } 411 var resp structs.SingleAllocResponse 412 start := time.Now() 413 if err := msgpackrpc.CallWithCodec(codec, "Alloc.GetAlloc", get, &resp); err != nil { 414 t.Fatalf("err: %v", err) 415 } 416 417 if elapsed := time.Since(start); elapsed < 200*time.Millisecond { 418 t.Fatalf("should block (returned in %s) %#v", elapsed, resp) 419 } 420 if resp.Index != 200 { 421 t.Fatalf("Bad index: %d %d", resp.Index, 200) 422 } 423 if resp.Alloc == nil || resp.Alloc.ID != alloc2.ID { 424 t.Fatalf("bad: %#v", resp.Alloc) 425 } 426 } 427 428 func TestAllocEndpoint_GetAllocs(t *testing.T) { 429 t.Parallel() 430 431 s1, cleanupS1 := TestServer(t, nil) 432 defer cleanupS1() 433 codec := rpcClient(t, s1) 434 testutil.WaitForLeader(t, s1.RPC) 435 436 // Create the register request 437 alloc := mock.Alloc() 438 alloc2 := mock.Alloc() 439 state := s1.fsm.State() 440 state.UpsertJobSummary(998, mock.JobSummary(alloc.JobID)) 441 state.UpsertJobSummary(999, mock.JobSummary(alloc2.JobID)) 442 err := state.UpsertAllocs(1000, []*structs.Allocation{alloc, alloc2}) 443 if err != nil { 444 t.Fatalf("err: %v", err) 445 } 446 447 // Lookup the allocs 448 get := &structs.AllocsGetRequest{ 449 AllocIDs: []string{alloc.ID, alloc2.ID}, 450 QueryOptions: structs.QueryOptions{ 451 Region: "global", 452 }, 453 } 454 var resp structs.AllocsGetResponse 455 if err := msgpackrpc.CallWithCodec(codec, "Alloc.GetAllocs", get, &resp); err != nil { 456 t.Fatalf("err: %v", err) 457 } 458 if resp.Index != 1000 { 459 t.Fatalf("Bad index: %d %d", resp.Index, 1000) 460 } 461 462 if len(resp.Allocs) != 2 { 463 t.Fatalf("bad: %#v", resp.Allocs) 464 } 465 466 // Lookup nonexistent allocs. 467 get = &structs.AllocsGetRequest{ 468 AllocIDs: []string{"foo"}, 469 QueryOptions: structs.QueryOptions{Region: "global"}, 470 } 471 if err := msgpackrpc.CallWithCodec(codec, "Alloc.GetAllocs", get, &resp); err == nil { 472 t.Fatalf("expect error") 473 } 474 } 475 476 func TestAllocEndpoint_GetAllocs_Blocking(t *testing.T) { 477 t.Parallel() 478 479 s1, cleanupS1 := TestServer(t, nil) 480 defer cleanupS1() 481 state := s1.fsm.State() 482 codec := rpcClient(t, s1) 483 testutil.WaitForLeader(t, s1.RPC) 484 485 // Create the allocs 486 alloc1 := mock.Alloc() 487 alloc2 := mock.Alloc() 488 489 // First create an unrelated alloc 490 time.AfterFunc(100*time.Millisecond, func() { 491 state.UpsertJobSummary(99, mock.JobSummary(alloc1.JobID)) 492 err := state.UpsertAllocs(100, []*structs.Allocation{alloc1}) 493 if err != nil { 494 t.Fatalf("err: %v", err) 495 } 496 }) 497 498 // Create the alloc we are watching later 499 time.AfterFunc(200*time.Millisecond, func() { 500 state.UpsertJobSummary(199, mock.JobSummary(alloc2.JobID)) 501 err := state.UpsertAllocs(200, []*structs.Allocation{alloc2}) 502 if err != nil { 503 t.Fatalf("err: %v", err) 504 } 505 }) 506 507 // Lookup the allocs 508 get := &structs.AllocsGetRequest{ 509 AllocIDs: []string{alloc1.ID, alloc2.ID}, 510 QueryOptions: structs.QueryOptions{ 511 Region: "global", 512 MinQueryIndex: 150, 513 }, 514 } 515 var resp structs.AllocsGetResponse 516 start := time.Now() 517 if err := msgpackrpc.CallWithCodec(codec, "Alloc.GetAllocs", get, &resp); err != nil { 518 t.Fatalf("err: %v", err) 519 } 520 521 if elapsed := time.Since(start); elapsed < 200*time.Millisecond { 522 t.Fatalf("should block (returned in %s) %#v", elapsed, resp) 523 } 524 if resp.Index != 200 { 525 t.Fatalf("Bad index: %d %d", resp.Index, 200) 526 } 527 if len(resp.Allocs) != 2 { 528 t.Fatalf("bad: %#v", resp.Allocs) 529 } 530 } 531 532 func TestAllocEndpoint_UpdateDesiredTransition(t *testing.T) { 533 t.Parallel() 534 require := require.New(t) 535 536 s1, _, cleanupS1 := TestACLServer(t, nil) 537 defer cleanupS1() 538 codec := rpcClient(t, s1) 539 testutil.WaitForLeader(t, s1.RPC) 540 541 // Create the register request 542 alloc := mock.Alloc() 543 alloc2 := mock.Alloc() 544 state := s1.fsm.State() 545 require.Nil(state.UpsertJobSummary(998, mock.JobSummary(alloc.JobID))) 546 require.Nil(state.UpsertJobSummary(999, mock.JobSummary(alloc2.JobID))) 547 require.Nil(state.UpsertAllocs(1000, []*structs.Allocation{alloc, alloc2})) 548 549 t1 := &structs.DesiredTransition{ 550 Migrate: helper.BoolToPtr(true), 551 } 552 553 // Update the allocs desired status 554 get := &structs.AllocUpdateDesiredTransitionRequest{ 555 Allocs: map[string]*structs.DesiredTransition{ 556 alloc.ID: t1, 557 alloc2.ID: t1, 558 }, 559 Evals: []*structs.Evaluation{ 560 { 561 ID: uuid.Generate(), 562 Namespace: alloc.Namespace, 563 Priority: alloc.Job.Priority, 564 Type: alloc.Job.Type, 565 TriggeredBy: structs.EvalTriggerNodeDrain, 566 JobID: alloc.Job.ID, 567 JobModifyIndex: alloc.Job.ModifyIndex, 568 Status: structs.EvalStatusPending, 569 }, 570 { 571 ID: uuid.Generate(), 572 Namespace: alloc2.Namespace, 573 Priority: alloc2.Job.Priority, 574 Type: alloc2.Job.Type, 575 TriggeredBy: structs.EvalTriggerNodeDrain, 576 JobID: alloc2.Job.ID, 577 JobModifyIndex: alloc2.Job.ModifyIndex, 578 Status: structs.EvalStatusPending, 579 }, 580 }, 581 WriteRequest: structs.WriteRequest{ 582 Region: "global", 583 }, 584 } 585 586 // Try without permissions 587 var resp structs.GenericResponse 588 err := msgpackrpc.CallWithCodec(codec, "Alloc.UpdateDesiredTransition", get, &resp) 589 require.NotNil(err) 590 require.True(structs.IsErrPermissionDenied(err)) 591 592 // Try with permissions 593 get.WriteRequest.AuthToken = s1.getLeaderAcl() 594 var resp2 structs.GenericResponse 595 require.Nil(msgpackrpc.CallWithCodec(codec, "Alloc.UpdateDesiredTransition", get, &resp2)) 596 require.NotZero(resp2.Index) 597 598 // Look up the allocations 599 out1, err := state.AllocByID(nil, alloc.ID) 600 require.Nil(err) 601 out2, err := state.AllocByID(nil, alloc.ID) 602 require.Nil(err) 603 e1, err := state.EvalByID(nil, get.Evals[0].ID) 604 require.Nil(err) 605 e2, err := state.EvalByID(nil, get.Evals[1].ID) 606 require.Nil(err) 607 608 require.NotNil(out1.DesiredTransition.Migrate) 609 require.NotNil(out2.DesiredTransition.Migrate) 610 require.NotNil(e1) 611 require.NotNil(e2) 612 require.True(*out1.DesiredTransition.Migrate) 613 require.True(*out2.DesiredTransition.Migrate) 614 } 615 616 func TestAllocEndpoint_Stop_ACL(t *testing.T) { 617 t.Parallel() 618 require := require.New(t) 619 620 s1, _, cleanupS1 := TestACLServer(t, nil) 621 defer cleanupS1() 622 codec := rpcClient(t, s1) 623 testutil.WaitForLeader(t, s1.RPC) 624 625 // Create the register request 626 alloc := mock.Alloc() 627 alloc2 := mock.Alloc() 628 state := s1.fsm.State() 629 require.Nil(state.UpsertJobSummary(998, mock.JobSummary(alloc.JobID))) 630 require.Nil(state.UpsertJobSummary(999, mock.JobSummary(alloc2.JobID))) 631 require.Nil(state.UpsertAllocs(1000, []*structs.Allocation{alloc, alloc2})) 632 633 req := &structs.AllocStopRequest{ 634 AllocID: alloc.ID, 635 } 636 req.Namespace = structs.DefaultNamespace 637 req.Region = alloc.Job.Region 638 639 // Try without permissions 640 var resp structs.AllocStopResponse 641 err := msgpackrpc.CallWithCodec(codec, "Alloc.Stop", req, &resp) 642 require.True(structs.IsErrPermissionDenied(err), "expected permissions error, got: %v", err) 643 644 // Try with management permissions 645 req.WriteRequest.AuthToken = s1.getLeaderAcl() 646 var resp2 structs.AllocStopResponse 647 require.Nil(msgpackrpc.CallWithCodec(codec, "Alloc.Stop", req, &resp2)) 648 require.NotZero(resp2.Index) 649 650 // Try with alloc-lifecycle permissions 651 validToken := mock.CreatePolicyAndToken(t, state, 1002, "valid", 652 mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityAllocLifecycle})) 653 req.WriteRequest.AuthToken = validToken.SecretID 654 req.AllocID = alloc2.ID 655 656 var resp3 structs.AllocStopResponse 657 require.Nil(msgpackrpc.CallWithCodec(codec, "Alloc.Stop", req, &resp3)) 658 require.NotZero(resp3.Index) 659 660 // Look up the allocations 661 out1, err := state.AllocByID(nil, alloc.ID) 662 require.Nil(err) 663 out2, err := state.AllocByID(nil, alloc2.ID) 664 require.Nil(err) 665 e1, err := state.EvalByID(nil, resp2.EvalID) 666 require.Nil(err) 667 e2, err := state.EvalByID(nil, resp3.EvalID) 668 require.Nil(err) 669 670 require.NotNil(out1.DesiredTransition.Migrate) 671 require.NotNil(out2.DesiredTransition.Migrate) 672 require.NotNil(e1) 673 require.NotNil(e2) 674 require.True(*out1.DesiredTransition.Migrate) 675 require.True(*out2.DesiredTransition.Migrate) 676 }