github.com/nimakaviani/cli@v6.37.1-0.20180619223813-e734901a73fa+incompatible/command/v3/v3_push_original_command_test.go (about) 1 package v3_test 2 3 import ( 4 "errors" 5 "time" 6 7 "code.cloudfoundry.org/cli/actor/actionerror" 8 "code.cloudfoundry.org/cli/actor/pushaction" 9 "code.cloudfoundry.org/cli/actor/v2action" 10 "code.cloudfoundry.org/cli/actor/v3action" 11 "code.cloudfoundry.org/cli/actor/v3action/v3actionfakes" 12 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant" 13 "code.cloudfoundry.org/cli/api/cloudcontroller/ccversion" 14 "code.cloudfoundry.org/cli/command/commandfakes" 15 "code.cloudfoundry.org/cli/command/flag" 16 "code.cloudfoundry.org/cli/command/translatableerror" 17 "code.cloudfoundry.org/cli/command/v3" 18 "code.cloudfoundry.org/cli/command/v3/shared" 19 "code.cloudfoundry.org/cli/command/v3/shared/sharedfakes" 20 "code.cloudfoundry.org/cli/command/v3/v3fakes" 21 "code.cloudfoundry.org/cli/types" 22 "code.cloudfoundry.org/cli/util/configv3" 23 "code.cloudfoundry.org/cli/util/ui" 24 . "github.com/onsi/ginkgo" 25 . "github.com/onsi/ginkgo/extensions/table" 26 . "github.com/onsi/gomega" 27 . "github.com/onsi/gomega/gbytes" 28 ) 29 30 var _ = Describe("v3-push Command", func() { 31 var ( 32 cmd v3.V3PushCommand 33 testUI *ui.UI 34 fakeConfig *commandfakes.FakeConfig 35 fakeSharedActor *commandfakes.FakeSharedActor 36 fakeNOAAClient *v3actionfakes.FakeNOAAClient 37 fakeActor *v3fakes.FakeOriginalV3PushActor 38 fakeV2PushActor *v3fakes.FakeOriginalV2PushActor 39 fakeV2AppActor *sharedfakes.FakeV2AppRouteActor 40 binaryName string 41 executeErr error 42 app string 43 userName string 44 spaceName string 45 orgName string 46 ) 47 48 BeforeEach(func() { 49 testUI = ui.NewTestUI(nil, NewBuffer(), NewBuffer()) 50 fakeConfig = new(commandfakes.FakeConfig) 51 fakeSharedActor = new(commandfakes.FakeSharedActor) 52 fakeActor = new(v3fakes.FakeOriginalV3PushActor) 53 fakeV2PushActor = new(v3fakes.FakeOriginalV2PushActor) 54 fakeV2AppActor = new(sharedfakes.FakeV2AppRouteActor) 55 fakeNOAAClient = new(v3actionfakes.FakeNOAAClient) 56 57 fakeConfig.StagingTimeoutReturns(10 * time.Minute) 58 59 binaryName = "faceman" 60 fakeConfig.BinaryNameReturns(binaryName) 61 app = "some-app" 62 userName = "banana" 63 spaceName = "some-space" 64 orgName = "some-org" 65 66 appSummaryDisplayer := shared.AppSummaryDisplayer{ 67 UI: testUI, 68 Config: fakeConfig, 69 Actor: fakeActor, 70 V2AppRouteActor: fakeV2AppActor, 71 AppName: app, 72 } 73 packageDisplayer := shared.NewPackageDisplayer( 74 testUI, 75 fakeConfig, 76 ) 77 78 cmd = v3.V3PushCommand{ 79 RequiredArgs: flag.AppName{AppName: app}, 80 81 UI: testUI, 82 Config: fakeConfig, 83 SharedActor: fakeSharedActor, 84 OriginalActor: fakeActor, 85 OriginalV2PushActor: fakeV2PushActor, 86 87 NOAAClient: fakeNOAAClient, 88 AppSummaryDisplayer: appSummaryDisplayer, 89 PackageDisplayer: packageDisplayer, 90 } 91 fakeActor.CloudControllerAPIVersionReturns(ccversion.MinVersionV3) 92 93 // we stub out StagePackage out here so the happy paths below don't hang 94 fakeActor.StagePackageStub = func(_ string, _ string) (<-chan v3action.Droplet, <-chan v3action.Warnings, <-chan error) { 95 dropletStream := make(chan v3action.Droplet) 96 warningsStream := make(chan v3action.Warnings) 97 errorStream := make(chan error) 98 99 go func() { 100 defer close(dropletStream) 101 defer close(warningsStream) 102 defer close(errorStream) 103 }() 104 105 return dropletStream, warningsStream, errorStream 106 } 107 }) 108 109 JustBeforeEach(func() { 110 executeErr = cmd.Execute(nil) 111 }) 112 113 Context("when the API version is below the minimum", func() { 114 BeforeEach(func() { 115 fakeActor.CloudControllerAPIVersionReturns("0.0.0") 116 }) 117 118 It("returns a MinimumAPIVersionNotMetError", func() { 119 Expect(executeErr).To(MatchError(translatableerror.MinimumAPIVersionNotMetError{ 120 CurrentVersion: "0.0.0", 121 MinimumVersion: ccversion.MinVersionV3, 122 })) 123 }) 124 125 It("displays the experimental warning", func() { 126 Expect(testUI.Err).To(Say("This command is in EXPERIMENTAL stage and may change without notice")) 127 }) 128 }) 129 130 DescribeTable("argument combinations", 131 func(dockerImage string, dockerUsername string, dockerPassword string, 132 buildpacks []string, appPath string, 133 expectedErr error) { 134 cmd.DockerImage.Path = dockerImage 135 cmd.DockerUsername = dockerUsername 136 fakeConfig.DockerPasswordReturns(dockerPassword) 137 cmd.Buildpacks = buildpacks 138 cmd.AppPath = flag.PathWithExistenceCheck(appPath) 139 Expect(cmd.Execute(nil)).To(MatchError(expectedErr)) 140 }, 141 Entry("docker username", 142 "", "some-docker-username", "", []string{}, "", 143 translatableerror.RequiredFlagsError{ 144 Arg1: "--docker-image, -o", 145 Arg2: "--docker-username", 146 }), 147 Entry("docker username, password", 148 "", "some-docker-username", "my-password", []string{}, "", 149 translatableerror.RequiredFlagsError{ 150 Arg1: "--docker-image, -o", 151 Arg2: "--docker-username", 152 }), 153 Entry("docker username, app path", 154 "", "some-docker-username", "", []string{}, "some/app/path", 155 translatableerror.RequiredFlagsError{ 156 Arg1: "--docker-image, -o", 157 Arg2: "--docker-username", 158 }), 159 Entry("docker username, buildpacks", 160 "", "some-docker-username", "", []string{"ruby_buildpack"}, "", 161 translatableerror.RequiredFlagsError{ 162 Arg1: "--docker-image, -o", 163 Arg2: "--docker-username", 164 }), 165 Entry("docker image, docker username", 166 "some-docker-image", "some-docker-username", "", []string{}, "", 167 translatableerror.DockerPasswordNotSetError{}), 168 Entry("docker image, app path", 169 "some-docker-image", "", "", []string{}, "some/app/path", 170 translatableerror.ArgumentCombinationError{ 171 Args: []string{"--docker-image", "-o", "-p"}, 172 }), 173 Entry("docker image, buildpacks", 174 "some-docker-image", "", "", []string{"ruby_buildpack"}, "", 175 translatableerror.ArgumentCombinationError{ 176 Args: []string{"-b", "--docker-image", "-o"}, 177 }), 178 ) 179 180 Context("when checking target fails", func() { 181 BeforeEach(func() { 182 fakeSharedActor.CheckTargetReturns(actionerror.NotLoggedInError{BinaryName: binaryName}) 183 }) 184 185 It("returns an error", func() { 186 Expect(executeErr).To(MatchError(actionerror.NotLoggedInError{BinaryName: binaryName})) 187 188 Expect(fakeSharedActor.CheckTargetCallCount()).To(Equal(1)) 189 checkTargetedOrg, checkTargetedSpace := fakeSharedActor.CheckTargetArgsForCall(0) 190 Expect(checkTargetedOrg).To(BeTrue()) 191 Expect(checkTargetedSpace).To(BeTrue()) 192 }) 193 }) 194 195 Context("when the user is logged in", func() { 196 BeforeEach(func() { 197 fakeConfig.CurrentUserReturns(configv3.User{Name: userName}, nil) 198 fakeConfig.TargetedSpaceReturns(configv3.Space{Name: spaceName, GUID: "some-space-guid"}) 199 fakeConfig.TargetedOrganizationReturns(configv3.Organization{Name: orgName, GUID: "some-org-guid"}) 200 }) 201 202 Context("when looking up the application returns some api error", func() { 203 BeforeEach(func() { 204 fakeActor.GetApplicationByNameAndSpaceReturns(v3action.Application{}, v3action.Warnings{"get-warning"}, errors.New("some-error")) 205 }) 206 207 It("returns the error and displays all warnings", func() { 208 Expect(executeErr).To(MatchError("some-error")) 209 210 Expect(testUI.Err).To(Say("get-warning")) 211 }) 212 }) 213 214 Context("when the application doesn't exist", func() { 215 It("doesn't stop the application", func() { 216 Expect(fakeActor.StopApplicationCallCount()).To(Equal(0)) 217 }) 218 219 BeforeEach(func() { 220 fakeActor.GetApplicationByNameAndSpaceReturns(v3action.Application{}, v3action.Warnings{"get-warning"}, actionerror.ApplicationNotFoundError{Name: "some-app"}) 221 }) 222 223 Context("when creating the application returns an error", func() { 224 var expectedErr error 225 226 BeforeEach(func() { 227 expectedErr = errors.New("I am an error") 228 fakeActor.CreateApplicationInSpaceReturns(v3action.Application{}, v3action.Warnings{"I am a warning", "I am also a warning"}, expectedErr) 229 }) 230 231 It("displays the warnings and error", func() { 232 Expect(executeErr).To(MatchError(expectedErr)) 233 234 Expect(testUI.Err).To(Say("I am a warning")) 235 Expect(testUI.Err).To(Say("I am also a warning")) 236 Expect(testUI.Out).ToNot(Say("app some-app in org some-org / space some-space as banana...")) 237 }) 238 }) 239 240 Context("when creating the application does not error", func() { 241 BeforeEach(func() { 242 fakeActor.CreateApplicationInSpaceReturns(v3action.Application{Name: "some-app", GUID: "some-app-guid"}, v3action.Warnings{"I am a warning", "I am also a warning"}, nil) 243 }) 244 245 It("calls CreateApplication", func() { 246 Expect(fakeActor.CreateApplicationInSpaceCallCount()).To(Equal(1), "Expected CreateApplicationInSpace to be called once") 247 createApp, createSpaceGUID := fakeActor.CreateApplicationInSpaceArgsForCall(0) 248 Expect(createApp).To(Equal(v3action.Application{ 249 Name: "some-app", 250 LifecycleType: constant.AppLifecycleTypeBuildpack, 251 })) 252 Expect(createSpaceGUID).To(Equal("some-space-guid")) 253 }) 254 255 Context("when creating the package fails", func() { 256 var expectedErr error 257 258 BeforeEach(func() { 259 expectedErr = errors.New("I am an error") 260 fakeActor.CreateAndUploadBitsPackageByApplicationNameAndSpaceReturns(v3action.Package{}, v3action.Warnings{"I am a package warning", "I am also a package warning"}, expectedErr) 261 }) 262 263 It("displays the header and error", func() { 264 Expect(executeErr).To(MatchError(expectedErr)) 265 266 Expect(testUI.Out).To(Say("Uploading and creating bits package for app some-app in org some-org / space some-space as banana...")) 267 268 Expect(testUI.Err).To(Say("I am a package warning")) 269 Expect(testUI.Err).To(Say("I am also a package warning")) 270 271 Expect(testUI.Out).ToNot(Say("Staging package for %s in org some-org / space some-space as banana...", app)) 272 }) 273 }) 274 275 Context("when creating the package succeeds", func() { 276 BeforeEach(func() { 277 fakeActor.CreateAndUploadBitsPackageByApplicationNameAndSpaceReturns(v3action.Package{GUID: "some-guid"}, v3action.Warnings{"I am a package warning", "I am also a package warning"}, nil) 278 }) 279 280 Context("when the -p flag is provided", func() { 281 BeforeEach(func() { 282 cmd.AppPath = "some-app-path" 283 }) 284 285 It("creates the package with the provided path", func() { 286 Expect(testUI.Out).To(Say("Uploading and creating bits package for app %s in org %s / space %s as %s", app, orgName, spaceName, userName)) 287 Expect(testUI.Err).To(Say("I am a package warning")) 288 Expect(testUI.Err).To(Say("I am also a package warning")) 289 Expect(testUI.Out).To(Say("OK")) 290 Expect(testUI.Out).To(Say("Staging package for app %s in org some-org / space some-space as banana...", app)) 291 292 Expect(fakeActor.CreateAndUploadBitsPackageByApplicationNameAndSpaceCallCount()).To(Equal(1)) 293 _, _, appPath := fakeActor.CreateAndUploadBitsPackageByApplicationNameAndSpaceArgsForCall(0) 294 295 Expect(appPath).To(Equal("some-app-path")) 296 }) 297 }) 298 299 Context("when the -o flag is provided", func() { 300 BeforeEach(func() { 301 cmd.DockerImage.Path = "example.com/docker/docker/docker:docker" 302 fakeActor.CreateDockerPackageByApplicationNameAndSpaceReturns(v3action.Package{GUID: "some-guid"}, v3action.Warnings{"I am a docker package warning", "I am also a docker package warning"}, nil) 303 }) 304 305 It("creates a docker package with the provided image path", func() { 306 307 Expect(testUI.Out).To(Say("Creating docker package for app %s in org %s / space %s as %s", app, orgName, spaceName, userName)) 308 Expect(testUI.Err).To(Say("I am a docker package warning")) 309 Expect(testUI.Err).To(Say("I am also a docker package warning")) 310 Expect(testUI.Out).To(Say("OK")) 311 Expect(testUI.Out).To(Say("Staging package for app %s in org some-org / space some-space as banana...", app)) 312 313 Expect(fakeActor.CreateDockerPackageByApplicationNameAndSpaceCallCount()).To(Equal(1)) 314 _, _, dockerImageCredentials := fakeActor.CreateDockerPackageByApplicationNameAndSpaceArgsForCall(0) 315 316 Expect(dockerImageCredentials.Path).To(Equal("example.com/docker/docker/docker:docker")) 317 }) 318 }) 319 320 Context("when neither -p nor -o flags are provided", func() { 321 It("calls CreateAndUploadBitsPackageByApplicationNameAndSpace with empty string", func() { 322 Expect(testUI.Out).To(Say("Uploading and creating bits package for app %s in org %s / space %s as %s", app, orgName, spaceName, userName)) 323 324 Expect(fakeActor.CreateAndUploadBitsPackageByApplicationNameAndSpaceCallCount()).To(Equal(1)) 325 _, _, appPath := fakeActor.CreateAndUploadBitsPackageByApplicationNameAndSpaceArgsForCall(0) 326 327 Expect(appPath).To(BeEmpty()) 328 }) 329 }) 330 331 Context("when getting streaming logs fails", func() { 332 var expectedErr error 333 BeforeEach(func() { 334 expectedErr = errors.New("something is wrong!") 335 fakeActor.GetStreamingLogsForApplicationByNameAndSpaceReturns(nil, nil, v3action.Warnings{"some-logging-warning", "some-other-logging-warning"}, expectedErr) 336 }) 337 338 It("returns the error and displays warnings", func() { 339 Expect(executeErr).To(Equal(expectedErr)) 340 341 Expect(testUI.Out).To(Say("Staging package for app %s in org some-org / space some-space as banana...", app)) 342 343 Expect(testUI.Err).To(Say("some-logging-warning")) 344 Expect(testUI.Err).To(Say("some-other-logging-warning")) 345 346 }) 347 }) 348 349 Context("when --no-start is provided", func() { 350 BeforeEach(func() { 351 cmd.NoStart = true 352 }) 353 354 It("does not stage the package and returns", func() { 355 Expect(testUI.Out).To(Say("Uploading and creating bits package for app %s in org %s / space %s as %s", app, orgName, spaceName, userName)) 356 Expect(fakeActor.CreateAndUploadBitsPackageByApplicationNameAndSpaceCallCount()).To(Equal(1)) 357 Expect(fakeActor.GetStreamingLogsForApplicationByNameAndSpaceCallCount()).To(Equal(0)) 358 359 Expect(executeErr).ToNot(HaveOccurred()) 360 }) 361 }) 362 363 Context("when the logging does not error", func() { 364 var allLogsWritten chan bool 365 366 BeforeEach(func() { 367 allLogsWritten = make(chan bool) 368 fakeActor.GetStreamingLogsForApplicationByNameAndSpaceStub = func(appName string, spaceGUID string, client v3action.NOAAClient) (<-chan *v3action.LogMessage, <-chan error, v3action.Warnings, error) { 369 logStream := make(chan *v3action.LogMessage) 370 errorStream := make(chan error) 371 372 go func() { 373 logStream <- v3action.NewLogMessage("Here are some staging logs!", 1, time.Now(), v3action.StagingLog, "sourceInstance") 374 logStream <- v3action.NewLogMessage("Here are some other staging logs!", 1, time.Now(), v3action.StagingLog, "sourceInstance") 375 logStream <- v3action.NewLogMessage("not from staging", 1, time.Now(), "potato", "sourceInstance") 376 allLogsWritten <- true 377 }() 378 379 return logStream, errorStream, v3action.Warnings{"steve for all I care"}, nil 380 } 381 }) 382 383 Context("when the staging returns an error", func() { 384 var expectedErr error 385 386 BeforeEach(func() { 387 expectedErr = errors.New("any gibberish") 388 fakeActor.StagePackageStub = func(packageGUID string, _ string) (<-chan v3action.Droplet, <-chan v3action.Warnings, <-chan error) { 389 dropletStream := make(chan v3action.Droplet) 390 warningsStream := make(chan v3action.Warnings) 391 errorStream := make(chan error) 392 393 go func() { 394 <-allLogsWritten 395 defer close(dropletStream) 396 defer close(warningsStream) 397 defer close(errorStream) 398 warningsStream <- v3action.Warnings{"some-staging-warning", "some-other-staging-warning"} 399 errorStream <- expectedErr 400 }() 401 402 return dropletStream, warningsStream, errorStream 403 } 404 }) 405 406 It("returns the error and displays warnings", func() { 407 Expect(executeErr).To(Equal(expectedErr)) 408 409 Expect(testUI.Out).To(Say("Staging package for app %s in org some-org / space some-space as banana...", app)) 410 411 Expect(testUI.Err).To(Say("some-staging-warning")) 412 Expect(testUI.Err).To(Say("some-other-staging-warning")) 413 414 Expect(testUI.Out).ToNot(Say("Setting app some-app to droplet some-droplet-guid in org some-org / space some-space as banana...")) 415 }) 416 }) 417 418 Context("when the staging is successful", func() { 419 BeforeEach(func() { 420 fakeActor.StagePackageStub = func(packageGUID string, _ string) (<-chan v3action.Droplet, <-chan v3action.Warnings, <-chan error) { 421 dropletStream := make(chan v3action.Droplet) 422 warningsStream := make(chan v3action.Warnings) 423 errorStream := make(chan error) 424 425 go func() { 426 <-allLogsWritten 427 defer close(dropletStream) 428 defer close(warningsStream) 429 defer close(errorStream) 430 warningsStream <- v3action.Warnings{"some-staging-warning", "some-other-staging-warning"} 431 dropletStream <- v3action.Droplet{GUID: "some-droplet-guid"} 432 }() 433 434 return dropletStream, warningsStream, errorStream 435 } 436 }) 437 438 It("outputs the staging message and warnings", func() { 439 Expect(executeErr).ToNot(HaveOccurred()) 440 441 Expect(testUI.Out).To(Say("Staging package for app %s in org some-org / space some-space as banana...", app)) 442 Expect(testUI.Out).To(Say("OK")) 443 444 Expect(testUI.Err).To(Say("some-staging-warning")) 445 Expect(testUI.Err).To(Say("some-other-staging-warning")) 446 }) 447 448 It("stages the package", func() { 449 Expect(executeErr).ToNot(HaveOccurred()) 450 Expect(fakeActor.StagePackageCallCount()).To(Equal(1)) 451 guidArg, _ := fakeActor.StagePackageArgsForCall(0) 452 Expect(guidArg).To(Equal("some-guid")) 453 }) 454 455 It("displays staging logs and their warnings", func() { 456 Expect(testUI.Out).To(Say("Here are some staging logs!")) 457 Expect(testUI.Out).To(Say("Here are some other staging logs!")) 458 Expect(testUI.Out).ToNot(Say("not from staging")) 459 460 Expect(testUI.Err).To(Say("steve for all I care")) 461 462 Expect(fakeActor.GetStreamingLogsForApplicationByNameAndSpaceCallCount()).To(Equal(1)) 463 appName, spaceGUID, noaaClient := fakeActor.GetStreamingLogsForApplicationByNameAndSpaceArgsForCall(0) 464 Expect(appName).To(Equal(app)) 465 Expect(spaceGUID).To(Equal("some-space-guid")) 466 Expect(noaaClient).To(Equal(fakeNOAAClient)) 467 468 guidArg, _ := fakeActor.StagePackageArgsForCall(0) 469 Expect(guidArg).To(Equal("some-guid")) 470 }) 471 472 Context("when setting the droplet fails", func() { 473 BeforeEach(func() { 474 fakeActor.SetApplicationDropletReturns(v3action.Warnings{"droplet-warning-1", "droplet-warning-2"}, errors.New("some-error")) 475 }) 476 477 It("returns the error", func() { 478 Expect(executeErr).To(Equal(errors.New("some-error"))) 479 480 Expect(testUI.Out).To(Say("Setting app some-app to droplet some-droplet-guid in org some-org / space some-space as banana...")) 481 482 Expect(testUI.Err).To(Say("droplet-warning-1")) 483 Expect(testUI.Err).To(Say("droplet-warning-2")) 484 485 Expect(testUI.Out).ToNot(Say("Starting app some-app in org some-org / space some-space as banana\\.\\.\\.")) 486 }) 487 }) 488 489 Context("when setting the application droplet is successful", func() { 490 BeforeEach(func() { 491 fakeActor.SetApplicationDropletReturns(v3action.Warnings{"droplet-warning-1", "droplet-warning-2"}, nil) 492 }) 493 494 It("displays that the droplet was assigned", func() { 495 Expect(testUI.Out).To(Say("Staging package for app %s in org some-org / space some-space as banana...", app)) 496 Expect(testUI.Out).To(Say("OK")) 497 498 Expect(testUI.Out).ToNot(Say("Stopping .*")) 499 500 Expect(testUI.Out).To(Say("Setting app some-app to droplet some-droplet-guid in org some-org / space some-space as banana...")) 501 502 Expect(testUI.Err).To(Say("droplet-warning-1")) 503 Expect(testUI.Err).To(Say("droplet-warning-2")) 504 Expect(testUI.Out).To(Say("OK")) 505 506 Expect(fakeActor.SetApplicationDropletCallCount()).To(Equal(1)) 507 appName, spaceGUID, dropletGUID := fakeActor.SetApplicationDropletArgsForCall(0) 508 Expect(appName).To(Equal("some-app")) 509 Expect(spaceGUID).To(Equal("some-space-guid")) 510 Expect(dropletGUID).To(Equal("some-droplet-guid")) 511 }) 512 513 Context("when --no-route flag is set to true", func() { 514 BeforeEach(func() { 515 cmd.NoRoute = true 516 }) 517 518 It("does not create any routes", func() { 519 Expect(fakeV2PushActor.CreateAndMapDefaultApplicationRouteCallCount()).To(Equal(0)) 520 521 Expect(fakeActor.StartApplicationCallCount()).To(Equal(1)) 522 }) 523 }) 524 525 Context("when buildpack(s) are provided via -b flag", func() { 526 BeforeEach(func() { 527 cmd.Buildpacks = []string{"some-buildpack"} 528 }) 529 530 It("creates the app with the specified buildpack and prints the buildpack name in the summary", func() { 531 Expect(fakeActor.CreateApplicationInSpaceCallCount()).To(Equal(1), "Expected CreateApplicationInSpace to be called once") 532 createApp, createSpaceGUID := fakeActor.CreateApplicationInSpaceArgsForCall(0) 533 Expect(createApp).To(Equal(v3action.Application{ 534 Name: "some-app", 535 LifecycleType: constant.AppLifecycleTypeBuildpack, 536 LifecycleBuildpacks: []string{"some-buildpack"}, 537 })) 538 Expect(createSpaceGUID).To(Equal("some-space-guid")) 539 }) 540 }) 541 542 Context("when a docker image is specified", func() { 543 BeforeEach(func() { 544 cmd.DockerImage.Path = "example.com/docker/docker/docker:docker" 545 }) 546 547 It("creates the app with a docker lifecycle", func() { 548 Expect(fakeActor.CreateApplicationInSpaceCallCount()).To(Equal(1), "Expected CreateApplicationInSpace to be called once") 549 createApp, createSpaceGUID := fakeActor.CreateApplicationInSpaceArgsForCall(0) 550 Expect(createApp).To(Equal(v3action.Application{ 551 Name: "some-app", 552 LifecycleType: constant.AppLifecycleTypeDocker, 553 })) 554 Expect(createSpaceGUID).To(Equal("some-space-guid")) 555 }) 556 }) 557 558 Context("when mapping routes fails", func() { 559 BeforeEach(func() { 560 fakeV2PushActor.CreateAndMapDefaultApplicationRouteReturns(pushaction.Warnings{"route-warning"}, errors.New("some-error")) 561 }) 562 563 It("returns the error", func() { 564 Expect(executeErr).To(MatchError("some-error")) 565 Expect(testUI.Out).To(Say("Mapping routes\\.\\.\\.")) 566 Expect(testUI.Err).To(Say("route-warning")) 567 568 Expect(fakeActor.StartApplicationCallCount()).To(Equal(0)) 569 }) 570 }) 571 572 Context("when mapping routes succeeds", func() { 573 BeforeEach(func() { 574 fakeV2PushActor.CreateAndMapDefaultApplicationRouteReturns(pushaction.Warnings{"route-warning"}, nil) 575 }) 576 577 It("displays the header and OK", func() { 578 Expect(testUI.Out).To(Say("Mapping routes\\.\\.\\.")) 579 Expect(testUI.Out).To(Say("OK")) 580 581 Expect(testUI.Err).To(Say("route-warning")) 582 583 Expect(fakeV2PushActor.CreateAndMapDefaultApplicationRouteCallCount()).To(Equal(1), "Expected CreateAndMapDefaultApplicationRoute to be called") 584 orgArg, spaceArg, appArg := fakeV2PushActor.CreateAndMapDefaultApplicationRouteArgsForCall(0) 585 Expect(orgArg).To(Equal("some-org-guid")) 586 Expect(spaceArg).To(Equal("some-space-guid")) 587 Expect(appArg).To(Equal(v2action.Application{Name: "some-app", GUID: "some-app-guid"})) 588 589 Expect(fakeActor.StartApplicationCallCount()).To(Equal(1)) 590 }) 591 592 Context("when starting the application fails", func() { 593 BeforeEach(func() { 594 fakeActor.StartApplicationReturns(v3action.Application{}, v3action.Warnings{"start-warning-1", "start-warning-2"}, errors.New("some-error")) 595 }) 596 597 It("says that the app failed to start", func() { 598 Expect(executeErr).To(Equal(errors.New("some-error"))) 599 Expect(testUI.Out).To(Say("Starting app some-app in org some-org / space some-space as banana\\.\\.\\.")) 600 601 Expect(testUI.Err).To(Say("start-warning-1")) 602 Expect(testUI.Err).To(Say("start-warning-2")) 603 604 Expect(testUI.Out).ToNot(Say("Showing health and status for app some-app in org some-org / space some-space as banana\\.\\.\\.")) 605 }) 606 }) 607 608 Context("when starting the application succeeds", func() { 609 BeforeEach(func() { 610 fakeActor.StartApplicationReturns(v3action.Application{GUID: "some-app-guid"}, v3action.Warnings{"start-warning-1", "start-warning-2"}, nil) 611 }) 612 613 It("says that the app was started and outputs warnings", func() { 614 Expect(testUI.Out).To(Say("Starting app some-app in org some-org / space some-space as banana\\.\\.\\.")) 615 616 Expect(testUI.Err).To(Say("start-warning-1")) 617 Expect(testUI.Err).To(Say("start-warning-2")) 618 Expect(testUI.Out).To(Say("OK")) 619 620 Expect(fakeActor.StartApplicationCallCount()).To(Equal(1)) 621 appGUID := fakeActor.StartApplicationArgsForCall(0) 622 Expect(appGUID).To(Equal("some-app-guid")) 623 }) 624 }) 625 626 Context("when polling the start fails", func() { 627 BeforeEach(func() { 628 fakeActor.PollStartStub = func(appGUID string, warnings chan<- v3action.Warnings) error { 629 warnings <- v3action.Warnings{"some-poll-warning-1", "some-poll-warning-2"} 630 return errors.New("some-error") 631 } 632 }) 633 634 It("displays all warnings and fails", func() { 635 Expect(testUI.Out).To(Say("Waiting for app to start\\.\\.\\.")) 636 637 Expect(testUI.Err).To(Say("some-poll-warning-1")) 638 Expect(testUI.Err).To(Say("some-poll-warning-2")) 639 640 Expect(executeErr).To(MatchError("some-error")) 641 }) 642 }) 643 644 Context("when polling times out", func() { 645 BeforeEach(func() { 646 fakeActor.PollStartReturns(actionerror.StartupTimeoutError{}) 647 }) 648 649 It("returns the StartupTimeoutError", func() { 650 Expect(executeErr).To(MatchError(translatableerror.StartupTimeoutError{ 651 AppName: "some-app", 652 BinaryName: binaryName, 653 })) 654 }) 655 }) 656 657 Context("when polling the start succeeds", func() { 658 BeforeEach(func() { 659 fakeActor.PollStartStub = func(appGUID string, warnings chan<- v3action.Warnings) error { 660 warnings <- v3action.Warnings{"some-poll-warning-1", "some-poll-warning-2"} 661 return nil 662 } 663 }) 664 665 It("displays all warnings", func() { 666 Expect(testUI.Out).To(Say("Waiting for app to start\\.\\.\\.")) 667 668 Expect(testUI.Err).To(Say("some-poll-warning-1")) 669 Expect(testUI.Err).To(Say("some-poll-warning-2")) 670 671 Expect(executeErr).ToNot(HaveOccurred()) 672 }) 673 674 Context("when displaying the application info fails", func() { 675 BeforeEach(func() { 676 var expectedErr error 677 expectedErr = actionerror.ApplicationNotFoundError{Name: app} 678 fakeActor.GetApplicationSummaryByNameAndSpaceReturns(v3action.ApplicationSummary{}, v3action.Warnings{"display-warning-1", "display-warning-2"}, expectedErr) 679 }) 680 681 It("returns the error and prints warnings", func() { 682 Expect(executeErr).To(Equal(actionerror.ApplicationNotFoundError{Name: app})) 683 684 Expect(testUI.Out).To(Say("Showing health and status for app some-app in org some-org / space some-space as banana\\.\\.\\.")) 685 686 Expect(testUI.Err).To(Say("display-warning-1")) 687 Expect(testUI.Err).To(Say("display-warning-2")) 688 689 Expect(testUI.Out).ToNot(Say("name:\\s+some-app")) 690 }) 691 }) 692 693 Context("when getting the application summary is successful", func() { 694 BeforeEach(func() { 695 summary := v3action.ApplicationSummary{ 696 Application: v3action.Application{ 697 Name: "some-app", 698 GUID: "some-app-guid", 699 State: "started", 700 }, 701 CurrentDroplet: v3action.Droplet{ 702 Stack: "cflinuxfs2", 703 Buildpacks: []v3action.Buildpack{ 704 { 705 Name: "ruby_buildpack", 706 DetectOutput: "some-detect-output", 707 }, 708 }, 709 }, 710 ProcessSummaries: []v3action.ProcessSummary{ 711 { 712 Process: v3action.Process{ 713 Type: "worker", 714 MemoryInMB: types.NullUint64{Value: 64, IsSet: true}, 715 }, 716 InstanceDetails: []v3action.ProcessInstance{ 717 v3action.ProcessInstance{ 718 Index: 0, 719 State: constant.ProcessInstanceRunning, 720 MemoryUsage: 4000000, 721 DiskUsage: 4000000, 722 MemoryQuota: 67108864, 723 DiskQuota: 8000000, 724 Uptime: int(time.Now().Sub(time.Unix(1371859200, 0)).Seconds()), 725 }, 726 }, 727 }, 728 }, 729 } 730 731 fakeActor.GetApplicationSummaryByNameAndSpaceReturns(summary, v3action.Warnings{"display-warning-1", "display-warning-2"}, nil) 732 }) 733 734 Context("when getting the application routes fails", func() { 735 BeforeEach(func() { 736 fakeV2AppActor.GetApplicationRoutesReturns([]v2action.Route{}, 737 v2action.Warnings{"route-warning-1", "route-warning-2"}, errors.New("some-error")) 738 }) 739 740 It("displays all warnings and returns the error", func() { 741 Expect(executeErr).To(MatchError("some-error")) 742 743 Expect(testUI.Out).To(Say("Showing health and status for app some-app in org some-org / space some-space as banana\\.\\.\\.")) 744 745 Expect(testUI.Err).To(Say("display-warning-1")) 746 Expect(testUI.Err).To(Say("display-warning-2")) 747 Expect(testUI.Err).To(Say("route-warning-1")) 748 Expect(testUI.Err).To(Say("route-warning-2")) 749 750 Expect(testUI.Out).ToNot(Say("name:\\s+some-app")) 751 }) 752 }) 753 754 Context("when getting the application routes is successful", func() { 755 BeforeEach(func() { 756 fakeV2AppActor.GetApplicationRoutesReturns([]v2action.Route{ 757 {Domain: v2action.Domain{Name: "some-other-domain"}}, { 758 Domain: v2action.Domain{Name: "some-domain"}}}, 759 v2action.Warnings{"route-warning-1", "route-warning-2"}, nil) 760 }) 761 762 It("prints the application summary and outputs warnings", func() { 763 Expect(executeErr).ToNot(HaveOccurred()) 764 765 Expect(testUI.Out).To(Say("(?m)Showing health and status for app some-app in org some-org / space some-space as banana\\.\\.\\.\n\n")) 766 Expect(testUI.Out).To(Say("name:\\s+some-app")) 767 Expect(testUI.Out).To(Say("requested state:\\s+started")) 768 Expect(testUI.Out).To(Say("processes:\\s+worker:1/1")) 769 Expect(testUI.Out).To(Say("memory usage:\\s+64M x 1")) 770 Expect(testUI.Out).To(Say("routes:\\s+some-other-domain, some-domain")) 771 Expect(testUI.Out).To(Say("stack:\\s+cflinuxfs2")) 772 Expect(testUI.Out).To(Say("(?m)buildpacks:\\s+some-detect-output\n\n")) 773 774 Expect(testUI.Out).To(Say("worker:1/1")) 775 Expect(testUI.Out).To(Say("\\s+state\\s+since\\s+cpu\\s+memory\\s+disk")) 776 Expect(testUI.Out).To(Say("#0\\s+running\\s+2013-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} [AP]M\\s+0.0%\\s+3.8M of 64M\\s+3.8M of 7.6M")) 777 778 Expect(testUI.Err).To(Say("display-warning-1")) 779 Expect(testUI.Err).To(Say("display-warning-2")) 780 Expect(testUI.Err).To(Say("route-warning-1")) 781 Expect(testUI.Err).To(Say("route-warning-2")) 782 783 Expect(fakeActor.GetApplicationSummaryByNameAndSpaceCallCount()).To(Equal(1)) 784 appName, spaceGUID := fakeActor.GetApplicationSummaryByNameAndSpaceArgsForCall(0) 785 Expect(appName).To(Equal("some-app")) 786 Expect(spaceGUID).To(Equal("some-space-guid")) 787 788 Expect(fakeV2AppActor.GetApplicationRoutesCallCount()).To(Equal(1)) 789 Expect(fakeV2AppActor.GetApplicationRoutesArgsForCall(0)).To(Equal("some-app-guid")) 790 }) 791 }) 792 }) 793 }) 794 }) 795 }) 796 }) 797 }) 798 }) 799 }) 800 }) 801 802 Context("when looking up the application succeeds", func() { 803 BeforeEach(func() { 804 fakeActor.GetApplicationByNameAndSpaceReturns(v3action.Application{ 805 Name: "some-app", 806 GUID: "some-app-guid", 807 }, v3action.Warnings{"get-warning"}, nil) 808 }) 809 810 It("updates the application", func() { 811 Expect(fakeActor.CreateApplicationInSpaceCallCount()).To(Equal(0)) 812 Expect(fakeActor.UpdateApplicationCallCount()).To(Equal(1)) 813 }) 814 815 Context("when updating the application fails", func() { 816 BeforeEach(func() { 817 fakeActor.UpdateApplicationReturns(v3action.Application{}, v3action.Warnings{"update-warning-1"}, errors.New("some-error")) 818 }) 819 820 It("returns the error and displays warnings", func() { 821 Expect(executeErr).To(MatchError("some-error")) 822 823 Expect(testUI.Err).To(Say("get-warning")) 824 Expect(testUI.Err).To(Say("update-warning")) 825 }) 826 }) 827 828 Context("when a docker image is provided", func() { 829 BeforeEach(func() { 830 cmd.DockerImage.Path = "example.com/docker/docker/docker:docker" 831 cmd.DockerUsername = "username" 832 fakeConfig.DockerPasswordReturns("password") 833 }) 834 835 Context("when a username/password are provided", func() { 836 It("updates the app with the provided credentials", func() { 837 appName, spaceGuid, dockerImageCredentials := fakeActor.CreateDockerPackageByApplicationNameAndSpaceArgsForCall(0) 838 Expect(appName).To(Equal("some-app")) 839 Expect(spaceGuid).To(Equal("some-space-guid")) 840 Expect(dockerImageCredentials.Path).To(Equal(cmd.DockerImage.Path)) 841 Expect(dockerImageCredentials.Username).To(Equal("username")) 842 Expect(dockerImageCredentials.Password).To(Equal("password")) 843 }) 844 }) 845 846 It("updates the app with a docker lifecycle", func() { 847 Expect(fakeActor.UpdateApplicationCallCount()).To(Equal(1), "Expected UpdateApplication to be called once") 848 updateApp := fakeActor.UpdateApplicationArgsForCall(0) 849 Expect(updateApp).To(Equal(v3action.Application{ 850 GUID: "some-app-guid", 851 LifecycleType: constant.AppLifecycleTypeDocker, 852 })) 853 }) 854 }) 855 856 Context("when the app has a buildpack lifecycle", func() { 857 Context("when a buildpack was not provided", func() { 858 BeforeEach(func() { 859 cmd.Buildpacks = []string{} 860 }) 861 862 It("does not update the buildpack", func() { 863 appArg := fakeActor.UpdateApplicationArgsForCall(0) 864 Expect(appArg).To(Equal(v3action.Application{ 865 GUID: "some-app-guid", 866 LifecycleType: constant.AppLifecycleTypeBuildpack, 867 LifecycleBuildpacks: []string{}, 868 })) 869 }) 870 }) 871 872 Context("when a buildpack was provided", func() { 873 BeforeEach(func() { 874 cmd.Buildpacks = []string{"some-buildpack"} 875 }) 876 877 It("updates the buildpack", func() { 878 appArg := fakeActor.UpdateApplicationArgsForCall(0) 879 Expect(appArg).To(Equal(v3action.Application{ 880 GUID: "some-app-guid", 881 LifecycleType: constant.AppLifecycleTypeBuildpack, 882 LifecycleBuildpacks: []string{"some-buildpack"}, 883 })) 884 }) 885 }) 886 887 Context("when multiple buildpacks are provided", func() { 888 BeforeEach(func() { 889 cmd.Buildpacks = []string{"some-buildpack-1", "some-buildpack-2"} 890 }) 891 892 It("updates the buildpacks", func() { 893 appArg := fakeActor.UpdateApplicationArgsForCall(0) 894 Expect(appArg).To(Equal(v3action.Application{ 895 GUID: "some-app-guid", 896 LifecycleType: constant.AppLifecycleTypeBuildpack, 897 LifecycleBuildpacks: []string{"some-buildpack-1", "some-buildpack-2"}, 898 })) 899 }) 900 901 Context("when default was also provided", func() { 902 BeforeEach(func() { 903 cmd.Buildpacks = []string{"default", "some-buildpack-2"} 904 }) 905 906 It("returns the ConflictingBuildpacksError", func() { 907 Expect(executeErr).To(Equal(translatableerror.ConflictingBuildpacksError{})) 908 Expect(fakeActor.UpdateApplicationCallCount()).To(Equal(0)) 909 }) 910 }) 911 912 Context("when null was also provided", func() { 913 BeforeEach(func() { 914 cmd.Buildpacks = []string{"null", "some-buildpack-2"} 915 }) 916 917 It("returns the ConflictingBuildpacksError", func() { 918 Expect(executeErr).To(Equal(translatableerror.ConflictingBuildpacksError{})) 919 Expect(fakeActor.UpdateApplicationCallCount()).To(Equal(0)) 920 }) 921 }) 922 }) 923 }) 924 925 Context("when updating the application succeeds", func() { 926 Context("when the application is stopped", func() { 927 BeforeEach(func() { 928 fakeActor.UpdateApplicationReturns(v3action.Application{GUID: "some-app-guid", State: constant.ApplicationStopped}, v3action.Warnings{"update-warning"}, nil) 929 }) 930 931 It("skips stopping the application and pushes it", func() { 932 Expect(executeErr).ToNot(HaveOccurred()) 933 934 Expect(testUI.Err).To(Say("get-warning")) 935 Expect(testUI.Err).To(Say("update-warning")) 936 937 Expect(testUI.Out).ToNot(Say("Stopping")) 938 939 Expect(fakeActor.StopApplicationCallCount()).To(Equal(0), "Expected StopApplication to not be called") 940 941 Expect(fakeActor.StartApplicationCallCount()).To(Equal(1), "Expected StartApplication to be called") 942 }) 943 }) 944 945 Context("when the application is started", func() { 946 BeforeEach(func() { 947 fakeActor.UpdateApplicationReturns(v3action.Application{GUID: "some-app-guid", State: constant.ApplicationStarted}, nil, nil) 948 }) 949 950 It("stops the application and pushes it", func() { 951 Expect(executeErr).ToNot(HaveOccurred()) 952 Expect(testUI.Out).To(Say("Stopping app some-app in org some-org / space some-space as banana...")) 953 954 Expect(fakeActor.StopApplicationCallCount()).To(Equal(1)) 955 956 Expect(fakeActor.StartApplicationCallCount()).To(Equal(1), "Expected StartApplication to be called") 957 }) 958 959 Context("when no-start is provided", func() { 960 BeforeEach(func() { 961 cmd.NoStart = true 962 }) 963 964 It("stops the application and returns", func() { 965 Expect(executeErr).ToNot(HaveOccurred()) 966 Expect(testUI.Out).To(Say("Stopping app some-app in org some-org / space some-space as banana...")) 967 968 Expect(fakeActor.StopApplicationCallCount()).To(Equal(1)) 969 970 Expect(fakeActor.StartApplicationCallCount()).To(Equal(0)) 971 }) 972 }) 973 }) 974 }) 975 }) 976 }) 977 })