github.com/jenspinney/cli@v6.42.1-0.20190207184520-7450c600020e+incompatible/command/v6/push_command_test.go (about)

     1  package v6_test
     2  
     3  import (
     4  	"errors"
     5  	"io/ioutil"
     6  	"os"
     7  	"path/filepath"
     8  	"regexp"
     9  	"time"
    10  
    11  	"code.cloudfoundry.org/cli/actor/actionerror"
    12  	"code.cloudfoundry.org/cli/actor/pushaction"
    13  	"code.cloudfoundry.org/cli/actor/v2action"
    14  	"code.cloudfoundry.org/cli/actor/v2v3action"
    15  	"code.cloudfoundry.org/cli/actor/v3action"
    16  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv2/constant"
    17  	v3constant "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/translatableerror"
    21  	. "code.cloudfoundry.org/cli/command/v6"
    22  	"code.cloudfoundry.org/cli/command/v6/shared/sharedfakes"
    23  	"code.cloudfoundry.org/cli/command/v6/v6fakes"
    24  	"code.cloudfoundry.org/cli/types"
    25  	"code.cloudfoundry.org/cli/util/configv3"
    26  	"code.cloudfoundry.org/cli/util/manifest"
    27  	"code.cloudfoundry.org/cli/util/ui"
    28  	"github.com/cloudfoundry/bosh-cli/director/template"
    29  	. "github.com/onsi/ginkgo"
    30  	. "github.com/onsi/ginkgo/extensions/table"
    31  	. "github.com/onsi/gomega"
    32  	. "github.com/onsi/gomega/gbytes"
    33  )
    34  
    35  var _ = Describe("push Command", func() {
    36  	var (
    37  		cmd                         PushCommand
    38  		testUI                      *ui.UI
    39  		fakeConfig                  *commandfakes.FakeConfig
    40  		fakeSharedActor             *commandfakes.FakeSharedActor
    41  		fakeActor                   *v6fakes.FakeV2PushActor
    42  		fakeRestartActor            *v6fakes.FakeRestartActor
    43  		fakeApplicationSummaryActor *sharedfakes.FakeApplicationSummaryActor
    44  		fakeProgressBar             *v6fakes.FakeProgressBar
    45  		input                       *Buffer
    46  		binaryName                  string
    47  
    48  		appName    string
    49  		executeErr error
    50  		pwd        string
    51  	)
    52  
    53  	BeforeEach(func() {
    54  		input = NewBuffer()
    55  		testUI = ui.NewTestUI(input, NewBuffer(), NewBuffer())
    56  		fakeConfig = new(commandfakes.FakeConfig)
    57  		fakeSharedActor = new(commandfakes.FakeSharedActor)
    58  		fakeActor = new(v6fakes.FakeV2PushActor)
    59  		fakeRestartActor = new(v6fakes.FakeRestartActor)
    60  		fakeApplicationSummaryActor = new(sharedfakes.FakeApplicationSummaryActor)
    61  		fakeProgressBar = new(v6fakes.FakeProgressBar)
    62  
    63  		cmd = PushCommand{
    64  			UI:                      testUI,
    65  			Config:                  fakeConfig,
    66  			SharedActor:             fakeSharedActor,
    67  			Actor:                   fakeActor,
    68  			RestartActor:            fakeRestartActor,
    69  			ApplicationSummaryActor: fakeApplicationSummaryActor,
    70  			ProgressBar:             fakeProgressBar,
    71  		}
    72  
    73  		appName = "some-app"
    74  		cmd.OptionalArgs.AppName = appName
    75  		binaryName = "faceman"
    76  		fakeConfig.BinaryNameReturns(binaryName)
    77  
    78  		var err error
    79  		pwd, err = os.Getwd()
    80  		Expect(err).ToNot(HaveOccurred())
    81  	})
    82  
    83  	Context("Execute", func() {
    84  		JustBeforeEach(func() {
    85  			executeErr = cmd.Execute(nil)
    86  		})
    87  
    88  		When("checking target fails", func() {
    89  			BeforeEach(func() {
    90  				fakeSharedActor.CheckTargetReturns(actionerror.NotLoggedInError{BinaryName: binaryName})
    91  			})
    92  
    93  			It("returns an error", func() {
    94  				Expect(executeErr).To(MatchError(actionerror.NotLoggedInError{BinaryName: binaryName}))
    95  
    96  				Expect(fakeSharedActor.CheckTargetCallCount()).To(Equal(1))
    97  				checkTargetedOrg, checkTargetedSpace := fakeSharedActor.CheckTargetArgsForCall(0)
    98  				Expect(checkTargetedOrg).To(BeTrue())
    99  				Expect(checkTargetedSpace).To(BeTrue())
   100  			})
   101  		})
   102  
   103  		When("the user is logged in, and org and space are targeted", func() {
   104  			BeforeEach(func() {
   105  				fakeConfig.HasTargetedOrganizationReturns(true)
   106  				fakeConfig.TargetedOrganizationReturns(configv3.Organization{GUID: "some-org-guid", Name: "some-org"})
   107  				fakeConfig.HasTargetedSpaceReturns(true)
   108  				fakeConfig.TargetedSpaceReturns(configv3.Space{GUID: "some-space-guid", Name: "some-space"})
   109  				fakeConfig.CurrentUserReturns(configv3.User{Name: "some-user"}, nil)
   110  			})
   111  
   112  			When("the push settings are valid", func() {
   113  				var appManifests []manifest.Application
   114  
   115  				BeforeEach(func() {
   116  					appManifests = []manifest.Application{
   117  						{
   118  							Name: appName,
   119  							Path: pwd,
   120  						},
   121  					}
   122  					fakeActor.MergeAndValidateSettingsAndManifestsReturns(appManifests, nil)
   123  				})
   124  
   125  				When("the settings can be converted to a valid config", func() {
   126  					var appConfigs []pushaction.ApplicationConfig
   127  
   128  					BeforeEach(func() {
   129  						appConfigs = []pushaction.ApplicationConfig{
   130  							{
   131  								CurrentApplication: pushaction.Application{Application: v2action.Application{Name: appName, State: constant.ApplicationStarted}},
   132  								DesiredApplication: pushaction.Application{Application: v2action.Application{Name: appName}},
   133  								CurrentRoutes: []v2action.Route{
   134  									{Host: "route1", Domain: v2action.Domain{Name: "example.com"}},
   135  									{Host: "route2", Domain: v2action.Domain{Name: "example.com"}},
   136  								},
   137  								DesiredRoutes: []v2action.Route{
   138  									{Host: "route3", Domain: v2action.Domain{Name: "example.com"}},
   139  									{Host: "route4", Domain: v2action.Domain{Name: "example.com"}},
   140  								},
   141  								Path: pwd,
   142  							},
   143  						}
   144  						fakeActor.ConvertToApplicationConfigsReturns(appConfigs, pushaction.Warnings{"some-config-warnings"}, nil)
   145  					})
   146  
   147  					When("the apply is successful", func() {
   148  						var updatedConfig pushaction.ApplicationConfig
   149  
   150  						BeforeEach(func() {
   151  							fakeActor.ApplyStub = func(_ pushaction.ApplicationConfig, _ pushaction.ProgressBar) (<-chan pushaction.ApplicationConfig, <-chan pushaction.Event, <-chan pushaction.Warnings, <-chan error) {
   152  								configStream := make(chan pushaction.ApplicationConfig, 1)
   153  								eventStream := make(chan pushaction.Event)
   154  								warningsStream := make(chan pushaction.Warnings)
   155  								errorStream := make(chan error)
   156  
   157  								updatedConfig = pushaction.ApplicationConfig{
   158  									CurrentApplication: pushaction.Application{Application: v2action.Application{Name: appName, GUID: "some-app-guid"}},
   159  									DesiredApplication: pushaction.Application{Application: v2action.Application{Name: appName, GUID: "some-app-guid"}},
   160  									Path:               pwd,
   161  								}
   162  
   163  								go func() {
   164  									defer GinkgoRecover()
   165  
   166  									Eventually(eventStream).Should(BeSent(pushaction.SettingUpApplication))
   167  									Eventually(eventStream).Should(BeSent(pushaction.CreatedApplication))
   168  									Eventually(eventStream).Should(BeSent(pushaction.UpdatedApplication))
   169  									Eventually(eventStream).Should(BeSent(pushaction.CreatingAndMappingRoutes))
   170  									Eventually(eventStream).Should(BeSent(pushaction.CreatedRoutes))
   171  									Eventually(eventStream).Should(BeSent(pushaction.BoundRoutes))
   172  									Eventually(eventStream).Should(BeSent(pushaction.UnmappingRoutes))
   173  									Eventually(eventStream).Should(BeSent(pushaction.ConfiguringServices))
   174  									Eventually(eventStream).Should(BeSent(pushaction.BoundServices))
   175  									Eventually(eventStream).Should(BeSent(pushaction.ResourceMatching))
   176  									Eventually(eventStream).Should(BeSent(pushaction.UploadingApplication))
   177  									Eventually(eventStream).Should(BeSent(pushaction.CreatingArchive))
   178  									Eventually(eventStream).Should(BeSent(pushaction.UploadingApplicationWithArchive))
   179  									Eventually(fakeProgressBar.ReadyCallCount).Should(Equal(1))
   180  									Eventually(eventStream).Should(BeSent(pushaction.RetryUpload))
   181  									Eventually(eventStream).Should(BeSent(pushaction.UploadWithArchiveComplete))
   182  									Eventually(fakeProgressBar.CompleteCallCount).Should(Equal(1))
   183  									Eventually(configStream).Should(BeSent(updatedConfig))
   184  									Eventually(eventStream).Should(BeSent(pushaction.Complete))
   185  									Eventually(warningsStream).Should(BeSent(pushaction.Warnings{"apply-1", "apply-2"}))
   186  									close(configStream)
   187  									close(eventStream)
   188  									close(warningsStream)
   189  									close(errorStream)
   190  								}()
   191  
   192  								return configStream, eventStream, warningsStream, errorStream
   193  							}
   194  
   195  							fakeRestartActor.RestartApplicationStub = func(app v2action.Application, client v2action.NOAAClient) (<-chan *v2action.LogMessage, <-chan error, <-chan v2action.ApplicationStateChange, <-chan string, <-chan error) {
   196  								messages := make(chan *v2action.LogMessage)
   197  								logErrs := make(chan error)
   198  								appState := make(chan v2action.ApplicationStateChange)
   199  								warnings := make(chan string)
   200  								errs := make(chan error)
   201  
   202  								go func() {
   203  									messages <- v2action.NewLogMessage("log message 1", 1, time.Unix(0, 0), "STG", "1")
   204  									messages <- v2action.NewLogMessage("log message 2", 1, time.Unix(0, 0), "STG", "1")
   205  									appState <- v2action.ApplicationStateStopping
   206  									appState <- v2action.ApplicationStateStaging
   207  									appState <- v2action.ApplicationStateStarting
   208  									close(messages)
   209  									close(logErrs)
   210  									close(appState)
   211  									close(warnings)
   212  									close(errs)
   213  								}()
   214  
   215  								return messages, logErrs, appState, warnings, errs
   216  							}
   217  
   218  							applicationSummary := v2action.ApplicationSummary{
   219  								Application: v2action.Application{
   220  									DetectedBuildpack:    types.FilteredString{IsSet: true, Value: "some-buildpack"},
   221  									DetectedStartCommand: types.FilteredString{IsSet: true, Value: "some start command"},
   222  									GUID:                 "some-app-guid",
   223  									Instances:            types.NullInt{Value: 3, IsSet: true},
   224  									Memory:               types.NullByteSizeInMb{IsSet: true, Value: 128},
   225  									Name:                 appName,
   226  									PackageUpdatedAt:     time.Unix(0, 0),
   227  									State:                "STARTED",
   228  								},
   229  								Stack: v2action.Stack{
   230  									Name: "potatos",
   231  								},
   232  								Routes: []v2action.Route{
   233  									{
   234  										Host: "banana",
   235  										Domain: v2action.Domain{
   236  											Name: "fruit.com",
   237  										},
   238  										Path: "/hi",
   239  									},
   240  									{
   241  										Domain: v2action.Domain{
   242  											Name: "foobar.com",
   243  										},
   244  										Port: types.NullInt{IsSet: true, Value: 13},
   245  									},
   246  								},
   247  							}
   248  							warnings := []string{"app-summary-warning"}
   249  
   250  							applicationSummary.RunningInstances = []v2action.ApplicationInstanceWithStats{{State: "RUNNING"}}
   251  
   252  							fakeRestartActor.GetApplicationSummaryByNameAndSpaceReturns(applicationSummary, warnings, nil)
   253  						})
   254  
   255  						When("no manifest is provided", func() {
   256  							It("passes through the command line flags", func() {
   257  								Expect(executeErr).ToNot(HaveOccurred())
   258  
   259  								Expect(fakeActor.MergeAndValidateSettingsAndManifestsCallCount()).To(Equal(1))
   260  								cmdSettings, _ := fakeActor.MergeAndValidateSettingsAndManifestsArgsForCall(0)
   261  								Expect(cmdSettings).To(Equal(pushaction.CommandLineSettings{
   262  									Name:             appName,
   263  									CurrentDirectory: pwd,
   264  								}))
   265  							})
   266  						})
   267  
   268  						When("a manifest is provided", func() {
   269  							var (
   270  								tmpDir       string
   271  								providedPath string
   272  
   273  								originalDir string
   274  							)
   275  
   276  							BeforeEach(func() {
   277  								var err error
   278  								tmpDir, err = ioutil.TempDir("", "push-command-test")
   279  								Expect(err).ToNot(HaveOccurred())
   280  
   281  								// OS X uses weird symlinks that causes problems for some tests
   282  								tmpDir, err = filepath.EvalSymlinks(tmpDir)
   283  								Expect(err).ToNot(HaveOccurred())
   284  
   285  								originalDir, err = os.Getwd()
   286  								Expect(err).ToNot(HaveOccurred())
   287  
   288  								cmd.OptionalArgs.AppName = ""
   289  							})
   290  
   291  							AfterEach(func() {
   292  								Expect(os.Chdir(originalDir)).ToNot(HaveOccurred())
   293  								Expect(os.RemoveAll(tmpDir)).ToNot(HaveOccurred())
   294  							})
   295  
   296  							Context("via a manifest.yml in the current directory", func() {
   297  								var expectedApps []manifest.Application
   298  
   299  								BeforeEach(func() {
   300  									err := os.Chdir(tmpDir)
   301  									Expect(err).ToNot(HaveOccurred())
   302  
   303  									providedPath = filepath.Join(tmpDir, "manifest.yml")
   304  									err = ioutil.WriteFile(providedPath, []byte("some manifest file"), 0666)
   305  									Expect(err).ToNot(HaveOccurred())
   306  
   307  									expectedApps = []manifest.Application{{Name: "some-app"}, {Name: "some-other-app"}}
   308  									fakeActor.ReadManifestReturns(expectedApps, nil, nil)
   309  								})
   310  
   311  								When("reading the manifest file is successful", func() {
   312  									It("merges app manifest and flags", func() {
   313  										Expect(executeErr).ToNot(HaveOccurred())
   314  
   315  										Expect(fakeActor.ReadManifestCallCount()).To(Equal(1))
   316  										Expect(fakeActor.ReadManifestArgsForCall(0)).To(Equal(providedPath))
   317  
   318  										Expect(fakeActor.MergeAndValidateSettingsAndManifestsCallCount()).To(Equal(1))
   319  										cmdSettings, manifestApps := fakeActor.MergeAndValidateSettingsAndManifestsArgsForCall(0)
   320  										Expect(cmdSettings).To(Equal(pushaction.CommandLineSettings{
   321  											CurrentDirectory: tmpDir,
   322  										}))
   323  										Expect(manifestApps).To(Equal(expectedApps))
   324  									})
   325  
   326  									It("outputs corresponding flavor text", func() {
   327  										Expect(executeErr).ToNot(HaveOccurred())
   328  
   329  										Expect(testUI.Out).To(Say(`Pushing from manifest to org some-org / space some-space as some-user\.\.\.`))
   330  										Expect(testUI.Out).To(Say("Using manifest file %s", regexp.QuoteMeta(providedPath)))
   331  									})
   332  								})
   333  
   334  								When("reading manifest file errors", func() {
   335  									var expectedErr error
   336  
   337  									BeforeEach(func() {
   338  										expectedErr = errors.New("I am an error!!!")
   339  
   340  										fakeActor.ReadManifestReturns(nil, nil, expectedErr)
   341  									})
   342  
   343  									It("returns the error", func() {
   344  										Expect(executeErr).To(MatchError(expectedErr))
   345  									})
   346  								})
   347  
   348  								When("--no-manifest is specified", func() {
   349  									BeforeEach(func() {
   350  										cmd.NoManifest = true
   351  									})
   352  
   353  									It("ignores the manifest file", func() {
   354  										Expect(executeErr).ToNot(HaveOccurred())
   355  
   356  										Expect(fakeActor.MergeAndValidateSettingsAndManifestsCallCount()).To(Equal(1))
   357  										cmdSettings, manifestApps := fakeActor.MergeAndValidateSettingsAndManifestsArgsForCall(0)
   358  										Expect(cmdSettings).To(Equal(pushaction.CommandLineSettings{
   359  											CurrentDirectory: tmpDir,
   360  										}))
   361  										Expect(manifestApps).To(BeNil())
   362  									})
   363  								})
   364  							})
   365  
   366  							Context("via a manifest.yaml in the current directory", func() {
   367  								BeforeEach(func() {
   368  									err := os.Chdir(tmpDir)
   369  									Expect(err).ToNot(HaveOccurred())
   370  
   371  									providedPath = filepath.Join(tmpDir, "manifest.yaml")
   372  									err = ioutil.WriteFile(providedPath, []byte("some manifest file"), 0666)
   373  									Expect(err).ToNot(HaveOccurred())
   374  								})
   375  
   376  								It("should read the manifest.yml", func() {
   377  									Expect(executeErr).ToNot(HaveOccurred())
   378  
   379  									Expect(fakeActor.ReadManifestCallCount()).To(Equal(1))
   380  									Expect(fakeActor.ReadManifestArgsForCall(0)).To(Equal(providedPath))
   381  								})
   382  							})
   383  
   384  							Context("via the -f flag", func() {
   385  								Context("given a path with filename 'manifest.yml'", func() {
   386  									BeforeEach(func() {
   387  										providedPath = filepath.Join(tmpDir, "manifest.yml")
   388  									})
   389  
   390  									When("the manifest.yml file does not exist", func() {
   391  										BeforeEach(func() {
   392  											cmd.PathToManifest = flag.PathWithExistenceCheck(providedPath)
   393  										})
   394  
   395  										It("returns an error", func() {
   396  											Expect(os.IsNotExist(executeErr)).To(BeTrue())
   397  
   398  											Expect(testUI.Out).ToNot(Say("Pushing from manifest"))
   399  											Expect(testUI.Out).ToNot(Say("Using manifest file"))
   400  
   401  											Expect(fakeActor.ReadManifestCallCount()).To(Equal(0))
   402  										})
   403  									})
   404  
   405  									When("the manifest.yml file exists", func() {
   406  										BeforeEach(func() {
   407  											err := ioutil.WriteFile(providedPath, []byte(`key: "value"`), 0666)
   408  											Expect(err).ToNot(HaveOccurred())
   409  
   410  											cmd.PathToManifest = flag.PathWithExistenceCheck(providedPath)
   411  										})
   412  
   413  										It("should read the manifest.yml file and outputs corresponding flavor text", func() {
   414  											Expect(executeErr).ToNot(HaveOccurred())
   415  
   416  											Expect(testUI.Out).To(Say(`Pushing from manifest to org some-org / space some-space as some-user\.\.\.`))
   417  											Expect(testUI.Out).To(Say("Using manifest file %s", regexp.QuoteMeta(providedPath)))
   418  
   419  											Expect(fakeActor.ReadManifestCallCount()).To(Equal(1))
   420  											Expect(fakeActor.ReadManifestArgsForCall(0)).To(Equal(providedPath))
   421  										})
   422  
   423  										Context("variable interpolation", func() {
   424  											Context("vars file only", func() {
   425  												When("a vars file is also provided", func() {
   426  													var providedVarsFilePath string
   427  
   428  													BeforeEach(func() {
   429  														providedVarsFilePath = filepath.Join(tmpDir, "vars-file.yml")
   430  														cmd.VarsFilePaths = []flag.PathWithExistenceCheck{flag.PathWithExistenceCheck(providedVarsFilePath)}
   431  													})
   432  
   433  													It("should read the vars-file.yml file and replace the variables in the manifest.yml file", func() {
   434  														Expect(executeErr).ToNot(HaveOccurred())
   435  
   436  														Expect(testUI.Out).To(Say(`Pushing from manifest to org some-org / space some-space as some-user\.\.\.`))
   437  														Expect(testUI.Out).To(Say("Using manifest file %s", regexp.QuoteMeta(providedPath)))
   438  
   439  														Expect(fakeActor.ReadManifestCallCount()).To(Equal(1))
   440  														manifest, varsFiles, vars := fakeActor.ReadManifestArgsForCall(0)
   441  														Expect(manifest).To(Equal(providedPath))
   442  														Expect(varsFiles).To(Equal([]string{providedVarsFilePath}))
   443  														Expect(vars).To(BeEmpty())
   444  													})
   445  												})
   446  
   447  												When("multiple vars files are provided", func() {
   448  													var (
   449  														firstProvidedVarsFilePath  string
   450  														secondProvidedVarsFilePath string
   451  													)
   452  
   453  													BeforeEach(func() {
   454  														firstProvidedVarsFilePath = filepath.Join(tmpDir, "vars-file-1.yml")
   455  														firstVarsFile := flag.PathWithExistenceCheck(firstProvidedVarsFilePath)
   456  
   457  														secondProvidedVarsFilePath = filepath.Join(tmpDir, "vars-file-2.yml")
   458  														secondVarsFile := flag.PathWithExistenceCheck(secondProvidedVarsFilePath)
   459  														cmd.VarsFilePaths = []flag.PathWithExistenceCheck{firstVarsFile, secondVarsFile}
   460  													})
   461  
   462  													It("should read the vars-file.yml file and replace the variables in the manifest.yml file", func() {
   463  														Expect(executeErr).ToNot(HaveOccurred())
   464  
   465  														Expect(testUI.Out).To(Say(`Pushing from manifest to org some-org / space some-space as some-user\.\.\.`))
   466  														Expect(testUI.Out).To(Say("Using manifest file %s", regexp.QuoteMeta(providedPath)))
   467  
   468  														Expect(fakeActor.ReadManifestCallCount()).To(Equal(1))
   469  														manifest, varsFiles, vars := fakeActor.ReadManifestArgsForCall(0)
   470  														Expect(manifest).To(Equal(providedPath))
   471  														Expect(varsFiles).To(Equal([]string{firstProvidedVarsFilePath, secondProvidedVarsFilePath}))
   472  														Expect(vars).To(BeEmpty())
   473  													})
   474  												})
   475  											})
   476  
   477  											Context("vars flag only", func() {
   478  												var vars []template.VarKV
   479  
   480  												BeforeEach(func() {
   481  													vars = []template.VarKV{
   482  														{Name: "some-var", Value: "some-value"},
   483  														{Name: "another-var", Value: 1},
   484  													}
   485  
   486  													cmd.Vars = vars
   487  												})
   488  
   489  												It("should read the vars and pass only the vars array to ReadManifest", func() {
   490  													Expect(executeErr).ToNot(HaveOccurred())
   491  
   492  													Expect(testUI.Out).To(Say(`Pushing from manifest to org some-org / space some-space as some-user\.\.\.`))
   493  													Expect(testUI.Out).To(Say("Using manifest file %s", regexp.QuoteMeta(providedPath)))
   494  
   495  													Expect(fakeActor.ReadManifestCallCount()).To(Equal(1))
   496  													manifest, varsFiles, vars := fakeActor.ReadManifestArgsForCall(0)
   497  													Expect(manifest).To(Equal(providedPath))
   498  													Expect(varsFiles).To(BeEmpty())
   499  													Expect(vars).To(ConsistOf([]template.VarKV{
   500  														{Name: "some-var", Value: "some-value"},
   501  														{Name: "another-var", Value: 1},
   502  													}))
   503  												})
   504  											})
   505  										})
   506  									})
   507  								})
   508  
   509  								Context("given a path that is a directory", func() {
   510  
   511  									var (
   512  										ymlFile  string
   513  										yamlFile string
   514  									)
   515  
   516  									BeforeEach(func() {
   517  										providedPath = tmpDir
   518  										cmd.PathToManifest = flag.PathWithExistenceCheck(providedPath)
   519  									})
   520  
   521  									When("the directory does not contain a 'manifest.y{a}ml' file", func() {
   522  										It("returns an error", func() {
   523  											Expect(executeErr).To(MatchError(translatableerror.ManifestFileNotFoundInDirectoryError{PathToManifest: providedPath}))
   524  											Expect(testUI.Out).ToNot(Say("Pushing from manifest"))
   525  											Expect(testUI.Out).ToNot(Say("Using manifest file"))
   526  
   527  											Expect(fakeActor.ReadManifestCallCount()).To(Equal(0))
   528  										})
   529  									})
   530  
   531  									When("the directory contains a 'manifest.yml' file", func() {
   532  										BeforeEach(func() {
   533  											ymlFile = filepath.Join(providedPath, "manifest.yml")
   534  											err := ioutil.WriteFile(ymlFile, []byte(`key: "value"`), 0666)
   535  											Expect(err).ToNot(HaveOccurred())
   536  										})
   537  
   538  										It("should read the manifest.yml file and outputs corresponding flavor text", func() {
   539  											Expect(executeErr).ToNot(HaveOccurred())
   540  
   541  											Expect(testUI.Out).To(Say(`Pushing from manifest to org some-org / space some-space as some-user\.\.\.`))
   542  											Expect(testUI.Out).To(Say("Using manifest file %s", regexp.QuoteMeta(ymlFile)))
   543  
   544  											Expect(fakeActor.ReadManifestCallCount()).To(Equal(1))
   545  											Expect(fakeActor.ReadManifestArgsForCall(0)).To(Equal(ymlFile))
   546  										})
   547  									})
   548  
   549  									When("the directory contains a 'manifest.yaml' file", func() {
   550  										BeforeEach(func() {
   551  											yamlFile = filepath.Join(providedPath, "manifest.yaml")
   552  											err := ioutil.WriteFile(yamlFile, []byte(`key: "value"`), 0666)
   553  											Expect(err).ToNot(HaveOccurred())
   554  										})
   555  
   556  										It("should read the manifest.yaml file and outputs corresponding flavor text", func() {
   557  											Expect(executeErr).ToNot(HaveOccurred())
   558  
   559  											Expect(testUI.Out).To(Say(`Pushing from manifest to org some-org / space some-space as some-user\.\.\.`))
   560  											Expect(testUI.Out).To(Say("Using manifest file %s", regexp.QuoteMeta(yamlFile)))
   561  
   562  											Expect(fakeActor.ReadManifestCallCount()).To(Equal(1))
   563  											Expect(fakeActor.ReadManifestArgsForCall(0)).To(Equal(yamlFile))
   564  										})
   565  									})
   566  
   567  									When("the directory contains both a 'manifest.yml' and 'manifest.yaml' file", func() {
   568  										BeforeEach(func() {
   569  											ymlFile = filepath.Join(providedPath, "manifest.yml")
   570  											err := ioutil.WriteFile(ymlFile, []byte(`key: "value"`), 0666)
   571  											Expect(err).ToNot(HaveOccurred())
   572  
   573  											yamlFile = filepath.Join(providedPath, "manifest.yaml")
   574  											err = ioutil.WriteFile(yamlFile, []byte(`key: "value"`), 0666)
   575  											Expect(err).ToNot(HaveOccurred())
   576  										})
   577  
   578  										It("should read the manifest.yml file and outputs corresponding flavor text", func() {
   579  											Expect(executeErr).ToNot(HaveOccurred())
   580  
   581  											Expect(testUI.Out).To(Say(`Pushing from manifest to org some-org / space some-space as some-user\.\.\.`))
   582  											Expect(testUI.Out).To(Say("Using manifest file %s", regexp.QuoteMeta(ymlFile)))
   583  
   584  											Expect(fakeActor.ReadManifestCallCount()).To(Equal(1))
   585  											Expect(fakeActor.ReadManifestArgsForCall(0)).To(Equal(ymlFile))
   586  										})
   587  									})
   588  								})
   589  							})
   590  						})
   591  
   592  						When("an app name and manifest are provided", func() {
   593  							var (
   594  								tmpDir         string
   595  								pathToManifest string
   596  
   597  								originalDir string
   598  							)
   599  
   600  							BeforeEach(func() {
   601  								var err error
   602  								tmpDir, err = ioutil.TempDir("", "push-command-test")
   603  								Expect(err).ToNot(HaveOccurred())
   604  
   605  								// OS X uses weird symlinks that causes problems for some tests
   606  								tmpDir, err = filepath.EvalSymlinks(tmpDir)
   607  								Expect(err).ToNot(HaveOccurred())
   608  
   609  								pathToManifest = filepath.Join(tmpDir, "manifest.yml")
   610  								err = ioutil.WriteFile(pathToManifest, []byte("some manfiest file"), 0666)
   611  								Expect(err).ToNot(HaveOccurred())
   612  
   613  								originalDir, err = os.Getwd()
   614  								Expect(err).ToNot(HaveOccurred())
   615  
   616  								err = os.Chdir(tmpDir)
   617  								Expect(err).ToNot(HaveOccurred())
   618  							})
   619  
   620  							AfterEach(func() {
   621  								Expect(os.Chdir(originalDir)).ToNot(HaveOccurred())
   622  								Expect(os.RemoveAll(tmpDir)).ToNot(HaveOccurred())
   623  							})
   624  
   625  							It("outputs corresponding flavor text", func() {
   626  								Expect(executeErr).ToNot(HaveOccurred())
   627  
   628  								Expect(testUI.Out).To(Say(`Pushing from manifest to org some-org / space some-space as some-user\.\.\.`))
   629  								Expect(testUI.Out).To(Say("Using manifest file %s", regexp.QuoteMeta(pathToManifest)))
   630  							})
   631  						})
   632  
   633  						It("converts the manifests to app configs and outputs config warnings", func() {
   634  							Expect(executeErr).ToNot(HaveOccurred())
   635  
   636  							Expect(testUI.Err).To(Say("some-config-warnings"))
   637  
   638  							Expect(fakeActor.ConvertToApplicationConfigsCallCount()).To(Equal(1))
   639  							orgGUID, spaceGUID, noStart, manifests := fakeActor.ConvertToApplicationConfigsArgsForCall(0)
   640  							Expect(orgGUID).To(Equal("some-org-guid"))
   641  							Expect(spaceGUID).To(Equal("some-space-guid"))
   642  							Expect(noStart).To(BeFalse())
   643  							Expect(manifests).To(Equal(appManifests))
   644  						})
   645  
   646  						It("outputs flavor text prior to generating app configuration", func() {
   647  							Expect(executeErr).ToNot(HaveOccurred())
   648  							Expect(testUI.Out).To(Say("Pushing app %s to org some-org / space some-space as some-user", appName))
   649  							Expect(testUI.Out).To(Say(`Getting app info\.\.\.`))
   650  						})
   651  
   652  						It("applies each of the application configurations", func() {
   653  							Expect(executeErr).ToNot(HaveOccurred())
   654  
   655  							Expect(fakeActor.ApplyCallCount()).To(Equal(1))
   656  							config, progressBar := fakeActor.ApplyArgsForCall(0)
   657  							Expect(config).To(Equal(appConfigs[0]))
   658  							Expect(progressBar).To(Equal(fakeProgressBar))
   659  						})
   660  
   661  						It("display diff of changes", func() {
   662  							Expect(executeErr).ToNot(HaveOccurred())
   663  
   664  							Expect(testUI.Out).To(Say(`\s+name:\s+%s`, appName))
   665  							Expect(testUI.Out).To(Say(`\s+path:\s+%s`, regexp.QuoteMeta(appConfigs[0].Path)))
   666  							Expect(testUI.Out).To(Say(`\s+routes:`))
   667  							for _, route := range appConfigs[0].CurrentRoutes {
   668  								Expect(testUI.Out).To(Say(route.String()))
   669  							}
   670  							for _, route := range appConfigs[0].DesiredRoutes {
   671  								Expect(testUI.Out).To(Say(route.String()))
   672  							}
   673  						})
   674  
   675  						When("the app starts", func() {
   676  							It("displays app events and warnings", func() {
   677  								Expect(executeErr).ToNot(HaveOccurred())
   678  
   679  								Expect(testUI.Out).To(Say(`Creating app with these attributes\.\.\.`))
   680  								Expect(testUI.Out).To(Say(`Mapping routes\.\.\.`))
   681  								Expect(testUI.Out).To(Say(`Unmapping routes\.\.\.`))
   682  								Expect(testUI.Out).To(Say(`Binding services\.\.\.`))
   683  								Expect(testUI.Out).To(Say(`Comparing local files to remote cache\.\.\.`))
   684  								Expect(testUI.Out).To(Say("All files found in remote cache; nothing to upload."))
   685  								Expect(testUI.Out).To(Say(`Waiting for API to complete processing files\.\.\.`))
   686  								Expect(testUI.Out).To(Say(`Packaging files to upload\.\.\.`))
   687  								Expect(testUI.Out).To(Say(`Uploading files\.\.\.`))
   688  								Expect(testUI.Out).To(Say(`Retrying upload due to an error\.\.\.`))
   689  								Expect(testUI.Out).To(Say(`Waiting for API to complete processing files\.\.\.`))
   690  								Expect(testUI.Out).To(Say(`Stopping app\.\.\.`))
   691  
   692  								Expect(testUI.Err).To(Say("some-config-warnings"))
   693  								Expect(testUI.Err).To(Say("apply-1"))
   694  								Expect(testUI.Err).To(Say("apply-2"))
   695  							})
   696  
   697  							It("displays app staging logs", func() {
   698  								Expect(executeErr).ToNot(HaveOccurred())
   699  
   700  								Expect(testUI.Out).To(Say("log message 1"))
   701  								Expect(testUI.Out).To(Say("log message 2"))
   702  
   703  								Expect(fakeRestartActor.RestartApplicationCallCount()).To(Equal(1))
   704  								appConfig, _ := fakeRestartActor.RestartApplicationArgsForCall(0)
   705  								Expect(appConfig).To(Equal(updatedConfig.CurrentApplication.Application))
   706  							})
   707  
   708  							Context("Process Information", func() {
   709  								BeforeEach(func() {
   710  									fakeApplicationSummaryActor.GetApplicationSummaryByNameAndSpaceReturns(
   711  										v2v3action.ApplicationSummary{
   712  											ApplicationSummary: v3action.ApplicationSummary{
   713  												Application: v3action.Application{
   714  													Name: appName,
   715  												},
   716  												ProcessSummaries: v3action.ProcessSummaries{
   717  													{
   718  														Process: v3action.Process{
   719  															Type:       "aba",
   720  															Command:    *types.NewFilteredString("some-command-1"),
   721  															MemoryInMB: types.NullUint64{Value: 32, IsSet: true},
   722  															DiskInMB:   types.NullUint64{Value: 1024, IsSet: true},
   723  														},
   724  													},
   725  													{
   726  														Process: v3action.Process{
   727  															Type:       "console",
   728  															Command:    *types.NewFilteredString("some-command-2"),
   729  															MemoryInMB: types.NullUint64{Value: 16, IsSet: true},
   730  															DiskInMB:   types.NullUint64{Value: 512, IsSet: true},
   731  														},
   732  													},
   733  												},
   734  											},
   735  										},
   736  										v2v3action.Warnings{"combo-summary-warning"},
   737  										nil)
   738  								})
   739  
   740  								It("displays process information", func() {
   741  									Expect(executeErr).ToNot(HaveOccurred())
   742  
   743  									Expect(testUI.Out).To(Say(`name:\s+%s`, appName))
   744  									Expect(testUI.Out).To(Say(`type:\s+aba`))
   745  									Expect(testUI.Out).To(Say(`instances:\s+0/0`))
   746  									Expect(testUI.Out).To(Say(`memory usage:\s+32M`))
   747  									Expect(testUI.Out).To(Say(`start command:\s+some-command-1`))
   748  									Expect(testUI.Out).To(Say(`type:\s+console`))
   749  									Expect(testUI.Out).To(Say(`instances:\s+0/0`))
   750  									Expect(testUI.Out).To(Say(`memory usage:\s+16M`))
   751  									Expect(testUI.Out).To(Say(`start command:\s+some-command-2`))
   752  
   753  									Expect(testUI.Err).To(Say("combo-summary-warning"))
   754  
   755  									Expect(fakeApplicationSummaryActor.GetApplicationSummaryByNameAndSpaceCallCount()).To(Equal(1))
   756  									passedAppName, spaceGUID, withObfuscatedValues := fakeApplicationSummaryActor.GetApplicationSummaryByNameAndSpaceArgsForCall(0)
   757  									Expect(passedAppName).To(Equal(appName))
   758  									Expect(spaceGUID).To(Equal("some-space-guid"))
   759  									Expect(withObfuscatedValues).To(BeTrue())
   760  								})
   761  							})
   762  
   763  							When("the start command is explicitly set", func() {
   764  								BeforeEach(func() {
   765  									v3ApplicationSummary := v3action.ApplicationSummary{
   766  										Application: v3action.Application{
   767  											Name: appName,
   768  										},
   769  										ProcessSummaries: v3action.ProcessSummaries{
   770  											{
   771  												Process: v3action.Process{
   772  													Type:       "aba",
   773  													Command:    *types.NewFilteredString("a-different-start-command"),
   774  													MemoryInMB: types.NullUint64{Value: 32, IsSet: true},
   775  													DiskInMB:   types.NullUint64{Value: 1024, IsSet: true},
   776  												},
   777  												InstanceDetails: []v3action.ProcessInstance{
   778  													v3action.ProcessInstance{
   779  														State: v3constant.ProcessInstanceRunning,
   780  													},
   781  												},
   782  											},
   783  										},
   784  									}
   785  
   786  									warnings := []string{"app-summary-warning"}
   787  									applicationSummary := v2v3action.ApplicationSummary{
   788  										ApplicationSummary: v3ApplicationSummary,
   789  									}
   790  
   791  									fakeApplicationSummaryActor.GetApplicationSummaryByNameAndSpaceReturns(applicationSummary, warnings, nil)
   792  								})
   793  
   794  								It("displays the correct start command", func() {
   795  									Expect(executeErr).ToNot(HaveOccurred())
   796  									Expect(testUI.Out).To(Say(`name:\s+%s`, appName))
   797  									Expect(testUI.Out).To(Say(`start command:\s+a-different-start-command`))
   798  								})
   799  							})
   800  						})
   801  
   802  						When("no-start is set", func() {
   803  							BeforeEach(func() {
   804  								cmd.NoStart = true
   805  								v3ApplicationSummary := v3action.ApplicationSummary{
   806  									Application: v3action.Application{
   807  										Name:  appName,
   808  										State: v3constant.ApplicationStopped,
   809  									},
   810  									ProcessSummaries: v3action.ProcessSummaries{
   811  										{
   812  											Process: v3action.Process{
   813  												Type:       "aba",
   814  												Command:    *types.NewFilteredString("a-different-start-command"),
   815  												MemoryInMB: types.NullUint64{Value: 32, IsSet: true},
   816  												DiskInMB:   types.NullUint64{Value: 1024, IsSet: true},
   817  											},
   818  											InstanceDetails: []v3action.ProcessInstance{},
   819  										},
   820  									},
   821  								}
   822  
   823  								warnings := []string{"app-summary-warning"}
   824  								applicationSummary := v2v3action.ApplicationSummary{
   825  									ApplicationSummary: v3ApplicationSummary,
   826  								}
   827  
   828  								fakeApplicationSummaryActor.GetApplicationSummaryByNameAndSpaceReturns(applicationSummary, warnings, nil)
   829  							})
   830  
   831  							When("the app is not running", func() {
   832  								It("does not start the app", func() {
   833  									Expect(executeErr).ToNot(HaveOccurred())
   834  									Expect(testUI.Out).To(Say(`Waiting for API to complete processing files\.\.\.`))
   835  									Expect(testUI.Out).To(Say(`name:\s+%s`, appName))
   836  									Expect(testUI.Out).To(Say(`requested state:\s+stopped`))
   837  
   838  									Expect(fakeRestartActor.RestartApplicationCallCount()).To(Equal(0))
   839  								})
   840  							})
   841  						})
   842  					})
   843  
   844  					When("the apply errors", func() {
   845  						var expectedErr error
   846  
   847  						BeforeEach(func() {
   848  							expectedErr = errors.New("no wayz dude")
   849  							fakeActor.ApplyStub = func(_ pushaction.ApplicationConfig, _ pushaction.ProgressBar) (<-chan pushaction.ApplicationConfig, <-chan pushaction.Event, <-chan pushaction.Warnings, <-chan error) {
   850  								configStream := make(chan pushaction.ApplicationConfig)
   851  								eventStream := make(chan pushaction.Event)
   852  								warningsStream := make(chan pushaction.Warnings)
   853  								errorStream := make(chan error)
   854  
   855  								go func() {
   856  									defer GinkgoRecover()
   857  
   858  									Eventually(warningsStream).Should(BeSent(pushaction.Warnings{"apply-1", "apply-2"}))
   859  									Eventually(errorStream).Should(BeSent(expectedErr))
   860  									close(configStream)
   861  									close(eventStream)
   862  									close(warningsStream)
   863  									close(errorStream)
   864  								}()
   865  
   866  								return configStream, eventStream, warningsStream, errorStream
   867  							}
   868  						})
   869  
   870  						It("outputs the warnings and returns the error", func() {
   871  							Expect(executeErr).To(MatchError(expectedErr))
   872  
   873  							Expect(testUI.Err).To(Say("some-config-warnings"))
   874  							Expect(testUI.Err).To(Say("apply-1"))
   875  							Expect(testUI.Err).To(Say("apply-2"))
   876  						})
   877  					})
   878  				})
   879  
   880  				When("there is an error converting the app setting into a config", func() {
   881  					var expectedErr error
   882  
   883  					BeforeEach(func() {
   884  						expectedErr = errors.New("no wayz dude")
   885  						fakeActor.ConvertToApplicationConfigsReturns(nil, pushaction.Warnings{"some-config-warnings"}, expectedErr)
   886  					})
   887  
   888  					It("outputs the warnings and returns the error", func() {
   889  						Expect(executeErr).To(MatchError(expectedErr))
   890  
   891  						Expect(testUI.Err).To(Say("some-config-warnings"))
   892  					})
   893  				})
   894  			})
   895  
   896  			When("the push settings are invalid", func() {
   897  				var expectedErr error
   898  
   899  				BeforeEach(func() {
   900  					expectedErr = errors.New("no wayz dude")
   901  					fakeActor.MergeAndValidateSettingsAndManifestsReturns(nil, expectedErr)
   902  				})
   903  
   904  				It("returns the error", func() {
   905  					Expect(executeErr).To(MatchError(expectedErr))
   906  				})
   907  			})
   908  		})
   909  	})
   910  
   911  	Describe("GetCommandLineSettings", func() {
   912  		Context("valid flag combinations", func() {
   913  			var (
   914  				settings               pushaction.CommandLineSettings
   915  				commandLineSettingsErr error
   916  			)
   917  
   918  			JustBeforeEach(func() {
   919  				settings, commandLineSettingsErr = cmd.GetCommandLineSettings()
   920  				Expect(commandLineSettingsErr).ToNot(HaveOccurred())
   921  			})
   922  
   923  			When("general app settings are given", func() {
   924  				BeforeEach(func() {
   925  					cmd.Buildpacks = []string{"some-buildpack"}
   926  					cmd.Command = flag.Command{FilteredString: types.FilteredString{IsSet: true, Value: "echo foo bar baz"}}
   927  					cmd.DiskQuota = flag.Megabytes{NullUint64: types.NullUint64{Value: 1024, IsSet: true}}
   928  					cmd.HealthCheckTimeout = 14
   929  					cmd.HealthCheckType = flag.HealthCheckTypeWithDeprecatedValue{Type: "http"}
   930  					cmd.Instances = flag.Instances{NullInt: types.NullInt{Value: 12, IsSet: true}}
   931  					cmd.Memory = flag.Megabytes{NullUint64: types.NullUint64{Value: 100, IsSet: true}}
   932  					cmd.StackName = "some-stack"
   933  				})
   934  
   935  				It("sets them on the command line settings", func() {
   936  					Expect(commandLineSettingsErr).ToNot(HaveOccurred())
   937  					Expect(settings.Buildpacks).To(ConsistOf("some-buildpack"))
   938  					Expect(settings.Command).To(Equal(types.FilteredString{IsSet: true, Value: "echo foo bar baz"}))
   939  					Expect(settings.DiskQuota).To(Equal(uint64(1024)))
   940  					Expect(settings.HealthCheckTimeout).To(BeEquivalentTo(14))
   941  					Expect(settings.HealthCheckType).To(Equal("http"))
   942  					Expect(settings.Instances).To(Equal(types.NullInt{Value: 12, IsSet: true}))
   943  					Expect(settings.Memory).To(Equal(uint64(100)))
   944  					Expect(settings.StackName).To(Equal("some-stack"))
   945  				})
   946  			})
   947  
   948  			Context("route related flags", func() {
   949  				When("given customed route settings", func() {
   950  					BeforeEach(func() {
   951  						cmd.Domain = "some-domain"
   952  					})
   953  
   954  					It("sets NoHostname on the command line settings", func() {
   955  						Expect(settings.DefaultRouteDomain).To(Equal("some-domain"))
   956  					})
   957  				})
   958  
   959  				When("--hostname is given", func() {
   960  					BeforeEach(func() {
   961  						cmd.Hostname = "some-hostname"
   962  					})
   963  
   964  					It("sets DefaultRouteHostname on the command line settings", func() {
   965  						Expect(settings.DefaultRouteHostname).To(Equal("some-hostname"))
   966  					})
   967  				})
   968  
   969  				When("--no-hostname is given", func() {
   970  					BeforeEach(func() {
   971  						cmd.NoHostname = true
   972  					})
   973  
   974  					It("sets NoHostname on the command line settings", func() {
   975  						Expect(settings.NoHostname).To(BeTrue())
   976  					})
   977  				})
   978  
   979  				When("--random-route is given", func() {
   980  					BeforeEach(func() {
   981  						cmd.RandomRoute = true
   982  					})
   983  
   984  					It("sets --random-route on the command line settings", func() {
   985  						Expect(commandLineSettingsErr).ToNot(HaveOccurred())
   986  						Expect(settings.RandomRoute).To(BeTrue())
   987  					})
   988  				})
   989  
   990  				When("--route-path is given", func() {
   991  					BeforeEach(func() {
   992  						cmd.RoutePath = flag.RoutePath{Path: "/some-path"}
   993  					})
   994  
   995  					It("sets --route-path on the command line settings", func() {
   996  						Expect(commandLineSettingsErr).ToNot(HaveOccurred())
   997  						Expect(settings.RoutePath).To(Equal("/some-path"))
   998  					})
   999  				})
  1000  
  1001  				When("--no-route is given", func() {
  1002  					BeforeEach(func() {
  1003  						cmd.NoRoute = true
  1004  					})
  1005  
  1006  					It("sets NoRoute on the command line settings", func() {
  1007  						Expect(settings.NoRoute).To(BeTrue())
  1008  					})
  1009  				})
  1010  			})
  1011  
  1012  			Context("app bits", func() {
  1013  				When("-p flag is given", func() {
  1014  					BeforeEach(func() {
  1015  						cmd.AppPath = "some-directory-path"
  1016  					})
  1017  
  1018  					It("sets ProvidedAppPath", func() {
  1019  						Expect(settings.ProvidedAppPath).To(Equal("some-directory-path"))
  1020  					})
  1021  				})
  1022  
  1023  				When("the -o flag is given", func() {
  1024  					BeforeEach(func() {
  1025  						cmd.DockerImage.Path = "some-docker-image-path"
  1026  					})
  1027  
  1028  					It("creates command line setting from command line arguments", func() {
  1029  						Expect(settings.DockerImage).To(Equal("some-docker-image-path"))
  1030  					})
  1031  
  1032  					Context("--docker-username flags is given", func() {
  1033  						BeforeEach(func() {
  1034  							cmd.DockerUsername = "some-docker-username"
  1035  						})
  1036  
  1037  						Context("the docker password environment variable is set", func() {
  1038  							BeforeEach(func() {
  1039  								fakeConfig.DockerPasswordReturns("some-docker-password")
  1040  							})
  1041  
  1042  							It("creates command line setting from command line arguments and config", func() {
  1043  								Expect(testUI.Out).To(Say("Using docker repository password from environment variable CF_DOCKER_PASSWORD."))
  1044  
  1045  								Expect(settings.Name).To(Equal(appName))
  1046  								Expect(settings.DockerImage).To(Equal("some-docker-image-path"))
  1047  								Expect(settings.DockerUsername).To(Equal("some-docker-username"))
  1048  								Expect(settings.DockerPassword).To(Equal("some-docker-password"))
  1049  							})
  1050  						})
  1051  
  1052  						Context("the docker password environment variable is *not* set", func() {
  1053  							BeforeEach(func() {
  1054  								input.Write([]byte("some-docker-password\n"))
  1055  							})
  1056  
  1057  							It("prompts the user for a password", func() {
  1058  								Expect(testUI.Out).To(Say("Environment variable CF_DOCKER_PASSWORD not set."))
  1059  								Expect(testUI.Out).To(Say("Docker password"))
  1060  
  1061  								Expect(settings.Name).To(Equal(appName))
  1062  								Expect(settings.DockerImage).To(Equal("some-docker-image-path"))
  1063  								Expect(settings.DockerUsername).To(Equal("some-docker-username"))
  1064  								Expect(settings.DockerPassword).To(Equal("some-docker-password"))
  1065  							})
  1066  						})
  1067  					})
  1068  				})
  1069  			})
  1070  		})
  1071  
  1072  		DescribeTable("validation errors when flags are passed",
  1073  			func(setup func(), expectedErr error) {
  1074  				setup()
  1075  				_, commandLineSettingsErr := cmd.GetCommandLineSettings()
  1076  				Expect(commandLineSettingsErr).To(MatchError(expectedErr))
  1077  			},
  1078  
  1079  			Entry("--droplet and --docker-username",
  1080  				func() {
  1081  					cmd.DropletPath = "some-droplet-path"
  1082  					cmd.DockerUsername = "some-docker-username"
  1083  				},
  1084  				translatableerror.ArgumentCombinationError{Args: []string{"--droplet", "--docker-username", "-p"}}),
  1085  
  1086  			Entry("--droplet and --docker-image",
  1087  				func() {
  1088  					cmd.DropletPath = "some-droplet-path"
  1089  					cmd.DockerImage.Path = "some-docker-image"
  1090  				},
  1091  				translatableerror.ArgumentCombinationError{Args: []string{"--droplet", "--docker-image", "-o"}}),
  1092  
  1093  			Entry("--droplet and -p",
  1094  				func() {
  1095  					cmd.DropletPath = "some-droplet-path"
  1096  					cmd.AppPath = "some-directory-path"
  1097  				},
  1098  				translatableerror.ArgumentCombinationError{Args: []string{"--droplet", "-p"}}),
  1099  
  1100  			Entry("-o and -p",
  1101  				func() {
  1102  					cmd.DockerImage.Path = "some-docker-image"
  1103  					cmd.AppPath = "some-directory-path"
  1104  				},
  1105  				translatableerror.ArgumentCombinationError{Args: []string{"--docker-image, -o", "-p"}}),
  1106  
  1107  			Entry("-b and --docker-image",
  1108  				func() {
  1109  					cmd.DockerImage.Path = "some-docker-image"
  1110  					cmd.Buildpacks = []string{"some-buildpack"}
  1111  				},
  1112  				translatableerror.ArgumentCombinationError{Args: []string{"-b", "--docker-image, -o"}}),
  1113  
  1114  			Entry("--docker-username (without DOCKER_PASSWORD env set)",
  1115  				func() {
  1116  					cmd.DockerUsername = "some-docker-username"
  1117  				},
  1118  				translatableerror.RequiredFlagsError{Arg1: "--docker-image, -o", Arg2: "--docker-username"}),
  1119  
  1120  			Entry("-d and --no-route",
  1121  				func() {
  1122  					cmd.Domain = "some-domain"
  1123  					cmd.NoRoute = true
  1124  				},
  1125  				translatableerror.ArgumentCombinationError{Args: []string{"-d", "--no-route"}}),
  1126  
  1127  			Entry("--hostname and --no-hostname",
  1128  				func() {
  1129  					cmd.Hostname = "po-tate-toe"
  1130  					cmd.NoHostname = true
  1131  				},
  1132  				translatableerror.ArgumentCombinationError{Args: []string{"--hostname", "-n", "--no-hostname"}}),
  1133  
  1134  			Entry("--hostname and --no-route",
  1135  				func() {
  1136  					cmd.Hostname = "po-tate-toe"
  1137  					cmd.NoRoute = true
  1138  				},
  1139  				translatableerror.ArgumentCombinationError{Args: []string{"--hostname", "-n", "--no-route"}}),
  1140  
  1141  			Entry("--no-hostname and --no-route",
  1142  				func() {
  1143  					cmd.NoHostname = true
  1144  					cmd.NoRoute = true
  1145  				},
  1146  				translatableerror.ArgumentCombinationError{Args: []string{"--no-hostname", "--no-route"}}),
  1147  
  1148  			Entry("-f and --no-manifest",
  1149  				func() {
  1150  					cmd.PathToManifest = "/some/path.yml"
  1151  					cmd.NoManifest = true
  1152  				},
  1153  				translatableerror.ArgumentCombinationError{Args: []string{"-f", "--no-manifest"}}),
  1154  
  1155  			Entry("--random-route and --hostname",
  1156  				func() {
  1157  					cmd.Hostname = "po-tate-toe"
  1158  					cmd.RandomRoute = true
  1159  				},
  1160  				translatableerror.ArgumentCombinationError{Args: []string{"--hostname", "-n", "--random-route"}}),
  1161  
  1162  			Entry("--random-route and --no-hostname",
  1163  				func() {
  1164  					cmd.RandomRoute = true
  1165  					cmd.NoHostname = true
  1166  				},
  1167  				translatableerror.ArgumentCombinationError{Args: []string{"--no-hostname", "--random-route"}}),
  1168  
  1169  			Entry("--random-route and --no-route",
  1170  				func() {
  1171  					cmd.RandomRoute = true
  1172  					cmd.NoRoute = true
  1173  				},
  1174  				translatableerror.ArgumentCombinationError{Args: []string{"--no-route", "--random-route"}}),
  1175  
  1176  			Entry("--random-route and --route-path",
  1177  				func() {
  1178  					cmd.RoutePath = flag.RoutePath{Path: "/bananas"}
  1179  					cmd.RandomRoute = true
  1180  				},
  1181  				translatableerror.ArgumentCombinationError{Args: []string{"--random-route", "--route-path"}}),
  1182  
  1183  			Entry("--route-path and --no-route",
  1184  				func() {
  1185  					cmd.RoutePath = flag.RoutePath{Path: "/bananas"}
  1186  					cmd.NoRoute = true
  1187  				},
  1188  				translatableerror.ArgumentCombinationError{Args: []string{"--route-path", "--no-route"}}),
  1189  		)
  1190  	})
  1191  })