github.com/jrxfive/nomad@v0.6.1-0.20170802162750-1fef470e89bf/nomad/deployment_endpoint_test.go (about) 1 package nomad 2 3 import ( 4 "testing" 5 "time" 6 7 memdb "github.com/hashicorp/go-memdb" 8 msgpackrpc "github.com/hashicorp/net-rpc-msgpackrpc" 9 "github.com/hashicorp/nomad/helper" 10 "github.com/hashicorp/nomad/nomad/mock" 11 "github.com/hashicorp/nomad/nomad/structs" 12 "github.com/hashicorp/nomad/testutil" 13 "github.com/stretchr/testify/assert" 14 ) 15 16 func TestDeploymentEndpoint_GetDeployment(t *testing.T) { 17 t.Parallel() 18 s1 := testServer(t, nil) 19 defer s1.Shutdown() 20 codec := rpcClient(t, s1) 21 testutil.WaitForLeader(t, s1.RPC) 22 assert := assert.New(t) 23 24 // Create the deployment 25 j := mock.Job() 26 d := mock.Deployment() 27 d.JobID = j.ID 28 state := s1.fsm.State() 29 30 assert.Nil(state.UpsertJob(999, j), "UpsertJob") 31 assert.Nil(state.UpsertDeployment(1000, d), "UpsertDeployment") 32 33 // Lookup the deployments 34 get := &structs.DeploymentSpecificRequest{ 35 DeploymentID: d.ID, 36 QueryOptions: structs.QueryOptions{Region: "global"}, 37 } 38 var resp structs.SingleDeploymentResponse 39 assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.GetDeployment", get, &resp), "RPC") 40 assert.EqualValues(resp.Index, 1000, "resp.Index") 41 assert.Equal(d, resp.Deployment, "Returned deployment not equal") 42 } 43 44 func TestDeploymentEndpoint_GetDeployment_Blocking(t *testing.T) { 45 t.Parallel() 46 s1 := testServer(t, nil) 47 defer s1.Shutdown() 48 codec := rpcClient(t, s1) 49 testutil.WaitForLeader(t, s1.RPC) 50 state := s1.fsm.State() 51 assert := assert.New(t) 52 53 // Create the deployments 54 j1 := mock.Job() 55 j2 := mock.Job() 56 d1 := mock.Deployment() 57 d1.JobID = j1.ID 58 d2 := mock.Deployment() 59 d2.JobID = j2.ID 60 61 assert.Nil(state.UpsertJob(98, j1), "UpsertJob") 62 assert.Nil(state.UpsertJob(99, j2), "UpsertJob") 63 64 // Upsert a deployment we are not interested in first. 65 time.AfterFunc(100*time.Millisecond, func() { 66 assert.Nil(state.UpsertDeployment(100, d1), "UpsertDeployment") 67 }) 68 69 // Upsert another deployment later which should trigger the watch. 70 time.AfterFunc(200*time.Millisecond, func() { 71 assert.Nil(state.UpsertDeployment(200, d2), "UpsertDeployment") 72 }) 73 74 // Lookup the deployments 75 get := &structs.DeploymentSpecificRequest{ 76 DeploymentID: d2.ID, 77 QueryOptions: structs.QueryOptions{ 78 Region: "global", 79 MinQueryIndex: 150, 80 }, 81 } 82 start := time.Now() 83 var resp structs.SingleDeploymentResponse 84 assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.GetDeployment", get, &resp), "RPC") 85 if elapsed := time.Since(start); elapsed < 200*time.Millisecond { 86 t.Fatalf("should block (returned in %s) %#v", elapsed, resp) 87 } 88 assert.EqualValues(resp.Index, 200, "resp.Index") 89 assert.Equal(d2, resp.Deployment, "deployments equal") 90 } 91 92 func TestDeploymentEndpoint_Fail(t *testing.T) { 93 t.Parallel() 94 s1 := testServer(t, func(c *Config) { 95 c.NumSchedulers = 0 // Prevent automatic dequeue 96 }) 97 defer s1.Shutdown() 98 codec := rpcClient(t, s1) 99 testutil.WaitForLeader(t, s1.RPC) 100 assert := assert.New(t) 101 102 // Create the deployment 103 j := mock.Job() 104 d := mock.Deployment() 105 d.JobID = j.ID 106 state := s1.fsm.State() 107 108 assert.Nil(state.UpsertJob(999, j), "UpsertJob") 109 assert.Nil(state.UpsertDeployment(1000, d), "UpsertDeployment") 110 111 // Mark the deployment as failed 112 req := &structs.DeploymentFailRequest{ 113 DeploymentID: d.ID, 114 WriteRequest: structs.WriteRequest{Region: "global"}, 115 } 116 117 // Fetch the response 118 var resp structs.DeploymentUpdateResponse 119 assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.Fail", req, &resp), "RPC") 120 assert.NotEqual(resp.Index, uint64(0), "bad response index") 121 122 // Lookup the evaluation 123 ws := memdb.NewWatchSet() 124 eval, err := state.EvalByID(ws, resp.EvalID) 125 assert.Nil(err, "EvalByID failed") 126 assert.NotNil(eval, "Expect eval") 127 assert.Equal(eval.CreateIndex, resp.EvalCreateIndex, "eval index mismatch") 128 assert.Equal(eval.TriggeredBy, structs.EvalTriggerDeploymentWatcher, "eval trigger") 129 assert.Equal(eval.JobID, d.JobID, "eval job id") 130 assert.Equal(eval.DeploymentID, d.ID, "eval deployment id") 131 assert.Equal(eval.Status, structs.EvalStatusPending, "eval status") 132 133 // Lookup the deployment 134 dout, err := state.DeploymentByID(ws, d.ID) 135 assert.Nil(err, "DeploymentByID failed") 136 assert.Equal(dout.Status, structs.DeploymentStatusFailed, "wrong status") 137 assert.Equal(dout.StatusDescription, structs.DeploymentStatusDescriptionFailedByUser, "wrong status description") 138 assert.Equal(dout.ModifyIndex, resp.DeploymentModifyIndex, "wrong modify index") 139 } 140 141 func TestDeploymentEndpoint_Fail_Rollback(t *testing.T) { 142 t.Parallel() 143 s1 := testServer(t, func(c *Config) { 144 c.NumSchedulers = 0 // Prevent automatic dequeue 145 }) 146 defer s1.Shutdown() 147 codec := rpcClient(t, s1) 148 testutil.WaitForLeader(t, s1.RPC) 149 assert := assert.New(t) 150 state := s1.fsm.State() 151 152 // Create the original job 153 j := mock.Job() 154 j.Stable = true 155 j.TaskGroups[0].Update = structs.DefaultUpdateStrategy.Copy() 156 j.TaskGroups[0].Update.MaxParallel = 2 157 j.TaskGroups[0].Update.AutoRevert = true 158 assert.Nil(state.UpsertJob(998, j), "UpsertJob") 159 160 // Create the second job, deployment and alloc 161 j2 := j.Copy() 162 j2.Stable = false 163 164 d := mock.Deployment() 165 d.TaskGroups["web"].AutoRevert = true 166 d.JobID = j2.ID 167 d.JobVersion = j2.Version 168 169 a := mock.Alloc() 170 a.JobID = j.ID 171 a.DeploymentID = d.ID 172 173 assert.Nil(state.UpsertJob(999, j2), "UpsertJob") 174 assert.Nil(state.UpsertDeployment(1000, d), "UpsertDeployment") 175 assert.Nil(state.UpsertAllocs(1001, []*structs.Allocation{a}), "UpsertAllocs") 176 177 // Mark the deployment as failed 178 req := &structs.DeploymentFailRequest{ 179 DeploymentID: d.ID, 180 WriteRequest: structs.WriteRequest{Region: "global"}, 181 } 182 183 // Fetch the response 184 var resp structs.DeploymentUpdateResponse 185 assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.Fail", req, &resp), "RPC") 186 assert.NotEqual(resp.Index, uint64(0), "bad response index") 187 assert.NotNil(resp.RevertedJobVersion, "bad revert version") 188 assert.EqualValues(0, *resp.RevertedJobVersion, "bad revert version") 189 190 // Lookup the evaluation 191 ws := memdb.NewWatchSet() 192 eval, err := state.EvalByID(ws, resp.EvalID) 193 assert.Nil(err, "EvalByID failed") 194 assert.NotNil(eval, "Expect eval") 195 assert.Equal(eval.CreateIndex, resp.EvalCreateIndex, "eval index mismatch") 196 assert.Equal(eval.TriggeredBy, structs.EvalTriggerDeploymentWatcher, "eval trigger") 197 assert.Equal(eval.JobID, d.JobID, "eval job id") 198 assert.Equal(eval.DeploymentID, d.ID, "eval deployment id") 199 assert.Equal(eval.Status, structs.EvalStatusPending, "eval status") 200 201 // Lookup the deployment 202 expectedDesc := structs.DeploymentStatusDescriptionRollback(structs.DeploymentStatusDescriptionFailedByUser, 0) 203 dout, err := state.DeploymentByID(ws, d.ID) 204 assert.Nil(err, "DeploymentByID failed") 205 assert.Equal(dout.Status, structs.DeploymentStatusFailed, "wrong status") 206 assert.Equal(dout.StatusDescription, expectedDesc, "wrong status description") 207 assert.Equal(resp.DeploymentModifyIndex, dout.ModifyIndex, "wrong modify index") 208 209 // Lookup the job 210 jout, err := state.JobByID(ws, j.ID) 211 assert.Nil(err, "JobByID") 212 assert.NotNil(jout, "job") 213 assert.EqualValues(2, jout.Version, "reverted job version") 214 } 215 216 func TestDeploymentEndpoint_Pause(t *testing.T) { 217 t.Parallel() 218 s1 := testServer(t, func(c *Config) { 219 c.NumSchedulers = 0 // Prevent automatic dequeue 220 }) 221 defer s1.Shutdown() 222 codec := rpcClient(t, s1) 223 testutil.WaitForLeader(t, s1.RPC) 224 assert := assert.New(t) 225 226 // Create the deployment 227 j := mock.Job() 228 d := mock.Deployment() 229 d.JobID = j.ID 230 state := s1.fsm.State() 231 232 assert.Nil(state.UpsertJob(999, j), "UpsertJob") 233 assert.Nil(state.UpsertDeployment(1000, d), "UpsertDeployment") 234 235 // Mark the deployment as failed 236 req := &structs.DeploymentPauseRequest{ 237 DeploymentID: d.ID, 238 Pause: true, 239 WriteRequest: structs.WriteRequest{Region: "global"}, 240 } 241 242 // Fetch the response 243 var resp structs.DeploymentUpdateResponse 244 assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.Pause", req, &resp), "RPC") 245 assert.NotEqual(resp.Index, uint64(0), "bad response index") 246 assert.Zero(resp.EvalCreateIndex, "Shouldn't create eval") 247 assert.Zero(resp.EvalID, "Shouldn't create eval") 248 249 // Lookup the deployment 250 ws := memdb.NewWatchSet() 251 dout, err := state.DeploymentByID(ws, d.ID) 252 assert.Nil(err, "DeploymentByID failed") 253 assert.Equal(dout.Status, structs.DeploymentStatusPaused, "wrong status") 254 assert.Equal(dout.StatusDescription, structs.DeploymentStatusDescriptionPaused, "wrong status description") 255 assert.Equal(dout.ModifyIndex, resp.DeploymentModifyIndex, "wrong modify index") 256 } 257 258 func TestDeploymentEndpoint_Promote(t *testing.T) { 259 t.Parallel() 260 s1 := testServer(t, func(c *Config) { 261 c.NumSchedulers = 0 // Prevent automatic dequeue 262 }) 263 defer s1.Shutdown() 264 codec := rpcClient(t, s1) 265 testutil.WaitForLeader(t, s1.RPC) 266 assert := assert.New(t) 267 268 // Create the deployment, job and canary 269 j := mock.Job() 270 j.TaskGroups[0].Update = structs.DefaultUpdateStrategy.Copy() 271 j.TaskGroups[0].Update.MaxParallel = 2 272 j.TaskGroups[0].Update.Canary = 2 273 d := mock.Deployment() 274 d.TaskGroups["web"].DesiredCanaries = 2 275 d.JobID = j.ID 276 a := mock.Alloc() 277 d.TaskGroups[a.TaskGroup].PlacedCanaries = []string{a.ID} 278 a.DeploymentID = d.ID 279 a.DeploymentStatus = &structs.AllocDeploymentStatus{ 280 Healthy: helper.BoolToPtr(true), 281 } 282 283 state := s1.fsm.State() 284 assert.Nil(state.UpsertJob(999, j), "UpsertJob") 285 assert.Nil(state.UpsertDeployment(1000, d), "UpsertDeployment") 286 assert.Nil(state.UpsertAllocs(1001, []*structs.Allocation{a}), "UpsertAllocs") 287 288 // Promote the deployment 289 req := &structs.DeploymentPromoteRequest{ 290 DeploymentID: d.ID, 291 All: true, 292 WriteRequest: structs.WriteRequest{Region: "global"}, 293 } 294 295 // Fetch the response 296 var resp structs.DeploymentUpdateResponse 297 assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.Promote", req, &resp), "RPC") 298 assert.NotEqual(resp.Index, uint64(0), "bad response index") 299 300 // Lookup the evaluation 301 ws := memdb.NewWatchSet() 302 eval, err := state.EvalByID(ws, resp.EvalID) 303 assert.Nil(err, "EvalByID failed") 304 assert.NotNil(eval, "Expect eval") 305 assert.Equal(eval.CreateIndex, resp.EvalCreateIndex, "eval index mismatch") 306 assert.Equal(eval.TriggeredBy, structs.EvalTriggerDeploymentWatcher, "eval trigger") 307 assert.Equal(eval.JobID, d.JobID, "eval job id") 308 assert.Equal(eval.DeploymentID, d.ID, "eval deployment id") 309 assert.Equal(eval.Status, structs.EvalStatusPending, "eval status") 310 311 // Lookup the deployment 312 dout, err := state.DeploymentByID(ws, d.ID) 313 assert.Nil(err, "DeploymentByID failed") 314 assert.Equal(dout.Status, structs.DeploymentStatusRunning, "wrong status") 315 assert.Equal(dout.StatusDescription, structs.DeploymentStatusDescriptionRunning, "wrong status description") 316 assert.Equal(dout.ModifyIndex, resp.DeploymentModifyIndex, "wrong modify index") 317 assert.Len(dout.TaskGroups, 1, "should have one group") 318 assert.Contains(dout.TaskGroups, "web", "should have web group") 319 assert.True(dout.TaskGroups["web"].Promoted, "web group should be promoted") 320 } 321 322 func TestDeploymentEndpoint_SetAllocHealth(t *testing.T) { 323 t.Parallel() 324 s1 := testServer(t, func(c *Config) { 325 c.NumSchedulers = 0 // Prevent automatic dequeue 326 }) 327 defer s1.Shutdown() 328 codec := rpcClient(t, s1) 329 testutil.WaitForLeader(t, s1.RPC) 330 assert := assert.New(t) 331 332 // Create the deployment, job and canary 333 j := mock.Job() 334 j.TaskGroups[0].Update = structs.DefaultUpdateStrategy.Copy() 335 j.TaskGroups[0].Update.MaxParallel = 2 336 d := mock.Deployment() 337 d.JobID = j.ID 338 a := mock.Alloc() 339 a.JobID = j.ID 340 a.DeploymentID = d.ID 341 342 state := s1.fsm.State() 343 assert.Nil(state.UpsertJob(999, j), "UpsertJob") 344 assert.Nil(state.UpsertDeployment(1000, d), "UpsertDeployment") 345 assert.Nil(state.UpsertAllocs(1001, []*structs.Allocation{a}), "UpsertAllocs") 346 347 // Set the alloc as healthy 348 req := &structs.DeploymentAllocHealthRequest{ 349 DeploymentID: d.ID, 350 HealthyAllocationIDs: []string{a.ID}, 351 WriteRequest: structs.WriteRequest{Region: "global"}, 352 } 353 354 // Fetch the response 355 var resp structs.DeploymentUpdateResponse 356 assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.SetAllocHealth", req, &resp), "RPC") 357 assert.NotZero(resp.Index, "bad response index") 358 359 // Lookup the evaluation 360 ws := memdb.NewWatchSet() 361 eval, err := state.EvalByID(ws, resp.EvalID) 362 assert.Nil(err, "EvalByID failed") 363 assert.NotNil(eval, "Expect eval") 364 assert.Equal(eval.CreateIndex, resp.EvalCreateIndex, "eval index mismatch") 365 assert.Equal(eval.TriggeredBy, structs.EvalTriggerDeploymentWatcher, "eval trigger") 366 assert.Equal(eval.JobID, d.JobID, "eval job id") 367 assert.Equal(eval.DeploymentID, d.ID, "eval deployment id") 368 assert.Equal(eval.Status, structs.EvalStatusPending, "eval status") 369 370 // Lookup the deployment 371 dout, err := state.DeploymentByID(ws, d.ID) 372 assert.Nil(err, "DeploymentByID failed") 373 assert.Equal(dout.Status, structs.DeploymentStatusRunning, "wrong status") 374 assert.Equal(dout.StatusDescription, structs.DeploymentStatusDescriptionRunning, "wrong status description") 375 assert.Equal(resp.DeploymentModifyIndex, dout.ModifyIndex, "wrong modify index") 376 assert.Len(dout.TaskGroups, 1, "should have one group") 377 assert.Contains(dout.TaskGroups, "web", "should have web group") 378 assert.Equal(1, dout.TaskGroups["web"].HealthyAllocs, "should have one healthy") 379 380 // Lookup the allocation 381 aout, err := state.AllocByID(ws, a.ID) 382 assert.Nil(err, "AllocByID") 383 assert.NotNil(aout, "alloc") 384 assert.NotNil(aout.DeploymentStatus, "alloc deployment status") 385 assert.NotNil(aout.DeploymentStatus.Healthy, "alloc deployment healthy") 386 assert.True(*aout.DeploymentStatus.Healthy, "alloc deployment healthy") 387 } 388 389 func TestDeploymentEndpoint_SetAllocHealth_Rollback(t *testing.T) { 390 t.Parallel() 391 s1 := testServer(t, func(c *Config) { 392 c.NumSchedulers = 0 // Prevent automatic dequeue 393 }) 394 defer s1.Shutdown() 395 codec := rpcClient(t, s1) 396 testutil.WaitForLeader(t, s1.RPC) 397 assert := assert.New(t) 398 state := s1.fsm.State() 399 400 // Create the original job 401 j := mock.Job() 402 j.Stable = true 403 j.TaskGroups[0].Update = structs.DefaultUpdateStrategy.Copy() 404 j.TaskGroups[0].Update.MaxParallel = 2 405 j.TaskGroups[0].Update.AutoRevert = true 406 assert.Nil(state.UpsertJob(998, j), "UpsertJob") 407 408 // Create the second job, deployment and alloc 409 j2 := j.Copy() 410 j2.Stable = false 411 412 d := mock.Deployment() 413 d.TaskGroups["web"].AutoRevert = true 414 d.JobID = j2.ID 415 d.JobVersion = j2.Version 416 417 a := mock.Alloc() 418 a.JobID = j.ID 419 a.DeploymentID = d.ID 420 421 assert.Nil(state.UpsertJob(999, j2), "UpsertJob") 422 assert.Nil(state.UpsertDeployment(1000, d), "UpsertDeployment") 423 assert.Nil(state.UpsertAllocs(1001, []*structs.Allocation{a}), "UpsertAllocs") 424 425 // Set the alloc as unhealthy 426 req := &structs.DeploymentAllocHealthRequest{ 427 DeploymentID: d.ID, 428 UnhealthyAllocationIDs: []string{a.ID}, 429 WriteRequest: structs.WriteRequest{Region: "global"}, 430 } 431 432 // Fetch the response 433 var resp structs.DeploymentUpdateResponse 434 assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.SetAllocHealth", req, &resp), "RPC") 435 assert.NotZero(resp.Index, "bad response index") 436 assert.NotNil(resp.RevertedJobVersion, "bad revert version") 437 assert.EqualValues(0, *resp.RevertedJobVersion, "bad revert version") 438 439 // Lookup the evaluation 440 ws := memdb.NewWatchSet() 441 eval, err := state.EvalByID(ws, resp.EvalID) 442 assert.Nil(err, "EvalByID failed") 443 assert.NotNil(eval, "Expect eval") 444 assert.Equal(eval.CreateIndex, resp.EvalCreateIndex, "eval index mismatch") 445 assert.Equal(eval.TriggeredBy, structs.EvalTriggerDeploymentWatcher, "eval trigger") 446 assert.Equal(eval.JobID, d.JobID, "eval job id") 447 assert.Equal(eval.DeploymentID, d.ID, "eval deployment id") 448 assert.Equal(eval.Status, structs.EvalStatusPending, "eval status") 449 450 // Lookup the deployment 451 expectedDesc := structs.DeploymentStatusDescriptionRollback(structs.DeploymentStatusDescriptionFailedAllocations, 0) 452 dout, err := state.DeploymentByID(ws, d.ID) 453 assert.Nil(err, "DeploymentByID failed") 454 assert.Equal(dout.Status, structs.DeploymentStatusFailed, "wrong status") 455 assert.Equal(dout.StatusDescription, expectedDesc, "wrong status description") 456 assert.Equal(resp.DeploymentModifyIndex, dout.ModifyIndex, "wrong modify index") 457 assert.Len(dout.TaskGroups, 1, "should have one group") 458 assert.Contains(dout.TaskGroups, "web", "should have web group") 459 assert.Equal(1, dout.TaskGroups["web"].UnhealthyAllocs, "should have one healthy") 460 461 // Lookup the allocation 462 aout, err := state.AllocByID(ws, a.ID) 463 assert.Nil(err, "AllocByID") 464 assert.NotNil(aout, "alloc") 465 assert.NotNil(aout.DeploymentStatus, "alloc deployment status") 466 assert.NotNil(aout.DeploymentStatus.Healthy, "alloc deployment healthy") 467 assert.False(*aout.DeploymentStatus.Healthy, "alloc deployment healthy") 468 469 // Lookup the job 470 jout, err := state.JobByID(ws, j.ID) 471 assert.Nil(err, "JobByID") 472 assert.NotNil(jout, "job") 473 assert.EqualValues(2, jout.Version, "reverted job version") 474 } 475 476 func TestDeploymentEndpoint_List(t *testing.T) { 477 t.Parallel() 478 s1 := testServer(t, nil) 479 defer s1.Shutdown() 480 codec := rpcClient(t, s1) 481 testutil.WaitForLeader(t, s1.RPC) 482 assert := assert.New(t) 483 484 // Create the register request 485 j := mock.Job() 486 d := mock.Deployment() 487 d.JobID = j.ID 488 state := s1.fsm.State() 489 490 assert.Nil(state.UpsertJob(999, j), "UpsertJob") 491 assert.Nil(state.UpsertDeployment(1000, d), "UpsertDeployment") 492 493 // Lookup the deployments 494 get := &structs.DeploymentListRequest{ 495 QueryOptions: structs.QueryOptions{Region: "global"}, 496 } 497 var resp structs.DeploymentListResponse 498 assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.List", get, &resp), "RPC") 499 assert.EqualValues(resp.Index, 1000, "Wrong Index") 500 assert.Len(resp.Deployments, 1, "Deployments") 501 assert.Equal(resp.Deployments[0].ID, d.ID, "Deployment ID") 502 503 // Lookup the deploys by prefix 504 get = &structs.DeploymentListRequest{ 505 QueryOptions: structs.QueryOptions{Region: "global", Prefix: d.ID[:4]}, 506 } 507 508 var resp2 structs.DeploymentListResponse 509 assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.List", get, &resp2), "RPC") 510 assert.EqualValues(resp.Index, 1000, "Wrong Index") 511 assert.Len(resp2.Deployments, 1, "Deployments") 512 assert.Equal(resp2.Deployments[0].ID, d.ID, "Deployment ID") 513 } 514 515 func TestDeploymentEndpoint_List_Blocking(t *testing.T) { 516 t.Parallel() 517 s1 := testServer(t, nil) 518 defer s1.Shutdown() 519 state := s1.fsm.State() 520 codec := rpcClient(t, s1) 521 testutil.WaitForLeader(t, s1.RPC) 522 assert := assert.New(t) 523 524 // Create the deployment 525 j := mock.Job() 526 d := mock.Deployment() 527 d.JobID = j.ID 528 529 assert.Nil(state.UpsertJob(999, j), "UpsertJob") 530 531 // Upsert alloc triggers watches 532 time.AfterFunc(100*time.Millisecond, func() { 533 assert.Nil(state.UpsertDeployment(3, d), "UpsertDeployment") 534 }) 535 536 req := &structs.DeploymentListRequest{ 537 QueryOptions: structs.QueryOptions{ 538 Region: "global", 539 MinQueryIndex: 1, 540 }, 541 } 542 start := time.Now() 543 var resp structs.DeploymentListResponse 544 assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.List", req, &resp), "RPC") 545 assert.EqualValues(resp.Index, 3, "Wrong Index") 546 assert.Len(resp.Deployments, 1, "Deployments") 547 assert.Equal(resp.Deployments[0].ID, d.ID, "Deployment ID") 548 if elapsed := time.Since(start); elapsed < 100*time.Millisecond { 549 t.Fatalf("should block (returned in %s) %#v", elapsed, resp) 550 } 551 552 // Deployment updates trigger watches 553 d2 := d.Copy() 554 d2.Status = structs.DeploymentStatusPaused 555 time.AfterFunc(100*time.Millisecond, func() { 556 assert.Nil(state.UpsertDeployment(5, d2), "UpsertDeployment") 557 }) 558 559 req.MinQueryIndex = 3 560 start = time.Now() 561 var resp2 structs.DeploymentListResponse 562 assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.List", req, &resp2), "RPC") 563 assert.EqualValues(5, resp2.Index, "Wrong Index") 564 assert.Len(resp2.Deployments, 1, "Deployments") 565 assert.Equal(d2.ID, resp2.Deployments[0].ID, "Deployment ID") 566 if elapsed := time.Since(start); elapsed < 100*time.Millisecond { 567 t.Fatalf("should block (returned in %s) %#v", elapsed, resp2) 568 } 569 } 570 571 func TestDeploymentEndpoint_Allocations(t *testing.T) { 572 t.Parallel() 573 s1 := testServer(t, nil) 574 defer s1.Shutdown() 575 codec := rpcClient(t, s1) 576 testutil.WaitForLeader(t, s1.RPC) 577 assert := assert.New(t) 578 579 // Create the register request 580 j := mock.Job() 581 d := mock.Deployment() 582 d.JobID = j.ID 583 a := mock.Alloc() 584 a.DeploymentID = d.ID 585 summary := mock.JobSummary(a.JobID) 586 state := s1.fsm.State() 587 588 assert.Nil(state.UpsertJob(998, j), "UpsertJob") 589 assert.Nil(state.UpsertJobSummary(999, summary), "UpsertJobSummary") 590 assert.Nil(state.UpsertDeployment(1000, d), "UpsertDeployment") 591 assert.Nil(state.UpsertAllocs(1001, []*structs.Allocation{a}), "UpsertAllocs") 592 593 // Lookup the allocations 594 get := &structs.DeploymentSpecificRequest{ 595 DeploymentID: d.ID, 596 QueryOptions: structs.QueryOptions{Region: "global"}, 597 } 598 var resp structs.AllocListResponse 599 assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.Allocations", get, &resp), "RPC") 600 assert.EqualValues(1001, resp.Index, "Wrong Index") 601 assert.Len(resp.Allocations, 1, "Allocations") 602 assert.Equal(a.ID, resp.Allocations[0].ID, "Allocation ID") 603 } 604 605 func TestDeploymentEndpoint_Allocations_Blocking(t *testing.T) { 606 t.Parallel() 607 s1 := testServer(t, nil) 608 defer s1.Shutdown() 609 state := s1.fsm.State() 610 codec := rpcClient(t, s1) 611 testutil.WaitForLeader(t, s1.RPC) 612 assert := assert.New(t) 613 614 // Create the alloc 615 j := mock.Job() 616 d := mock.Deployment() 617 d.JobID = j.ID 618 a := mock.Alloc() 619 a.DeploymentID = d.ID 620 summary := mock.JobSummary(a.JobID) 621 622 assert.Nil(state.UpsertJob(1, j), "UpsertJob") 623 assert.Nil(state.UpsertDeployment(2, d), "UpsertDeployment") 624 assert.Nil(state.UpsertJobSummary(3, summary), "UpsertJobSummary") 625 626 // Upsert alloc triggers watches 627 time.AfterFunc(100*time.Millisecond, func() { 628 assert.Nil(state.UpsertAllocs(4, []*structs.Allocation{a}), "UpsertAllocs") 629 }) 630 631 req := &structs.DeploymentSpecificRequest{ 632 DeploymentID: d.ID, 633 QueryOptions: structs.QueryOptions{ 634 Region: "global", 635 MinQueryIndex: 1, 636 }, 637 } 638 start := time.Now() 639 var resp structs.AllocListResponse 640 assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.Allocations", req, &resp), "RPC") 641 assert.EqualValues(4, resp.Index, "Wrong Index") 642 assert.Len(resp.Allocations, 1, "Allocations") 643 assert.Equal(a.ID, resp.Allocations[0].ID, "Allocation ID") 644 if elapsed := time.Since(start); elapsed < 100*time.Millisecond { 645 t.Fatalf("should block (returned in %s) %#v", elapsed, resp) 646 } 647 648 // Client updates trigger watches 649 a2 := mock.Alloc() 650 a2.ID = a.ID 651 a2.DeploymentID = a.DeploymentID 652 a2.ClientStatus = structs.AllocClientStatusRunning 653 time.AfterFunc(100*time.Millisecond, func() { 654 assert.Nil(state.UpsertJobSummary(5, mock.JobSummary(a2.JobID)), "UpsertJobSummary") 655 assert.Nil(state.UpdateAllocsFromClient(6, []*structs.Allocation{a2}), "bpdateAllocsFromClient") 656 }) 657 658 req.MinQueryIndex = 4 659 start = time.Now() 660 var resp2 structs.AllocListResponse 661 assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.Allocations", req, &resp2), "RPC") 662 assert.EqualValues(6, resp2.Index, "Wrong Index") 663 assert.Len(resp2.Allocations, 1, "Allocations") 664 assert.Equal(a.ID, resp2.Allocations[0].ID, "Allocation ID") 665 assert.Equal(structs.AllocClientStatusRunning, resp2.Allocations[0].ClientStatus, "Client Status") 666 if elapsed := time.Since(start); elapsed < 100*time.Millisecond { 667 t.Fatalf("should block (returned in %s) %#v", elapsed, resp2) 668 } 669 } 670 671 func TestDeploymentEndpoint_Reap(t *testing.T) { 672 t.Parallel() 673 s1 := testServer(t, nil) 674 defer s1.Shutdown() 675 codec := rpcClient(t, s1) 676 testutil.WaitForLeader(t, s1.RPC) 677 assert := assert.New(t) 678 679 // Create the register request 680 d1 := mock.Deployment() 681 assert.Nil(s1.fsm.State().UpsertDeployment(1000, d1), "UpsertDeployment") 682 683 // Reap the eval 684 get := &structs.DeploymentDeleteRequest{ 685 Deployments: []string{d1.ID}, 686 WriteRequest: structs.WriteRequest{Region: "global"}, 687 } 688 var resp structs.GenericResponse 689 assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.Reap", get, &resp), "RPC") 690 assert.NotZero(resp.Index, "bad response index") 691 692 // Ensure deleted 693 ws := memdb.NewWatchSet() 694 outD, err := s1.fsm.State().DeploymentByID(ws, d1.ID) 695 assert.Nil(err, "DeploymentByID") 696 assert.Nil(outD, "Deleted Deployment") 697 }