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