1package vm_test 2 3import ( 4 "errors" 5 "time" 6 7 . "github.com/cloudfoundry/bosh-cli/deployment/vm" 8 . "github.com/onsi/ginkgo" 9 . "github.com/onsi/gomega" 10 11 biagentclient "github.com/cloudfoundry/bosh-agent/agentclient" 12 bias "github.com/cloudfoundry/bosh-agent/agentclient/applyspec" 13 bicloud "github.com/cloudfoundry/bosh-cli/cloud" 14 biconfig "github.com/cloudfoundry/bosh-cli/config" 15 bidisk "github.com/cloudfoundry/bosh-cli/deployment/disk" 16 bideplmanifest "github.com/cloudfoundry/bosh-cli/deployment/manifest" 17 "github.com/cloudfoundry/bosh-utils/logger/loggerfakes" 18 biproperty "github.com/cloudfoundry/bosh-utils/property" 19 fakesys "github.com/cloudfoundry/bosh-utils/system/fakes" 20 21 fakebiagentclient "github.com/cloudfoundry/bosh-agent/agentclient/fakes" 22 fakebicloud "github.com/cloudfoundry/bosh-cli/cloud/fakes" 23 fakebiconfig "github.com/cloudfoundry/bosh-cli/config/fakes" 24 fakebidisk "github.com/cloudfoundry/bosh-cli/deployment/disk/fakes" 25 fakebivm "github.com/cloudfoundry/bosh-cli/deployment/vm/fakes" 26 fakebiui "github.com/cloudfoundry/bosh-cli/ui/fakes" 27) 28 29var _ = Describe("VM", func() { 30 var ( 31 vm VM 32 fakeVMRepo *fakebiconfig.FakeVMRepo 33 fakeStemcellRepo *fakebiconfig.FakeStemcellRepo 34 fakeDiskDeployer *fakebivm.FakeDiskDeployer 35 fakeAgentClient *fakebiagentclient.FakeAgentClient 36 fakeCloud *fakebicloud.FakeCloud 37 applySpec bias.ApplySpec 38 diskPool bideplmanifest.DiskPool 39 timeService *FakeClock 40 fs *fakesys.FakeFileSystem 41 logger *loggerfakes.FakeLogger 42 ) 43 44 BeforeEach(func() { 45 fakeAgentClient = &fakebiagentclient.FakeAgentClient{} 46 timeService = &FakeClock{Times: []time.Time{time.Now(), time.Now().Add(10 * time.Minute)}} 47 48 // apply spec is only being passed to the agent client, so it doesn't need much content for testing 49 applySpec = bias.ApplySpec{ 50 Deployment: "fake-deployment-name", 51 } 52 53 diskPool = bideplmanifest.DiskPool{ 54 Name: "fake-persistent-disk-pool-name", 55 DiskSize: 1024, 56 CloudProperties: biproperty.Map{ 57 "fake-disk-pool-cloud-property-key": "fake-disk-pool-cloud-property-value", 58 }, 59 } 60 61 logger = &loggerfakes.FakeLogger{} 62 fs = fakesys.NewFakeFileSystem() 63 fakeCloud = fakebicloud.NewFakeCloud() 64 fakeVMRepo = fakebiconfig.NewFakeVMRepo() 65 fakeStemcellRepo = fakebiconfig.NewFakeStemcellRepo() 66 fakeDiskDeployer = fakebivm.NewFakeDiskDeployer() 67 vm = NewVM( 68 "fake-vm-cid", 69 fakeVMRepo, 70 fakeStemcellRepo, 71 fakeDiskDeployer, 72 fakeAgentClient, 73 fakeCloud, 74 timeService, 75 fs, 76 logger, 77 ) 78 }) 79 80 Describe("Exists", func() { 81 It("returns true when the vm exists", func() { 82 fakeCloud.HasVMFound = true 83 84 exists, err := vm.Exists() 85 Expect(err).ToNot(HaveOccurred()) 86 Expect(exists).To(BeTrue()) 87 }) 88 89 It("returns false when the vm does not exist", func() { 90 fakeCloud.HasVMFound = false 91 92 exists, err := vm.Exists() 93 Expect(err).ToNot(HaveOccurred()) 94 Expect(exists).To(BeFalse()) 95 }) 96 97 It("returns error when checking fails", func() { 98 fakeCloud.HasVMErr = errors.New("fake-has-vm-error") 99 100 _, err := vm.Exists() 101 Expect(err).To(HaveOccurred()) 102 Expect(err.Error()).To(ContainSubstring("fake-has-vm-error")) 103 }) 104 }) 105 106 Describe("UpdateDisks", func() { 107 var expectedDisks []bidisk.Disk 108 109 BeforeEach(func() { 110 fakeDisk := fakebidisk.NewFakeDisk("fake-disk-cid") 111 expectedDisks = []bidisk.Disk{fakeDisk} 112 fakeDiskDeployer.SetDeployBehavior(expectedDisks, nil) 113 }) 114 115 It("delegates to DiskDeployer.Deploy", func() { 116 fakeStage := fakebiui.NewFakeStage() 117 118 disks, err := vm.UpdateDisks(diskPool, fakeStage) 119 Expect(err).NotTo(HaveOccurred()) 120 Expect(disks).To(Equal(expectedDisks)) 121 122 Expect(fakeDiskDeployer.DeployInputs).To(Equal([]fakebivm.DeployInput{ 123 { 124 DiskPool: diskPool, 125 Cloud: fakeCloud, 126 VM: vm, 127 EventLoggerStage: fakeStage, 128 }, 129 })) 130 }) 131 }) 132 133 Describe("Drain", func() { 134 It("drains and waits a specific amount of time", func() { 135 fakeAgentClient.DrainReturns(15, nil) 136 err := vm.Drain() 137 Expect(err).ToNot(HaveOccurred()) 138 Expect(fakeAgentClient.DrainCallCount()).To(Equal(1)) 139 Expect(len(timeService.SleepCalls)).To(Equal(1)) 140 Expect(timeService.SleepCalls[0]).To(Equal(15 * time.Second)) 141 }) 142 143 It("drains, waits, and retries until given a positive result", func() { 144 fakeAgentClient.DrainReturnsOnCall(0, -15, nil) 145 fakeAgentClient.DrainReturnsOnCall(1, -16, nil) 146 fakeAgentClient.DrainReturnsOnCall(2, 10, nil) 147 err := vm.Drain() 148 Expect(err).ToNot(HaveOccurred()) 149 Expect(fakeAgentClient.DrainCallCount()).To(Equal(3)) 150 Expect(fakeAgentClient.DrainArgsForCall(0)).To(Equal("shutdown")) 151 Expect(fakeAgentClient.DrainArgsForCall(1)).To(Equal("status")) 152 Expect(fakeAgentClient.DrainArgsForCall(2)).To(Equal("status")) 153 Expect(len(timeService.SleepCalls)).To(Equal(3)) 154 Expect(timeService.SleepCalls[0]).To(Equal(15 * time.Second)) 155 Expect(timeService.SleepCalls[1]).To(Equal(16 * time.Second)) 156 Expect(timeService.SleepCalls[2]).To(Equal(10 * time.Second)) 157 }) 158 159 Context("when draining an agent fails", func() { 160 BeforeEach(func() { 161 fakeAgentClient.DrainReturns(0, errors.New("fake-drain-error")) 162 }) 163 164 It("returns an error", func() { 165 err := vm.Drain() 166 Expect(err).To(HaveOccurred()) 167 Expect(err.Error()).To(ContainSubstring("fake-drain-error")) 168 }) 169 }) 170 171 Context("when drain get_status fails", func() { 172 BeforeEach(func() { 173 fakeAgentClient.DrainReturnsOnCall(0, -15, nil) 174 fakeAgentClient.DrainReturnsOnCall(1, 0, errors.New("fake-drain-error")) 175 }) 176 177 It("returns an error", func() { 178 err := vm.Drain() 179 Expect(err).To(HaveOccurred()) 180 Expect(err.Error()).To(ContainSubstring("fake-drain-error")) 181 }) 182 }) 183 }) 184 185 Describe("Stop", func() { 186 It("stops agent services", func() { 187 err := vm.Stop() 188 Expect(err).ToNot(HaveOccurred()) 189 Expect(fakeAgentClient.StopCallCount()).To(Equal(1)) 190 }) 191 192 Context("when stopping an agent fails", func() { 193 BeforeEach(func() { 194 fakeAgentClient.StopReturns(errors.New("fake-stop-error")) 195 }) 196 197 It("returns an error", func() { 198 err := vm.Stop() 199 Expect(err).To(HaveOccurred()) 200 Expect(err.Error()).To(ContainSubstring("fake-stop-error")) 201 }) 202 }) 203 }) 204 205 Describe("Apply", func() { 206 It("sends apply spec to the agent", func() { 207 err := vm.Apply(applySpec) 208 Expect(err).ToNot(HaveOccurred()) 209 Expect(fakeAgentClient.ApplyArgsForCall(0)).To(Equal(applySpec)) 210 }) 211 212 Context("when sending apply spec to the agent fails", func() { 213 BeforeEach(func() { 214 fakeAgentClient.ApplyReturns(errors.New("fake-agent-apply-err")) 215 }) 216 217 It("returns an error", func() { 218 err := vm.Apply(applySpec) 219 Expect(err).To(HaveOccurred()) 220 Expect(err.Error()).To(ContainSubstring("fake-agent-apply-err")) 221 }) 222 }) 223 }) 224 225 Describe("Start", func() { 226 It("starts agent services", func() { 227 err := vm.Start() 228 Expect(err).ToNot(HaveOccurred()) 229 Expect(fakeAgentClient.StartCallCount()).To(Equal(1)) 230 }) 231 232 Context("when starting an agent fails", func() { 233 BeforeEach(func() { 234 fakeAgentClient.StartReturns(errors.New("fake-start-error")) 235 }) 236 237 It("returns an error", func() { 238 err := vm.Start() 239 Expect(err).To(HaveOccurred()) 240 Expect(err.Error()).To(ContainSubstring("fake-start-error")) 241 }) 242 }) 243 }) 244 245 Describe("WaitToBeRunning", func() { 246 var invocations int 247 248 BeforeEach(func() { 249 responses := []struct { 250 state biagentclient.AgentState 251 err error 252 }{ 253 {biagentclient.AgentState{JobState: "pending"}, nil}, 254 {biagentclient.AgentState{JobState: "pending"}, nil}, 255 {biagentclient.AgentState{JobState: "running"}, nil}, 256 } 257 fakeAgentClient.GetStateStub = func() (biagentclient.AgentState, error) { 258 i := responses[invocations] 259 invocations++ 260 return i.state, i.err 261 } 262 }) 263 264 It("waits until agent reports state as running", func() { 265 err := vm.WaitToBeRunning(5, 0) 266 Expect(err).ToNot(HaveOccurred()) 267 Expect(invocations).To(Equal(3)) 268 }) 269 }) 270 271 Describe("AttachDisk", func() { 272 var disk *fakebidisk.FakeDisk 273 274 BeforeEach(func() { 275 fakeTime := time.Date(2016, time.November, 10, 23, 0, 0, 0, time.UTC) 276 timeService = &FakeClock{Times: []time.Time{fakeTime, time.Now(), time.Now().Add(10 * time.Minute)}} 277 disk = fakebidisk.NewFakeDisk("fake-disk-cid") 278 279 metadata := bicloud.VMMetadata{ 280 "director": "bosh-init", 281 "deployment": "some-deployment", 282 "name": "some-instance-group/0", 283 "job": "some-instance-group", 284 "instance_group": "some-instance-group", 285 "index": "0", 286 "custom_tag1": "custom_value1", 287 "custom_tag2": "custom_value2", 288 } 289 vm = NewVMWithMetadata( 290 "fake-vm-cid", 291 fakeVMRepo, 292 fakeStemcellRepo, 293 fakeDiskDeployer, 294 fakeAgentClient, 295 fakeCloud, 296 timeService, 297 fs, 298 logger, 299 metadata, 300 ) 301 }) 302 303 It("attaches disk to vm in the cloud", func() { 304 err := vm.AttachDisk(disk) 305 Expect(err).ToNot(HaveOccurred()) 306 Expect(fakeCloud.AttachDiskInput).To(Equal(fakebicloud.AttachDiskInput{ 307 VMCID: "fake-vm-cid", 308 DiskCID: "fake-disk-cid", 309 })) 310 }) 311 312 It("does not call agent AddPersistentDisk when diskHints are nil", func() { 313 fakeCloud.AttachDiskHints = nil 314 315 err := vm.AttachDisk(disk) 316 317 Expect(err).ToNot(HaveOccurred()) 318 Expect(fakeAgentClient.AddPersistentDiskCallCount()).To(Equal(0)) 319 }) 320 321 It("adds the persistent disk to the agent", func() { 322 fakeCloud.AttachDiskHints = "/dev/sdb" 323 324 err := vm.AttachDisk(disk) 325 326 Expect(err).ToNot(HaveOccurred()) 327 Expect(fakeAgentClient.AddPersistentDiskCallCount()).To(Equal(1)) 328 diskCid, diskHints := fakeAgentClient.AddPersistentDiskArgsForCall(0) 329 Expect(diskCid).To(Equal("fake-disk-cid")) 330 Expect(diskHints).To(Equal("/dev/sdb")) 331 }) 332 333 It("sends mount disk to the agent after pinging the agent", func() { 334 err := vm.AttachDisk(disk) 335 Expect(err).ToNot(HaveOccurred()) 336 Expect(fakeAgentClient.PingCallCount()).To(Equal(1)) 337 Expect(fakeAgentClient.MountDiskArgsForCall(0)).To(Equal("fake-disk-cid")) 338 }) 339 340 Context("when metadata is set", func() { 341 It("sets the metadata to the disk", func() { 342 expectedDiskMetadata := bicloud.DiskMetadata{ 343 "director": "bosh-init", 344 "deployment": "some-deployment", 345 "instance_group": "some-instance-group", 346 "instance_index": "0", 347 "attached_at": "2016-11-10T23:00:00Z", 348 "custom_tag1": "custom_value1", 349 "custom_tag2": "custom_value2", 350 } 351 352 err := vm.AttachDisk(disk) 353 Expect(err).ToNot(HaveOccurred()) 354 355 Expect(fakeCloud.SetDiskMetadataCid).To(Equal("fake-disk-cid")) 356 Expect(fakeCloud.SetDiskMetadataMetadata).To(Equal(expectedDiskMetadata)) 357 }) 358 359 Context("when setting metadata is not supported by the CPI", func() { 360 BeforeEach(func() { 361 cmdError := bicloud.CmdError{ 362 Type: bicloud.NotImplementedError, 363 } 364 fakeCloud.SetDiskMetadataError = bicloud.NewCPIError("set_disk_metadata", cmdError) 365 }) 366 367 It("logs a warning", func() { 368 err := vm.AttachDisk(disk) 369 Expect(err).ToNot(HaveOccurred()) 370 371 Expect(logger.WarnCallCount()).To(Equal(1)) 372 tag, msg, _ := logger.WarnArgsForCall(0) 373 Expect(tag).To(Equal("vm")) 374 Expect(msg).To(Equal("'SetDiskMetadata' not implemented by CPI")) 375 }) 376 }) 377 378 Context("when setting metadata fails", func() { 379 BeforeEach(func() { 380 cmdError := bicloud.CmdError{ 381 Message: "some error", 382 } 383 fakeCloud.SetDiskMetadataError = bicloud.NewCPIError("set_disk_metadata", cmdError) 384 }) 385 386 It("returns an error", func() { 387 err := vm.AttachDisk(disk) 388 Expect(err).To(HaveOccurred()) 389 Expect(err.Error()).To(ContainSubstring("some error")) 390 Expect(err.Error()).To(ContainSubstring("Setting disk metadata")) 391 }) 392 }) 393 }) 394 395 Context("when AddPersistentDisk returns 'unknown message add_persistent_disk'", func() { 396 BeforeEach(func() { 397 fakeCloud.AttachDiskHints = "/dev/sdb" 398 fakeAgentClient.AddPersistentDiskReturns(errors.New("Agent responded with error: unknown message add_persistent_disk")) 399 }) 400 401 It("recovers from unimplemented AddPersistentDisk in the agent", func() { 402 err := vm.AttachDisk(disk) 403 Expect(err).ToNot(HaveOccurred()) 404 }) 405 }) 406 407 Context("when AddPersistentDisk returns anything other than 'unknown message add_persistent_disk'", func() { 408 BeforeEach(func() { 409 fakeCloud.AttachDiskHints = "/dev/sdb" 410 fakeAgentClient.AddPersistentDiskReturns(errors.New("fake-agent-error")) 411 }) 412 413 It("fails with the AddPersistentDisk error", func() { 414 err := vm.AttachDisk(disk) 415 Expect(err).To(HaveOccurred()) 416 Expect(err.Error()).To(ContainSubstring("fake-agent-error")) 417 }) 418 }) 419 420 Context("when attaching disk to cloud fails", func() { 421 BeforeEach(func() { 422 fakeCloud.AttachDiskErr = errors.New("fake-attach-error") 423 }) 424 425 It("returns an error", func() { 426 err := vm.AttachDisk(disk) 427 Expect(err).To(HaveOccurred()) 428 Expect(err.Error()).To(ContainSubstring("fake-attach-error")) 429 430 Expect(fakeAgentClient.PingCallCount()).To(Equal(0)) 431 }) 432 }) 433 434 Context("when mounting disk fails", func() { 435 BeforeEach(func() { 436 fakeAgentClient.MountDiskReturns(errors.New("fake-mount-error")) 437 }) 438 439 It("returns an error", func() { 440 err := vm.AttachDisk(disk) 441 Expect(err).To(HaveOccurred()) 442 Expect(err.Error()).To(ContainSubstring("fake-mount-error")) 443 }) 444 }) 445 446 It("returns an error if pinging fails", func() { 447 fakeAgentClient.PingReturns("", errors.New("fake-error")) 448 449 err := vm.AttachDisk(disk) 450 Expect(err).To(HaveOccurred()) 451 Expect(err.Error()).To(ContainSubstring("fake-error")) 452 453 Expect(fakeAgentClient.MountDiskCallCount()).To(Equal(0)) 454 }) 455 }) 456 457 Describe("DetachDisk", func() { 458 var disk *fakebidisk.FakeDisk 459 460 BeforeEach(func() { 461 disk = fakebidisk.NewFakeDisk("fake-disk-cid") 462 }) 463 464 It("removes the disk from the vm", func() { 465 err := vm.DetachDisk(disk) 466 Expect(err).ToNot(HaveOccurred()) 467 Expect(fakeAgentClient.RemovePersistentDiskCallCount()).To(Equal(1)) 468 Expect(fakeAgentClient.RemovePersistentDiskArgsForCall(0)).To(Equal(disk.CID())) 469 }) 470 471 It("detaches disk from vm in the cloud", func() { 472 err := vm.DetachDisk(disk) 473 Expect(err).ToNot(HaveOccurred()) 474 Expect(fakeCloud.DetachDiskInput).To(Equal(fakebicloud.DetachDiskInput{ 475 VMCID: "fake-vm-cid", 476 DiskCID: "fake-disk-cid", 477 })) 478 Expect(fakeAgentClient.PingCallCount()).To(Equal(1)) 479 }) 480 481 Context("when RemovePersistentDisk returns 'unknown message remove_persistent_disk'", func() { 482 BeforeEach(func() { 483 fakeAgentClient.RemovePersistentDiskReturns(errors.New("Agent responded with error: unknown message remove_persistent_disk")) 484 }) 485 486 It("recovers from unimplemented RemovePersistentDisk in the agent", func() { 487 err := vm.DetachDisk(disk) 488 Expect(err).ToNot(HaveOccurred()) 489 }) 490 }) 491 492 Context("when RemovePersistentDisk returns anything other than 'unknown message remove_persistent_disk'", func() { 493 BeforeEach(func() { 494 fakeAgentClient.RemovePersistentDiskReturns(errors.New("fake-agent-error")) 495 }) 496 497 It("fails with the RemovePersistentDisk error", func() { 498 err := vm.DetachDisk(disk) 499 Expect(err).To(HaveOccurred()) 500 Expect(err.Error()).To(ContainSubstring("fake-agent-error")) 501 }) 502 }) 503 504 Context("when detaching disk to cloud fails", func() { 505 BeforeEach(func() { 506 fakeCloud.DetachDiskErr = errors.New("fake-detach-error") 507 }) 508 509 It("returns an error", func() { 510 err := vm.DetachDisk(disk) 511 Expect(err).To(HaveOccurred()) 512 Expect(err.Error()).To(ContainSubstring("fake-detach-error")) 513 514 Expect(fakeAgentClient.PingCallCount()).To(Equal(0)) 515 }) 516 }) 517 518 It("returns an error if pinging fails", func() { 519 fakeAgentClient.PingReturns("", errors.New("fake-error")) 520 521 err := vm.DetachDisk(disk) 522 Expect(err).To(HaveOccurred()) 523 Expect(err.Error()).To(ContainSubstring("fake-error")) 524 }) 525 }) 526 527 Describe("UnmountDisk", func() { 528 var disk *fakebidisk.FakeDisk 529 530 BeforeEach(func() { 531 disk = fakebidisk.NewFakeDisk("fake-disk-cid") 532 }) 533 534 It("sends unmount disk to the agent", func() { 535 err := vm.UnmountDisk(disk) 536 Expect(err).ToNot(HaveOccurred()) 537 Expect(fakeAgentClient.UnmountDiskArgsForCall(0)).To(Equal("fake-disk-cid")) 538 }) 539 540 Context("when unmounting disk fails", func() { 541 BeforeEach(func() { 542 fakeAgentClient.UnmountDiskReturns(errors.New("fake-unmount-error")) 543 }) 544 545 It("returns an error", func() { 546 err := vm.UnmountDisk(disk) 547 Expect(err).To(HaveOccurred()) 548 Expect(err.Error()).To(ContainSubstring("fake-unmount-error")) 549 }) 550 }) 551 }) 552 553 Describe("Disks", func() { 554 BeforeEach(func() { 555 fakeAgentClient.ListDiskReturns([]string{"fake-disk-cid-1", "fake-disk-cid-2"}, nil) 556 }) 557 558 It("returns disks that are reported by the agent", func() { 559 disks, err := vm.Disks() 560 Expect(err).ToNot(HaveOccurred()) 561 expectedFirstDisk := bidisk.NewDisk(biconfig.DiskRecord{CID: "fake-disk-cid-1"}, nil, nil) 562 expectedSecondDisk := bidisk.NewDisk(biconfig.DiskRecord{CID: "fake-disk-cid-2"}, nil, nil) 563 Expect(disks).To(Equal([]bidisk.Disk{expectedFirstDisk, expectedSecondDisk})) 564 }) 565 566 Context("when listing disks fails", func() { 567 BeforeEach(func() { 568 fakeAgentClient.ListDiskReturns([]string{}, errors.New("fake-list-disk-error")) 569 }) 570 571 It("returns an error", func() { 572 _, err := vm.Disks() 573 Expect(err).To(HaveOccurred()) 574 Expect(err.Error()).To(ContainSubstring("fake-list-disk-error")) 575 }) 576 }) 577 }) 578 579 Describe("Delete", func() { 580 It("deletes vm in the cloud", func() { 581 err := vm.Delete() 582 Expect(err).ToNot(HaveOccurred()) 583 Expect(fakeCloud.DeleteVMInput).To(Equal(fakebicloud.DeleteVMInput{ 584 VMCID: "fake-vm-cid", 585 })) 586 }) 587 588 It("deletes VM in the vm repo", func() { 589 err := vm.Delete() 590 Expect(err).ToNot(HaveOccurred()) 591 Expect(fakeVMRepo.ClearCurrentCalled).To(BeTrue()) 592 }) 593 594 It("clears current stemcell in the stemcell repo", func() { 595 err := vm.Delete() 596 Expect(err).ToNot(HaveOccurred()) 597 Expect(fakeStemcellRepo.ClearCurrentCalled).To(BeTrue()) 598 }) 599 600 Context("when deleting vm in the cloud fails", func() { 601 BeforeEach(func() { 602 fakeCloud.DeleteVMErr = errors.New("fake-delete-vm-error") 603 }) 604 605 It("returns an error", func() { 606 err := vm.Delete() 607 Expect(err).To(HaveOccurred()) 608 Expect(err.Error()).To(ContainSubstring("fake-delete-vm-error")) 609 }) 610 }) 611 612 Context("when deleting vm in the cloud fails with VMNotFoundError", func() { 613 var deleteErr = bicloud.NewCPIError("delete_vm", bicloud.CmdError{ 614 Type: bicloud.VMNotFoundError, 615 Message: "fake-vm-not-found-message", 616 }) 617 618 BeforeEach(func() { 619 fakeCloud.DeleteVMErr = deleteErr 620 }) 621 622 It("deletes vm in the cloud", func() { 623 err := vm.Delete() 624 Expect(err).To(HaveOccurred()) 625 Expect(err).To(Equal(deleteErr)) 626 Expect(fakeCloud.DeleteVMInput).To(Equal(fakebicloud.DeleteVMInput{ 627 VMCID: "fake-vm-cid", 628 })) 629 }) 630 631 It("deletes VM in the vm repo", func() { 632 err := vm.Delete() 633 Expect(err).To(HaveOccurred()) 634 Expect(err).To(Equal(deleteErr)) 635 Expect(fakeVMRepo.ClearCurrentCalled).To(BeTrue()) 636 }) 637 638 It("clears current stemcell in the stemcell repo", func() { 639 err := vm.Delete() 640 Expect(err).To(HaveOccurred()) 641 Expect(err).To(Equal(deleteErr)) 642 Expect(fakeStemcellRepo.ClearCurrentCalled).To(BeTrue()) 643 }) 644 }) 645 }) 646 647 Describe("MigrateDisk", func() { 648 It("sends migrate_disk to the agent", func() { 649 err := vm.MigrateDisk() 650 Expect(err).ToNot(HaveOccurred()) 651 Expect(fakeAgentClient.MigrateDiskCallCount()).To(Equal(1)) 652 }) 653 654 Context("when migrating disk fails", func() { 655 BeforeEach(func() { 656 fakeAgentClient.MigrateDiskReturns(errors.New("fake-migrate-error")) 657 }) 658 659 It("returns an error", func() { 660 err := vm.MigrateDisk() 661 Expect(err).To(HaveOccurred()) 662 Expect(err.Error()).To(ContainSubstring("fake-migrate-error")) 663 }) 664 }) 665 }) 666 667 Describe("GetState", func() { 668 BeforeEach(func() { 669 fakeAgentClient.GetStateReturns(biagentclient.AgentState{JobState: "testing"}, nil) 670 }) 671 672 It("sends get_state to the agent", func() { 673 agentState, err := vm.GetState() 674 Expect(err).ToNot(HaveOccurred()) 675 Expect(agentState).To(Equal(biagentclient.AgentState{JobState: "testing"})) 676 }) 677 }) 678}) 679 680type FakeClock struct { 681 Times []time.Time 682 SleepCalls []time.Duration 683} 684 685func (c *FakeClock) Sleep(t time.Duration) { 686 c.SleepCalls = append(c.SleepCalls, t) 687} 688 689func (c *FakeClock) Now() time.Time { 690 t1 := c.Times[0] 691 c.Times = c.Times[1:] 692 return t1 693} 694