github.com/arunkumar7540/cli@v6.45.0+incompatible/command/v7/push_command_test.go (about) 1 package v7_test 2 3 import ( 4 "errors" 5 "time" 6 7 . "github.com/onsi/gomega/gstruct" 8 9 "code.cloudfoundry.org/cli/actor/actionerror" 10 "code.cloudfoundry.org/cli/actor/v7action" 11 "code.cloudfoundry.org/cli/actor/v7action/v7actionfakes" 12 "code.cloudfoundry.org/cli/actor/v7pushaction" 13 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant" 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/v6/v6fakes" 18 . "code.cloudfoundry.org/cli/command/v7" 19 "code.cloudfoundry.org/cli/command/v7/v7fakes" 20 "code.cloudfoundry.org/cli/types" 21 "code.cloudfoundry.org/cli/util/configv3" 22 "code.cloudfoundry.org/cli/util/ui" 23 "github.com/cloudfoundry/bosh-cli/director/template" 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 type Step struct { 31 Error error 32 Event v7pushaction.Event 33 Warnings v7pushaction.Warnings 34 } 35 36 func FillInEvents(tuples []Step) (<-chan v7pushaction.Event, <-chan v7pushaction.Warnings, <-chan error) { 37 eventStream := make(chan v7pushaction.Event) 38 warningsStream := make(chan v7pushaction.Warnings) 39 errorStream := make(chan error) 40 41 go func() { 42 defer close(eventStream) 43 defer close(warningsStream) 44 defer close(errorStream) 45 46 for _, tuple := range tuples { 47 warningsStream <- tuple.Warnings 48 if tuple.Error != nil { 49 errorStream <- tuple.Error 50 return 51 } else { 52 eventStream <- tuple.Event 53 } 54 } 55 }() 56 57 return eventStream, warningsStream, errorStream 58 } 59 60 func FillInValues(tuples []Step, state v7pushaction.PushPlan) func(v7pushaction.PushPlan, v7pushaction.ProgressBar) (<-chan v7pushaction.PushPlan, <-chan v7pushaction.Event, <-chan v7pushaction.Warnings, <-chan error) { 61 return func(v7pushaction.PushPlan, v7pushaction.ProgressBar) (<-chan v7pushaction.PushPlan, <-chan v7pushaction.Event, <-chan v7pushaction.Warnings, <-chan error) { 62 stateStream := make(chan v7pushaction.PushPlan) 63 64 eventStream := make(chan v7pushaction.Event) 65 warningsStream := make(chan v7pushaction.Warnings) 66 errorStream := make(chan error) 67 68 go func() { 69 defer close(stateStream) 70 defer close(eventStream) 71 defer close(warningsStream) 72 defer close(errorStream) 73 74 for _, tuple := range tuples { 75 warningsStream <- tuple.Warnings 76 if tuple.Error != nil { 77 errorStream <- tuple.Error 78 return 79 } else { 80 eventStream <- tuple.Event 81 } 82 } 83 84 stateStream <- state 85 eventStream <- v7pushaction.Complete 86 }() 87 88 return stateStream, eventStream, warningsStream, errorStream 89 } 90 } 91 92 type LogEvent struct { 93 Log *v7action.LogMessage 94 Error error 95 } 96 97 func ReturnLogs(logevents []LogEvent, passedWarnings v7action.Warnings, passedError error) func(appName string, spaceGUID string, client v7action.NOAAClient) (<-chan *v7action.LogMessage, <-chan error, v7action.Warnings, error) { 98 return func(appName string, spaceGUID string, client v7action.NOAAClient) (<-chan *v7action.LogMessage, <-chan error, v7action.Warnings, error) { 99 logStream := make(chan *v7action.LogMessage) 100 errStream := make(chan error) 101 go func() { 102 defer close(logStream) 103 defer close(errStream) 104 105 for _, log := range logevents { 106 if log.Log != nil { 107 logStream <- log.Log 108 } 109 if log.Error != nil { 110 errStream <- log.Error 111 } 112 } 113 }() 114 115 return logStream, errStream, passedWarnings, passedError 116 } 117 } 118 119 var _ = Describe("push Command", func() { 120 var ( 121 cmd PushCommand 122 input *Buffer 123 testUI *ui.UI 124 fakeConfig *commandfakes.FakeConfig 125 fakeSharedActor *commandfakes.FakeSharedActor 126 fakeActor *v7fakes.FakePushActor 127 fakeVersionActor *v7fakes.FakeV7ActorForPush 128 fakeProgressBar *v6fakes.FakeProgressBar 129 fakeNOAAClient *v7actionfakes.FakeNOAAClient 130 fakeManifestLocator *v7fakes.FakeManifestLocator 131 binaryName string 132 executeErr error 133 134 appName1 string 135 appName2 string 136 userName string 137 spaceName string 138 orgName string 139 pwd string 140 fakeManifestParser *v7fakes.FakeManifestParser 141 ) 142 143 BeforeEach(func() { 144 input = NewBuffer() 145 testUI = ui.NewTestUI(input, NewBuffer(), NewBuffer()) 146 fakeConfig = new(commandfakes.FakeConfig) 147 fakeSharedActor = new(commandfakes.FakeSharedActor) 148 fakeActor = new(v7fakes.FakePushActor) 149 fakeVersionActor = new(v7fakes.FakeV7ActorForPush) 150 fakeProgressBar = new(v6fakes.FakeProgressBar) 151 fakeNOAAClient = new(v7actionfakes.FakeNOAAClient) 152 153 appName1 = "first-app" 154 appName2 = "second-app" 155 userName = "some-user" 156 spaceName = "some-space" 157 orgName = "some-org" 158 pwd = "/push/cmd/test" 159 fakeManifestLocator = new(v7fakes.FakeManifestLocator) 160 fakeManifestParser = new(v7fakes.FakeManifestParser) 161 162 binaryName = "faceman" 163 fakeConfig.BinaryNameReturns(binaryName) 164 fakeConfig.ExperimentalReturns(true) // TODO: Delete once we remove the experimental flag 165 166 cmd = PushCommand{ 167 UI: testUI, 168 Config: fakeConfig, 169 Actor: fakeActor, 170 VersionActor: fakeVersionActor, 171 SharedActor: fakeSharedActor, 172 ProgressBar: fakeProgressBar, 173 NOAAClient: fakeNOAAClient, 174 PWD: pwd, 175 ManifestLocator: fakeManifestLocator, 176 ManifestParser: fakeManifestParser, 177 } 178 }) 179 180 Describe("Execute", func() { 181 JustBeforeEach(func() { 182 executeErr = cmd.Execute(nil) 183 }) 184 185 BeforeEach(func() { 186 pushPlanChannel := make(chan []v7pushaction.PushPlan, 1) 187 pushPlanChannel <- []v7pushaction.PushPlan{ 188 {Application: v7action.Application{Name: appName1}}, 189 {Application: v7action.Application{Name: appName2}}, 190 } 191 close(pushPlanChannel) 192 events, warnings, errors := FillInEvents([]Step{ 193 { 194 Warnings: v7pushaction.Warnings{"some-warning-1"}, 195 Event: v7pushaction.ApplyManifest, 196 }, 197 { 198 Warnings: v7pushaction.Warnings{"some-warning-2"}, 199 Event: v7pushaction.ApplyManifestComplete, 200 }, 201 }) 202 203 fakeActor.PrepareSpaceReturns(pushPlanChannel, events, warnings, errors) 204 205 fakeActor.ActualizeStub = FillInValues([]Step{{}}, v7pushaction.PushPlan{}) 206 }) 207 208 When("checking target fails", func() { 209 BeforeEach(func() { 210 fakeSharedActor.CheckTargetReturns(actionerror.NoOrganizationTargetedError{BinaryName: binaryName}) 211 }) 212 213 It("returns an error", func() { 214 Expect(executeErr).To(MatchError(actionerror.NoOrganizationTargetedError{BinaryName: binaryName})) 215 216 Expect(fakeSharedActor.CheckTargetCallCount()).To(Equal(1)) 217 checkTargetedOrg, checkTargetedSpace := fakeSharedActor.CheckTargetArgsForCall(0) 218 Expect(checkTargetedOrg).To(BeTrue()) 219 Expect(checkTargetedSpace).To(BeTrue()) 220 }) 221 }) 222 223 When("checking target fails because the user is not logged in", func() { 224 BeforeEach(func() { 225 fakeSharedActor.CheckTargetReturns(actionerror.NotLoggedInError{BinaryName: binaryName}) 226 }) 227 228 It("returns an error", func() { 229 Expect(executeErr).To(MatchError(actionerror.NotLoggedInError{BinaryName: binaryName})) 230 231 Expect(fakeSharedActor.CheckTargetCallCount()).To(Equal(1)) 232 checkTargetedOrg, checkTargetedSpace := fakeSharedActor.CheckTargetArgsForCall(0) 233 Expect(checkTargetedOrg).To(BeTrue()) 234 Expect(checkTargetedSpace).To(BeTrue()) 235 }) 236 }) 237 238 When("the user is logged in, and org and space are targeted", func() { 239 BeforeEach(func() { 240 fakeConfig.CurrentUserReturns(configv3.User{Name: userName}, nil) 241 242 fakeConfig.TargetedOrganizationReturns(configv3.Organization{ 243 Name: orgName, 244 GUID: "some-org-guid", 245 }) 246 fakeConfig.TargetedSpaceReturns(configv3.Space{ 247 Name: spaceName, 248 GUID: "some-space-guid", 249 }) 250 }) 251 252 It("displays the experimental warning", func() { 253 Expect(testUI.Err).To(Say("This command is in EXPERIMENTAL stage and may change without notice")) 254 }) 255 256 When("invalid flags are passed", func() { 257 BeforeEach(func() { 258 cmd.DockerUsername = "some-docker-username" 259 }) 260 261 It("returns a validation error", func() { 262 Expect(executeErr).To(MatchError(translatableerror.RequiredFlagsError{Arg1: "--docker-image, -o", Arg2: "--docker-username"})) 263 }) 264 }) 265 266 Describe("reading manifest", func() { 267 BeforeEach(func() { 268 fakeManifestLocator.PathReturns("", true, nil) 269 }) 270 271 When("Reading the manifest fails", func() { 272 BeforeEach(func() { 273 fakeManifestParser.InterpolateAndParseReturns(errors.New("oh no")) 274 }) 275 It("returns the error", func() { 276 Expect(executeErr).To(MatchError("oh no")) 277 }) 278 }) 279 280 When("Reading the manifest succeeds", func() { 281 It("interpolates the manifest", func() { 282 Expect(executeErr).ToNot(HaveOccurred()) 283 Expect(fakeManifestParser.InterpolateAndParseCallCount()).To(Equal(1)) 284 }) 285 286 When("the manifest contains private docker images", func() { 287 It("returns docker password", func() { 288 Expect(executeErr).ToNot(HaveOccurred()) 289 Expect(fakeManifestParser.ContainsPrivateDockerImagesCallCount()).To(Equal(1)) 290 }) 291 }) 292 }) 293 294 When("no manifest flag", func() { 295 BeforeEach(func() { 296 cmd.NoManifest = true 297 }) 298 299 It("does not read the manifest", func() { 300 Expect(executeErr).ToNot(HaveOccurred()) 301 302 Expect(fakeManifestParser.InterpolateAndParseCallCount()).To(Equal(0)) 303 }) 304 }) 305 306 When("multi app manifest + flag overrides", func() { 307 BeforeEach(func() { 308 fakeManifestParser.ContainsMultipleAppsReturns(true) 309 cmd.NoRoute = true 310 }) 311 312 It("returns an error", func() { 313 Expect(executeErr).To(MatchError(translatableerror.CommandLineArgsWithMultipleAppsError{})) 314 }) 315 }) 316 }) 317 318 Describe("delegating to Actor.CreatePushPlans", func() { 319 BeforeEach(func() { 320 cmd.OptionalArgs.AppName = appName1 321 }) 322 323 It("delegates the correct values", func() { 324 Expect(fakeActor.CreatePushPlansCallCount()).To(Equal(1)) 325 actualAppName, actualSpaceGUID, actualOrgGUID, _, _ := fakeActor.CreatePushPlansArgsForCall(0) 326 327 Expect(actualAppName).To(Equal(appName1)) 328 Expect(actualSpaceGUID).To(Equal("some-space-guid")) 329 Expect(actualOrgGUID).To(Equal("some-org-guid")) 330 }) 331 332 When("Creating the pushPlans errors", func() { 333 BeforeEach(func() { 334 fakeActor.CreatePushPlansReturns(nil, errors.New("panic")) 335 }) 336 337 It("passes up the error", func() { 338 Expect(executeErr).To(MatchError(errors.New("panic"))) 339 Expect(fakeActor.PrepareSpaceCallCount()).To(Equal(0)) 340 }) 341 }) 342 343 When("creating push plans succeeds", func() { 344 BeforeEach(func() { 345 fakeActor.CreatePushPlansReturns( 346 []v7pushaction.PushPlan{ 347 {Application: v7action.Application{Name: appName1}, SpaceGUID: "some-space-guid"}, 348 {Application: v7action.Application{Name: appName2}, SpaceGUID: "some-space-guid"}, 349 }, nil, 350 ) 351 }) 352 353 Describe("delegating to Actor.PrepareSpace", func() { 354 It("delegates to PrepareSpace", func() { 355 actualPushPlans, actualParser := fakeActor.PrepareSpaceArgsForCall(0) 356 Expect(actualPushPlans).To(ConsistOf( 357 v7pushaction.PushPlan{Application: v7action.Application{Name: appName1}, SpaceGUID: "some-space-guid"}, 358 v7pushaction.PushPlan{Application: v7action.Application{Name: appName2}, SpaceGUID: "some-space-guid"}, 359 )) 360 Expect(actualParser).To(Equal(fakeManifestParser)) 361 }) 362 363 When("Actor.PrepareSpace has no errors", func() { 364 Describe("delegating to Actor.UpdateApplicationSettings", func() { 365 When("there are no flag overrides", func() { 366 BeforeEach(func() { 367 fakeActor.UpdateApplicationSettingsReturns( 368 []v7pushaction.PushPlan{ 369 {Application: v7action.Application{Name: appName1}}, 370 {Application: v7action.Application{Name: appName2}}, 371 }, 372 v7pushaction.Warnings{"conceptualize-warning-1"}, nil) 373 }) 374 375 It("generates a push plan with the specified app path", func() { 376 Expect(executeErr).ToNot(HaveOccurred()) 377 Expect(testUI.Out).To(Say( 378 "Pushing apps %s, %s to org some-org / space some-space as some-user", 379 appName1, 380 appName2, 381 )) 382 Expect(testUI.Out).To(Say(`Getting app info\.\.\.`)) 383 Expect(testUI.Err).To(Say("conceptualize-warning-1")) 384 385 Expect(fakeActor.UpdateApplicationSettingsCallCount()).To(Equal(1)) 386 actualPushPlans := fakeActor.UpdateApplicationSettingsArgsForCall(0) 387 Expect(actualPushPlans).To(ConsistOf( 388 v7pushaction.PushPlan{Application: v7action.Application{Name: appName1}, SpaceGUID: "some-space-guid"}, 389 v7pushaction.PushPlan{Application: v7action.Application{Name: appName2}, SpaceGUID: "some-space-guid"}, 390 )) 391 }) 392 393 Describe("delegating to Actor.Actualize", func() { 394 When("Actualize returns success", func() { 395 BeforeEach(func() { 396 fakeActor.ActualizeStub = FillInValues([]Step{ 397 {}, 398 }, v7pushaction.PushPlan{Application: v7action.Application{GUID: "potato"}}) 399 }) 400 401 Describe("actualize events", func() { 402 BeforeEach(func() { 403 fakeActor.ActualizeStub = FillInValues([]Step{ 404 { 405 Event: v7pushaction.SkippingApplicationCreation, 406 Warnings: v7pushaction.Warnings{"skipping app creation warnings"}, 407 }, 408 { 409 Event: v7pushaction.CreatingApplication, 410 Warnings: v7pushaction.Warnings{"app creation warnings"}, 411 }, 412 { 413 Event: v7pushaction.CreatingAndMappingRoutes, 414 }, 415 { 416 Event: v7pushaction.CreatedRoutes, 417 Warnings: v7pushaction.Warnings{"routes warnings"}, 418 }, 419 { 420 Event: v7pushaction.CreatingArchive, 421 }, 422 { 423 Event: v7pushaction.UploadingApplicationWithArchive, 424 Warnings: v7pushaction.Warnings{"upload app archive warning"}, 425 }, 426 { 427 Event: v7pushaction.RetryUpload, 428 Warnings: v7pushaction.Warnings{"retry upload warning"}, 429 }, 430 { 431 Event: v7pushaction.UploadWithArchiveComplete, 432 }, 433 { 434 Event: v7pushaction.RestartingApplication, 435 }, 436 }, v7pushaction.PushPlan{}) 437 }) 438 439 It("actualizes the application and displays events/warnings", func() { 440 Expect(executeErr).ToNot(HaveOccurred()) 441 442 Expect(fakeProgressBar.ReadyCallCount()).Should(Equal(2)) 443 Expect(fakeProgressBar.CompleteCallCount()).Should(Equal(2)) 444 445 Expect(testUI.Out).To(Say("Updating app first-app...")) 446 Expect(testUI.Err).To(Say("skipping app creation warnings")) 447 448 Expect(testUI.Out).To(Say("Creating app first-app...")) 449 Expect(testUI.Err).To(Say("app creation warnings")) 450 451 Expect(testUI.Out).To(Say("Mapping routes...")) 452 Expect(testUI.Err).To(Say("routes warnings")) 453 454 Expect(testUI.Out).To(Say("Packaging files to upload...")) 455 456 Expect(testUI.Out).To(Say("Uploading files...")) 457 Expect(testUI.Err).To(Say("upload app archive warning")) 458 459 Expect(testUI.Out).To(Say("Retrying upload due to an error...")) 460 Expect(testUI.Err).To(Say("retry upload warning")) 461 462 Expect(testUI.Out).To(Say("Waiting for API to complete processing files...")) 463 464 Expect(testUI.Out).To(Say("Waiting for app first-app to start...")) 465 466 Expect(testUI.Out).To(Say("Updating app second-app...")) 467 Expect(testUI.Err).To(Say("skipping app creation warnings")) 468 469 Expect(testUI.Out).To(Say("Creating app second-app...")) 470 Expect(testUI.Err).To(Say("app creation warnings")) 471 472 Expect(testUI.Out).To(Say("Mapping routes...")) 473 Expect(testUI.Err).To(Say("routes warnings")) 474 475 Expect(testUI.Out).To(Say("Packaging files to upload...")) 476 477 Expect(testUI.Out).To(Say("Uploading files...")) 478 Expect(testUI.Err).To(Say("upload app archive warning")) 479 480 Expect(testUI.Out).To(Say("Retrying upload due to an error...")) 481 Expect(testUI.Err).To(Say("retry upload warning")) 482 483 Expect(testUI.Out).To(Say("Waiting for API to complete processing files...")) 484 485 Expect(testUI.Out).To(Say("Waiting for app second-app to start...")) 486 }) 487 }) 488 489 Describe("staging logs", func() { 490 BeforeEach(func() { 491 fakeActor.ActualizeStub = FillInValues([]Step{ 492 { 493 Event: v7pushaction.StartingStaging, 494 }, 495 }, v7pushaction.PushPlan{}) 496 }) 497 498 When("there are no logging errors", func() { 499 BeforeEach(func() { 500 fakeVersionActor.GetStreamingLogsForApplicationByNameAndSpaceStub = ReturnLogs( 501 []LogEvent{ 502 {Log: v7action.NewLogMessage("log-message-1", 1, time.Now(), v7action.StagingLog, "source-instance")}, 503 {Log: v7action.NewLogMessage("log-message-2", 1, time.Now(), v7action.StagingLog, "source-instance")}, 504 {Log: v7action.NewLogMessage("log-message-3", 1, time.Now(), "potato", "source-instance")}, 505 }, 506 v7action.Warnings{"log-warning-1", "log-warning-2"}, 507 nil, 508 ) 509 }) 510 511 It("displays the staging logs and warnings", func() { 512 Expect(testUI.Out).To(Say("Staging app and tracing logs...")) 513 514 Expect(testUI.Err).To(Say("log-warning-1")) 515 Expect(testUI.Err).To(Say("log-warning-2")) 516 517 Eventually(testUI.Out).Should(Say("log-message-1")) 518 Eventually(testUI.Out).Should(Say("log-message-2")) 519 Eventually(testUI.Out).ShouldNot(Say("log-message-3")) 520 521 Expect(fakeVersionActor.GetStreamingLogsForApplicationByNameAndSpaceCallCount()).To(Equal(2)) 522 passedAppName, spaceGUID, _ := fakeVersionActor.GetStreamingLogsForApplicationByNameAndSpaceArgsForCall(0) 523 Expect(passedAppName).To(Equal(appName1)) 524 Expect(spaceGUID).To(Equal("some-space-guid")) 525 passedAppName, spaceGUID, _ = fakeVersionActor.GetStreamingLogsForApplicationByNameAndSpaceArgsForCall(1) 526 Expect(passedAppName).To(Equal(appName2)) 527 Expect(spaceGUID).To(Equal("some-space-guid")) 528 529 }) 530 }) 531 532 When("there are logging errors", func() { 533 BeforeEach(func() { 534 fakeVersionActor.GetStreamingLogsForApplicationByNameAndSpaceStub = ReturnLogs( 535 []LogEvent{ 536 {Error: errors.New("some-random-err")}, 537 {Error: actionerror.NOAATimeoutError{}}, 538 {Log: v7action.NewLogMessage("log-message-1", 1, time.Now(), v7action.StagingLog, "source-instance")}, 539 }, 540 v7action.Warnings{"log-warning-1", "log-warning-2"}, 541 nil, 542 ) 543 }) 544 545 It("displays the errors as warnings", func() { 546 Expect(testUI.Out).To(Say("Staging app and tracing logs...")) 547 548 Expect(testUI.Err).To(Say("log-warning-1")) 549 Expect(testUI.Err).To(Say("log-warning-2")) 550 Eventually(testUI.Err).Should(Say("some-random-err")) 551 Eventually(testUI.Err).Should(Say("timeout connecting to log server, no log will be shown")) 552 553 Eventually(testUI.Out).Should(Say("log-message-1")) 554 }) 555 }) 556 }) 557 558 When("when getting the application summary succeeds", func() { 559 BeforeEach(func() { 560 summary := v7action.ApplicationSummary{ 561 Application: v7action.Application{}, 562 CurrentDroplet: v7action.Droplet{}, 563 ProcessSummaries: v7action.ProcessSummaries{}, 564 } 565 fakeVersionActor.GetApplicationSummaryByNameAndSpaceReturnsOnCall(0, summary, v7action.Warnings{"app-1-summary-warning-1", "app-1-summary-warning-2"}, nil) 566 fakeVersionActor.GetApplicationSummaryByNameAndSpaceReturnsOnCall(1, summary, v7action.Warnings{"app-2-summary-warning-1", "app-2-summary-warning-2"}, nil) 567 }) 568 569 // TODO: Don't test the shared.AppSummaryDisplayer.AppDisplay method. 570 // Use DI to pass in a new AppSummaryDisplayer to the Command instead. 571 It("displays the app summary", func() { 572 Expect(executeErr).ToNot(HaveOccurred()) 573 Expect(fakeVersionActor.GetApplicationSummaryByNameAndSpaceCallCount()).To(Equal(2)) 574 }) 575 }) 576 577 When("getting the application summary fails", func() { 578 BeforeEach(func() { 579 fakeVersionActor.GetApplicationSummaryByNameAndSpaceReturns( 580 v7action.ApplicationSummary{}, 581 v7action.Warnings{"get-application-summary-warnings"}, 582 errors.New("get-application-summary-error"), 583 ) 584 }) 585 586 It("does not display the app summary", func() { 587 Expect(testUI.Out).ToNot(Say(`requested state:`)) 588 }) 589 590 It("returns the error from GetApplicationSummaryByNameAndSpace", func() { 591 Expect(executeErr).To(MatchError("get-application-summary-error")) 592 }) 593 594 It("prints the warnings", func() { 595 Expect(testUI.Err).To(Say("get-application-summary-warnings")) 596 }) 597 }) 598 599 }) 600 601 When("actualize returns an error", func() { 602 When("the error is generic", func() { 603 BeforeEach(func() { 604 fakeActor.ActualizeStub = FillInValues([]Step{ 605 { 606 Error: errors.New("anti avant garde naming"), 607 }, 608 }, v7pushaction.PushPlan{}) 609 }) 610 611 It("returns the error", func() { 612 Expect(executeErr).To(MatchError("anti avant garde naming")) 613 }) 614 }) 615 616 When("the error is a startup timeout error", func() { 617 BeforeEach(func() { 618 fakeActor.ActualizeStub = FillInValues([]Step{ 619 { 620 Error: actionerror.StartupTimeoutError{}, 621 }, 622 }, v7pushaction.PushPlan{}) 623 }) 624 625 It("returns the StartupTimeoutError and prints warnings", func() { 626 Expect(executeErr).To(MatchError(translatableerror.StartupTimeoutError{ 627 AppName: "first-app", 628 BinaryName: binaryName, 629 })) 630 }) 631 }) 632 633 When("the error is a process crashed error", func() { 634 BeforeEach(func() { 635 fakeActor.ActualizeStub = FillInValues([]Step{ 636 { 637 Error: actionerror.AllInstancesCrashedError{}, 638 }, 639 }, v7pushaction.PushPlan{}) 640 }) 641 642 It("returns the ApplicationUnableToStartError", func() { 643 Expect(executeErr).To(MatchError(translatableerror.ApplicationUnableToStartError{ 644 AppName: "first-app", 645 BinaryName: binaryName, 646 })) 647 }) 648 649 It("displays the app summary", func() { 650 Expect(executeErr).To(HaveOccurred()) 651 Expect(fakeVersionActor.GetApplicationSummaryByNameAndSpaceCallCount()).To(Equal(1)) 652 }) 653 }) 654 }) 655 }) 656 }) 657 658 When("flag overrides are specified", func() { 659 BeforeEach(func() { 660 cmd.AppPath = "some/app/path" 661 }) 662 663 It("generates a push plan with the specified flag overrides", func() { 664 Expect(fakeActor.CreatePushPlansCallCount()).To(Equal(1)) 665 _, _, _, _, overrides := fakeActor.CreatePushPlansArgsForCall(0) 666 Expect(overrides).To(MatchFields(IgnoreExtras, Fields{ 667 "ProvidedAppPath": Equal("some/app/path"), 668 })) 669 }) 670 }) 671 672 When("conceptualize returns an error", func() { 673 var expectedErr error 674 675 BeforeEach(func() { 676 expectedErr = errors.New("some-error") 677 fakeActor.UpdateApplicationSettingsReturns(nil, v7pushaction.Warnings{"some-warning-1"}, expectedErr) 678 }) 679 680 It("generates a push plan with the specified app path", func() { 681 Expect(executeErr).To(MatchError(expectedErr)) 682 Expect(testUI.Err).To(Say("some-warning-1")) 683 }) 684 }) 685 }) 686 }) 687 688 When("Actor.PrepareSpace has an error", func() { 689 var pushPlansChannel chan []v7pushaction.PushPlan 690 691 BeforeEach(func() { 692 pushPlansChannel = make(chan []v7pushaction.PushPlan) 693 close(pushPlansChannel) 694 events, warnings, errors := FillInEvents([]Step{ 695 { 696 Warnings: v7pushaction.Warnings{"prepare-space-warning-1"}, 697 Error: errors.New("prepare-space-error-1"), 698 }, 699 }) 700 701 fakeActor.PrepareSpaceReturns(pushPlansChannel, events, warnings, errors) 702 }) 703 704 It("returns the error", func() { 705 Expect(executeErr).To(MatchError(errors.New("prepare-space-error-1"))) 706 Expect(testUI.Err).To(Say("prepare-space-warning-1")) 707 }) 708 709 It("does not delegate to UpdateApplicationSettings", func() { 710 Expect(fakeActor.UpdateApplicationSettingsCallCount()).To(Equal(0)) 711 }) 712 713 It("does not delegate to Actualize", func() { 714 Expect(fakeActor.ActualizeCallCount()).To(Equal(0)) 715 }) 716 }) 717 718 When("Actor.PrepareSpace has no errors but returns no apps", func() { 719 var pushPlansChannel chan []v7pushaction.PushPlan 720 721 BeforeEach(func() { 722 pushPlansChannel = make(chan []v7pushaction.PushPlan) 723 close(pushPlansChannel) 724 events, warnings, errors := FillInEvents([]Step{ 725 { 726 Warnings: v7pushaction.Warnings{"prepare-no-app-or-manifest-space-warning"}, 727 Error: nil, 728 }, 729 }) 730 731 fakeActor.PrepareSpaceReturns(pushPlansChannel, events, warnings, errors) 732 }) 733 734 It("returns the error", func() { 735 Expect(executeErr).To(MatchError(translatableerror.AppNameOrManifestRequiredError{})) 736 Expect(testUI.Err).To(Say("prepare-no-app-or-manifest-space-warning")) 737 }) 738 739 It("does not delegate to UpdateApplicationSettings", func() { 740 Expect(fakeActor.UpdateApplicationSettingsCallCount()).To(Equal(0)) 741 }) 742 743 It("does not delegate to Actualize", func() { 744 Expect(fakeActor.ActualizeCallCount()).To(Equal(0)) 745 }) 746 747 }) 748 }) 749 }) 750 }) 751 }) 752 }) 753 754 Describe("ValidateAllowedFlagsForMultipleApps", func() { 755 When("manifest contains a single app", func() { 756 DescribeTable("returns nil when", 757 func(setup func()) { 758 setup() 759 Expect(cmd.ValidateAllowedFlagsForMultipleApps(false)).ToNot(HaveOccurred()) 760 }, 761 Entry("buildpacks is specified", 762 func() { 763 cmd.Buildpacks = []string{"buildpack-1", "buildpack-2"} 764 }), 765 Entry("disk is specified", 766 func() { 767 cmd.Disk = flag.Megabytes{NullUint64: types.NullUint64{IsSet: true}} 768 }), 769 Entry("droplet is specified", 770 func() { 771 cmd.DropletPath = "some-droplet.tgz" 772 }), 773 ) 774 }) 775 776 When("manifest contains multiple apps", func() { 777 DescribeTable("throws an error when", 778 func(setup func()) { 779 setup() 780 Expect(cmd.ValidateAllowedFlagsForMultipleApps(true)).To(MatchError(translatableerror.CommandLineArgsWithMultipleAppsError{})) 781 }, 782 783 Entry("buildpacks is specified", 784 func() { 785 cmd.Buildpacks = []string{"buildpack-1", "buildpack-2"} 786 }), 787 Entry("disk is specified", 788 func() { 789 cmd.Disk = flag.Megabytes{NullUint64: types.NullUint64{IsSet: true}} 790 }), 791 Entry("droplet is specified", 792 func() { 793 cmd.DropletPath = "some-droplet.tgz" 794 }), 795 Entry("docker image is specified", 796 func() { 797 cmd.DockerImage = flag.DockerImage{Path: "some-docker"} 798 }), 799 Entry("docker username is specified", 800 func() { 801 fakeConfig.DockerPasswordReturns("some-password") 802 cmd.DockerUsername = "docker-username" 803 }), 804 Entry("health check type is specified", 805 func() { 806 cmd.HealthCheckType = flag.HealthCheckType{Type: constant.HTTP} 807 }), 808 Entry("health check HTTP endpoint is specified", 809 func() { 810 cmd.HealthCheckHTTPEndpoint = "some-endpoint" 811 }), 812 Entry("health check timeout is specified", 813 func() { 814 cmd.HealthCheckTimeout = flag.PositiveInteger{Value: 5} 815 }), 816 Entry("instances is specified", 817 func() { 818 cmd.Instances = flag.Instances{NullInt: types.NullInt{IsSet: true}} 819 }), 820 Entry("stack is specified", 821 func() { 822 cmd.Stack = "some-stack" 823 }), 824 Entry("memory is specified", 825 func() { 826 cmd.Memory = flag.Megabytes{NullUint64: types.NullUint64{IsSet: true}} 827 }), 828 Entry("provided app path is specified", 829 func() { 830 cmd.AppPath = "some-app-path" 831 }), 832 Entry("skip route creation is specified", 833 func() { 834 cmd.NoRoute = true 835 }), 836 Entry("start command is specified", 837 func() { 838 cmd.StartCommand = flag.Command{FilteredString: types.FilteredString{IsSet: true}} 839 }), 840 ) 841 842 DescribeTable("is nil when", 843 func(setup func()) { 844 setup() 845 Expect(cmd.ValidateAllowedFlagsForMultipleApps(true)).ToNot(HaveOccurred()) 846 }, 847 Entry("no flags are specified", func() {}), 848 Entry("path is specified", 849 func() { 850 cmd.PathToManifest = flag.PathWithExistenceCheck("/some/path") 851 }), 852 Entry("no-start is specified", 853 func() { 854 cmd.NoStart = true 855 }), 856 Entry("single app name is specified, even with disallowed flags", 857 func() { 858 cmd.OptionalArgs.AppName = "some-app-name" 859 860 cmd.Stack = "some-stack" 861 cmd.NoRoute = true 862 cmd.DockerImage = flag.DockerImage{Path: "some-docker"} 863 cmd.Instances = flag.Instances{NullInt: types.NullInt{IsSet: true}} 864 }), 865 ) 866 }) 867 }) 868 869 Describe("GetFlagOverrides", func() { 870 var ( 871 overrides v7pushaction.FlagOverrides 872 overridesErr error 873 ) 874 875 BeforeEach(func() { 876 cmd.Buildpacks = []string{"buildpack-1", "buildpack-2"} 877 cmd.Stack = "validStack" 878 cmd.HealthCheckType = flag.HealthCheckType{Type: constant.Port} 879 cmd.HealthCheckHTTPEndpoint = "/health-check-http-endpoint" 880 cmd.HealthCheckTimeout = flag.PositiveInteger{Value: 7} 881 cmd.Memory = flag.Megabytes{NullUint64: types.NullUint64{Value: 100, IsSet: true}} 882 cmd.Disk = flag.Megabytes{NullUint64: types.NullUint64{Value: 1024, IsSet: true}} 883 cmd.DropletPath = flag.PathWithExistenceCheck("some-droplet.tgz") 884 cmd.StartCommand = flag.Command{FilteredString: types.FilteredString{IsSet: true, Value: "some-start-command"}} 885 cmd.NoRoute = true 886 cmd.NoStart = true 887 cmd.Instances = flag.Instances{NullInt: types.NullInt{Value: 10, IsSet: true}} 888 }) 889 890 JustBeforeEach(func() { 891 overrides, overridesErr = cmd.GetFlagOverrides() 892 Expect(overridesErr).ToNot(HaveOccurred()) 893 }) 894 895 It("sets them on the flag overrides", func() { 896 Expect(overridesErr).ToNot(HaveOccurred()) 897 Expect(overrides.Buildpacks).To(ConsistOf("buildpack-1", "buildpack-2")) 898 Expect(overrides.DropletPath).To(Equal("some-droplet.tgz")) 899 Expect(overrides.Stack).To(Equal("validStack")) 900 Expect(overrides.HealthCheckType).To(Equal(constant.Port)) 901 Expect(overrides.HealthCheckEndpoint).To(Equal("/health-check-http-endpoint")) 902 Expect(overrides.HealthCheckTimeout).To(BeEquivalentTo(7)) 903 Expect(overrides.Memory).To(Equal(types.NullUint64{Value: 100, IsSet: true})) 904 Expect(overrides.Disk).To(Equal(types.NullUint64{Value: 1024, IsSet: true})) 905 Expect(overrides.StartCommand).To(Equal(types.FilteredString{IsSet: true, Value: "some-start-command"})) 906 Expect(overrides.SkipRouteCreation).To(BeTrue()) 907 Expect(overrides.NoStart).To(BeTrue()) 908 Expect(overrides.Instances).To(Equal(types.NullInt{Value: 10, IsSet: true})) 909 }) 910 911 When("a docker image is provided", func() { 912 BeforeEach(func() { 913 cmd.DockerImage = flag.DockerImage{Path: "some-docker-image"} 914 }) 915 916 It("sets docker image on the flag overrides", func() { 917 Expect(overridesErr).ToNot(HaveOccurred()) 918 Expect(overrides.DockerImage).To(Equal("some-docker-image")) 919 }) 920 }) 921 }) 922 923 Describe("ReadManifest", func() { 924 var ( 925 somePath string 926 executeErr error 927 ) 928 929 BeforeEach(func() { 930 somePath = "/some/path" 931 }) 932 933 JustBeforeEach(func() { 934 executeErr = cmd.ReadManifest() 935 }) 936 937 When("No path is provided", func() { 938 BeforeEach(func() { 939 cmd.PWD = somePath 940 }) 941 942 When("a manifest exists in the current dir", func() { 943 BeforeEach(func() { 944 fakeManifestLocator.PathReturns("/manifest/path", true, nil) 945 }) 946 947 It("uses the manifest in the current directory", func() { 948 Expect(executeErr).ToNot(HaveOccurred()) 949 950 Expect(fakeManifestLocator.PathCallCount()).To(Equal(1)) 951 Expect(fakeManifestLocator.PathArgsForCall(0)).To(Equal(cmd.PWD)) 952 953 Expect(fakeManifestParser.InterpolateAndParseCallCount()).To(Equal(1)) 954 actualManifestPath, _, _ := fakeManifestParser.InterpolateAndParseArgsForCall(0) 955 Expect(actualManifestPath).To(Equal("/manifest/path")) 956 }) 957 }) 958 959 When("there is not a manifest in the current dir", func() { 960 BeforeEach(func() { 961 fakeManifestLocator.PathReturns("", false, nil) 962 }) 963 964 It("ignores the file not found error", func() { 965 Expect(executeErr).ToNot(HaveOccurred()) 966 967 Expect(fakeManifestParser.InterpolateAndParseCallCount()).To(Equal(0)) 968 }) 969 }) 970 971 When("when there is an error locating the manifest in the current directory", func() { 972 BeforeEach(func() { 973 fakeManifestLocator.PathReturns("", false, errors.New("err-location")) 974 }) 975 976 It("ignores the file not found error", func() { 977 Expect(executeErr).To(MatchError("err-location")) 978 979 Expect(fakeManifestParser.InterpolateAndParseCallCount()).To(Equal(0)) 980 }) 981 }) 982 }) 983 984 When("The -f flag is specified", func() { 985 BeforeEach(func() { 986 cmd.PathToManifest = flag.PathWithExistenceCheck(somePath) 987 fakeManifestLocator.PathReturns("/manifest/path", true, nil) 988 }) 989 990 It("reads the manifest and passes through to PrepareSpace", func() { 991 Expect(executeErr).ToNot(HaveOccurred()) 992 993 Expect(fakeManifestLocator.PathCallCount()).To(Equal(1)) 994 Expect(fakeManifestLocator.PathArgsForCall(0)).To(Equal(somePath)) 995 996 Expect(fakeManifestParser.InterpolateAndParseCallCount()).To(Equal(1)) 997 actualManifestPath, _, _ := fakeManifestParser.InterpolateAndParseArgsForCall(0) 998 Expect(actualManifestPath).To(Equal("/manifest/path")) 999 }) 1000 }) 1001 1002 When("--vars-files are specified", func() { 1003 var varsFiles []string 1004 1005 BeforeEach(func() { 1006 fakeManifestLocator.PathReturns("/manifest/path", true, nil) 1007 varsFiles = []string{"path1", "path2"} 1008 for _, path := range varsFiles { 1009 cmd.PathsToVarsFiles = append(cmd.PathsToVarsFiles, flag.PathWithExistenceCheck(path)) 1010 } 1011 }) 1012 1013 It("passes vars files to the manifest parser", func() { 1014 Expect(executeErr).ToNot(HaveOccurred()) 1015 1016 Expect(fakeManifestParser.InterpolateAndParseCallCount()).To(Equal(1)) 1017 _, actualVarsFiles, _ := fakeManifestParser.InterpolateAndParseArgsForCall(0) 1018 Expect(actualVarsFiles).To(Equal(varsFiles)) 1019 }) 1020 }) 1021 1022 When("The --var flag is provided", func() { 1023 var vars []template.VarKV 1024 1025 BeforeEach(func() { 1026 fakeManifestLocator.PathReturns("/manifest/path", true, nil) 1027 vars = []template.VarKV{ 1028 {Name: "put-var-here", Value: "turtle"}, 1029 } 1030 cmd.Vars = vars 1031 }) 1032 1033 It("passes vars files to the manifest parser", func() { 1034 Expect(executeErr).ToNot(HaveOccurred()) 1035 1036 Expect(fakeManifestParser.InterpolateAndParseCallCount()).To(Equal(1)) 1037 _, _, actualVars := fakeManifestParser.InterpolateAndParseArgsForCall(0) 1038 Expect(actualVars).To(Equal(vars)) 1039 }) 1040 }) 1041 }) 1042 1043 DescribeTable("ValidateFlags returns an error", 1044 func(setup func(), expectedErr error) { 1045 setup() 1046 err := cmd.ValidateFlags() 1047 if expectedErr == nil { 1048 Expect(err).To(BeNil()) 1049 } else { 1050 Expect(err).To(MatchError(expectedErr)) 1051 } 1052 }, 1053 1054 Entry("when docker username flag is passed *without* docker flag", 1055 func() { 1056 cmd.DockerUsername = "some-docker-username" 1057 }, 1058 translatableerror.RequiredFlagsError{Arg1: "--docker-image, -o", Arg2: "--docker-username"}), 1059 1060 Entry("when docker and buildpacks flags are passed", 1061 func() { 1062 cmd.DockerImage.Path = "some-docker-image" 1063 cmd.Buildpacks = []string{"some-buildpack"} 1064 }, 1065 translatableerror.ArgumentCombinationError{Args: []string{"--buildpack, -b", "--docker-image, -o"}}), 1066 1067 Entry("when docker and stack flags are passed", 1068 func() { 1069 cmd.DockerImage.Path = "some-docker-image" 1070 cmd.Stack = "validStack" 1071 }, 1072 translatableerror.ArgumentCombinationError{Args: []string{"--stack, -s", "--docker-image, -o"}}), 1073 1074 Entry("when docker and path flags are passed", 1075 func() { 1076 cmd.DockerImage.Path = "some-docker-image" 1077 cmd.AppPath = "some-directory-path" 1078 }, 1079 translatableerror.ArgumentCombinationError{Args: []string{"--docker-image, -o", "--path, -p"}}), 1080 1081 Entry("when -u http does not have a matching --endpoint", 1082 func() { 1083 cmd.HealthCheckType.Type = constant.HTTP 1084 }, 1085 translatableerror.RequiredFlagsError{Arg1: "--endpoint", Arg2: "--health-check-type=http, -u=http"}), 1086 1087 Entry("when --endpoint does not have a matching -u", 1088 func() { 1089 cmd.HealthCheckHTTPEndpoint = "/health" 1090 }, 1091 translatableerror.RequiredFlagsError{Arg1: "--health-check-type=http, -u=http", Arg2: "--endpoint"}), 1092 1093 Entry("when --endpoint has a matching -u=process instead of a -u=http", 1094 func() { 1095 cmd.HealthCheckHTTPEndpoint = "/health" 1096 cmd.HealthCheckType.Type = constant.Process 1097 }, 1098 translatableerror.RequiredFlagsError{Arg1: "--health-check-type=http, -u=http", Arg2: "--endpoint"}), 1099 1100 Entry("when --endpoint has a matching -u=port instead of a -u=http", 1101 func() { 1102 cmd.HealthCheckHTTPEndpoint = "/health" 1103 cmd.HealthCheckType.Type = constant.Port 1104 }, 1105 translatableerror.RequiredFlagsError{Arg1: "--health-check-type=http, -u=http", Arg2: "--endpoint"}), 1106 1107 Entry("when -u http does have a matching --endpoint", 1108 func() { 1109 cmd.HealthCheckType.Type = constant.HTTP 1110 cmd.HealthCheckHTTPEndpoint = "/health" 1111 }, 1112 nil), 1113 1114 Entry("when droplet and path flags are passed", 1115 func() { 1116 cmd.DropletPath = "some-droplet.tgz" 1117 cmd.AppPath = "/my/app" 1118 }, 1119 translatableerror.ArgumentCombinationError{ 1120 Args: []string{ 1121 "--droplet", "--docker-image, -o", "--docker-username", "-p", 1122 }, 1123 }), 1124 1125 Entry("when droplet and docker image flags are passed", 1126 func() { 1127 cmd.DropletPath = "some-droplet.tgz" 1128 cmd.DockerImage.Path = "docker-image" 1129 }, 1130 translatableerror.ArgumentCombinationError{ 1131 Args: []string{ 1132 "--droplet", "--docker-image, -o", "--docker-username", "-p", 1133 }, 1134 }), 1135 1136 Entry("when droplet, docker image, and docker username flags are passed", 1137 func() { 1138 cmd.DropletPath = "some-droplet.tgz" 1139 cmd.DockerImage.Path = "docker-image" 1140 cmd.DockerUsername = "docker-username" 1141 }, 1142 translatableerror.ArgumentCombinationError{ 1143 Args: []string{ 1144 "--droplet", "--docker-image, -o", "--docker-username", "-p", 1145 }, 1146 }), 1147 ) 1148 })