github.com/geofffranks/garden-linux@v0.0.0-20160715111146-26c893169cfa/linux_backend/linux_backend_test.go (about) 1 package linux_backend_test 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 "io/ioutil" 8 "net" 9 "os" 10 "path" 11 "time" 12 13 . "github.com/onsi/ginkgo" 14 . "github.com/onsi/gomega" 15 "code.cloudfoundry.org/lager/lagertest" 16 17 "code.cloudfoundry.org/garden" 18 "code.cloudfoundry.org/garden-linux/container_repository" 19 "code.cloudfoundry.org/garden-linux/linux_backend" 20 "code.cloudfoundry.org/garden-linux/linux_backend/fakes" 21 "code.cloudfoundry.org/garden-linux/sysinfo/fake_sysinfo" 22 ) 23 24 var _ = Describe("LinuxBackend", func() { 25 var logger *lagertest.TestLogger 26 27 var fakeResourcePool *fakes.FakeResourcePool 28 var fakeSystemInfo *fake_sysinfo.FakeProvider 29 var fakeContainerProvider *fakes.FakeContainerProvider 30 var fakeHealthCheck *fakes.FakeHealthChecker 31 var containerRepo linux_backend.ContainerRepository 32 var linuxBackend *linux_backend.LinuxBackend 33 var snapshotsPath string 34 var maxContainers int 35 var fakeContainers map[string]*fakes.FakeContainer 36 37 newTestContainer := func(spec linux_backend.LinuxContainerSpec) *fakes.FakeContainer { 38 container := new(fakes.FakeContainer) 39 container.HandleReturns(spec.Handle) 40 container.GraceTimeReturns(spec.GraceTime) 41 42 if spec.ID == "" { 43 spec.ID = spec.Handle 44 } 45 46 container.IDReturns(spec.ID) 47 48 container.HasPropertiesStub = func(props garden.Properties) bool { 49 for k, v := range props { 50 if value, ok := spec.Properties[k]; !ok || (ok && value != v) { 51 return false 52 } 53 } 54 return true 55 } 56 57 return container 58 } 59 60 registerTestContainer := func(container *fakes.FakeContainer) *fakes.FakeContainer { 61 fakeContainers[container.Handle()] = container 62 return container 63 } 64 65 BeforeEach(func() { 66 fakeContainers = make(map[string]*fakes.FakeContainer) 67 logger = lagertest.NewTestLogger("test") 68 fakeResourcePool = new(fakes.FakeResourcePool) 69 containerRepo = container_repository.New() 70 fakeSystemInfo = new(fake_sysinfo.FakeProvider) 71 fakeHealthCheck = new(fakes.FakeHealthChecker) 72 73 snapshotsPath = "" 74 maxContainers = 0 75 76 id := 0 77 fakeResourcePool.AcquireStub = func(spec garden.ContainerSpec) (linux_backend.LinuxContainerSpec, error) { 78 if spec.Handle == "" { 79 id = id + 1 80 spec.Handle = fmt.Sprintf("handle-%d", id) 81 } 82 return linux_backend.LinuxContainerSpec{ContainerSpec: spec}, nil 83 } 84 85 fakeResourcePool.RestoreStub = func(snapshot io.Reader) (linux_backend.LinuxContainerSpec, error) { 86 b, err := ioutil.ReadAll(snapshot) 87 Expect(err).NotTo(HaveOccurred()) 88 89 return linux_backend.LinuxContainerSpec{ 90 ID: string(b), 91 ContainerSpec: garden.ContainerSpec{Handle: string(b)}, 92 }, nil 93 } 94 95 fakeContainerProvider = new(fakes.FakeContainerProvider) 96 fakeContainerProvider.ProvideContainerStub = func(spec linux_backend.LinuxContainerSpec) linux_backend.Container { 97 if c, ok := fakeContainers[spec.Handle]; ok { 98 return c 99 } 100 101 return newTestContainer(spec) 102 } 103 104 fakeResourcePool.AcquireStub = func(spec garden.ContainerSpec) (linux_backend.LinuxContainerSpec, error) { 105 return linux_backend.LinuxContainerSpec{ 106 ContainerSpec: spec, 107 Resources: &linux_backend.Resources{ 108 Network: &linux_backend.Network{ 109 IP: net.ParseIP("192.0.2.1"), 110 }, 111 }, 112 }, nil 113 } 114 }) 115 116 JustBeforeEach(func() { 117 linuxBackend = linux_backend.New( 118 logger, 119 fakeResourcePool, 120 containerRepo, 121 fakeContainerProvider, 122 fakeSystemInfo, 123 fakeHealthCheck, 124 snapshotsPath, 125 maxContainers, 126 ) 127 }) 128 129 Describe("Setup", func() { 130 It("sets up the container pool", func() { 131 err := linuxBackend.Setup() 132 Expect(err).ToNot(HaveOccurred()) 133 134 Expect(fakeResourcePool.SetupCallCount()).To(Equal(1)) 135 }) 136 }) 137 138 Describe("Ping", func() { 139 It("should not return an error normally", func() { 140 Expect(linuxBackend.Ping()).To(Succeed()) 141 }) 142 143 Context("when a health check fails", func() { 144 var sick error 145 146 BeforeEach(func() { 147 sick = errors.New("sick as a parrot") 148 fakeHealthCheck.HealthCheckReturns(sick) 149 }) 150 151 It("should return an unrecoverable error", func() { 152 Expect(linuxBackend.Ping()).To(MatchError(garden.UnrecoverableError{"sick as a parrot"})) 153 }) 154 }) 155 }) 156 157 Describe("Start", func() { 158 var tmpdir string 159 160 BeforeEach(func() { 161 var err error 162 163 tmpdir, err = ioutil.TempDir(os.TempDir(), "garden-server-test") 164 Expect(err).ToNot(HaveOccurred()) 165 166 snapshotsPath = path.Join(tmpdir, "snapshots") 167 }) 168 169 It("creates the snapshots directory if it's not already there", func() { 170 err := linuxBackend.Start() 171 Expect(err).ToNot(HaveOccurred()) 172 173 stat, err := os.Stat(snapshotsPath) 174 Expect(err).ToNot(HaveOccurred()) 175 176 Expect(stat.IsDir()).To(BeTrue()) 177 }) 178 179 Context("when the snapshots directory fails to be created", func() { 180 BeforeEach(func() { 181 tmpfile, err := ioutil.TempFile(os.TempDir(), "garden-server-test") 182 Expect(err).ToNot(HaveOccurred()) 183 184 snapshotsPath = path.Join(tmpfile.Name(), "snapshots") 185 }) 186 187 It("fails to start", func() { 188 err := linuxBackend.Start() 189 Expect(err).To(HaveOccurred()) 190 }) 191 }) 192 193 Context("when no snapshots directory is given", func() { 194 It("successfully starts", func() { 195 err := linuxBackend.Start() 196 Expect(err).ToNot(HaveOccurred()) 197 }) 198 }) 199 200 Describe("when snapshots are present", func() { 201 var snapshotsPath string 202 203 BeforeEach(func() { 204 snapshotsPath = path.Join(tmpdir, "snapshots") 205 206 err := os.MkdirAll(snapshotsPath, 0755) 207 Expect(err).ToNot(HaveOccurred()) 208 209 file, err := os.Create(path.Join(snapshotsPath, "some-id")) 210 Expect(err).ToNot(HaveOccurred()) 211 212 file.Write([]byte("handle-a")) 213 file.Close() 214 215 file, err = os.Create(path.Join(snapshotsPath, "some-other-id")) 216 Expect(err).ToNot(HaveOccurred()) 217 218 file.Write([]byte("handle-b")) 219 file.Close() 220 }) 221 222 It("restores them via the container pool", func() { 223 Expect(fakeResourcePool.RestoreCallCount()).To(Equal(0)) 224 225 err := linuxBackend.Start() 226 Expect(err).ToNot(HaveOccurred()) 227 228 Expect(fakeResourcePool.RestoreCallCount()).To(Equal(2)) 229 }) 230 231 It("removes the snapshots", func() { 232 Expect(fakeResourcePool.RestoreCallCount()).To(Equal(0)) 233 234 err := linuxBackend.Start() 235 Expect(err).ToNot(HaveOccurred()) 236 237 _, err = os.Stat(path.Join(snapshotsPath, "some-id")) 238 Expect(err).To(HaveOccurred()) 239 240 _, err = os.Stat(path.Join(snapshotsPath, "some-other-id")) 241 Expect(err).To(HaveOccurred()) 242 }) 243 244 It("registers the containers", func() { 245 err := linuxBackend.Start() 246 Expect(err).ToNot(HaveOccurred()) 247 248 containers, err := linuxBackend.Containers(nil) 249 Expect(err).ToNot(HaveOccurred()) 250 251 Expect(containers).To(HaveLen(2)) 252 }) 253 254 It("keeps them when pruning the container pool", func() { 255 err := linuxBackend.Start() 256 Expect(err).ToNot(HaveOccurred()) 257 258 Expect(fakeResourcePool.PruneCallCount()).To(Equal(1)) 259 Expect(fakeResourcePool.PruneArgsForCall(0)).To(Equal(map[string]bool{ 260 "handle-a": true, 261 "handle-b": true, 262 })) 263 }) 264 265 Context("when restoring the container fails", func() { 266 disaster := errors.New("failed to restore") 267 268 BeforeEach(func() { 269 fakeResourcePool.RestoreReturns(linux_backend.LinuxContainerSpec{}, disaster) 270 }) 271 272 It("successfully starts anyway", func() { 273 err := linuxBackend.Start() 274 Expect(err).ToNot(HaveOccurred()) 275 }) 276 }) 277 }) 278 279 It("prunes the container pool", func() { 280 err := linuxBackend.Start() 281 Expect(err).ToNot(HaveOccurred()) 282 283 Expect(fakeResourcePool.PruneCallCount()).To(Equal(1)) 284 Expect(fakeResourcePool.PruneArgsForCall(0)).To(Equal(map[string]bool{})) 285 }) 286 287 Context("when pruning the container pool fails", func() { 288 disaster := errors.New("failed to prune") 289 290 BeforeEach(func() { 291 fakeResourcePool.PruneReturns(disaster) 292 }) 293 294 It("returns the error", func() { 295 err := linuxBackend.Start() 296 Expect(err).To(Equal(disaster)) 297 }) 298 }) 299 }) 300 301 Describe("Stop", func() { 302 var ( 303 container1 *fakes.FakeContainer 304 container2 *fakes.FakeContainer 305 ) 306 307 BeforeEach(func() { 308 container1 = registerTestContainer(newTestContainer(linux_backend.LinuxContainerSpec{ 309 ContainerSpec: garden.ContainerSpec{Handle: "container-1"}, 310 })) 311 containerRepo.Add(container1) 312 container2 = registerTestContainer(newTestContainer(linux_backend.LinuxContainerSpec{ 313 ContainerSpec: garden.ContainerSpec{Handle: "container-2"}, 314 })) 315 containerRepo.Add(container2) 316 }) 317 318 Context("when no snapshot directory is passed", func() { 319 It("stops succesfully without saving snapshots", func() { 320 Expect(func() { linuxBackend.Stop() }).ToNot(Panic()) 321 322 Expect(container1.SnapshotCallCount()).To(Equal(0)) 323 Expect(container2.SnapshotCallCount()).To(Equal(0)) 324 }) 325 }) 326 327 Context("when the snapshot directory is passed", func() { 328 BeforeEach(func() { 329 tmpdir, err := ioutil.TempDir(os.TempDir(), "garden-server-test") 330 Expect(err).ToNot(HaveOccurred()) 331 332 snapshotsPath = path.Join(tmpdir, "snapshots") 333 }) 334 335 JustBeforeEach(func() { 336 err := linuxBackend.Start() 337 Expect(err).ToNot(HaveOccurred()) 338 }) 339 340 It("takes a snapshot of each container", func() { 341 linuxBackend.Stop() 342 343 Expect(container1.SnapshotCallCount()).To(Equal(1)) 344 Expect(container2.SnapshotCallCount()).To(Equal(1)) 345 }) 346 347 It("cleans up each container", func() { 348 linuxBackend.Stop() 349 350 Expect(container1.CleanupCallCount()).To(Equal(1)) 351 Expect(container2.CleanupCallCount()).To(Equal(1)) 352 }) 353 }) 354 }) 355 356 Describe("Capacity", func() { 357 It("returns the right capacity values", func() { 358 fakeSystemInfo.TotalMemoryReturns(1111, nil) 359 fakeSystemInfo.TotalDiskReturns(2222, nil) 360 fakeResourcePool.MaxContainersReturns(42) 361 362 capacity, err := linuxBackend.Capacity() 363 Expect(err).ToNot(HaveOccurred()) 364 365 Expect(capacity.MemoryInBytes).To(Equal(uint64(1111))) 366 Expect(capacity.DiskInBytes).To(Equal(uint64(2222))) 367 Expect(capacity.MaxContainers).To(Equal(uint64(42))) 368 }) 369 370 Context("when the max containers argument is set", func() { 371 Context("and pool.MaxContainers is lower", func() { 372 BeforeEach(func() { 373 maxContainers = 60 374 fakeResourcePool.MaxContainersReturns(40) 375 }) 376 377 It("returns the pool.MaxContainers", func() { 378 capacity, err := linuxBackend.Capacity() 379 Expect(err).ToNot(HaveOccurred()) 380 Expect(capacity.MaxContainers).To(Equal(uint64(40))) 381 }) 382 }) 383 Context("and pool.MaxContainers is higher", func() { 384 BeforeEach(func() { 385 maxContainers = 50 386 fakeResourcePool.MaxContainersReturns(60) 387 }) 388 389 It("returns the max containers argument", func() { 390 capacity, err := linuxBackend.Capacity() 391 Expect(err).ToNot(HaveOccurred()) 392 Expect(capacity.MaxContainers).To(Equal(uint64(50))) 393 }) 394 }) 395 }) 396 397 Context("when getting memory info fails", func() { 398 disaster := errors.New("oh no!") 399 400 BeforeEach(func() { 401 fakeSystemInfo.TotalMemoryReturns(0, disaster) 402 }) 403 404 It("returns the error", func() { 405 _, err := linuxBackend.Capacity() 406 Expect(err).To(Equal(disaster)) 407 }) 408 }) 409 410 Context("when getting disk info fails", func() { 411 disaster := errors.New("oh no!") 412 413 BeforeEach(func() { 414 fakeSystemInfo.TotalMemoryReturns(0, disaster) 415 }) 416 417 It("returns the error", func() { 418 _, err := linuxBackend.Capacity() 419 Expect(err).To(Equal(disaster)) 420 }) 421 }) 422 }) 423 424 Describe("Create", func() { 425 It("acquires container resources from the pool", func() { 426 Expect(fakeResourcePool.AcquireCallCount()).To(Equal(0)) 427 428 spec := garden.ContainerSpec{Handle: "foo"} 429 430 _, err := linuxBackend.Create(spec) 431 Expect(err).ToNot(HaveOccurred()) 432 433 Expect(fakeResourcePool.AcquireArgsForCall(0)).To(Equal(spec)) 434 }) 435 436 It("starts the container", func() { 437 fakeContainer := registerTestContainer(newTestContainer( 438 linux_backend.LinuxContainerSpec{ 439 ContainerSpec: garden.ContainerSpec{Handle: "foo"}, 440 }, 441 )) 442 443 returnedContainer, err := linuxBackend.Create(garden.ContainerSpec{Handle: "foo"}) 444 Expect(err).ToNot(HaveOccurred()) 445 446 Expect(returnedContainer).To(Equal(fakeContainer)) 447 Expect(fakeContainer.StartCallCount()).To(Equal(1)) 448 }) 449 450 Context("when starting the container fails", func() { 451 It("destroys the container", func() { 452 container := registerTestContainer(newTestContainer( 453 linux_backend.LinuxContainerSpec{ 454 ContainerSpec: garden.ContainerSpec{Handle: "disastrous"}, 455 }, 456 )) 457 458 container.StartReturns(errors.New("insufficient banana!")) 459 460 _, err := linuxBackend.Create(garden.ContainerSpec{Handle: "disastrous"}) 461 Expect(err).To(HaveOccurred()) 462 Expect(fakeResourcePool.ReleaseCallCount()).To(Equal(1)) 463 Expect(fakeResourcePool.ReleaseArgsForCall(0).Handle).To(Equal("disastrous")) 464 }) 465 }) 466 467 It("registers the container", func() { 468 container, err := linuxBackend.Create(garden.ContainerSpec{}) 469 Expect(err).ToNot(HaveOccurred()) 470 471 foundContainer, err := linuxBackend.Lookup(container.Handle()) 472 Expect(err).ToNot(HaveOccurred()) 473 474 Expect(foundContainer).To(Equal(container)) 475 }) 476 477 Context("when creating the container fails", func() { 478 disaster := errors.New("failed to create") 479 480 BeforeEach(func() { 481 fakeResourcePool.AcquireReturns(linux_backend.LinuxContainerSpec{}, disaster) 482 }) 483 484 It("returns the error", func() { 485 container, err := linuxBackend.Create(garden.ContainerSpec{}) 486 Expect(err).To(HaveOccurred()) 487 Expect(err).To(Equal(disaster)) 488 489 Expect(container).To(BeNil()) 490 }) 491 }) 492 493 Context("when a container with the given handle already exists", func() { 494 It("returns a HandleExistsError", func() { 495 _, err := linuxBackend.Create(garden.ContainerSpec{Handle: "foo-handle"}) 496 Expect(err).ToNot(HaveOccurred()) 497 498 _, err = linuxBackend.Create(garden.ContainerSpec{Handle: "foo-handle"}) 499 Expect(err).To(Equal(linux_backend.HandleExistsError{"foo-handle"})) 500 }) 501 }) 502 503 Context("when a container acquires the same IP address as an existing container", func() { 504 var fakeContainer *fakes.FakeContainer 505 506 JustBeforeEach(func() { 507 fakeContainer = registerTestContainer(newTestContainer(linux_backend.LinuxContainerSpec{ 508 ContainerSpec: garden.ContainerSpec{Handle: "foo"}, 509 })) 510 containerRepo.Add(fakeContainer) 511 fakeContainer.InfoReturns(garden.ContainerInfo{ 512 ContainerIP: "192.0.2.1", 513 }, nil) 514 }) 515 516 Context("when a container in the repo returns an error from Info", func() { 517 JustBeforeEach(func() { 518 fakeContainer.InfoReturns(garden.ContainerInfo{}, errors.New("info-errored")) 519 }) 520 521 It("returns an error", func() { 522 _, err := linuxBackend.Create(garden.ContainerSpec{}) 523 Expect(err).To(HaveOccurred()) 524 }) 525 526 It("releases resources", func() { 527 _, err := linuxBackend.Create(garden.ContainerSpec{}) 528 Expect(err).To(HaveOccurred()) 529 Expect(fakeResourcePool.ReleaseCallCount()).To(Equal(1)) 530 }) 531 }) 532 533 It("returns an error", func() { 534 _, err := linuxBackend.Create(garden.ContainerSpec{}) 535 Expect(err).To(MatchError("IP address 192.0.2.1 has already been acquired by container 'foo' - garden-linux may be in an unexpected state")) 536 }) 537 538 It("does not start the container", func() { 539 _, err := linuxBackend.Create(garden.ContainerSpec{Handle: "foo"}) 540 Expect(err).To(HaveOccurred()) 541 542 Expect(fakeContainer.StartCallCount()).To(Equal(0)) 543 }) 544 545 It("releases resources", func() { 546 _, err := linuxBackend.Create(garden.ContainerSpec{}) 547 Expect(err).To(HaveOccurred()) 548 Expect(fakeResourcePool.ReleaseCallCount()).To(Equal(1)) 549 }) 550 }) 551 552 Context("when starting the container fails", func() { 553 disaster := errors.New("failed to start") 554 555 BeforeEach(func() { 556 container := new(fakes.FakeContainer) 557 fakeContainerProvider.ProvideContainerReturns(container) 558 container.StartReturns(disaster) 559 }) 560 561 It("returns the error", func() { 562 container, err := linuxBackend.Create(garden.ContainerSpec{}) 563 Expect(err).To(HaveOccurred()) 564 Expect(err).To(Equal(disaster)) 565 566 Expect(container).To(BeNil()) 567 }) 568 569 It("does not register the container", func() { 570 _, err := linuxBackend.Create(garden.ContainerSpec{}) 571 Expect(err).To(HaveOccurred()) 572 573 containers, err := linuxBackend.Containers(nil) 574 Expect(err).ToNot(HaveOccurred()) 575 576 Expect(containers).To(BeEmpty()) 577 }) 578 }) 579 580 Context("when the max containers parameter is set", func() { 581 BeforeEach(func() { 582 maxContainers = 2 583 }) 584 585 It("obeys the limit", func() { 586 _, err := linuxBackend.Create(garden.ContainerSpec{Handle: "container1"}) 587 Expect(err).ToNot(HaveOccurred()) 588 589 _, err = linuxBackend.Create(garden.ContainerSpec{Handle: "container2"}) 590 Expect(err).ToNot(HaveOccurred()) 591 592 _, err = linuxBackend.Create(garden.ContainerSpec{Handle: "container3"}) 593 Expect(err).To(MatchError("cannot create more than 2 containers")) 594 }) 595 }) 596 597 Context("when limits are set in the container spec", func() { 598 var containerSpec garden.ContainerSpec 599 var container *fakes.FakeContainer 600 601 BeforeEach(func() { 602 container = new(fakes.FakeContainer) 603 fakeContainerProvider.ProvideContainerReturns(container) 604 605 containerSpec = garden.ContainerSpec{ 606 Handle: "limits", 607 Limits: garden.Limits{ 608 Disk: garden.DiskLimits{ 609 InodeSoft: 1, 610 InodeHard: 2, 611 ByteSoft: 3, 612 ByteHard: 4, 613 Scope: garden.DiskLimitScopeExclusive, 614 }, 615 Memory: garden.MemoryLimits{ 616 LimitInBytes: 1024, 617 }, 618 }, 619 } 620 }) 621 622 It("applies the limits", func() { 623 _, err := linuxBackend.Create(containerSpec) 624 Expect(err).ToNot(HaveOccurred()) 625 626 Expect(container.LimitCPUCallCount()).To(Equal(0)) 627 Expect(container.LimitBandwidthCallCount()).To(Equal(0)) 628 629 Expect(container.LimitDiskCallCount()).To(Equal(1)) 630 Expect(container.LimitDiskArgsForCall(0)).To(Equal(containerSpec.Limits.Disk)) 631 632 Expect(container.LimitMemoryCallCount()).To(Equal(1)) 633 Expect(container.LimitMemoryArgsForCall(0)).To(Equal(containerSpec.Limits.Memory)) 634 }) 635 636 Context("when applying limits fails", func() { 637 limitErr := errors.New("failed to limit") 638 639 BeforeEach(func() { 640 container.LimitDiskReturns(limitErr) 641 }) 642 643 It("returns the error", func() { 644 _, err := linuxBackend.Create(containerSpec) 645 Expect(err).To(MatchError(limitErr)) 646 }) 647 }) 648 }) 649 }) 650 651 Describe("Destroy", func() { 652 var container *fakes.FakeContainer 653 654 resources := linux_backend.LinuxContainerSpec{ID: "something"} 655 656 JustBeforeEach(func() { 657 container = new(fakes.FakeContainer) 658 container.HandleReturns("some-handle") 659 container.ResourceSpecReturns(resources) 660 661 containerRepo.Add(container) 662 }) 663 664 It("removes the given container's resoureces from the pool", func() { 665 Expect(fakeResourcePool.ReleaseCallCount()).To(Equal(0)) 666 667 err := linuxBackend.Destroy("some-handle") 668 Expect(err).ToNot(HaveOccurred()) 669 670 Expect(fakeResourcePool.ReleaseCallCount()).To(Equal(1)) 671 Expect(fakeResourcePool.ReleaseArgsForCall(0)).To(Equal(resources)) 672 }) 673 674 It("unregisters the container", func() { 675 err := linuxBackend.Destroy("some-handle") 676 Expect(err).ToNot(HaveOccurred()) 677 678 _, err = linuxBackend.Lookup("some-handle") 679 Expect(err).To(HaveOccurred()) 680 Expect(err).To(MatchError(garden.ContainerNotFoundError{"some-handle"})) 681 }) 682 683 Context("when the container does not exist", func() { 684 It("returns ContainerNotFoundError", func() { 685 err := linuxBackend.Destroy("bogus-handle") 686 Expect(err).To(HaveOccurred()) 687 Expect(err).To(Equal(garden.ContainerNotFoundError{"bogus-handle"})) 688 }) 689 }) 690 691 Context("when destroying the container fails", func() { 692 disaster := errors.New("failed to destroy") 693 694 BeforeEach(func() { 695 fakeResourcePool.ReleaseReturns(disaster) 696 }) 697 698 It("returns the error", func() { 699 err := linuxBackend.Destroy("some-handle") 700 Expect(err).To(HaveOccurred()) 701 Expect(err).To(Equal(disaster)) 702 }) 703 704 It("does not unregister the container", func() { 705 err := linuxBackend.Destroy("some-handle") 706 Expect(err).To(HaveOccurred()) 707 708 foundContainer, err := linuxBackend.Lookup("some-handle") 709 Expect(err).ToNot(HaveOccurred()) 710 Expect(foundContainer).To(Equal(container)) 711 }) 712 }) 713 }) 714 715 Describe("BulkInfo", func() { 716 newContainer := func(handle string) *fakes.FakeContainer { 717 fakeContainer := &fakes.FakeContainer{} 718 fakeContainer.HandleReturns(handle) 719 fakeContainer.InfoReturns( 720 garden.ContainerInfo{ 721 HostIP: "hostip for " + handle, 722 }, 723 nil, 724 ) 725 return fakeContainer 726 } 727 728 container1 := newContainer("handle1") 729 container2 := newContainer("handle2") 730 handles := []string{"handle1", "handle2"} 731 732 BeforeEach(func() { 733 containerRepo.Add(container1) 734 containerRepo.Add(container2) 735 }) 736 737 It("returns info about containers", func() { 738 bulkInfo, err := linuxBackend.BulkInfo(handles) 739 Expect(err).ToNot(HaveOccurred()) 740 741 Expect(bulkInfo).To(Equal(map[string]garden.ContainerInfoEntry{ 742 container1.Handle(): garden.ContainerInfoEntry{ 743 Info: garden.ContainerInfo{ 744 HostIP: "hostip for handle1", 745 }, 746 }, 747 container2.Handle(): garden.ContainerInfoEntry{ 748 Info: garden.ContainerInfo{ 749 HostIP: "hostip for handle2", 750 }, 751 }, 752 })) 753 }) 754 755 Context("when not all of the handles in the system are requested", func() { 756 handles := []string{"handle2"} 757 758 It("returns info about the specified containers", func() { 759 bulkInfo, err := linuxBackend.BulkInfo(handles) 760 Expect(err).ToNot(HaveOccurred()) 761 762 Expect(bulkInfo).To(Equal(map[string]garden.ContainerInfoEntry{ 763 container2.Handle(): garden.ContainerInfoEntry{ 764 Info: garden.ContainerInfo{ 765 HostIP: "hostip for handle2", 766 }, 767 }, 768 })) 769 }) 770 }) 771 772 Context("when getting one of the infos for a container fails", func() { 773 handles := []string{"handle1", "handle2"} 774 775 BeforeEach(func() { 776 container2.InfoReturns(garden.ContainerInfo{}, errors.New("Oh no!")) 777 }) 778 779 It("returns the err for the failed container", func() { 780 bulkInfo, err := linuxBackend.BulkInfo(handles) 781 Expect(err).ToNot(HaveOccurred()) 782 783 Expect(bulkInfo).To(Equal(map[string]garden.ContainerInfoEntry{ 784 container1.Handle(): garden.ContainerInfoEntry{ 785 Info: garden.ContainerInfo{ 786 HostIP: "hostip for handle1", 787 }, 788 }, 789 container2.Handle(): garden.ContainerInfoEntry{ 790 Err: garden.NewError("Oh no!"), 791 }, 792 })) 793 }) 794 }) 795 }) 796 797 Describe("BulkMetrics", func() { 798 newContainer := func(n uint64) *fakes.FakeContainer { 799 fakeContainer := &fakes.FakeContainer{} 800 fakeContainer.HandleReturns(fmt.Sprintf("handle%d", n)) 801 fakeContainer.MetricsReturns( 802 garden.Metrics{ 803 DiskStat: garden.ContainerDiskStat{ 804 TotalInodesUsed: n, 805 }, 806 }, 807 nil, 808 ) 809 return fakeContainer 810 } 811 812 container1 := newContainer(1) 813 container2 := newContainer(2) 814 handles := []string{"handle1", "handle2"} 815 816 BeforeEach(func() { 817 containerRepo.Add(container1) 818 containerRepo.Add(container2) 819 }) 820 821 It("returns info about containers", func() { 822 bulkMetrics, err := linuxBackend.BulkMetrics(handles) 823 Expect(err).ToNot(HaveOccurred()) 824 825 Expect(bulkMetrics).To(Equal(map[string]garden.ContainerMetricsEntry{ 826 container1.Handle(): garden.ContainerMetricsEntry{ 827 Metrics: garden.Metrics{ 828 DiskStat: garden.ContainerDiskStat{ 829 TotalInodesUsed: 1, 830 }, 831 }, 832 }, 833 container2.Handle(): garden.ContainerMetricsEntry{ 834 Metrics: garden.Metrics{ 835 DiskStat: garden.ContainerDiskStat{ 836 TotalInodesUsed: 2, 837 }, 838 }, 839 }, 840 })) 841 }) 842 843 Context("when not all of the handles in the system are requested", func() { 844 handles := []string{"handle2"} 845 846 It("returns info about the specified containers", func() { 847 bulkMetrics, err := linuxBackend.BulkMetrics(handles) 848 Expect(err).ToNot(HaveOccurred()) 849 850 Expect(bulkMetrics).To(Equal(map[string]garden.ContainerMetricsEntry{ 851 container2.Handle(): garden.ContainerMetricsEntry{ 852 Metrics: garden.Metrics{ 853 DiskStat: garden.ContainerDiskStat{ 854 TotalInodesUsed: 2, 855 }, 856 }, 857 }, 858 })) 859 }) 860 }) 861 862 Context("when getting one of the infos for a container fails", func() { 863 handles := []string{"handle1", "handle2"} 864 865 BeforeEach(func() { 866 container2.MetricsReturns(garden.Metrics{}, errors.New("Oh no!")) 867 }) 868 869 It("returns the err for the failed container", func() { 870 bulkMetrics, err := linuxBackend.BulkMetrics(handles) 871 Expect(err).ToNot(HaveOccurred()) 872 873 Expect(bulkMetrics).To(Equal(map[string]garden.ContainerMetricsEntry{ 874 container1.Handle(): garden.ContainerMetricsEntry{ 875 Metrics: garden.Metrics{ 876 DiskStat: garden.ContainerDiskStat{ 877 TotalInodesUsed: 1, 878 }, 879 }, 880 }, 881 container2.Handle(): garden.ContainerMetricsEntry{ 882 Err: garden.NewError("Oh no!"), 883 }, 884 })) 885 }) 886 }) 887 }) 888 889 Describe("Lookup", func() { 890 It("returns the container", func() { 891 container, err := linuxBackend.Create(garden.ContainerSpec{}) 892 Expect(err).ToNot(HaveOccurred()) 893 894 foundContainer, err := linuxBackend.Lookup(container.Handle()) 895 Expect(err).ToNot(HaveOccurred()) 896 897 Expect(foundContainer).To(Equal(container)) 898 }) 899 900 Context("when the handle is not found", func() { 901 It("returns ContainerNotFoundError", func() { 902 foundContainer, err := linuxBackend.Lookup("bogus-handle") 903 Expect(err).To(HaveOccurred()) 904 Expect(foundContainer).To(BeNil()) 905 906 Expect(err).To(Equal(garden.ContainerNotFoundError{"bogus-handle"})) 907 }) 908 }) 909 }) 910 911 Describe("Containers", func() { 912 It("returns a list of all existing containers", func() { 913 container1, err := linuxBackend.Create(garden.ContainerSpec{Handle: "container-1"}) 914 Expect(err).ToNot(HaveOccurred()) 915 916 container2, err := linuxBackend.Create(garden.ContainerSpec{Handle: "container-2"}) 917 Expect(err).ToNot(HaveOccurred()) 918 919 containers, err := linuxBackend.Containers(nil) 920 Expect(err).ToNot(HaveOccurred()) 921 922 Expect(containers).To(ContainElement(container1)) 923 Expect(containers).To(ContainElement(container2)) 924 }) 925 926 Context("when given properties to filter by", func() { 927 It("returns only containers with matching properties", func() { 928 container1, err := linuxBackend.Create(garden.ContainerSpec{ 929 Handle: "container-1", 930 Properties: garden.Properties{"a": "b"}, 931 }) 932 Expect(err).ToNot(HaveOccurred()) 933 934 container2, err := linuxBackend.Create(garden.ContainerSpec{ 935 Handle: "container-2", 936 Properties: garden.Properties{"a": "b"}, 937 }) 938 Expect(err).ToNot(HaveOccurred()) 939 940 container3, err := linuxBackend.Create(garden.ContainerSpec{ 941 Handle: "container-3", 942 Properties: garden.Properties{"a": "b", "c": "d", "e": "f"}, 943 }) 944 Expect(err).ToNot(HaveOccurred()) 945 946 containers, err := linuxBackend.Containers( 947 garden.Properties{"a": "b", "e": "f"}, 948 ) 949 Expect(err).ToNot(HaveOccurred()) 950 951 Expect(containers).ToNot(ContainElement(container1)) 952 Expect(containers).ToNot(ContainElement(container2)) 953 Expect(containers).To(ContainElement(container3)) 954 }) 955 }) 956 957 Describe("logging", func() { 958 It("should log the entry and exit when there are containers", func() { 959 _, err := linuxBackend.Create(garden.ContainerSpec{ 960 Handle: "container-1", 961 Properties: garden.Properties{"a": "b"}, 962 }) 963 Expect(err).ToNot(HaveOccurred()) 964 965 _, err = linuxBackend.Create(garden.ContainerSpec{ 966 Handle: "container-2", 967 Properties: garden.Properties{"a": "b"}, 968 }) 969 Expect(err).ToNot(HaveOccurred()) 970 971 _, err = linuxBackend.Containers( 972 garden.Properties{"a": "b"}, 973 ) 974 Expect(err).ToNot(HaveOccurred()) 975 976 Expect(logger.LogMessages()).To(ConsistOf("test.backend.containers.started", "test.backend.containers.matched", "test.backend.containers.matched", "test.backend.containers.ending")) 977 978 logs := logger.Logs() 979 Expect(logs[3].Data["handles"]).To(ConsistOf("container-1", "container-2")) 980 }) 981 982 It("should log the entry and exit when there are no containers", func() { 983 _, err := linuxBackend.Containers( 984 garden.Properties{"a": "b", "e": "f"}, 985 ) 986 Expect(err).ToNot(HaveOccurred()) 987 988 Expect(logger.LogMessages()).To(ConsistOf("test.backend.containers.started", "test.backend.containers.ending")) 989 }) 990 }) 991 }) 992 993 Describe("GraceTime", func() { 994 It("returns the container's grace time", func() { 995 container, err := linuxBackend.Create(garden.ContainerSpec{ 996 GraceTime: time.Second, 997 }) 998 Expect(err).ToNot(HaveOccurred()) 999 1000 Expect(linuxBackend.GraceTime(container)).To(Equal(time.Second)) 1001 }) 1002 }) 1003 })