github.com/randomtask1155/cli@v6.41.1-0.20181227003417-a98eed78cbde+incompatible/command/v7/push_command_test.go (about)

     1  package v7_test
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  	"time"
    10  
    11  	"code.cloudfoundry.org/cli/command/translatableerror"
    12  
    13  	"code.cloudfoundry.org/cli/actor/actionerror"
    14  	"code.cloudfoundry.org/cli/actor/v7action"
    15  	"code.cloudfoundry.org/cli/actor/v7action/v7actionfakes"
    16  	"code.cloudfoundry.org/cli/actor/v7pushaction"
    17  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant"
    18  	"code.cloudfoundry.org/cli/command/commandfakes"
    19  	"code.cloudfoundry.org/cli/command/flag"
    20  	"code.cloudfoundry.org/cli/command/v6/v6fakes"
    21  	. "code.cloudfoundry.org/cli/command/v7"
    22  	"code.cloudfoundry.org/cli/command/v7/v7fakes"
    23  	"code.cloudfoundry.org/cli/types"
    24  	"code.cloudfoundry.org/cli/util/configv3"
    25  	"code.cloudfoundry.org/cli/util/ui"
    26  	. "github.com/onsi/ginkgo"
    27  	. "github.com/onsi/ginkgo/extensions/table"
    28  	. "github.com/onsi/gomega"
    29  	. "github.com/onsi/gomega/gbytes"
    30  	. "github.com/onsi/gomega/gstruct"
    31  )
    32  
    33  type Step struct {
    34  	Error    error
    35  	Event    v7pushaction.Event
    36  	Warnings v7pushaction.Warnings
    37  }
    38  
    39  func FillInValues(tuples []Step, state v7pushaction.PushState) func(v7pushaction.PushState, v7pushaction.ProgressBar) (<-chan v7pushaction.PushState, <-chan v7pushaction.Event, <-chan v7pushaction.Warnings, <-chan error) {
    40  	return func(v7pushaction.PushState, v7pushaction.ProgressBar) (<-chan v7pushaction.PushState, <-chan v7pushaction.Event, <-chan v7pushaction.Warnings, <-chan error) {
    41  		stateStream := make(chan v7pushaction.PushState)
    42  
    43  		eventStream := make(chan v7pushaction.Event)
    44  		warningsStream := make(chan v7pushaction.Warnings)
    45  		errorStream := make(chan error)
    46  
    47  		go func() {
    48  			defer close(stateStream)
    49  			defer close(eventStream)
    50  			defer close(warningsStream)
    51  			defer close(errorStream)
    52  
    53  			for _, tuple := range tuples {
    54  				warningsStream <- tuple.Warnings
    55  				if tuple.Error != nil {
    56  					errorStream <- tuple.Error
    57  					return
    58  				} else {
    59  					eventStream <- tuple.Event
    60  				}
    61  			}
    62  
    63  			stateStream <- state
    64  			eventStream <- v7pushaction.Complete
    65  		}()
    66  
    67  		return stateStream, eventStream, warningsStream, errorStream
    68  	}
    69  }
    70  
    71  type LogEvent struct {
    72  	Log   *v7action.LogMessage
    73  	Error error
    74  }
    75  
    76  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) {
    77  	return func(appName string, spaceGUID string, client v7action.NOAAClient) (<-chan *v7action.LogMessage, <-chan error, v7action.Warnings, error) {
    78  		logStream := make(chan *v7action.LogMessage)
    79  		errStream := make(chan error)
    80  		go func() {
    81  			defer close(logStream)
    82  			defer close(errStream)
    83  
    84  			for _, log := range logevents {
    85  				if log.Log != nil {
    86  					logStream <- log.Log
    87  				}
    88  				if log.Error != nil {
    89  					errStream <- log.Error
    90  				}
    91  			}
    92  		}()
    93  
    94  		return logStream, errStream, passedWarnings, passedError
    95  	}
    96  }
    97  
    98  var _ = Describe("push Command", func() {
    99  	var (
   100  		cmd              PushCommand
   101  		input            *Buffer
   102  		testUI           *ui.UI
   103  		fakeConfig       *commandfakes.FakeConfig
   104  		fakeSharedActor  *commandfakes.FakeSharedActor
   105  		fakeActor        *v7fakes.FakePushActor
   106  		fakeVersionActor *v7fakes.FakeV7ActorForPush
   107  		fakeProgressBar  *v6fakes.FakeProgressBar
   108  		fakeNOAAClient   *v7actionfakes.FakeNOAAClient
   109  		binaryName       string
   110  		executeErr       error
   111  
   112  		appName   string
   113  		userName  string
   114  		spaceName string
   115  		orgName   string
   116  		pwd       string
   117  	)
   118  
   119  	BeforeEach(func() {
   120  		input = NewBuffer()
   121  		testUI = ui.NewTestUI(input, NewBuffer(), NewBuffer())
   122  		fakeConfig = new(commandfakes.FakeConfig)
   123  		fakeSharedActor = new(commandfakes.FakeSharedActor)
   124  		fakeActor = new(v7fakes.FakePushActor)
   125  		fakeVersionActor = new(v7fakes.FakeV7ActorForPush)
   126  		fakeProgressBar = new(v6fakes.FakeProgressBar)
   127  		fakeNOAAClient = new(v7actionfakes.FakeNOAAClient)
   128  
   129  		appName = "some-app"
   130  		userName = "some-user"
   131  		spaceName = "some-space"
   132  		orgName = "some-org"
   133  		pwd = "/push/cmd/test"
   134  
   135  		binaryName = "faceman"
   136  		fakeConfig.BinaryNameReturns(binaryName)
   137  		fakeConfig.ExperimentalReturns(true) // TODO: Delete once we remove the experimental flag
   138  
   139  		cmd = PushCommand{
   140  			RequiredArgs: flag.AppName{AppName: "some-app"},
   141  			UI:           testUI,
   142  			Config:       fakeConfig,
   143  			Actor:        fakeActor,
   144  			VersionActor: fakeVersionActor,
   145  			SharedActor:  fakeSharedActor,
   146  			ProgressBar:  fakeProgressBar,
   147  			NOAAClient:   fakeNOAAClient,
   148  			PWD:          pwd,
   149  		}
   150  	})
   151  
   152  	Describe("Execute", func() {
   153  		JustBeforeEach(func() {
   154  			executeErr = cmd.Execute(nil)
   155  		})
   156  
   157  		When("checking target fails", func() {
   158  			BeforeEach(func() {
   159  				fakeSharedActor.CheckTargetReturns(actionerror.NoOrganizationTargetedError{BinaryName: binaryName})
   160  			})
   161  
   162  			It("returns an error", func() {
   163  				Expect(executeErr).To(MatchError(actionerror.NoOrganizationTargetedError{BinaryName: binaryName}))
   164  
   165  				Expect(fakeSharedActor.CheckTargetCallCount()).To(Equal(1))
   166  				checkTargetedOrg, checkTargetedSpace := fakeSharedActor.CheckTargetArgsForCall(0)
   167  				Expect(checkTargetedOrg).To(BeTrue())
   168  				Expect(checkTargetedSpace).To(BeTrue())
   169  			})
   170  		})
   171  
   172  		When("checking target fails because the user is not logged in", func() {
   173  			BeforeEach(func() {
   174  				fakeSharedActor.CheckTargetReturns(actionerror.NotLoggedInError{BinaryName: binaryName})
   175  			})
   176  
   177  			It("returns an error", func() {
   178  				Expect(executeErr).To(MatchError(actionerror.NotLoggedInError{BinaryName: binaryName}))
   179  
   180  				Expect(fakeSharedActor.CheckTargetCallCount()).To(Equal(1))
   181  				checkTargetedOrg, checkTargetedSpace := fakeSharedActor.CheckTargetArgsForCall(0)
   182  				Expect(checkTargetedOrg).To(BeTrue())
   183  				Expect(checkTargetedSpace).To(BeTrue())
   184  			})
   185  		})
   186  
   187  		When("the user is logged in, and org and space are targeted", func() {
   188  			BeforeEach(func() {
   189  				fakeConfig.CurrentUserReturns(configv3.User{Name: userName}, nil)
   190  
   191  				fakeConfig.TargetedOrganizationReturns(configv3.Organization{
   192  					Name: orgName,
   193  					GUID: "some-org-guid",
   194  				})
   195  				fakeConfig.TargetedSpaceReturns(configv3.Space{
   196  					Name: spaceName,
   197  					GUID: "some-space-guid",
   198  				})
   199  			})
   200  
   201  			It("displays the experimental warning", func() {
   202  				Expect(testUI.Err).To(Say("This command is in EXPERIMENTAL stage and may change without notice"))
   203  			})
   204  
   205  			When("invalid flags are passed", func() {
   206  				BeforeEach(func() {
   207  					cmd.DockerUsername = "some-docker-username"
   208  				})
   209  
   210  				It("returns a validation error", func() {
   211  					Expect(executeErr).To(MatchError(translatableerror.RequiredFlagsError{Arg1: "--docker-image, -o", Arg2: "--docker-username"}))
   212  				})
   213  			})
   214  
   215  			Describe("manifest", func() {
   216  				var tempDir string
   217  
   218  				BeforeEach(func() {
   219  					var err error
   220  					tempDir, err = ioutil.TempDir("", "manifest-push-unit")
   221  					Expect(err).ToNot(HaveOccurred())
   222  				})
   223  
   224  				AfterEach(func() {
   225  					Expect(os.RemoveAll(tempDir)).ToNot(HaveOccurred())
   226  				})
   227  
   228  				When("No path is provided", func() {
   229  					BeforeEach(func() {
   230  						cmd.PWD = tempDir
   231  					})
   232  
   233  					When("there is a manifest file in the current dir", func() {
   234  						var yamlContents []byte
   235  
   236  						BeforeEach(func() {
   237  							yamlContents = []byte(`---\n- banana`)
   238  							pathToYAMLFile := filepath.Join(tempDir, "manifest.yml")
   239  							err := ioutil.WriteFile(pathToYAMLFile, yamlContents, 0644)
   240  							Expect(err).ToNot(HaveOccurred())
   241  						})
   242  
   243  						It("reads the manifest and passes through to conceptualize", func() {
   244  							Expect(executeErr).ToNot(HaveOccurred())
   245  							Expect(fakeActor.ConceptualizeCallCount()).To(Equal(1))
   246  							_, _, _, _, _, manifest := fakeActor.ConceptualizeArgsForCall(0)
   247  							Expect(manifest).To(Equal(yamlContents))
   248  						})
   249  					})
   250  
   251  					When("there is not a manifest in the current dir", func() {
   252  						It("does not pass a manifest to conceptualize", func() {
   253  							Expect(executeErr).ToNot(HaveOccurred())
   254  							Expect(fakeActor.ConceptualizeCallCount()).To(Equal(1))
   255  							_, _, _, _, _, manifest := fakeActor.ConceptualizeArgsForCall(0)
   256  							Expect(manifest).To(BeNil())
   257  						})
   258  					})
   259  				})
   260  
   261  				When("The -f flag is specified", func() {
   262  					When("The manifest exists at the path", func() {
   263  						var yamlContents []byte
   264  
   265  						BeforeEach(func() {
   266  							yamlContents = []byte(`---\n- banana`)
   267  							pathToYAMLFile := filepath.Join(tempDir, "manifest.yml")
   268  							err := ioutil.WriteFile(pathToYAMLFile, yamlContents, 0644)
   269  							Expect(err).ToNot(HaveOccurred())
   270  							cmd.PathToManifest = flag.PathWithExistenceCheck(pathToYAMLFile)
   271  						})
   272  
   273  						It("reads the manifest and passes through to conceptualize", func() {
   274  							Expect(executeErr).ToNot(HaveOccurred())
   275  							Expect(fakeActor.ConceptualizeCallCount()).To(Equal(1))
   276  							_, _, _, _, _, manifest := fakeActor.ConceptualizeArgsForCall(0)
   277  							Expect(manifest).To(Equal(yamlContents))
   278  						})
   279  					})
   280  
   281  					When("The manifest does not exist at the path", func() {
   282  						BeforeEach(func() {
   283  							cmd.PathToManifest = "/some/non-existant/path.yml"
   284  						})
   285  
   286  						It("throws an error", func() {
   287  							Expect(os.IsNotExist(executeErr)).To(BeTrue(), fmt.Sprintf("expected to get an 'is not exists' error but got %#v", executeErr))
   288  						})
   289  					})
   290  				})
   291  			})
   292  
   293  			When("there are no flag overrides", func() {
   294  				BeforeEach(func() {
   295  					fakeActor.ConceptualizeReturns(
   296  						[]v7pushaction.PushState{
   297  							{
   298  								Application: v7action.Application{Name: appName},
   299  							},
   300  						},
   301  						v7pushaction.Warnings{"some-warning-1"}, nil)
   302  				})
   303  
   304  				When("the app is successfully actualized", func() {
   305  					BeforeEach(func() {
   306  						fakeActor.ActualizeStub = FillInValues([]Step{
   307  							{},
   308  						}, v7pushaction.PushState{Application: v7action.Application{GUID: "potato"}})
   309  					})
   310  
   311  					Describe("actualize events", func() {
   312  						BeforeEach(func() {
   313  							fakeActor.ActualizeStub = FillInValues([]Step{
   314  								{
   315  									Event:    v7pushaction.SkippingApplicationCreation,
   316  									Warnings: v7pushaction.Warnings{"skipping app creation warnings"},
   317  								},
   318  								{
   319  									Event:    v7pushaction.CreatingApplication,
   320  									Warnings: v7pushaction.Warnings{"app creation warnings"},
   321  								},
   322  								{
   323  									Event: v7pushaction.CreatingAndMappingRoutes,
   324  								},
   325  								{
   326  									Event:    v7pushaction.CreatedRoutes,
   327  									Warnings: v7pushaction.Warnings{"routes warnings"},
   328  								},
   329  								{
   330  									Event: v7pushaction.CreatingArchive,
   331  								},
   332  								{
   333  									Event:    v7pushaction.UploadingApplicationWithArchive,
   334  									Warnings: v7pushaction.Warnings{"upload app archive warning"},
   335  								},
   336  								{
   337  									Event:    v7pushaction.RetryUpload,
   338  									Warnings: v7pushaction.Warnings{"retry upload warning"},
   339  								},
   340  								{
   341  									Event: v7pushaction.UploadWithArchiveComplete,
   342  								},
   343  								{
   344  									Event: v7pushaction.StagingComplete,
   345  								},
   346  							}, v7pushaction.PushState{})
   347  						})
   348  
   349  						It("generates a push state with the specified app path", func() {
   350  							Expect(executeErr).ToNot(HaveOccurred())
   351  							Expect(testUI.Out).To(Say("Pushing app %s to org some-org / space some-space as some-user", appName))
   352  							Expect(testUI.Out).To(Say(`Getting app info\.\.\.`))
   353  							Expect(testUI.Err).To(Say("some-warning-1"))
   354  
   355  							Expect(fakeActor.ConceptualizeCallCount()).To(Equal(1))
   356  							name, spaceGUID, orgGUID, currentDirectory, _, _ := fakeActor.ConceptualizeArgsForCall(0)
   357  							Expect(name).To(Equal(appName))
   358  							Expect(spaceGUID).To(Equal("some-space-guid"))
   359  							Expect(orgGUID).To(Equal("some-org-guid"))
   360  							Expect(currentDirectory).To(Equal(pwd))
   361  						})
   362  
   363  						It("actualizes the application and displays events/warnings", func() {
   364  							Expect(executeErr).ToNot(HaveOccurred())
   365  
   366  							Expect(testUI.Out).To(Say("Updating app some-app..."))
   367  							Expect(testUI.Err).To(Say("skipping app creation warnings"))
   368  
   369  							Expect(testUI.Out).To(Say("Creating app some-app..."))
   370  							Expect(testUI.Err).To(Say("app creation warnings"))
   371  
   372  							Expect(testUI.Out).To(Say("Mapping routes..."))
   373  							Expect(testUI.Err).To(Say("routes warnings"))
   374  
   375  							Expect(testUI.Out).To(Say("Packaging files to upload..."))
   376  
   377  							Expect(testUI.Out).To(Say("Uploading files..."))
   378  							Expect(testUI.Err).To(Say("upload app archive warning"))
   379  							Expect(fakeProgressBar.ReadyCallCount()).Should(Equal(1))
   380  
   381  							Expect(testUI.Out).To(Say("Retrying upload due to an error..."))
   382  							Expect(testUI.Err).To(Say("retry upload warning"))
   383  
   384  							Expect(testUI.Out).To(Say("Waiting for API to complete processing files..."))
   385  
   386  							Expect(testUI.Out).To(Say("Waiting for app to start..."))
   387  							Expect(fakeProgressBar.CompleteCallCount()).Should(Equal(1))
   388  						})
   389  					})
   390  
   391  					Describe("staging logs", func() {
   392  						BeforeEach(func() {
   393  							fakeActor.ActualizeStub = FillInValues([]Step{
   394  								{
   395  									Event: v7pushaction.StartingStaging,
   396  								},
   397  							}, v7pushaction.PushState{})
   398  						})
   399  
   400  						When("there are no logging errors", func() {
   401  							BeforeEach(func() {
   402  								fakeVersionActor.GetStreamingLogsForApplicationByNameAndSpaceStub = ReturnLogs(
   403  									[]LogEvent{
   404  										{Log: v7action.NewLogMessage("log-message-1", 1, time.Now(), v7action.StagingLog, "source-instance")},
   405  										{Log: v7action.NewLogMessage("log-message-2", 1, time.Now(), v7action.StagingLog, "source-instance")},
   406  										{Log: v7action.NewLogMessage("log-message-3", 1, time.Now(), "potato", "source-instance")},
   407  									},
   408  									v7action.Warnings{"log-warning-1", "log-warning-2"},
   409  									nil,
   410  								)
   411  							})
   412  
   413  							It("displays the staging logs and warnings", func() {
   414  								Expect(testUI.Out).To(Say("Staging app and tracing logs..."))
   415  
   416  								Expect(testUI.Err).To(Say("log-warning-1"))
   417  								Expect(testUI.Err).To(Say("log-warning-2"))
   418  
   419  								Eventually(testUI.Out).Should(Say("log-message-1"))
   420  								Eventually(testUI.Out).Should(Say("log-message-2"))
   421  								Eventually(testUI.Out).ShouldNot(Say("log-message-3"))
   422  
   423  								Expect(fakeVersionActor.GetStreamingLogsForApplicationByNameAndSpaceCallCount()).To(Equal(1))
   424  								passedAppName, spaceGUID, _ := fakeVersionActor.GetStreamingLogsForApplicationByNameAndSpaceArgsForCall(0)
   425  								Expect(passedAppName).To(Equal(appName))
   426  								Expect(spaceGUID).To(Equal("some-space-guid"))
   427  							})
   428  						})
   429  
   430  						When("there are logging errors", func() {
   431  							BeforeEach(func() {
   432  								fakeVersionActor.GetStreamingLogsForApplicationByNameAndSpaceStub = ReturnLogs(
   433  									[]LogEvent{
   434  										{Error: errors.New("some-random-err")},
   435  										{Error: actionerror.NOAATimeoutError{}},
   436  										{Log: v7action.NewLogMessage("log-message-1", 1, time.Now(), v7action.StagingLog, "source-instance")},
   437  									},
   438  									v7action.Warnings{"log-warning-1", "log-warning-2"},
   439  									nil,
   440  								)
   441  							})
   442  
   443  							It("displays the errors as warnings", func() {
   444  								Expect(testUI.Out).To(Say("Staging app and tracing logs..."))
   445  
   446  								Expect(testUI.Err).To(Say("log-warning-1"))
   447  								Expect(testUI.Err).To(Say("log-warning-2"))
   448  								Eventually(testUI.Err).Should(Say("some-random-err"))
   449  								Eventually(testUI.Err).Should(Say("timeout connecting to log server, no log will be shown"))
   450  
   451  								Eventually(testUI.Out).Should(Say("log-message-1"))
   452  							})
   453  						})
   454  					})
   455  
   456  					When("restarting the app succeeds", func() {
   457  						BeforeEach(func() {
   458  							fakeVersionActor.RestartApplicationReturns(v7action.Warnings{"some-restart-warning"}, nil)
   459  
   460  							summary := v7action.ApplicationSummary{
   461  								Application: v7action.Application{
   462  									Name:  appName,
   463  									State: constant.ApplicationStarted,
   464  								},
   465  								CurrentDroplet: v7action.Droplet{
   466  									Stack: "cflinuxfs2",
   467  									Buildpacks: []v7action.Buildpack{
   468  										{
   469  											Name:         "ruby_buildpack",
   470  											DetectOutput: "some-detect-output",
   471  										},
   472  										{
   473  											Name:         "some-buildpack",
   474  											DetectOutput: "",
   475  										},
   476  									},
   477  								},
   478  								ProcessSummaries: v7action.ProcessSummaries{
   479  									{
   480  										Process: v7action.Process{
   481  											Type:    constant.ProcessTypeWeb,
   482  											Command: *types.NewFilteredString("some-command-1"),
   483  										},
   484  									},
   485  									{
   486  										Process: v7action.Process{
   487  											Type:    "console",
   488  											Command: *types.NewFilteredString("some-command-2"),
   489  										},
   490  									},
   491  								},
   492  							}
   493  							fakeVersionActor.GetApplicationSummaryByNameAndSpaceReturns(summary, v7action.Warnings{"app-summary-warning-1", "app-summary-warning-2"}, nil)
   494  						})
   495  
   496  						It("restarts the app and displays warnings", func() {
   497  							Expect(executeErr).ToNot(HaveOccurred())
   498  
   499  							Expect(testUI.Err).To(Say("some-restart-warning"))
   500  
   501  							Expect(fakeVersionActor.RestartApplicationCallCount()).To(Equal(1))
   502  							Expect(fakeVersionActor.RestartApplicationArgsForCall(0)).To(Equal("potato"))
   503  						})
   504  
   505  						It("displays the app summary", func() {
   506  							Expect(executeErr).ToNot(HaveOccurred())
   507  							Expect(testUI.Out).To(Say(`name:\s+some-app`))
   508  							Expect(testUI.Out).To(Say(`requested state:\s+started`))
   509  							Expect(testUI.Out).To(Say("type:\\s+web"))
   510  							Expect(testUI.Out).To(Say("start command:\\s+some-command-1"))
   511  							Expect(testUI.Out).To(Say("type:\\s+console"))
   512  							Expect(testUI.Out).To(Say("start command:\\s+some-command-2"))
   513  
   514  							Expect(testUI.Err).To(Say("warning-1"))
   515  							Expect(testUI.Err).To(Say("warning-2"))
   516  
   517  							Expect(fakeVersionActor.GetApplicationSummaryByNameAndSpaceCallCount()).To(Equal(1))
   518  							name, spaceGUID, withObfuscatedValues, _ := fakeVersionActor.GetApplicationSummaryByNameAndSpaceArgsForCall(0)
   519  							Expect(name).To(Equal("some-app"))
   520  							Expect(spaceGUID).To(Equal("some-space-guid"))
   521  							Expect(withObfuscatedValues).To(BeTrue())
   522  						})
   523  					})
   524  
   525  					When("restarting the app fails", func() {
   526  						When("restarting fails in a generic way", func() {
   527  							BeforeEach(func() {
   528  								fakeVersionActor.RestartApplicationReturns(v7action.Warnings{"some-restart-warning"}, errors.New("restart failure"))
   529  							})
   530  
   531  							It("returns an error and any warnings", func() {
   532  								Expect(executeErr).To(MatchError("restart failure"))
   533  								Expect(testUI.Err).To(Say("some-restart-warning"))
   534  
   535  							})
   536  						})
   537  
   538  						When("the error is an AllInstancesCrashedError", func() {
   539  							BeforeEach(func() {
   540  								fakeVersionActor.RestartApplicationReturns(nil, actionerror.AllInstancesCrashedError{})
   541  							})
   542  
   543  							It("returns the ApplicationUnableToStartError", func() {
   544  								Expect(executeErr).To(MatchError(translatableerror.ApplicationUnableToStartError{
   545  									AppName:    "some-app",
   546  									BinaryName: binaryName,
   547  								}))
   548  							})
   549  
   550  						})
   551  
   552  						When("restart times out", func() {
   553  							BeforeEach(func() {
   554  								fakeVersionActor.RestartApplicationReturns(v7action.Warnings{"some-restart-warning"}, actionerror.StartupTimeoutError{})
   555  							})
   556  
   557  							It("returns the StartupTimeoutError and prints warnings", func() {
   558  								Expect(executeErr).To(MatchError(translatableerror.StartupTimeoutError{
   559  									AppName:    "some-app",
   560  									BinaryName: binaryName,
   561  								}))
   562  
   563  								Expect(testUI.Err).To(Say("some-restart-warning"))
   564  							})
   565  						})
   566  					})
   567  				})
   568  
   569  				When("actualizing fails", func() {
   570  					BeforeEach(func() {
   571  						fakeActor.ActualizeStub = FillInValues([]Step{
   572  							{
   573  								Error: errors.New("anti avant garde naming"),
   574  							},
   575  						}, v7pushaction.PushState{})
   576  					})
   577  
   578  					It("returns the error", func() {
   579  						Expect(executeErr).To(MatchError("anti avant garde naming"))
   580  					})
   581  				})
   582  			})
   583  
   584  			When("flag overrides are specified", func() {
   585  				BeforeEach(func() {
   586  					cmd.AppPath = "some/app/path"
   587  				})
   588  
   589  				It("generates a push state with the specified flag overrides", func() {
   590  					Expect(fakeActor.ConceptualizeCallCount()).To(Equal(1))
   591  					_, _, _, _, overrides, _ := fakeActor.ConceptualizeArgsForCall(0)
   592  					Expect(overrides).To(MatchFields(IgnoreExtras, Fields{
   593  						"ProvidedAppPath": Equal("some/app/path"),
   594  					}))
   595  				})
   596  			})
   597  
   598  			When("conceptualize returns an error", func() {
   599  				var expectedErr error
   600  
   601  				BeforeEach(func() {
   602  					expectedErr = errors.New("some-error")
   603  					fakeActor.ConceptualizeReturns(nil, v7pushaction.Warnings{"some-warning-1"}, expectedErr)
   604  				})
   605  
   606  				It("generates a push state with the specified app path", func() {
   607  					Expect(executeErr).To(MatchError(expectedErr))
   608  					Expect(testUI.Err).To(Say("some-warning-1"))
   609  				})
   610  			})
   611  		})
   612  	})
   613  
   614  	Describe("GetFlagOverrides", func() {
   615  		var (
   616  			overrides    v7pushaction.FlagOverrides
   617  			overridesErr error
   618  		)
   619  
   620  		BeforeEach(func() {
   621  			cmd.Buildpacks = []string{"buildpack-1", "buildpack-2"}
   622  			cmd.HealthCheckType = flag.HealthCheckType{Type: constant.Port}
   623  			cmd.Memory = flag.Megabytes{NullUint64: types.NullUint64{Value: 100, IsSet: true}}
   624  			cmd.StartCommand = flag.Command{FilteredString: types.FilteredString{IsSet: true, Value: "some-start-command"}}
   625  			cmd.NoRoute = true
   626  			cmd.NoStart = true
   627  			cmd.Instances = flag.Instances{NullInt: types.NullInt{Value: 10, IsSet: true}}
   628  		})
   629  
   630  		JustBeforeEach(func() {
   631  			overrides, overridesErr = cmd.GetFlagOverrides()
   632  			Expect(overridesErr).ToNot(HaveOccurred())
   633  		})
   634  
   635  		It("sets them on the flag overrides", func() {
   636  			Expect(overridesErr).ToNot(HaveOccurred())
   637  			Expect(overrides.Buildpacks).To(ConsistOf("buildpack-1", "buildpack-2"))
   638  			Expect(overrides.HealthCheckType).To(Equal(constant.Port))
   639  			Expect(overrides.Memory).To(Equal(types.NullUint64{Value: 100, IsSet: true}))
   640  			Expect(overrides.StartCommand).To(Equal(types.FilteredString{IsSet: true, Value: "some-start-command"}))
   641  			Expect(overrides.SkipRouteCreation).To(BeTrue())
   642  			Expect(overrides.NoStart).To(BeTrue())
   643  			Expect(overrides.Instances).To(Equal(types.NullInt{Value: 10, IsSet: true}))
   644  		})
   645  
   646  		When("a docker image is provided", func() {
   647  			BeforeEach(func() {
   648  				cmd.DockerImage = flag.DockerImage{Path: "some-docker-image"}
   649  			})
   650  
   651  			It("sets docker image on the flag overrides", func() {
   652  				Expect(overridesErr).ToNot(HaveOccurred())
   653  				Expect(overrides.DockerImage).To(Equal("some-docker-image"))
   654  			})
   655  
   656  			When("docker username is provided", func() {
   657  				When("a password is provided via environment variable", func() {
   658  					BeforeEach(func() {
   659  						cmd.DockerUsername = "some-docker-username"
   660  						fakeConfig.DockerPasswordReturns("some-docker-password")
   661  					})
   662  
   663  					It("takes the password from the environment", func() {
   664  						Expect(overridesErr).ToNot(HaveOccurred())
   665  
   666  						Expect(testUI.Out).ToNot(Say("Environment variable CF_DOCKER_PASSWORD not set."))
   667  						Expect(testUI.Out).ToNot(Say("Docker password"))
   668  
   669  						Expect(testUI.Out).To(Say("Using docker repository password from environment variable CF_DOCKER_PASSWORD."))
   670  
   671  						Expect(overrides.DockerUsername).To(Equal("some-docker-username"))
   672  						Expect(overrides.DockerPassword).To(Equal("some-docker-password"))
   673  					})
   674  				})
   675  
   676  				When("no password is provided", func() {
   677  					BeforeEach(func() {
   678  						cmd.DockerUsername = "some-docker-username"
   679  						input.Write([]byte("some-docker-password\n"))
   680  					})
   681  
   682  					It("prompts for a password", func() {
   683  						Expect(overridesErr).ToNot(HaveOccurred())
   684  
   685  						Expect(testUI.Out).To(Say("Environment variable CF_DOCKER_PASSWORD not set."))
   686  						Expect(testUI.Out).To(Say("Docker password"))
   687  
   688  						Expect(overrides.DockerUsername).To(Equal("some-docker-username"))
   689  						Expect(overrides.DockerPassword).To(Equal("some-docker-password"))
   690  					})
   691  				})
   692  			})
   693  		})
   694  	})
   695  
   696  	DescribeTable("ValidateFlags returns an error",
   697  		func(setup func(), expectedErr error) {
   698  			setup()
   699  			err := cmd.ValidateFlags()
   700  			Expect(err).To(MatchError(expectedErr))
   701  		},
   702  
   703  		Entry("when docker username flag is passed *without* docker flag",
   704  			func() {
   705  				cmd.DockerUsername = "some-docker-username"
   706  			},
   707  			translatableerror.RequiredFlagsError{Arg1: "--docker-image, -o", Arg2: "--docker-username"}),
   708  
   709  		Entry("when docker and buildpacks flags are passed",
   710  			func() {
   711  				cmd.DockerImage.Path = "some-docker-image"
   712  				cmd.Buildpacks = []string{"some-buildpack"}
   713  			},
   714  			translatableerror.ArgumentCombinationError{Args: []string{"--buildpack, -b", "--docker-image, -o"}}),
   715  
   716  		Entry("when docker and path flags are passed",
   717  			func() {
   718  				cmd.DockerImage.Path = "some-docker-image"
   719  				cmd.AppPath = "some-directory-path"
   720  			},
   721  			translatableerror.ArgumentCombinationError{Args: []string{"--docker-image, -o", "--path, -p"}}),
   722  	)
   723  
   724  })