github.com/niteshexa/cloudfoundry_cli@v7.1.0+incompatible/command/v7/shared/app_stager_test.go (about) 1 package shared_test 2 3 import ( 4 "context" 5 "errors" 6 "time" 7 8 "code.cloudfoundry.org/cli/actor/actionerror" 9 "code.cloudfoundry.org/cli/actor/sharedaction" 10 "code.cloudfoundry.org/cli/actor/sharedaction/sharedactionfakes" 11 "code.cloudfoundry.org/cli/actor/v7action" 12 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant" 13 "code.cloudfoundry.org/cli/command/commandfakes" 14 "code.cloudfoundry.org/cli/command/v7/shared" 15 "code.cloudfoundry.org/cli/command/v7/v7fakes" 16 "code.cloudfoundry.org/cli/resources" 17 "code.cloudfoundry.org/cli/util/configv3" 18 "code.cloudfoundry.org/cli/util/ui" 19 . "github.com/onsi/ginkgo" 20 . "github.com/onsi/gomega" 21 . "github.com/onsi/gomega/gbytes" 22 ) 23 24 var _ = Describe("app stager", func() { 25 var ( 26 appStager shared.AppStager 27 executeErr error 28 testUI *ui.UI 29 fakeConfig *commandfakes.FakeConfig 30 fakeActor *v7fakes.FakeActor 31 fakeLogCacheClient *sharedactionfakes.FakeLogCacheClient 32 33 app resources.Application 34 space configv3.Space 35 organization configv3.Organization 36 pkgGUID string 37 strategy constant.DeploymentStrategy 38 noWait bool 39 appAction constant.ApplicationAction 40 41 allLogsWritten chan bool 42 closedTheStreams bool 43 ) 44 45 const dropletCreateTime = "2017-08-14T21:16:42Z" 46 47 Context("StageAndStart", func() { 48 BeforeEach(func() { 49 testUI = ui.NewTestUI(nil, NewBuffer(), NewBuffer()) 50 fakeConfig = new(commandfakes.FakeConfig) 51 fakeConfig.BinaryNameReturns("some-binary-name") 52 fakeActor = new(v7fakes.FakeActor) 53 fakeLogCacheClient = new(sharedactionfakes.FakeLogCacheClient) 54 allLogsWritten = make(chan bool) 55 56 pkgGUID = "package-guid" 57 app = resources.Application{GUID: "app-guid", Name: "app-name"} 58 space = configv3.Space{Name: "some-space", GUID: "some-space-guid"} 59 organization = configv3.Organization{Name: "some-org"} 60 strategy = constant.DeploymentStrategyDefault 61 appAction = constant.ApplicationRestarting 62 63 fakeActor.GetStreamingLogsForApplicationByNameAndSpaceStub = func(appName string, spaceGUID string, client sharedaction.LogCacheClient) (<-chan sharedaction.LogMessage, <-chan error, context.CancelFunc, v7action.Warnings, error) { 64 logStream := make(chan sharedaction.LogMessage) 65 errorStream := make(chan error) 66 closedTheStreams = false 67 68 cancelFunc := func() { 69 if closedTheStreams { 70 return 71 } 72 closedTheStreams = true 73 close(logStream) 74 close(errorStream) 75 } 76 go func() { 77 logStream <- *sharedaction.NewLogMessage("Here's an output log!", "OUT", time.Now(), "OUT", "sourceInstance-1") 78 logStream <- *sharedaction.NewLogMessage("Here's a staging log!", sharedaction.StagingLog, time.Now(), sharedaction.StagingLog, "sourceInstance-2") 79 errorStream <- errors.New("something bad happened while trying to get staging logs") 80 allLogsWritten <- true 81 }() 82 83 return logStream, errorStream, cancelFunc, v7action.Warnings{"get-logs-warning"}, nil 84 } 85 fakeActor.StagePackageStub = func(packageGUID, appName, spaceGUID string) (<-chan resources.Droplet, <-chan v7action.Warnings, <-chan error) { 86 dropletStream := make(chan resources.Droplet) 87 warningsStream := make(chan v7action.Warnings) 88 errorStream := make(chan error) 89 90 go func() { 91 <-allLogsWritten 92 defer close(dropletStream) 93 defer close(warningsStream) 94 defer close(errorStream) 95 warningsStream <- v7action.Warnings{"stage-package-warning"} 96 dropletStream <- resources.Droplet{ 97 GUID: "some-droplet-guid", 98 CreatedAt: dropletCreateTime, 99 State: constant.DropletStaged, 100 } 101 }() 102 103 return dropletStream, warningsStream, errorStream 104 } 105 }) 106 107 JustBeforeEach(func() { 108 appStager = shared.NewAppStager(fakeActor, testUI, fakeConfig, fakeLogCacheClient) 109 executeErr = appStager.StageAndStart( 110 app, 111 space, 112 organization, 113 pkgGUID, 114 strategy, 115 noWait, 116 appAction, 117 ) 118 }) 119 120 It("stages and starts the app", func() { 121 Expect(executeErr).ToNot(HaveOccurred()) 122 Expect(testUI.Out).To(Say("Staging app and tracing logs...")) 123 124 user, err := fakeConfig.CurrentUser() 125 Expect(err).NotTo(HaveOccurred()) 126 Expect(testUI.Out).To(Say(`Restarting app %s in org %s / space %s as %s\.\.\.`, app.Name, organization.Name, space.Name, user.Name)) 127 Expect(testUI.Out).To(Say("Waiting for app to start...")) 128 }) 129 130 When("staging fails", func() { 131 JustAfterEach(func() { 132 Expect(closedTheStreams).To(BeTrue()) 133 }) 134 BeforeEach(func() { 135 expectedErr := errors.New("package-staging-error") 136 fakeActor.StagePackageStub = func(packageGUID, _, _ string) (<-chan resources.Droplet, <-chan v7action.Warnings, <-chan error) { 137 138 dropletStream := make(chan resources.Droplet) 139 warningsStream := make(chan v7action.Warnings) 140 errorStream := make(chan error) 141 142 go func() { 143 <-allLogsWritten 144 defer close(dropletStream) 145 defer close(warningsStream) 146 defer close(errorStream) 147 warningsStream <- v7action.Warnings{"some-package-warning", "some-other-package-warning"} 148 errorStream <- expectedErr 149 }() 150 151 return dropletStream, warningsStream, errorStream 152 } 153 }) 154 155 It("displays all warnings and returns an error", func() { 156 Expect(executeErr).To(MatchError("package-staging-error")) 157 158 Expect(testUI.Err).To(Say("some-package-warning")) 159 Expect(testUI.Err).To(Say("some-other-package-warning")) 160 }) 161 }) 162 163 When("starting fails", func() { 164 BeforeEach(func() { 165 fakeActor.StartApplicationReturns( 166 v7action.Warnings{"start-app-warning"}, errors.New("start-app-error")) 167 }) 168 169 It("returns an error", func() { 170 Expect(executeErr).To(MatchError("start-app-error")) 171 }) 172 }) 173 }) 174 175 Context("StageApp", func() { 176 var ( 177 droplet resources.Droplet 178 ) 179 180 BeforeEach(func() { 181 testUI = ui.NewTestUI(nil, NewBuffer(), NewBuffer()) 182 fakeConfig = new(commandfakes.FakeConfig) 183 fakeConfig.BinaryNameReturns("some-binary-name") 184 fakeActor = new(v7fakes.FakeActor) 185 fakeLogCacheClient = new(sharedactionfakes.FakeLogCacheClient) 186 allLogsWritten = make(chan bool) 187 188 pkgGUID = "package-guid" 189 app = resources.Application{GUID: "app-guid", Name: "app-name"} 190 space = configv3.Space{Name: "some-space", GUID: "some-space-guid"} 191 organization = configv3.Organization{Name: "some-org"} 192 strategy = constant.DeploymentStrategyDefault 193 194 fakeActor.GetStreamingLogsForApplicationByNameAndSpaceStub = func(appName string, spaceGUID string, client sharedaction.LogCacheClient) (<-chan sharedaction.LogMessage, <-chan error, context.CancelFunc, v7action.Warnings, error) { 195 logStream := make(chan sharedaction.LogMessage) 196 errorStream := make(chan error) 197 closedTheStreams = false 198 199 cancelFunc := func() { 200 if closedTheStreams { 201 return 202 } 203 closedTheStreams = true 204 close(logStream) 205 close(errorStream) 206 } 207 go func() { 208 logStream <- *sharedaction.NewLogMessage("Here's an output log!", "OUT", time.Now(), "OUT", "sourceInstance-1") 209 logStream <- *sharedaction.NewLogMessage("Here's a staging log!", sharedaction.StagingLog, time.Now(), sharedaction.StagingLog, "sourceInstance-2") 210 errorStream <- errors.New("something bad happened while trying to get staging logs") 211 allLogsWritten <- true 212 }() 213 214 return logStream, errorStream, cancelFunc, v7action.Warnings{"get-logs-warning"}, nil 215 } 216 fakeActor.StagePackageStub = func(packageGUID, appName, spaceGUID string) (<-chan resources.Droplet, <-chan v7action.Warnings, <-chan error) { 217 dropletStream := make(chan resources.Droplet) 218 warningsStream := make(chan v7action.Warnings) 219 errorStream := make(chan error) 220 221 go func() { 222 <-allLogsWritten 223 defer close(dropletStream) 224 defer close(warningsStream) 225 defer close(errorStream) 226 warningsStream <- v7action.Warnings{"stage-package-warning"} 227 dropletStream <- resources.Droplet{ 228 GUID: "some-droplet-guid", 229 CreatedAt: dropletCreateTime, 230 State: constant.DropletStaged, 231 } 232 }() 233 234 return dropletStream, warningsStream, errorStream 235 } 236 }) 237 238 JustBeforeEach(func() { 239 appStager = shared.NewAppStager(fakeActor, testUI, fakeConfig, fakeLogCacheClient) 240 droplet, executeErr = appStager.StageApp( 241 app, 242 pkgGUID, 243 space, 244 ) 245 }) 246 247 It("stages the app and polls until it is complete", func() { 248 Expect(testUI.Out).To(Say("Staging app and tracing logs...")) 249 Expect(executeErr).ToNot(HaveOccurred()) 250 Expect(droplet).To(Equal( 251 resources.Droplet{ 252 GUID: "some-droplet-guid", 253 CreatedAt: dropletCreateTime, 254 State: constant.DropletStaged, 255 }, 256 )) 257 Expect(fakeActor.StagePackageCallCount()).To(Equal(1)) 258 inputPackageGUID, inputAppName, inputSpaceGUID := fakeActor.StagePackageArgsForCall(0) 259 Expect(inputPackageGUID).To(Equal("package-guid")) 260 Expect(inputAppName).To(Equal("app-name")) 261 Expect(inputSpaceGUID).To(Equal("some-space-guid")) 262 }) 263 264 When("getting logs for the app fails because getting the app fails", func() { 265 BeforeEach(func() { 266 fakeActor.GetStreamingLogsForApplicationByNameAndSpaceReturns( 267 nil, nil, nil, v7action.Warnings{"get-log-streaming-warning"}, errors.New("get-log-streaming-error")) 268 }) 269 270 It("displays all warnings and returns an error", func() { 271 Expect(executeErr).To(MatchError("get-log-streaming-error")) 272 Expect(droplet).To(Equal(resources.Droplet{})) 273 Expect(testUI.Err).To(Say("get-log-streaming-warning")) 274 }) 275 }) 276 277 When("staging the package fails", func() { 278 JustAfterEach(func() { 279 Expect(closedTheStreams).To(BeTrue()) 280 }) 281 BeforeEach(func() { 282 expectedErr := errors.New("package-staging-error") 283 fakeActor.StagePackageStub = func(packageGUID, _, _ string) (<-chan resources.Droplet, <-chan v7action.Warnings, <-chan error) { 284 285 dropletStream := make(chan resources.Droplet) 286 warningsStream := make(chan v7action.Warnings) 287 errorStream := make(chan error) 288 289 go func() { 290 <-allLogsWritten 291 defer close(dropletStream) 292 defer close(warningsStream) 293 defer close(errorStream) 294 warningsStream <- v7action.Warnings{"some-package-warning", "some-other-package-warning"} 295 errorStream <- expectedErr 296 }() 297 298 return dropletStream, warningsStream, errorStream 299 } 300 }) 301 302 It("displays all warnings and returns an error", func() { 303 Expect(executeErr).To(MatchError("package-staging-error")) 304 305 Expect(testUI.Err).To(Say("some-package-warning")) 306 Expect(testUI.Err).To(Say("some-other-package-warning")) 307 }) 308 }) 309 }) 310 311 Context("StartApp", func() { 312 var ( 313 droplet resources.Droplet 314 ) 315 316 BeforeEach(func() { 317 testUI = ui.NewTestUI(nil, NewBuffer(), NewBuffer()) 318 fakeConfig = new(commandfakes.FakeConfig) 319 fakeConfig.BinaryNameReturns("some-binary-name") 320 fakeActor = new(v7fakes.FakeActor) 321 fakeLogCacheClient = new(sharedactionfakes.FakeLogCacheClient) 322 allLogsWritten = make(chan bool) 323 324 strategy = constant.DeploymentStrategyDefault 325 noWait = true 326 appAction = constant.ApplicationRestarting 327 328 app = resources.Application{GUID: "app-guid", Name: "app-name", State: constant.ApplicationStarted} 329 droplet = resources.Droplet{GUID: "droplet-guid"} 330 space = configv3.Space{Name: "some-space", GUID: "some-space-guid"} 331 organization = configv3.Organization{Name: "some-org"} 332 333 fakeConfig.CurrentUserReturns(configv3.User{Name: "steve"}, nil) 334 fakeActor.GetDetailedAppSummaryReturns(v7action.DetailedApplicationSummary{}, v7action.Warnings{"application-summary-warning-1", "application-summary-warning-2"}, nil) 335 }) 336 337 JustBeforeEach(func() { 338 appStager = shared.NewAppStager(fakeActor, testUI, fakeConfig, fakeLogCacheClient) 339 executeErr = appStager.StartApp( 340 app, 341 droplet, 342 strategy, 343 noWait, 344 space, 345 organization, 346 appAction, 347 ) 348 }) 349 350 When("the deployment strategy is rolling", func() { 351 BeforeEach(func() { 352 strategy = constant.DeploymentStrategyRolling 353 fakeActor.CreateDeploymentByApplicationAndDropletReturns( 354 "some-deployment-guid", 355 v7action.Warnings{"create-deployment-warning"}, 356 nil, 357 ) 358 fakeActor.PollStartForRollingReturns( 359 v7action.Warnings{"poll-start-warning"}, 360 nil, 361 ) 362 363 }) 364 365 When("the app starts successfully", func() { 366 It("displays output for each step of deploying", func() { 367 Expect(executeErr).To(BeNil()) 368 369 Expect(testUI.Out).To(Say("Creating deployment for app %s...", app.Name)) 370 Expect(fakeActor.CreateDeploymentByApplicationAndDropletCallCount()).To(Equal(1)) 371 appGUID, dropletGUID := fakeActor.CreateDeploymentByApplicationAndDropletArgsForCall(0) 372 Expect(appGUID).To(Equal(app.GUID)) 373 Expect(dropletGUID).To(Equal("droplet-guid")) 374 Expect(testUI.Err).To(Say("create-deployment-warning")) 375 376 Expect(testUI.Out).To(Say("Waiting for app to deploy...")) 377 Expect(fakeActor.PollStartForRollingCallCount()).To(Equal(1)) 378 Expect(testUI.Err).To(Say("poll-start-warning")) 379 }) 380 }) 381 382 When("creating a deployment fails", func() { 383 BeforeEach(func() { 384 fakeActor.CreateDeploymentByApplicationAndDropletReturns( 385 "", 386 v7action.Warnings{"create-deployment-warning"}, 387 errors.New("create-deployment-error"), 388 ) 389 }) 390 391 It("returns an error", func() { 392 Expect(executeErr).To(MatchError("create-deployment-error")) 393 }) 394 }) 395 396 When("polling fails for a rolling restage", func() { 397 BeforeEach(func() { 398 fakeActor.PollStartForRollingReturns( 399 v7action.Warnings{"poll-start-warning"}, 400 errors.New("poll-start-error"), 401 ) 402 }) 403 404 It("returns an error", func() { 405 Expect(executeErr).To(MatchError("poll-start-error")) 406 }) 407 }) 408 }) 409 410 When("the deployment strategy is NOT rolling", func() { 411 BeforeEach(func() { 412 fakeActor.StopApplicationReturns( 413 v7action.Warnings{"stop-app-warning"}, nil) 414 fakeActor.SetApplicationDropletReturns( 415 v7action.Warnings{"set-droplet-warning"}, nil) 416 fakeActor.StartApplicationReturns( 417 v7action.Warnings{"start-app-warning"}, nil) 418 fakeActor.PollStartReturns( 419 v7action.Warnings{"poll-app-warning"}, nil) 420 421 }) 422 423 When("the app action is starting", func() { 424 BeforeEach(func() { 425 appAction = constant.ApplicationStarting 426 app = resources.Application{GUID: "app-guid", Name: "app-name", State: constant.ApplicationStopped} 427 }) 428 429 It("displays output for each step of starting", func() { 430 Expect(executeErr).To(BeNil()) 431 432 user, err := fakeConfig.CurrentUser() 433 Expect(err).NotTo(HaveOccurred()) 434 Expect(testUI.Out).To(Say(`Starting app %s in org %s / space %s as %s\.\.\.`, app.Name, organization.Name, space.Name, user.Name)) 435 436 Expect(testUI.Out).NotTo(Say("Stopping app...")) 437 Expect(fakeActor.StopApplicationCallCount()).To(Equal(0)) 438 439 Expect(fakeActor.SetApplicationDropletCallCount()).To(Equal(1)) 440 Expect(testUI.Err).To(Say("set-droplet-warning")) 441 442 Expect(testUI.Out).To(Say("Waiting for app to start...")) 443 Expect(fakeActor.StartApplicationCallCount()).To(Equal(1)) 444 Expect(testUI.Err).To(Say("start-app-warning")) 445 446 Expect(fakeActor.PollStartCallCount()).To(Equal(1)) 447 Expect(testUI.Err).To(Say("poll-app-warning")) 448 }) 449 450 When("the app is already started", func() { 451 BeforeEach(func() { 452 app = resources.Application{GUID: "app-guid", Name: "app-name", State: constant.ApplicationStarted} 453 }) 454 It("does not attempt to start the application", func() { 455 Expect(executeErr).To(BeNil()) 456 Expect(testUI.Out).To(Say(`App '%s' is already started.`, app.Name)) 457 Expect(fakeActor.StartApplicationCallCount()).To(Equal(0)) 458 }) 459 }) 460 }) 461 462 When("the app action is restarting", func() { 463 It("displays output for each step of restarting", func() { 464 Expect(executeErr).To(BeNil()) 465 466 user, err := fakeConfig.CurrentUser() 467 Expect(err).NotTo(HaveOccurred()) 468 Expect(testUI.Out).To(Say(`Restarting app %s in org %s / space %s as %s\.\.\.`, app.Name, organization.Name, space.Name, user.Name)) 469 470 Expect(testUI.Out).To(Say("Stopping app...")) 471 Expect(fakeActor.StopApplicationCallCount()).To(Equal(1)) 472 Expect(testUI.Err).To(Say("stop-app-warning")) 473 474 Expect(fakeActor.SetApplicationDropletCallCount()).To(Equal(1)) 475 Expect(testUI.Err).To(Say("set-droplet-warning")) 476 477 Expect(testUI.Out).To(Say("Waiting for app to start...")) 478 Expect(fakeActor.StartApplicationCallCount()).To(Equal(1)) 479 Expect(testUI.Err).To(Say("start-app-warning")) 480 481 Expect(fakeActor.PollStartCallCount()).To(Equal(1)) 482 Expect(testUI.Err).To(Say("poll-app-warning")) 483 }) 484 485 When("the app is already stopped", func() { 486 BeforeEach(func() { 487 app = resources.Application{GUID: "app-guid", Name: "app-name", State: constant.ApplicationStopped} 488 }) 489 490 It("does not stop the application", func() { 491 Expect(executeErr).To(BeNil()) 492 Expect(testUI.Out).NotTo(Say("Stopping app...")) 493 Expect(fakeActor.StopApplicationCallCount()).To(Equal(0)) 494 }) 495 }) 496 497 When("stopping the app fails", func() { 498 BeforeEach(func() { 499 fakeActor.StopApplicationReturns( 500 v7action.Warnings{"stop-app-warning"}, errors.New("stop-app-error")) 501 }) 502 503 It("returns an error", func() { 504 Expect(executeErr).To(MatchError("stop-app-error")) 505 }) 506 }) 507 }) 508 509 When("a droplet guid is not provided", func() { 510 BeforeEach(func() { 511 droplet = resources.Droplet{} 512 }) 513 514 It("does not try to set the application droplet", func() { 515 Expect(executeErr).To(BeNil()) 516 Expect(fakeActor.SetApplicationDropletCallCount()).To(Equal(0)) 517 }) 518 }) 519 520 When("setting the droplet fails", func() { 521 BeforeEach(func() { 522 fakeActor.SetApplicationDropletReturns( 523 v7action.Warnings{"set-droplet-warning"}, errors.New("set-droplet-error")) 524 }) 525 526 It("returns an error", func() { 527 Expect(executeErr).To(MatchError("set-droplet-error")) 528 }) 529 }) 530 531 When("starting the application fails", func() { 532 BeforeEach(func() { 533 fakeActor.StartApplicationReturns( 534 v7action.Warnings{"start-app-warning"}, errors.New("start-app-error")) 535 }) 536 537 It("returns an error", func() { 538 Expect(executeErr).To(MatchError("start-app-error")) 539 }) 540 }) 541 542 When("polling the application fails", func() { 543 BeforeEach(func() { 544 fakeActor.PollStartReturns( 545 v7action.Warnings{"poll-app-warning"}, errors.New("poll-app-error")) 546 }) 547 548 It("returns an error", func() { 549 Expect(executeErr).To(MatchError("poll-app-error")) 550 }) 551 }) 552 }) 553 554 It("Gets the detailed app summary", func() { 555 Expect(fakeActor.GetDetailedAppSummaryCallCount()).To(Equal(1)) 556 appName, targetedSpaceGUID, obfuscatedValues := fakeActor.GetDetailedAppSummaryArgsForCall(0) 557 Expect(appName).To(Equal(app.Name)) 558 Expect(targetedSpaceGUID).To(Equal("some-space-guid")) 559 Expect(obfuscatedValues).To(BeFalse()) 560 }) 561 562 It("displays the warnings", func() { 563 Expect(testUI.Err).To(Say("application-summary-warning-1")) 564 Expect(testUI.Err).To(Say("application-summary-warning-2")) 565 566 }) 567 568 When("getting the app summary fails", func() { 569 var expectedErr error 570 571 BeforeEach(func() { 572 expectedErr = actionerror.ApplicationNotFoundError{Name: app.Name} 573 fakeActor.GetDetailedAppSummaryReturns(v7action.DetailedApplicationSummary{}, v7action.Warnings{"application-summary-warning-1", "application-summary-warning-2"}, expectedErr) 574 }) 575 576 It("displays all warnings and returns an error", func() { 577 Expect(executeErr).To(Equal(actionerror.ApplicationNotFoundError{Name: app.Name})) 578 }) 579 }) 580 581 It("succeeds", func() { 582 Expect(executeErr).To(Not(HaveOccurred())) 583 }) 584 }) 585 })