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