github.com/jghiloni/cli@v6.28.1-0.20170628223758-0ce05fe032a2+incompatible/actor/v2action/application_test.go (about) 1 package v2action_test 2 3 import ( 4 "errors" 5 "time" 6 7 . "code.cloudfoundry.org/cli/actor/v2action" 8 "code.cloudfoundry.org/cli/actor/v2action/v2actionfakes" 9 "code.cloudfoundry.org/cli/api/cloudcontroller/ccerror" 10 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv2" 11 12 "github.com/cloudfoundry/sonde-go/events" 13 . "github.com/onsi/ginkgo" 14 . "github.com/onsi/gomega" 15 ) 16 17 var _ = Describe("Application Actions", func() { 18 var ( 19 actor *Actor 20 fakeCloudControllerClient *v2actionfakes.FakeCloudControllerClient 21 ) 22 23 BeforeEach(func() { 24 fakeCloudControllerClient = new(v2actionfakes.FakeCloudControllerClient) 25 actor = NewActor(fakeCloudControllerClient, nil) 26 }) 27 28 Describe("Application", func() { 29 var app Application 30 BeforeEach(func() { 31 app = Application{} 32 }) 33 34 Describe("CalculatedBuildpack", func() { 35 Context("when buildpack is set", func() { 36 BeforeEach(func() { 37 app.Buildpack = "foo" 38 app.DetectedBuildpack = "bar" 39 }) 40 41 It("returns back the buildpack", func() { 42 Expect(app.CalculatedBuildpack()).To(Equal("foo")) 43 }) 44 }) 45 46 Context("only detected buildpack is set", func() { 47 BeforeEach(func() { 48 app.DetectedBuildpack = "bar" 49 }) 50 51 It("returns back the detected buildpack", func() { 52 Expect(app.CalculatedBuildpack()).To(Equal("bar")) 53 }) 54 }) 55 56 Context("neither buildpack or detected buildpack is set", func() { 57 It("returns an empty string", func() { 58 Expect(app.CalculatedBuildpack()).To(BeEmpty()) 59 }) 60 }) 61 }) 62 63 Describe("CalculatedHealthCheckEndpoint", func() { 64 Context("when the health check type is http", func() { 65 BeforeEach(func() { 66 app.HealthCheckType = "http" 67 app.HealthCheckHTTPEndpoint = "/some-endpoint" 68 }) 69 70 It("returns the endpoint field", func() { 71 Expect(app.CalculatedHealthCheckEndpoint()).To(Equal( 72 "/some-endpoint")) 73 }) 74 }) 75 76 Context("when the health check type is not http", func() { 77 BeforeEach(func() { 78 app.HealthCheckType = "process" 79 app.HealthCheckHTTPEndpoint = "/some-endpoint" 80 }) 81 82 It("returns the empty string", func() { 83 Expect(app.CalculatedHealthCheckEndpoint()).To(Equal("")) 84 }) 85 }) 86 }) 87 88 Describe("StagingCompleted", func() { 89 Context("when staging the application completes", func() { 90 It("returns true", func() { 91 app.PackageState = ccv2.ApplicationPackageStaged 92 Expect(app.StagingCompleted()).To(BeTrue()) 93 }) 94 }) 95 96 Context("when the application is *not* staged", func() { 97 It("returns false", func() { 98 app.PackageState = ccv2.ApplicationPackageFailed 99 Expect(app.StagingCompleted()).To(BeFalse()) 100 }) 101 }) 102 }) 103 104 Describe("StagingFailed", func() { 105 Context("when staging the application fails", func() { 106 It("returns true", func() { 107 app.PackageState = ccv2.ApplicationPackageFailed 108 Expect(app.StagingFailed()).To(BeTrue()) 109 }) 110 }) 111 112 Context("when staging the application does *not* fail", func() { 113 It("returns false", func() { 114 app.PackageState = ccv2.ApplicationPackageStaged 115 Expect(app.StagingFailed()).To(BeFalse()) 116 }) 117 }) 118 }) 119 120 Describe("StagingFailedMessage", func() { 121 Context("when the application has a staging failed description", func() { 122 BeforeEach(func() { 123 app.StagingFailedDescription = "An app was not successfully detected by any available buildpack" 124 app.StagingFailedReason = "NoAppDetectedError" 125 }) 126 It("returns that description", func() { 127 Expect(app.StagingFailedMessage()).To(Equal("An app was not successfully detected by any available buildpack")) 128 }) 129 }) 130 131 Context("when the application does not have a staging failed description", func() { 132 BeforeEach(func() { 133 app.StagingFailedDescription = "" 134 app.StagingFailedReason = "NoAppDetectedError" 135 }) 136 It("returns the staging failed code", func() { 137 Expect(app.StagingFailedMessage()).To(Equal("NoAppDetectedError")) 138 }) 139 }) 140 }) 141 142 Describe("StagingFailedNoAppDetected", func() { 143 Context("when staging the application fails due to a no app detected error", func() { 144 It("returns true", func() { 145 app.StagingFailedReason = "NoAppDetectedError" 146 Expect(app.StagingFailedNoAppDetected()).To(BeTrue()) 147 }) 148 }) 149 150 Context("when staging the application fails due to any other reason", func() { 151 It("returns false", func() { 152 app.StagingFailedReason = "InsufficientResources" 153 Expect(app.StagingFailedNoAppDetected()).To(BeFalse()) 154 }) 155 }) 156 }) 157 158 Describe("Started", func() { 159 Context("when app is started", func() { 160 It("returns true", func() { 161 Expect(Application{State: ccv2.ApplicationStarted}.Started()).To(BeTrue()) 162 }) 163 }) 164 165 Context("when app is stopped", func() { 166 It("returns false", func() { 167 Expect(Application{State: ccv2.ApplicationStopped}.Started()).To(BeFalse()) 168 }) 169 }) 170 }) 171 }) 172 173 Describe("CreateApplication", func() { 174 Context("when the create is successful", func() { 175 var expectedApp ccv2.Application 176 BeforeEach(func() { 177 expectedApp = ccv2.Application{ 178 GUID: "some-app-guid", 179 Name: "some-app-name", 180 SpaceGUID: "some-space-guid", 181 } 182 fakeCloudControllerClient.CreateApplicationReturns(expectedApp, ccv2.Warnings{"some-app-warning-1"}, nil) 183 }) 184 185 It("creates and returns the application", func() { 186 newApp := Application{ 187 Name: "some-app-name", 188 SpaceGUID: "some-space-guid", 189 } 190 app, warnings, err := actor.CreateApplication(newApp) 191 Expect(err).ToNot(HaveOccurred()) 192 Expect(warnings).To(ConsistOf("some-app-warning-1")) 193 Expect(app).To(Equal(Application(expectedApp))) 194 195 Expect(fakeCloudControllerClient.CreateApplicationCallCount()).To(Equal(1)) 196 Expect(fakeCloudControllerClient.CreateApplicationArgsForCall(0)).To(Equal(ccv2.Application(newApp))) 197 }) 198 }) 199 200 Context("when the client returns back an error", func() { 201 var expectedErr error 202 BeforeEach(func() { 203 expectedErr = errors.New("some create app error") 204 fakeCloudControllerClient.CreateApplicationReturns(ccv2.Application{}, ccv2.Warnings{"some-app-warning-1"}, expectedErr) 205 }) 206 207 It("returns warnings and an error", func() { 208 newApp := Application{ 209 Name: "some-app-name", 210 SpaceGUID: "some-space-guid", 211 } 212 _, warnings, err := actor.CreateApplication(newApp) 213 Expect(warnings).To(ConsistOf("some-app-warning-1")) 214 Expect(err).To(MatchError(expectedErr)) 215 }) 216 }) 217 }) 218 219 Describe("GetApplication", func() { 220 Context("when the application exists", func() { 221 BeforeEach(func() { 222 fakeCloudControllerClient.GetApplicationReturns( 223 ccv2.Application{ 224 GUID: "some-app-guid", 225 Name: "some-app", 226 }, 227 ccv2.Warnings{"foo"}, 228 nil, 229 ) 230 }) 231 232 It("returns the application and warnings", func() { 233 app, warnings, err := actor.GetApplication("some-app-guid") 234 Expect(err).ToNot(HaveOccurred()) 235 Expect(app).To(Equal(Application{ 236 GUID: "some-app-guid", 237 Name: "some-app", 238 })) 239 Expect(warnings).To(Equal(Warnings{"foo"})) 240 241 Expect(fakeCloudControllerClient.GetApplicationCallCount()).To(Equal(1)) 242 Expect(fakeCloudControllerClient.GetApplicationArgsForCall(0)).To(Equal("some-app-guid")) 243 }) 244 }) 245 246 Context("when the application does not exist", func() { 247 BeforeEach(func() { 248 fakeCloudControllerClient.GetApplicationReturns(ccv2.Application{}, nil, ccerror.ResourceNotFoundError{}) 249 }) 250 251 It("returns an ApplicationNotFoundError", func() { 252 _, _, err := actor.GetApplication("some-app-guid") 253 Expect(err).To(MatchError(ApplicationNotFoundError{GUID: "some-app-guid"})) 254 }) 255 }) 256 }) 257 258 Describe("GetApplicationByNameAndSpace", func() { 259 Context("when the application exists", func() { 260 BeforeEach(func() { 261 fakeCloudControllerClient.GetApplicationsReturns( 262 []ccv2.Application{ 263 { 264 GUID: "some-app-guid", 265 Name: "some-app", 266 }, 267 }, 268 ccv2.Warnings{"foo"}, 269 nil, 270 ) 271 }) 272 273 It("returns the application and warnings", func() { 274 app, warnings, err := actor.GetApplicationByNameAndSpace("some-app", "some-space-guid") 275 Expect(err).ToNot(HaveOccurred()) 276 Expect(app).To(Equal(Application{ 277 GUID: "some-app-guid", 278 Name: "some-app", 279 })) 280 Expect(warnings).To(Equal(Warnings{"foo"})) 281 282 Expect(fakeCloudControllerClient.GetApplicationsCallCount()).To(Equal(1)) 283 Expect(fakeCloudControllerClient.GetApplicationsArgsForCall(0)).To(ConsistOf([]ccv2.Query{ 284 ccv2.Query{ 285 Filter: ccv2.NameFilter, 286 Operator: ccv2.EqualOperator, 287 Value: "some-app", 288 }, 289 ccv2.Query{ 290 Filter: ccv2.SpaceGUIDFilter, 291 Operator: ccv2.EqualOperator, 292 Value: "some-space-guid", 293 }, 294 })) 295 }) 296 }) 297 298 Context("when the application does not exists", func() { 299 BeforeEach(func() { 300 fakeCloudControllerClient.GetApplicationsReturns([]ccv2.Application{}, nil, nil) 301 }) 302 303 It("returns an ApplicationNotFoundError", func() { 304 _, _, err := actor.GetApplicationByNameAndSpace("some-app", "some-space-guid") 305 Expect(err).To(MatchError(ApplicationNotFoundError{Name: "some-app"})) 306 }) 307 }) 308 309 Context("when the cloud controller client returns an error", func() { 310 var expectedError error 311 312 BeforeEach(func() { 313 expectedError = errors.New("I am a CloudControllerClient Error") 314 fakeCloudControllerClient.GetApplicationsReturns([]ccv2.Application{}, nil, expectedError) 315 }) 316 317 It("returns the error", func() { 318 _, _, err := actor.GetApplicationByNameAndSpace("some-app", "some-space-guid") 319 Expect(err).To(MatchError(expectedError)) 320 }) 321 }) 322 }) 323 324 Describe("GetApplicationsBySpace", func() { 325 Context("when the there are applications in the space", func() { 326 BeforeEach(func() { 327 fakeCloudControllerClient.GetApplicationsReturns( 328 []ccv2.Application{ 329 { 330 GUID: "some-app-guid-1", 331 Name: "some-app-1", 332 }, 333 { 334 GUID: "some-app-guid-2", 335 Name: "some-app-2", 336 }, 337 }, 338 ccv2.Warnings{"warning-1", "warning-2"}, 339 nil, 340 ) 341 }) 342 343 It("returns the application and warnings", func() { 344 apps, warnings, err := actor.GetApplicationsBySpace("some-space-guid") 345 Expect(err).ToNot(HaveOccurred()) 346 Expect(apps).To(ConsistOf( 347 Application{ 348 GUID: "some-app-guid-1", 349 Name: "some-app-1", 350 }, 351 Application{ 352 GUID: "some-app-guid-2", 353 Name: "some-app-2", 354 }, 355 )) 356 Expect(warnings).To(ConsistOf("warning-1", "warning-2")) 357 358 Expect(fakeCloudControllerClient.GetApplicationsCallCount()).To(Equal(1)) 359 Expect(fakeCloudControllerClient.GetApplicationsArgsForCall(0)).To(ConsistOf([]ccv2.Query{ 360 ccv2.Query{ 361 Filter: ccv2.SpaceGUIDFilter, 362 Operator: ccv2.EqualOperator, 363 Value: "some-space-guid", 364 }, 365 })) 366 }) 367 }) 368 369 Context("when the cloud controller client returns an error", func() { 370 var expectedError error 371 372 BeforeEach(func() { 373 expectedError = errors.New("some cc error") 374 fakeCloudControllerClient.GetApplicationsReturns( 375 []ccv2.Application{}, 376 ccv2.Warnings{"warning-1", "warning-2"}, 377 expectedError) 378 }) 379 380 It("returns the error and warnings", func() { 381 _, warnings, err := actor.GetApplicationsBySpace("some-space-guid") 382 Expect(warnings).To(ConsistOf("warning-1", "warning-2")) 383 Expect(err).To(MatchError(expectedError)) 384 }) 385 }) 386 }) 387 388 Describe("GetRouteApplications", func() { 389 Context("when the CC client returns no errors", func() { 390 BeforeEach(func() { 391 fakeCloudControllerClient.GetRouteApplicationsReturns( 392 []ccv2.Application{ 393 { 394 GUID: "application-guid", 395 Name: "application-name", 396 }, 397 }, ccv2.Warnings{"route-applications-warning"}, nil) 398 }) 399 It("returns the applications bound to the route and warnings", func() { 400 applications, warnings, err := actor.GetRouteApplications("route-guid", nil) 401 Expect(fakeCloudControllerClient.GetRouteApplicationsCallCount()).To(Equal(1)) 402 Expect(fakeCloudControllerClient.GetRouteApplicationsArgsForCall(0)).To(Equal("route-guid")) 403 404 Expect(err).ToNot(HaveOccurred()) 405 Expect(warnings).To(ConsistOf("route-applications-warning")) 406 Expect(applications).To(ConsistOf( 407 Application{ 408 GUID: "application-guid", 409 Name: "application-name", 410 }, 411 )) 412 }) 413 }) 414 415 Context("when the CC client returns an error", func() { 416 BeforeEach(func() { 417 fakeCloudControllerClient.GetRouteApplicationsReturns( 418 []ccv2.Application{}, ccv2.Warnings{"route-applications-warning"}, errors.New("get-route-applications-error")) 419 }) 420 421 It("returns the error and warnings", func() { 422 apps, warnings, err := actor.GetRouteApplications("route-guid", nil) 423 Expect(fakeCloudControllerClient.GetRouteApplicationsCallCount()).To(Equal(1)) 424 Expect(fakeCloudControllerClient.GetRouteApplicationsArgsForCall(0)).To(Equal("route-guid")) 425 426 Expect(err).To(MatchError("get-route-applications-error")) 427 Expect(warnings).To(ConsistOf("route-applications-warning")) 428 Expect(apps).To(BeNil()) 429 }) 430 }) 431 432 Context("when a query parameter exists", func() { 433 It("passes the query to the client", func() { 434 expectedQuery := []ccv2.Query{ 435 { 436 Filter: ccv2.RouteGUIDFilter, 437 Operator: ccv2.EqualOperator, 438 Value: "route-guid", 439 }} 440 441 _, _, err := actor.GetRouteApplications("route-guid", expectedQuery) 442 Expect(err).ToNot(HaveOccurred()) 443 _, query := fakeCloudControllerClient.GetRouteApplicationsArgsForCall(0) 444 Expect(query).To(Equal(expectedQuery)) 445 }) 446 }) 447 }) 448 449 Describe("SetApplicationHealthCheckTypeByNameAndSpace", func() { 450 Context("when setting an http endpoint with a health check that is not http", func() { 451 It("returns an http health check invalid error", func() { 452 _, _, err := actor.SetApplicationHealthCheckTypeByNameAndSpace( 453 "some-app", "some-space-guid", "some-health-check-type", "/foo") 454 Expect(err).To(MatchError(HTTPHealthCheckInvalidError{})) 455 }) 456 }) 457 458 Context("when the app exists", func() { 459 Context("when the desired health check type is different", func() { 460 BeforeEach(func() { 461 fakeCloudControllerClient.GetApplicationsReturns( 462 []ccv2.Application{ 463 {GUID: "some-app-guid"}, 464 }, 465 ccv2.Warnings{"get application warning"}, 466 nil, 467 ) 468 fakeCloudControllerClient.UpdateApplicationReturns( 469 ccv2.Application{ 470 GUID: "some-app-guid", 471 HealthCheckType: "process", 472 }, 473 ccv2.Warnings{"update warnings"}, 474 nil, 475 ) 476 }) 477 478 It("sets the desired health check type and returns the warnings", func() { 479 returnedApp, warnings, err := actor.SetApplicationHealthCheckTypeByNameAndSpace( 480 "some-app", "some-space-guid", "process", "/") 481 Expect(err).ToNot(HaveOccurred()) 482 Expect(warnings).To(ConsistOf("get application warning", "update warnings")) 483 484 Expect(returnedApp).To(Equal(Application{ 485 GUID: "some-app-guid", 486 HealthCheckType: "process", 487 })) 488 489 Expect(fakeCloudControllerClient.UpdateApplicationCallCount()).To(Equal(1)) 490 app := fakeCloudControllerClient.UpdateApplicationArgsForCall(0) 491 Expect(app).To(Equal(ccv2.Application{ 492 GUID: "some-app-guid", 493 HealthCheckType: "process", 494 })) 495 }) 496 }) 497 498 Context("when the desired health check type is 'http'", func() { 499 Context("when the desired http endpoint is already set", func() { 500 BeforeEach(func() { 501 fakeCloudControllerClient.GetApplicationsReturns( 502 []ccv2.Application{ 503 {GUID: "some-app-guid", HealthCheckType: "http", HealthCheckHTTPEndpoint: "/"}, 504 }, 505 ccv2.Warnings{"get application warning"}, 506 nil, 507 ) 508 }) 509 510 It("does not send the update", func() { 511 _, warnings, err := actor.SetApplicationHealthCheckTypeByNameAndSpace( 512 "some-app", "some-space-guid", "http", "/") 513 Expect(err).ToNot(HaveOccurred()) 514 Expect(warnings).To(ConsistOf("get application warning")) 515 516 Expect(fakeCloudControllerClient.UpdateApplicationCallCount()).To(Equal(0)) 517 }) 518 }) 519 520 Context("when the desired http endpoint is not set", func() { 521 BeforeEach(func() { 522 fakeCloudControllerClient.GetApplicationsReturns( 523 []ccv2.Application{ 524 {GUID: "some-app-guid", HealthCheckType: "http", HealthCheckHTTPEndpoint: "/"}, 525 }, 526 ccv2.Warnings{"get application warning"}, 527 nil, 528 ) 529 fakeCloudControllerClient.UpdateApplicationReturns( 530 ccv2.Application{}, 531 ccv2.Warnings{"update warnings"}, 532 nil, 533 ) 534 }) 535 536 It("sets the desired health check type and returns the warnings", func() { 537 _, warnings, err := actor.SetApplicationHealthCheckTypeByNameAndSpace( 538 "some-app", "some-space-guid", "http", "/v2/anything") 539 Expect(err).ToNot(HaveOccurred()) 540 541 Expect(fakeCloudControllerClient.UpdateApplicationCallCount()).To(Equal(1)) 542 app := fakeCloudControllerClient.UpdateApplicationArgsForCall(0) 543 Expect(app).To(Equal(ccv2.Application{ 544 GUID: "some-app-guid", 545 HealthCheckType: "http", 546 HealthCheckHTTPEndpoint: "/v2/anything", 547 })) 548 549 Expect(warnings).To(ConsistOf("get application warning", "update warnings")) 550 }) 551 }) 552 }) 553 554 Context("when the application health check type is already set to the desired type", func() { 555 BeforeEach(func() { 556 fakeCloudControllerClient.GetApplicationsReturns( 557 []ccv2.Application{ 558 { 559 GUID: "some-app-guid", 560 HealthCheckType: "process", 561 }, 562 }, 563 ccv2.Warnings{"get application warning"}, 564 nil, 565 ) 566 }) 567 568 It("does not update the health check type", func() { 569 returnedApp, warnings, err := actor.SetApplicationHealthCheckTypeByNameAndSpace( 570 "some-app", "some-space-guid", "process", "/") 571 Expect(err).ToNot(HaveOccurred()) 572 Expect(warnings).To(ConsistOf("get application warning")) 573 Expect(returnedApp).To(Equal(Application{ 574 GUID: "some-app-guid", 575 HealthCheckType: "process", 576 })) 577 578 Expect(fakeCloudControllerClient.UpdateApplicationCallCount()).To(Equal(0)) 579 }) 580 }) 581 }) 582 583 Context("when getting the application returns an error", func() { 584 BeforeEach(func() { 585 fakeCloudControllerClient.GetApplicationsReturns( 586 []ccv2.Application{}, ccv2.Warnings{"get application warning"}, errors.New("get application error")) 587 }) 588 589 It("returns the error and warnings", func() { 590 _, warnings, err := actor.SetApplicationHealthCheckTypeByNameAndSpace( 591 "some-app", "some-space-guid", "process", "/") 592 593 Expect(warnings).To(ConsistOf("get application warning")) 594 Expect(err).To(MatchError("get application error")) 595 }) 596 }) 597 598 Context("when updating the application returns an error", func() { 599 var expectedErr error 600 601 BeforeEach(func() { 602 expectedErr = errors.New("foo bar") 603 fakeCloudControllerClient.GetApplicationsReturns( 604 []ccv2.Application{ 605 {GUID: "some-app-guid"}, 606 }, 607 ccv2.Warnings{"get application warning"}, 608 nil, 609 ) 610 fakeCloudControllerClient.UpdateApplicationReturns( 611 ccv2.Application{}, 612 ccv2.Warnings{"update warnings"}, 613 expectedErr, 614 ) 615 }) 616 617 It("returns the error and warnings", func() { 618 _, warnings, err := actor.SetApplicationHealthCheckTypeByNameAndSpace( 619 "some-app", "some-space-guid", "process", "/") 620 Expect(err).To(MatchError(expectedErr)) 621 Expect(warnings).To(ConsistOf("get application warning", "update warnings")) 622 }) 623 }) 624 }) 625 626 Describe("RestartApplication", func() { 627 var ( 628 app Application 629 fakeNOAAClient *v2actionfakes.FakeNOAAClient 630 fakeConfig *v2actionfakes.FakeConfig 631 632 messages <-chan *LogMessage 633 logErrs <-chan error 634 appStarting <-chan bool 635 warnings <-chan string 636 errs <-chan error 637 638 eventStream chan *events.LogMessage 639 errStream chan error 640 ) 641 642 BeforeEach(func() { 643 fakeConfig = new(v2actionfakes.FakeConfig) 644 fakeConfig.StagingTimeoutReturns(time.Minute) 645 fakeConfig.StartupTimeoutReturns(time.Minute) 646 647 app = Application{ 648 GUID: "some-app-guid", 649 Name: "some-app", 650 Instances: 2, 651 State: ccv2.ApplicationStopped, 652 } 653 654 fakeNOAAClient = new(v2actionfakes.FakeNOAAClient) 655 fakeNOAAClient.TailingLogsStub = func(_ string, _ string) (<-chan *events.LogMessage, <-chan error) { 656 eventStream = make(chan *events.LogMessage) 657 errStream = make(chan error) 658 return eventStream, errStream 659 } 660 661 closed := false 662 fakeNOAAClient.CloseStub = func() error { 663 if !closed { 664 closed = true 665 close(errStream) 666 close(eventStream) 667 } 668 return nil 669 } 670 671 fakeCloudControllerClient.UpdateApplicationReturns(ccv2.Application{GUID: "some-app-guid", 672 Instances: 2, 673 Name: "some-app", 674 }, ccv2.Warnings{"update-warning"}, nil) 675 676 appCount := 0 677 fakeCloudControllerClient.GetApplicationStub = func(appGUID string) (ccv2.Application, ccv2.Warnings, error) { 678 if appCount == 0 { 679 appCount += 1 680 return ccv2.Application{ 681 GUID: "some-app-guid", 682 Instances: 2, 683 Name: "some-app", 684 PackageState: ccv2.ApplicationPackagePending, 685 }, ccv2.Warnings{"app-warnings-1"}, nil 686 } 687 688 return ccv2.Application{ 689 GUID: "some-app-guid", 690 Name: "some-app", 691 Instances: 2, 692 PackageState: ccv2.ApplicationPackageStaged, 693 }, ccv2.Warnings{"app-warnings-2"}, nil 694 } 695 696 instanceCount := 0 697 fakeCloudControllerClient.GetApplicationInstancesByApplicationStub = func(guid string) (map[int]ccv2.ApplicationInstance, ccv2.Warnings, error) { 698 if instanceCount == 0 { 699 instanceCount += 1 700 return map[int]ccv2.ApplicationInstance{ 701 0: {State: ccv2.ApplicationInstanceStarting}, 702 1: {State: ccv2.ApplicationInstanceStarting}, 703 }, ccv2.Warnings{"app-instance-warnings-1"}, nil 704 } 705 706 return map[int]ccv2.ApplicationInstance{ 707 0: {State: ccv2.ApplicationInstanceStarting}, 708 1: {State: ccv2.ApplicationInstanceRunning}, 709 }, ccv2.Warnings{"app-instance-warnings-2"}, nil 710 } 711 }) 712 713 JustBeforeEach(func() { 714 messages, logErrs, appStarting, warnings, errs = actor.RestartApplication(app, fakeNOAAClient, fakeConfig) 715 }) 716 717 AfterEach(func() { 718 Eventually(messages).Should(BeClosed()) 719 Eventually(logErrs).Should(BeClosed()) 720 Eventually(appStarting).Should(BeClosed()) 721 Eventually(warnings).Should(BeClosed()) 722 Eventually(errs).Should(BeClosed()) 723 }) 724 725 Context("when the app is already running", func() { 726 BeforeEach(func() { 727 app.State = ccv2.ApplicationStarted 728 }) 729 730 It("stops, starts and polls for an app instance", func() { 731 Eventually(warnings).Should(Receive(Equal("update-warning"))) 732 Eventually(warnings).Should(Receive(Equal("update-warning"))) 733 Eventually(warnings).Should(Receive(Equal("app-warnings-1"))) 734 Eventually(warnings).Should(Receive(Equal("app-warnings-2"))) 735 Eventually(appStarting).Should(Receive(BeTrue())) 736 Eventually(warnings).Should(Receive(Equal("app-instance-warnings-1"))) 737 Eventually(warnings).Should(Receive(Equal("app-instance-warnings-2"))) 738 739 Expect(fakeConfig.PollingIntervalCallCount()).To(Equal(2)) 740 741 Expect(fakeCloudControllerClient.UpdateApplicationCallCount()).To(Equal(2)) 742 app := fakeCloudControllerClient.UpdateApplicationArgsForCall(0) 743 Expect(app).To(Equal(ccv2.Application{ 744 GUID: "some-app-guid", 745 State: ccv2.ApplicationStopped, 746 })) 747 748 app = fakeCloudControllerClient.UpdateApplicationArgsForCall(1) 749 Expect(app).To(Equal(ccv2.Application{ 750 GUID: "some-app-guid", 751 State: ccv2.ApplicationStarted, 752 })) 753 754 Expect(fakeCloudControllerClient.GetApplicationCallCount()).To(Equal(2)) 755 Expect(fakeCloudControllerClient.GetApplicationInstancesByApplicationCallCount()).To(Equal(2)) 756 Eventually(fakeNOAAClient.CloseCallCount).Should(Equal(2)) 757 }) 758 }) 759 760 Context("when the app is not running", func() { 761 It("starts and polls for an app instance", func() { 762 Eventually(warnings).Should(Receive(Equal("update-warning"))) 763 Eventually(warnings).Should(Receive(Equal("app-warnings-1"))) 764 Eventually(warnings).Should(Receive(Equal("app-warnings-2"))) 765 Eventually(appStarting).Should(Receive(BeTrue())) 766 Eventually(warnings).Should(Receive(Equal("app-instance-warnings-1"))) 767 Eventually(warnings).Should(Receive(Equal("app-instance-warnings-2"))) 768 769 Expect(fakeConfig.PollingIntervalCallCount()).To(Equal(2)) 770 771 Expect(fakeCloudControllerClient.UpdateApplicationCallCount()).To(Equal(1)) 772 app := fakeCloudControllerClient.UpdateApplicationArgsForCall(0) 773 Expect(app).To(Equal(ccv2.Application{ 774 GUID: "some-app-guid", 775 State: ccv2.ApplicationStarted, 776 })) 777 778 Expect(fakeCloudControllerClient.GetApplicationCallCount()).To(Equal(2)) 779 Expect(fakeCloudControllerClient.GetApplicationInstancesByApplicationCallCount()).To(Equal(2)) 780 Eventually(fakeNOAAClient.CloseCallCount).Should(Equal(2)) 781 }) 782 }) 783 784 Context("when the app has zero instances", func() { 785 BeforeEach(func() { 786 fakeCloudControllerClient.UpdateApplicationReturns(ccv2.Application{GUID: "some-app-guid", 787 Instances: 0, 788 Name: "some-app", 789 }, ccv2.Warnings{"update-warning"}, nil) 790 }) 791 792 It("starts and only polls for staging to finish", func() { 793 Eventually(warnings).Should(Receive(Equal("update-warning"))) 794 Eventually(warnings).Should(Receive(Equal("app-warnings-1"))) 795 Eventually(warnings).Should(Receive(Equal("app-warnings-2"))) 796 Consistently(appStarting).ShouldNot(Receive()) 797 798 Expect(fakeConfig.PollingIntervalCallCount()).To(Equal(1)) 799 800 Expect(fakeCloudControllerClient.UpdateApplicationCallCount()).To(Equal(1)) 801 app := fakeCloudControllerClient.UpdateApplicationArgsForCall(0) 802 Expect(app).To(Equal(ccv2.Application{ 803 GUID: "some-app-guid", 804 State: ccv2.ApplicationStarted, 805 })) 806 807 Expect(fakeCloudControllerClient.GetApplicationCallCount()).To(Equal(2)) 808 Expect(fakeCloudControllerClient.GetApplicationInstancesByApplicationCallCount()).To(Equal(0)) 809 }) 810 }) 811 812 Context("when updating the application fails", func() { 813 var expectedErr error 814 BeforeEach(func() { 815 expectedErr = errors.New("I am a banana!!!!") 816 fakeCloudControllerClient.UpdateApplicationReturns(ccv2.Application{}, ccv2.Warnings{"update-warning"}, expectedErr) 817 }) 818 819 It("sends the update error and never polls", func() { 820 Eventually(warnings).Should(Receive(Equal("update-warning"))) 821 Eventually(errs).Should(Receive(MatchError(expectedErr))) 822 823 Expect(fakeConfig.PollingIntervalCallCount()).To(Equal(0)) 824 Expect(fakeCloudControllerClient.GetApplicationCallCount()).To(Equal(0)) 825 Expect(fakeCloudControllerClient.GetApplicationInstancesByApplicationCallCount()).To(Equal(0)) 826 }) 827 }) 828 829 Context("staging issues", func() { 830 Context("when polling fails", func() { 831 var expectedErr error 832 BeforeEach(func() { 833 expectedErr = errors.New("I am a banana!!!!") 834 fakeCloudControllerClient.GetApplicationStub = func(appGUID string) (ccv2.Application, ccv2.Warnings, error) { 835 return ccv2.Application{}, ccv2.Warnings{"app-warnings-1"}, expectedErr 836 } 837 }) 838 839 It("sends the error and stops polling", func() { 840 Eventually(warnings).Should(Receive(Equal("update-warning"))) 841 Eventually(warnings).Should(Receive(Equal("app-warnings-1"))) 842 Eventually(errs).Should(Receive(MatchError(expectedErr))) 843 844 Expect(fakeConfig.PollingIntervalCallCount()).To(Equal(0)) 845 Expect(fakeCloudControllerClient.GetApplicationCallCount()).To(Equal(1)) 846 Expect(fakeCloudControllerClient.GetApplicationInstancesByApplicationCallCount()).To(Equal(0)) 847 }) 848 }) 849 850 Context("when the application fails to stage", func() { 851 Context("due to a NoAppDetectedError", func() { 852 BeforeEach(func() { 853 fakeCloudControllerClient.GetApplicationStub = func(appGUID string) (ccv2.Application, ccv2.Warnings, error) { 854 return ccv2.Application{ 855 GUID: "some-app-guid", 856 Name: "some-app", 857 Instances: 2, 858 PackageState: ccv2.ApplicationPackageFailed, 859 StagingFailedReason: "NoAppDetectedError", 860 }, ccv2.Warnings{"app-warnings-1"}, nil 861 } 862 }) 863 864 It("sends a StagingFailedNoAppDetectedError and stops polling", func() { 865 Eventually(warnings).Should(Receive(Equal("update-warning"))) 866 Eventually(warnings).Should(Receive(Equal("app-warnings-1"))) 867 Eventually(errs).Should(Receive(MatchError(StagingFailedNoAppDetectedError{Reason: "NoAppDetectedError"}))) 868 869 Expect(fakeConfig.PollingIntervalCallCount()).To(Equal(0)) 870 Expect(fakeConfig.StagingTimeoutCallCount()).To(Equal(1)) 871 Expect(fakeCloudControllerClient.GetApplicationCallCount()).To(Equal(1)) 872 Expect(fakeCloudControllerClient.GetApplicationInstancesByApplicationCallCount()).To(Equal(0)) 873 }) 874 }) 875 876 Context("due to any other error", func() { 877 BeforeEach(func() { 878 fakeCloudControllerClient.GetApplicationStub = func(appGUID string) (ccv2.Application, ccv2.Warnings, error) { 879 return ccv2.Application{ 880 GUID: "some-app-guid", 881 Name: "some-app", 882 Instances: 2, 883 PackageState: ccv2.ApplicationPackageFailed, 884 StagingFailedReason: "OhNoes", 885 }, ccv2.Warnings{"app-warnings-1"}, nil 886 } 887 }) 888 889 It("sends a StagingFailedError and stops polling", func() { 890 Eventually(warnings).Should(Receive(Equal("update-warning"))) 891 Eventually(warnings).Should(Receive(Equal("app-warnings-1"))) 892 Eventually(errs).Should(Receive(MatchError(StagingFailedError{Reason: "OhNoes"}))) 893 894 Expect(fakeConfig.PollingIntervalCallCount()).To(Equal(0)) 895 Expect(fakeConfig.StagingTimeoutCallCount()).To(Equal(1)) 896 Expect(fakeCloudControllerClient.GetApplicationCallCount()).To(Equal(1)) 897 Expect(fakeCloudControllerClient.GetApplicationInstancesByApplicationCallCount()).To(Equal(0)) 898 }) 899 }) 900 }) 901 902 Context("when the application takes too long to stage", func() { 903 BeforeEach(func() { 904 fakeConfig.StagingTimeoutReturns(0) 905 fakeCloudControllerClient.GetApplicationInstancesByApplicationStub = nil 906 }) 907 908 It("sends a timeout error and stops polling", func() { 909 Eventually(warnings).Should(Receive(Equal("update-warning"))) 910 Eventually(errs).Should(Receive(MatchError(StagingTimeoutError{Name: "some-app", Timeout: 0}))) 911 912 Expect(fakeConfig.PollingIntervalCallCount()).To(Equal(0)) 913 Expect(fakeConfig.StagingTimeoutCallCount()).To(Equal(2)) 914 Expect(fakeCloudControllerClient.GetApplicationCallCount()).To(Equal(0)) 915 Expect(fakeCloudControllerClient.GetApplicationInstancesByApplicationCallCount()).To(Equal(0)) 916 }) 917 }) 918 }) 919 920 Context("starting issues", func() { 921 Context("when polling fails", func() { 922 var expectedErr error 923 BeforeEach(func() { 924 expectedErr = errors.New("I am a banana!!!!") 925 fakeCloudControllerClient.GetApplicationInstancesByApplicationStub = func(guid string) (map[int]ccv2.ApplicationInstance, ccv2.Warnings, error) { 926 return nil, ccv2.Warnings{"app-instance-warnings-1"}, expectedErr 927 } 928 }) 929 930 It("sends the error and stops polling", func() { 931 Eventually(warnings).Should(Receive(Equal("update-warning"))) 932 Eventually(warnings).Should(Receive(Equal("app-warnings-1"))) 933 Eventually(warnings).Should(Receive(Equal("app-warnings-2"))) 934 Eventually(appStarting).Should(Receive(BeTrue())) 935 Eventually(warnings).Should(Receive(Equal("app-instance-warnings-1"))) 936 Eventually(errs).Should(Receive(MatchError(expectedErr))) 937 938 Expect(fakeConfig.PollingIntervalCallCount()).To(Equal(1)) 939 Expect(fakeCloudControllerClient.GetApplicationInstancesByApplicationCallCount()).To(Equal(1)) 940 }) 941 }) 942 943 Context("when the application takes too long to start", func() { 944 BeforeEach(func() { 945 fakeConfig.StartupTimeoutReturns(0) 946 }) 947 948 It("sends a timeout error and stops polling", func() { 949 Eventually(warnings).Should(Receive(Equal("update-warning"))) 950 Eventually(warnings).Should(Receive(Equal("app-warnings-1"))) 951 Eventually(warnings).Should(Receive(Equal("app-warnings-2"))) 952 Eventually(appStarting).Should(Receive(BeTrue())) 953 Eventually(errs).Should(Receive(MatchError(StartupTimeoutError{Name: "some-app"}))) 954 955 Expect(fakeConfig.PollingIntervalCallCount()).To(Equal(1)) 956 Expect(fakeConfig.StartupTimeoutCallCount()).To(Equal(1)) 957 Expect(fakeCloudControllerClient.GetApplicationInstancesByApplicationCallCount()).To(Equal(0)) 958 }) 959 }) 960 961 Context("when the application crashes", func() { 962 BeforeEach(func() { 963 fakeCloudControllerClient.GetApplicationInstancesByApplicationStub = func(guid string) (map[int]ccv2.ApplicationInstance, ccv2.Warnings, error) { 964 return map[int]ccv2.ApplicationInstance{ 965 0: {State: ccv2.ApplicationInstanceCrashed}, 966 }, ccv2.Warnings{"app-instance-warnings-1"}, nil 967 } 968 }) 969 970 It("returns an ApplicationInstanceCrashedError and stops polling", func() { 971 Eventually(warnings).Should(Receive(Equal("update-warning"))) 972 Eventually(warnings).Should(Receive(Equal("app-warnings-1"))) 973 Eventually(warnings).Should(Receive(Equal("app-warnings-2"))) 974 Eventually(appStarting).Should(Receive(BeTrue())) 975 Eventually(warnings).Should(Receive(Equal("app-instance-warnings-1"))) 976 Eventually(errs).Should(Receive(MatchError(ApplicationInstanceCrashedError{Name: "some-app"}))) 977 978 Expect(fakeConfig.PollingIntervalCallCount()).To(Equal(1)) 979 Expect(fakeConfig.StartupTimeoutCallCount()).To(Equal(1)) 980 Expect(fakeCloudControllerClient.GetApplicationInstancesByApplicationCallCount()).To(Equal(1)) 981 }) 982 }) 983 984 Context("when the application flaps", func() { 985 BeforeEach(func() { 986 fakeCloudControllerClient.GetApplicationInstancesByApplicationStub = func(guid string) (map[int]ccv2.ApplicationInstance, ccv2.Warnings, error) { 987 return map[int]ccv2.ApplicationInstance{ 988 0: {State: ccv2.ApplicationInstanceFlapping}, 989 }, ccv2.Warnings{"app-instance-warnings-1"}, nil 990 } 991 }) 992 993 It("returns an ApplicationInstanceFlappingError and stops polling", func() { 994 Eventually(warnings).Should(Receive(Equal("update-warning"))) 995 Eventually(warnings).Should(Receive(Equal("app-warnings-1"))) 996 Eventually(warnings).Should(Receive(Equal("app-warnings-2"))) 997 Eventually(appStarting).Should(Receive(BeTrue())) 998 Eventually(warnings).Should(Receive(Equal("app-instance-warnings-1"))) 999 Eventually(errs).Should(Receive(MatchError(ApplicationInstanceFlappingError{Name: "some-app"}))) 1000 1001 Expect(fakeConfig.PollingIntervalCallCount()).To(Equal(1)) 1002 Expect(fakeConfig.StartupTimeoutCallCount()).To(Equal(1)) 1003 Expect(fakeCloudControllerClient.GetApplicationInstancesByApplicationCallCount()).To(Equal(1)) 1004 }) 1005 }) 1006 }) 1007 }) 1008 1009 Describe("UpdateApplication", func() { 1010 Context("when the update is successful", func() { 1011 var expectedApp ccv2.Application 1012 BeforeEach(func() { 1013 expectedApp = ccv2.Application{ 1014 GUID: "some-app-guid", 1015 Name: "some-app-name", 1016 SpaceGUID: "some-space-guid", 1017 } 1018 fakeCloudControllerClient.UpdateApplicationReturns(expectedApp, ccv2.Warnings{"some-app-warning-1"}, nil) 1019 }) 1020 1021 It("updates and returns the application", func() { 1022 newApp := Application{ 1023 Name: "some-app-name", 1024 SpaceGUID: "some-space-guid", 1025 } 1026 app, warnings, err := actor.UpdateApplication(newApp) 1027 Expect(err).ToNot(HaveOccurred()) 1028 Expect(warnings).To(ConsistOf("some-app-warning-1")) 1029 Expect(app).To(Equal(Application(expectedApp))) 1030 1031 Expect(fakeCloudControllerClient.UpdateApplicationCallCount()).To(Equal(1)) 1032 Expect(fakeCloudControllerClient.UpdateApplicationArgsForCall(0)).To(Equal(ccv2.Application(newApp))) 1033 }) 1034 }) 1035 1036 Context("when the client returns back an error", func() { 1037 var expectedErr error 1038 BeforeEach(func() { 1039 expectedErr = errors.New("some update app error") 1040 fakeCloudControllerClient.UpdateApplicationReturns(ccv2.Application{}, ccv2.Warnings{"some-app-warning-1"}, expectedErr) 1041 }) 1042 1043 It("returns warnings and an error", func() { 1044 newApp := Application{ 1045 Name: "some-app-name", 1046 SpaceGUID: "some-space-guid", 1047 } 1048 _, warnings, err := actor.UpdateApplication(newApp) 1049 Expect(warnings).To(ConsistOf("some-app-warning-1")) 1050 Expect(err).To(MatchError(expectedErr)) 1051 }) 1052 }) 1053 }) 1054 })