github.com/ablease/cli@v6.37.1-0.20180613014814-3adbb7d7fb19+incompatible/command/v2/push_command_test.go (about)

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