github.com/mook-as/cf-cli@v7.0.0-beta.28.0.20200120190804-b91c115fae48+incompatible/integration/v6/experimental/v3_push_command_test.go (about)

     1  package experimental
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"net/http"
     7  	"os"
     8  	"regexp"
     9  	"time"
    10  
    11  	"code.cloudfoundry.org/cli/integration/helpers"
    12  	. "github.com/onsi/ginkgo"
    13  	. "github.com/onsi/gomega"
    14  	. "github.com/onsi/gomega/gbytes"
    15  	. "github.com/onsi/gomega/gexec"
    16  )
    17  
    18  var _ = Describe("v3-push command", func() {
    19  	var (
    20  		orgName           string
    21  		spaceName         string
    22  		appName           string
    23  		userName          string
    24  		PublicDockerImage = "cloudfoundry/diego-docker-app-custom"
    25  	)
    26  
    27  	BeforeEach(func() {
    28  		orgName = helpers.NewOrgName()
    29  		spaceName = helpers.NewSpaceName()
    30  		appName = helpers.PrefixedRandomName("app")
    31  		userName, _ = helpers.GetCredentials()
    32  		helpers.TurnOffExperimental()
    33  	})
    34  
    35  	AfterEach(func() {
    36  		helpers.TurnOnExperimental()
    37  	})
    38  
    39  	Describe("help", func() {
    40  		When("--help flag is set", func() {
    41  			It("Displays command usage to output", func() {
    42  				session := helpers.CF("v3-push", "--help")
    43  				Eventually(session).Should(Say("NAME:"))
    44  				Eventually(session).Should(Say("v3-push - Push a new app or sync changes to an existing app"))
    45  				Eventually(session).Should(Say("USAGE:"))
    46  				Eventually(session).Should(Say("cf v3-push APP_NAME \\[-b BUILDPACK\\]\\.\\.\\. \\[-p APP_PATH\\] \\[--no-route\\]"))
    47  				Eventually(session).Should(Say("cf v3-push APP_NAME --docker-image \\[REGISTRY_HOST:PORT/\\]IMAGE\\[:TAG\\] \\[--docker-username USERNAME\\] \\[--no-route\\]"))
    48  				Eventually(session).Should(Say("OPTIONS:"))
    49  				Eventually(session).Should(Say("-b\\s+Custom buildpack by name \\(e\\.g\\. my-buildpack\\) or Git URL \\(e\\.g\\. 'https://github.com/cloudfoundry/java-buildpack.git'\\) or Git URL with a branch or tag \\(e\\.g\\. 'https://github.com/cloudfoundry/java-buildpack\\.git#v3.3.0' for 'v3.3.0' tag\\)\\. To use built-in buildpacks only, specify 'default' or 'null'"))
    50  				Eventually(session).Should(Say("--docker-image, -o\\s+Docker image to use \\(e\\.g\\. user/docker-image-name\\)"))
    51  				Eventually(session).Should(Say("--docker-username\\s+Repository username; used with password from environment variable CF_DOCKER_PASSWORD"))
    52  				Eventually(session).Should(Say("--no-route\\s+Do not map a route to this app"))
    53  				Eventually(session).Should(Say("-p\\s+Path to app directory or to a zip file of the contents of the app directory"))
    54  				Eventually(session).Should(Say("ENVIRONMENT:"))
    55  				Eventually(session).Should(Say("CF_DOCKER_PASSWORD=\\s+Password used for private docker repository"))
    56  				Eventually(session).Should(Say("CF_STAGING_TIMEOUT=15\\s+Max wait time for buildpack staging, in minutes"))
    57  				Eventually(session).Should(Say("CF_STARTUP_TIMEOUT=5\\s+Max wait time for app instance startup, in minutes"))
    58  
    59  				Eventually(session).Should(Exit(0))
    60  			})
    61  		})
    62  	})
    63  
    64  	When("the app name is not provided", func() {
    65  		It("tells the user that the app name is required, prints help text, and exits 1", func() {
    66  			session := helpers.CF("v3-push")
    67  
    68  			Eventually(session.Err).Should(Say("Incorrect Usage: the required argument `APP_NAME` was not provided"))
    69  			Eventually(session).Should(Say("NAME:"))
    70  			Eventually(session).Should(Exit(1))
    71  		})
    72  	})
    73  
    74  	It("displays the experimental warning", func() {
    75  		session := helpers.CF("v3-push", appName)
    76  		Eventually(session.Err).Should(Say("This command is in EXPERIMENTAL stage and may change without notice"))
    77  		Eventually(session).Should(Exit())
    78  	})
    79  
    80  	When("the -b flag is not given an arg", func() {
    81  		It("tells the user that the flag requires an arg, prints help text, and exits 1", func() {
    82  			session := helpers.CF("v3-push", appName, "-b")
    83  
    84  			Eventually(session.Err).Should(Say("Incorrect Usage: expected argument for flag `-b'"))
    85  			Eventually(session).Should(Say("NAME:"))
    86  			Eventually(session).Should(Exit(1))
    87  		})
    88  	})
    89  
    90  	When("the -p flag is not given an arg", func() {
    91  		It("tells the user that the flag requires an arg, prints help text, and exits 1", func() {
    92  			session := helpers.CF("v3-push", appName, "-p")
    93  
    94  			Eventually(session.Err).Should(Say("Incorrect Usage: expected argument for flag `-p'"))
    95  			Eventually(session).Should(Say("NAME:"))
    96  			Eventually(session).Should(Exit(1))
    97  		})
    98  	})
    99  
   100  	When("the -p flag path does not exist", func() {
   101  		It("tells the user that the flag requires an arg, prints help text, and exits 1", func() {
   102  			session := helpers.CF("v3-push", appName, "-p", "path/that/does/not/exist")
   103  
   104  			Eventually(session.Err).Should(Say("Incorrect Usage: The specified path 'path/that/does/not/exist' does not exist."))
   105  			Eventually(session).Should(Say("NAME:"))
   106  			Eventually(session).Should(Exit(1))
   107  		})
   108  	})
   109  
   110  	When("the environment is not setup correctly", func() {
   111  		When("no API endpoint is set", func() {
   112  			BeforeEach(func() {
   113  				helpers.UnsetAPI()
   114  			})
   115  
   116  			It("fails with no API endpoint set message", func() {
   117  				session := helpers.CF("v3-push", appName)
   118  				Eventually(session).Should(Say("FAILED"))
   119  				Eventually(session.Err).Should(Say("No API endpoint set\\. Use 'cf login' or 'cf api' to target an endpoint\\."))
   120  				Eventually(session).Should(Exit(1))
   121  			})
   122  		})
   123  
   124  		When("not logged in", func() {
   125  			BeforeEach(func() {
   126  				helpers.LogoutCF()
   127  			})
   128  
   129  			It("fails with not logged in message", func() {
   130  				session := helpers.CF("v3-push", appName)
   131  				Eventually(session).Should(Say("FAILED"))
   132  				Eventually(session.Err).Should(Say("Not logged in\\. Use 'cf login' or 'cf login --sso' to log in\\."))
   133  				Eventually(session).Should(Exit(1))
   134  			})
   135  		})
   136  
   137  		When("there is no org set", func() {
   138  			BeforeEach(func() {
   139  				helpers.LogoutCF()
   140  				helpers.LoginCF()
   141  			})
   142  
   143  			It("fails with no org targeted error message", func() {
   144  				session := helpers.CF("v3-push", appName)
   145  				Eventually(session).Should(Say("FAILED"))
   146  				Eventually(session.Err).Should(Say("No org targeted, use 'cf target -o ORG' to target an org\\."))
   147  				Eventually(session).Should(Exit(1))
   148  			})
   149  		})
   150  
   151  		When("there is no space set", func() {
   152  			BeforeEach(func() {
   153  				helpers.LogoutCF()
   154  				helpers.LoginCF()
   155  				helpers.TargetOrg(ReadOnlyOrg)
   156  			})
   157  
   158  			It("fails with no space targeted error message", func() {
   159  				session := helpers.CF("v3-push", appName)
   160  				Eventually(session).Should(Say("FAILED"))
   161  				Eventually(session.Err).Should(Say("No space targeted, use 'cf target -s SPACE' to target a space\\."))
   162  				Eventually(session).Should(Exit(1))
   163  			})
   164  		})
   165  	})
   166  
   167  	When("the environment is set up correctly", func() {
   168  		var domainName string
   169  
   170  		BeforeEach(func() {
   171  			helpers.SetupCF(orgName, spaceName)
   172  			domainName = helpers.DefaultSharedDomain()
   173  		})
   174  
   175  		AfterEach(func() {
   176  			helpers.QuickDeleteOrg(orgName)
   177  		})
   178  
   179  		When("the app exists", func() {
   180  			var session *Session
   181  			BeforeEach(func() {
   182  				helpers.WithHelloWorldApp(func(appDir string) {
   183  					Eventually(helpers.CustomCF(helpers.CFEnv{WorkingDirectory: appDir}, "v3-push", appName)).Should(Exit(0))
   184  				})
   185  
   186  				helpers.WithHelloWorldApp(func(appDir string) {
   187  					session = helpers.CustomCF(helpers.CFEnv{WorkingDirectory: appDir}, "v3-push", appName, "-b", "https://github.com/cloudfoundry/staticfile-buildpack")
   188  					Eventually(session).Should(Exit(0))
   189  				})
   190  			})
   191  
   192  			It("pushes the app", func() {
   193  				Eventually(session).Should(Say("Updating app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   194  				Eventually(session).Should(Say("OK"))
   195  				Eventually(session).Should(Say(""))
   196  				Eventually(session).Should(Say("Uploading and creating bits package for app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   197  				Eventually(session).Should(Say("OK"))
   198  				Eventually(session).Should(Say(""))
   199  				Eventually(session).Should(Say("Stopping app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   200  				Eventually(session).Should(Say("OK"))
   201  				Eventually(session).Should(Say(""))
   202  				Eventually(session).Should(Say("Staging package for app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   203  				Eventually(session).Should(Say("OK"))
   204  				Eventually(session).Should(Say("Setting app %s to droplet .+ in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   205  				Eventually(session).Should(Say("OK"))
   206  				Eventually(session).Should(Say(""))
   207  				Eventually(session).Should(Say("Mapping routes\\.\\.\\."))
   208  				Eventually(session).Should(Say("OK"))
   209  				Eventually(session).Should(Say(""))
   210  				Eventually(session).Should(Say("Starting app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   211  				Eventually(session).Should(Say("OK"))
   212  				Eventually(session).Should(Say(""))
   213  				Eventually(session).Should(Say("Waiting for app to start\\.\\.\\."))
   214  				Eventually(session).Should(Say("Showing health and status for app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   215  				Eventually(session).Should(Say(""))
   216  				Eventually(session).Should(Say("name:\\s+%s", appName))
   217  				Eventually(session).Should(Say("requested state:\\s+started"))
   218  				Eventually(session).Should(Say("routes:\\s+%s\\.%s", appName, domainName))
   219  				Eventually(session).Should(Say("stack:\\s+cflinuxfs"))
   220  
   221  				// TODO: Uncomment when capi sorts out droplet buildpack name/detectoutput
   222  				// Eventually(session).Should(Say("buildpacks:\\s+https://github.com/cloudfoundry/staticfile-buildpack"))
   223  				Eventually(session).Should(Say(""))
   224  				Eventually(session).Should(Say("type:\\s+web"))
   225  				Eventually(session).Should(Say("instances:\\s+1/1"))
   226  				Eventually(session).Should(Say("memory usage:\\s+\\d+(M|G)"))
   227  				Eventually(session).Should(Say(`state\s+since\s+cpu\s+memory\s+disk`))
   228  				Eventually(session).Should(Say("#0\\s+running\\s+\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} [AP]M"))
   229  			})
   230  		})
   231  
   232  		When("the app does not already exist", func() {
   233  			var session *Session
   234  
   235  			BeforeEach(func() {
   236  				helpers.WithHelloWorldApp(func(appDir string) {
   237  					session = helpers.CustomCF(helpers.CFEnv{WorkingDirectory: appDir}, "v3-push", appName)
   238  					Eventually(session).Should(Exit(0))
   239  				})
   240  			})
   241  
   242  			It("pushes the app", func() {
   243  				Eventually(session).Should(Say("Creating app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   244  				Eventually(session).Should(Say("OK"))
   245  				Eventually(session).Should(Say(""))
   246  				Eventually(session).Should(Say("Uploading and creating bits package for app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   247  				Eventually(session).Should(Say("OK"))
   248  				Eventually(session).Should(Say(""))
   249  				Consistently(session).ShouldNot(Say("Stopping app %s", appName))
   250  				Eventually(session).Should(Say("Staging package for app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   251  				Eventually(session).Should(Say("OK"))
   252  				Eventually(session).Should(Say("Setting app %s to droplet .+ in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   253  				Eventually(session).Should(Say("OK"))
   254  				Eventually(session).Should(Say(""))
   255  				Eventually(session).Should(Say("Mapping routes\\.\\.\\."))
   256  				Eventually(session).Should(Say("OK"))
   257  				Eventually(session).Should(Say(""))
   258  				Eventually(session).Should(Say("Starting app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   259  				Eventually(session).Should(Say("OK"))
   260  				Eventually(session).Should(Say(""))
   261  				Eventually(session).Should(Say("Waiting for app to start\\.\\.\\."))
   262  				Eventually(session).Should(Say("Showing health and status for app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   263  				Eventually(session).Should(Say(""))
   264  				Eventually(session).Should(Say("name:\\s+%s", appName))
   265  				Eventually(session).Should(Say("requested state:\\s+started"))
   266  				Eventually(session).Should(Say("routes:\\s+%s\\.%s", appName, domainName))
   267  				Eventually(session).Should(Say("stack:\\s+cflinuxfs"))
   268  				Eventually(session).Should(Say("buildpacks:\\s+staticfile"))
   269  				Eventually(session).Should(Say(""))
   270  				Eventually(session).Should(Say("type:\\s+web"))
   271  				Eventually(session).Should(Say("instances:\\s+1/1"))
   272  				Eventually(session).Should(Say("memory usage:\\s+\\d+(M|G)"))
   273  				Eventually(session).Should(Say(`state\s+since\s+cpu\s+memory\s+disk`))
   274  				Eventually(session).Should(Say("#0\\s+running\\s+\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} [AP]M"))
   275  			})
   276  		})
   277  
   278  		When("the app crashes", func() {
   279  			var session *Session
   280  
   281  			BeforeEach(func() {
   282  				helpers.WithCrashingApp(func(appDir string) {
   283  					session = helpers.CustomCF(helpers.CFEnv{WorkingDirectory: appDir}, "v3-push", appName)
   284  					Eventually(session).Should(Exit(0))
   285  				})
   286  			})
   287  
   288  			It("pushes the app", func() {
   289  				Eventually(session).Should(Say("Creating app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   290  				Eventually(session).Should(Say("OK"))
   291  				Eventually(session).Should(Say(""))
   292  				Eventually(session).Should(Say("Uploading and creating bits package for app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   293  				Eventually(session).Should(Say("OK"))
   294  				Eventually(session).Should(Say(""))
   295  				Consistently(session).ShouldNot(Say("Stopping app %s", appName))
   296  				Eventually(session).Should(Say("Staging package for app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   297  				Eventually(session).Should(Say("OK"))
   298  				Eventually(session).Should(Say("Setting app %s to droplet .+ in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   299  				Eventually(session).Should(Say("OK"))
   300  				Eventually(session).Should(Say(""))
   301  				Eventually(session).Should(Say("Mapping routes\\.\\.\\."))
   302  				Eventually(session).Should(Say("OK"))
   303  				Eventually(session).Should(Say(""))
   304  				Eventually(session).Should(Say("Starting app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   305  				Eventually(session).Should(Say("OK"))
   306  				Eventually(session).Should(Say(""))
   307  				Eventually(session).Should(Say("Waiting for app to start\\.\\.\\."))
   308  				Eventually(session).Should(Say("Showing health and status for app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   309  				Eventually(session).Should(Say(""))
   310  				Eventually(session).Should(Say("name:\\s+%s", appName))
   311  				Eventually(session).Should(Say("requested state:\\s+started"))
   312  				Eventually(session).Should(Say("routes:\\s+%s\\.%s", appName, domainName))
   313  				Eventually(session).Should(Say("stack:\\s+cflinuxfs"))
   314  				Eventually(session).Should(Say("buildpacks:\\s+ruby"))
   315  				Eventually(session).Should(Say(""))
   316  				Eventually(session).Should(Say("type:\\s+web"))
   317  				Eventually(session).Should(Say("instances:\\s+0/1"))
   318  				Eventually(session).Should(Say("memory usage:\\s+\\d+(M|G)"))
   319  				Eventually(session).Should(Say(`state\s+since\s+cpu\s+memory\s+disk`))
   320  				Eventually(session).Should(Say("#0\\s+crashed\\s+\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} [AP]M"))
   321  			})
   322  		})
   323  
   324  		When("the -p flag is provided", func() {
   325  			When("the path is a directory", func() {
   326  				When("the directory contains files", func() {
   327  					It("pushes the app from the directory", func() {
   328  						helpers.WithHelloWorldApp(func(appDir string) {
   329  							session := helpers.CF("v3-push", appName, "-p", appDir)
   330  
   331  							Eventually(session).Should(Say("Starting app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   332  							Eventually(session).Should(Say("Waiting for app to start\\.\\.\\."))
   333  							Eventually(session).Should(Say("Showing health and status for app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   334  							Eventually(session).Should(Say(""))
   335  							Eventually(session).Should(Say("name:\\s+%s", appName))
   336  							Eventually(session).Should(Say("requested state:\\s+started"))
   337  							Eventually(session).Should(Say("routes:\\s+%s\\.%s", appName, domainName))
   338  							Eventually(session).Should(Say("stack:\\s+cflinuxfs"))
   339  							Eventually(session).Should(Say("buildpacks:\\s+staticfile"))
   340  							Eventually(session).Should(Say(""))
   341  							Eventually(session).Should(Say("type:\\s+web"))
   342  							Eventually(session).Should(Say("instances:\\s+1/1"))
   343  							Eventually(session).Should(Say("memory usage:\\s+\\d+(M|G)"))
   344  							Eventually(session).Should(Say(`state\s+since\s+cpu\s+memory\s+disk`))
   345  							Eventually(session).Should(Say("#0\\s+running\\s+\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} [AP]M"))
   346  
   347  							Eventually(session).Should(Exit(0))
   348  						})
   349  					})
   350  				})
   351  
   352  				When("the directory is empty", func() {
   353  					var emptyDir string
   354  
   355  					BeforeEach(func() {
   356  						var err error
   357  						emptyDir, err = ioutil.TempDir("", "integration-push-path-empty")
   358  						Expect(err).ToNot(HaveOccurred())
   359  					})
   360  
   361  					AfterEach(func() {
   362  						Expect(os.RemoveAll(emptyDir)).ToNot(HaveOccurred())
   363  					})
   364  
   365  					It("returns an error", func() {
   366  						session := helpers.CF("v3-push", appName, "-p", emptyDir)
   367  						Eventually(session.Err).Should(Say("No app files found in '%s'", regexp.QuoteMeta(emptyDir)))
   368  						Eventually(session).Should(Exit(1))
   369  					})
   370  				})
   371  			})
   372  
   373  			When("the path is a zip file", func() {
   374  				Context("pushing a zip file", func() {
   375  					var archive string
   376  
   377  					BeforeEach(func() {
   378  						helpers.WithHelloWorldApp(func(appDir string) {
   379  							tmpfile, err := ioutil.TempFile("", "push-archive-integration")
   380  							Expect(err).ToNot(HaveOccurred())
   381  							archive = tmpfile.Name()
   382  							Expect(tmpfile.Close())
   383  
   384  							err = helpers.Zipit(appDir, archive, "")
   385  							Expect(err).ToNot(HaveOccurred())
   386  						})
   387  					})
   388  
   389  					AfterEach(func() {
   390  						Expect(os.RemoveAll(archive)).ToNot(HaveOccurred())
   391  					})
   392  
   393  					It("pushes the app from the zip file", func() {
   394  						session := helpers.CF("v3-push", appName, "-p", archive)
   395  
   396  						Eventually(session).Should(Say("Starting app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   397  						Eventually(session).Should(Say("Waiting for app to start\\.\\.\\."))
   398  						Eventually(session).Should(Say("Showing health and status for app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   399  						Eventually(session).Should(Say(""))
   400  						Eventually(session).Should(Say("name:\\s+%s", appName))
   401  						Eventually(session).Should(Say("requested state:\\s+started"))
   402  						Eventually(session).Should(Say("routes:\\s+%s\\.%s", appName, domainName))
   403  						Eventually(session).Should(Say("stack:\\s+cflinuxfs"))
   404  						Eventually(session).Should(Say("buildpacks:\\s+staticfile"))
   405  						Eventually(session).Should(Say(""))
   406  						Eventually(session).Should(Say("type:\\s+web"))
   407  						Eventually(session).Should(Say("instances:\\s+1/1"))
   408  						Eventually(session).Should(Say("memory usage:\\s+\\d+(M|G)"))
   409  						Eventually(session).Should(Say(`state\s+since\s+cpu\s+memory\s+disk`))
   410  						Eventually(session).Should(Say("#0\\s+running\\s+\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} [AP]M"))
   411  
   412  						Eventually(session).Should(Exit(0))
   413  					})
   414  				})
   415  			})
   416  
   417  			When("the path is a symlink to a directory", func() {
   418  				var symlinkPath string
   419  
   420  				BeforeEach(func() {
   421  					tempFile, err := ioutil.TempFile("", "symlink-")
   422  					Expect(err).ToNot(HaveOccurred())
   423  					Expect(tempFile.Close()).To(Succeed())
   424  
   425  					symlinkPath = tempFile.Name()
   426  					Expect(os.Remove(symlinkPath)).To(Succeed())
   427  				})
   428  
   429  				AfterEach(func() {
   430  					Expect(os.Remove(symlinkPath)).To(Succeed())
   431  				})
   432  
   433  				It("creates and uploads the package from the directory", func() {
   434  					helpers.WithHelloWorldApp(func(appDir string) {
   435  						Expect(os.Symlink(appDir, symlinkPath)).To(Succeed())
   436  
   437  						session := helpers.CF("v3-push", appName, "-p", symlinkPath)
   438  
   439  						Eventually(session).Should(Say("Starting app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   440  						Eventually(session).Should(Say("Waiting for app to start\\.\\.\\."))
   441  						Eventually(session).Should(Say("Showing health and status for app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   442  						Eventually(session).Should(Say(""))
   443  						Eventually(session).Should(Say("name:\\s+%s", appName))
   444  						Eventually(session).Should(Say("requested state:\\s+started"))
   445  						Eventually(session).Should(Say("routes:\\s+%s\\.%s", appName, domainName))
   446  						Eventually(session).Should(Say("stack:\\s+cflinuxfs"))
   447  						Eventually(session).Should(Say("buildpacks:\\s+staticfile"))
   448  						Eventually(session).Should(Say(""))
   449  						Eventually(session).Should(Say("type:\\s+web"))
   450  						Eventually(session).Should(Say("instances:\\s+1/1"))
   451  						Eventually(session).Should(Say("memory usage:\\s+\\d+(M|G)"))
   452  						Eventually(session).Should(Say(`state\s+since\s+cpu\s+memory\s+disk`))
   453  						Eventually(session).Should(Say("#0\\s+running\\s+\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} [AP]M"))
   454  
   455  						Eventually(session).Should(Exit(0))
   456  					})
   457  				})
   458  			})
   459  
   460  			When("the path is a symlink to a zip file", func() {
   461  				var (
   462  					archive     string
   463  					symlinkPath string
   464  				)
   465  
   466  				BeforeEach(func() {
   467  					helpers.WithHelloWorldApp(func(appDir string) {
   468  						tmpfile, err := ioutil.TempFile("", "package-archive-integration")
   469  						Expect(err).ToNot(HaveOccurred())
   470  						archive = tmpfile.Name()
   471  						Expect(tmpfile.Close())
   472  
   473  						err = helpers.Zipit(appDir, archive, "")
   474  						Expect(err).ToNot(HaveOccurred())
   475  					})
   476  
   477  					tempFile, err := ioutil.TempFile("", "symlink-to-archive-")
   478  					Expect(err).ToNot(HaveOccurred())
   479  					Expect(tempFile.Close()).To(Succeed())
   480  
   481  					symlinkPath = tempFile.Name()
   482  					Expect(os.Remove(symlinkPath)).To(Succeed())
   483  					Expect(os.Symlink(archive, symlinkPath)).To(Succeed())
   484  				})
   485  
   486  				AfterEach(func() {
   487  					Expect(os.Remove(archive)).To(Succeed())
   488  					Expect(os.Remove(symlinkPath)).To(Succeed())
   489  				})
   490  
   491  				It("creates and uploads the package from the zip file", func() {
   492  					session := helpers.CF("v3-push", appName, "-p", symlinkPath)
   493  
   494  					Eventually(session).Should(Say("Starting app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   495  					Eventually(session).Should(Say("Waiting for app to start\\.\\.\\."))
   496  					Eventually(session).Should(Say("Showing health and status for app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   497  					Eventually(session).Should(Say(""))
   498  					Eventually(session).Should(Say("name:\\s+%s", appName))
   499  					Eventually(session).Should(Say("requested state:\\s+started"))
   500  					Eventually(session).Should(Say("routes:\\s+%s\\.%s", appName, domainName))
   501  					Eventually(session).Should(Say("stack:\\s+cflinuxfs"))
   502  					Eventually(session).Should(Say("buildpacks:\\s+staticfile"))
   503  					Eventually(session).Should(Say(""))
   504  					Eventually(session).Should(Say("type:\\s+web"))
   505  					Eventually(session).Should(Say("instances:\\s+1/1"))
   506  					Eventually(session).Should(Say("memory usage:\\s+\\d+(M|G)"))
   507  					Eventually(session).Should(Say(`state\s+since\s+cpu\s+memory\s+disk`))
   508  					Eventually(session).Should(Say("#0\\s+running\\s+\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} [AP]M"))
   509  
   510  					Eventually(session).Should(Exit(0))
   511  				})
   512  			})
   513  		})
   514  
   515  		When("the --no-route flag is set", func() {
   516  			var session *Session
   517  
   518  			BeforeEach(func() {
   519  				helpers.WithHelloWorldApp(func(appDir string) {
   520  					session = helpers.CustomCF(helpers.CFEnv{WorkingDirectory: appDir}, "v3-push", appName, "--no-route")
   521  					Eventually(session).Should(Exit(0))
   522  				})
   523  			})
   524  
   525  			It("does not map any routes to the app", func() {
   526  				Consistently(session).ShouldNot(Say("Mapping routes\\.\\.\\."))
   527  				Eventually(session).Should(Say("name:\\s+%s", appName))
   528  				Eventually(session).Should(Say("requested state:\\s+started"))
   529  				Eventually(session).Should(Say("routes:\\s+\n"))
   530  				Eventually(session).Should(Say("stack:\\s+cflinuxfs"))
   531  				Eventually(session).Should(Say("buildpacks:\\s+staticfile"))
   532  				Eventually(session).Should(Say(""))
   533  				Eventually(session).Should(Say("type:\\s+web"))
   534  				Eventually(session).Should(Say("instances:\\s+1/1"))
   535  				Eventually(session).Should(Say("memory usage:\\s+\\d+(M|G)"))
   536  				Eventually(session).Should(Say(`state\s+since\s+cpu\s+memory\s+disk`))
   537  				Eventually(session).Should(Say("#0\\s+running\\s+\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} [AP]M"))
   538  			})
   539  		})
   540  
   541  		When("the -b flag is set", func() {
   542  			var session *Session
   543  
   544  			When("pushing a multi-buildpack app", func() {
   545  				BeforeEach(func() {
   546  					helpers.WithMultiBuildpackApp(func(appDir string) {
   547  						session = helpers.CustomCF(helpers.CFEnv{WorkingDirectory: appDir}, "v3-push", appName, "-b", "ruby_buildpack", "-b", "go_buildpack")
   548  
   549  						// TODO: uncomment this expectation once capi-release displays all buildpacks on droplet
   550  						// Story: https://www.pivotaltracker.com/story/show/150425459
   551  						// Eventually(session).Should(Say("buildpacks:.*ruby_buildpack, go_buildpack"))
   552  
   553  						Eventually(session, 10*time.Minute).Should(Exit(0))
   554  					})
   555  				})
   556  
   557  				It("successfully compiles and runs the app", func() {
   558  					resp, err := http.Get(fmt.Sprintf("http://%s.%s", appName, helpers.DefaultSharedDomain()))
   559  					Expect(err).ToNot(HaveOccurred())
   560  					Expect(resp.StatusCode).To(Equal(http.StatusOK))
   561  				})
   562  			})
   563  
   564  			When("resetting the buildpack to default", func() {
   565  				BeforeEach(func() {
   566  					helpers.WithHelloWorldApp(func(appDir string) {
   567  						Eventually(helpers.CustomCF(helpers.CFEnv{WorkingDirectory: appDir}, "v3-push", appName, "-b", "java_buildpack")).Should(Exit(1))
   568  						session = helpers.CustomCF(helpers.CFEnv{WorkingDirectory: appDir}, "v3-push", appName, "-b", "default")
   569  						Eventually(session).Should(Exit(0))
   570  					})
   571  				})
   572  
   573  				It("successfully pushes the app", func() {
   574  					Eventually(session).Should(Say("name:\\s+%s", appName))
   575  					Eventually(session).Should(Say(`state\s+since\s+cpu\s+memory\s+disk`))
   576  					Eventually(session).Should(Say("#0\\s+running\\s+\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} [AP]M"))
   577  				})
   578  			})
   579  
   580  			When("omitting the buildpack", func() {
   581  				BeforeEach(func() {
   582  					helpers.WithHelloWorldApp(func(appDir string) {
   583  						Eventually(helpers.CustomCF(helpers.CFEnv{WorkingDirectory: appDir}, "v3-push", appName, "-b", "java_buildpack")).Should(Exit(1))
   584  						session = helpers.CustomCF(helpers.CFEnv{WorkingDirectory: appDir}, "v3-push", appName)
   585  						Eventually(session).Should(Exit(1))
   586  					})
   587  				})
   588  
   589  				It("continues using previously set buildpack", func() {
   590  					Eventually(session).Should(Say("FAILED"))
   591  				})
   592  			})
   593  
   594  			When("the buildpack is invalid", func() {
   595  				BeforeEach(func() {
   596  					helpers.WithHelloWorldApp(func(appDir string) {
   597  						session = helpers.CustomCF(helpers.CFEnv{WorkingDirectory: appDir}, "v3-push", appName, "-b", "wut")
   598  						Eventually(session).Should(Exit(1))
   599  					})
   600  				})
   601  
   602  				It("errors and does not push the app", func() {
   603  					Consistently(session).ShouldNot(Say("Creating app"))
   604  					Eventually(session).Should(Say("FAILED"))
   605  					Eventually(session.Err).Should(Say(`Buildpack "wut" must be an existing admin buildpack or a valid git URI`))
   606  				})
   607  			})
   608  
   609  			When("the buildpack is valid", func() {
   610  				BeforeEach(func() {
   611  					helpers.WithHelloWorldApp(func(appDir string) {
   612  						session = helpers.CustomCF(helpers.CFEnv{WorkingDirectory: appDir}, "v3-push", appName, "-b", "https://github.com/cloudfoundry/staticfile-buildpack")
   613  						Eventually(session).Should(Exit(0))
   614  					})
   615  				})
   616  
   617  				It("uses the specified buildpack", func() {
   618  					Eventually(session).Should(Say("name:\\s+%s", appName))
   619  					Eventually(session).Should(Say("requested state:\\s+started"))
   620  					Eventually(session).Should(Say("routes:\\s+%s\\.%s", appName, domainName))
   621  					Eventually(session).Should(Say("stack:\\s+cflinuxfs"))
   622  
   623  					// TODO: Uncomment when capi sorts out droplet buildpack name/detectoutput
   624  					// Eventually(session).Should(Say("buildpacks:\\s+https://github.com/cloudfoundry/staticfile-buildpack"))
   625  					Eventually(session).Should(Say(""))
   626  					Eventually(session).Should(Say("type:\\s+web"))
   627  					Eventually(session).Should(Say("instances:\\s+1/1"))
   628  					Eventually(session).Should(Say("memory usage:\\s+\\d+(M|G)"))
   629  					Eventually(session).Should(Say(`state\s+since\s+cpu\s+memory\s+disk`))
   630  					Eventually(session).Should(Say("#0\\s+running\\s+\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} [AP]M"))
   631  				})
   632  			})
   633  		})
   634  
   635  		When("the -o flag is set", func() {
   636  			When("the docker image is valid", func() {
   637  				It("uses the specified docker image", func() {
   638  					session := helpers.CF("v3-push", appName, "-o", PublicDockerImage)
   639  
   640  					Eventually(session).Should(Say("name:\\s+%s", appName))
   641  					Eventually(session).Should(Say("requested state:\\s+started"))
   642  					Eventually(session).Should(Say("routes:\\s+%s\\.%s", appName, domainName))
   643  					Eventually(session).Should(Say("stack:"))
   644  					Eventually(session).ShouldNot(Say("buildpacks:"))
   645  					Eventually(session).Should(Say("docker image:\\s+%s", PublicDockerImage))
   646  					Eventually(session).Should(Say(""))
   647  					Eventually(session).Should(Say("type:\\s+web"))
   648  					Eventually(session).Should(Say("instances:\\s+1/1"))
   649  					Eventually(session).Should(Say("memory usage:\\s+\\d+(M|G)"))
   650  					Eventually(session).Should(Say(`state\s+since\s+cpu\s+memory\s+disk`))
   651  					Eventually(session).Should(Say("#0\\s+running\\s+\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} [AP]M"))
   652  					Eventually(session).Should(Exit(0))
   653  				})
   654  			})
   655  
   656  			When("the docker image is invalid", func() {
   657  				It("displays an error and exits 1", func() {
   658  					session := helpers.CF("v3-push", appName, "-o", "some-invalid-docker-image")
   659  					Eventually(session).Should(Say("FAILED"))
   660  					Eventually(session.Err).Should(Say("StagingError - Staging error: staging failed"))
   661  					Eventually(session).Should(Exit(1))
   662  				})
   663  			})
   664  
   665  			When("a docker username and password are provided with a private image", func() {
   666  				var (
   667  					privateDockerImage    string
   668  					privateDockerUsername string
   669  					privateDockerPassword string
   670  				)
   671  
   672  				BeforeEach(func() {
   673  					privateDockerImage = os.Getenv("CF_INT_DOCKER_IMAGE")
   674  					privateDockerUsername = os.Getenv("CF_INT_DOCKER_USERNAME")
   675  					privateDockerPassword = os.Getenv("CF_INT_DOCKER_PASSWORD")
   676  
   677  					if privateDockerImage == "" || privateDockerUsername == "" || privateDockerPassword == "" {
   678  						Skip("CF_INT_DOCKER_IMAGE, CF_INT_DOCKER_USERNAME, or CF_INT_DOCKER_PASSWORD is not set")
   679  					}
   680  				})
   681  
   682  				It("uses the specified private docker image", func() {
   683  					session := helpers.CustomCF(
   684  						helpers.CFEnv{
   685  							EnvVars: map[string]string{"CF_DOCKER_PASSWORD": privateDockerPassword},
   686  						},
   687  						"v3-push", "--docker-username", privateDockerUsername, "--docker-image", privateDockerImage, appName,
   688  					)
   689  
   690  					Eventually(session).Should(Say("name:\\s+%s", appName))
   691  					Eventually(session).Should(Say("requested state:\\s+started"))
   692  					Eventually(session).Should(Say("routes:\\s+%s\\.%s", appName, domainName))
   693  					Eventually(session).Should(Say("stack:"))
   694  					Eventually(session).ShouldNot(Say("buildpacks:"))
   695  					Eventually(session).Should(Say("docker image:\\s+%s", privateDockerImage))
   696  					Eventually(session).Should(Say(""))
   697  					Eventually(session).Should(Say("type:\\s+web"))
   698  					Eventually(session).Should(Say("instances:\\s+1/1"))
   699  					Eventually(session).Should(Say("memory usage:\\s+\\d+(M|G)"))
   700  					Eventually(session).Should(Say(`state\s+since\s+cpu\s+memory\s+disk`))
   701  					Eventually(session).Should(Say("#0\\s+running\\s+\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} [AP]M"))
   702  					Eventually(session).Should(Exit(0))
   703  				})
   704  			})
   705  		})
   706  
   707  		Describe("argument combination errors", func() {
   708  			When("the --docker-username is provided without the -o flag", func() {
   709  				It("displays an error and exits 1", func() {
   710  					helpers.WithHelloWorldApp(func(appDir string) {
   711  						session := helpers.CF("v3-push", appName, "--docker-username", "some-username")
   712  						Eventually(session).Should(Say("FAILED"))
   713  						Eventually(session.Err).Should(Say("Incorrect Usage: '--docker-image, -o' and '--docker-username' must be used together."))
   714  						Eventually(session).Should(Say("NAME:"))
   715  						Eventually(session).Should(Exit(1))
   716  					})
   717  				})
   718  			})
   719  
   720  			When("the --docker-username and -p flags are provided together", func() {
   721  				It("displays an error and exits 1", func() {
   722  					helpers.WithHelloWorldApp(func(appDir string) {
   723  						session := helpers.CF("v3-push", appName, "--docker-username", "some-username", "-p", appDir)
   724  						Eventually(session).Should(Say("FAILED"))
   725  						Eventually(session.Err).Should(Say("Incorrect Usage: '--docker-image, -o' and '--docker-username' must be used together."))
   726  						Eventually(session).Should(Say("NAME:"))
   727  						Eventually(session).Should(Exit(1))
   728  					})
   729  				})
   730  			})
   731  
   732  			When("the --docker-username is provided without a password", func() {
   733  				var oldPassword string
   734  
   735  				BeforeEach(func() {
   736  					oldPassword = os.Getenv("CF_DOCKER_PASSWORD")
   737  					err := os.Unsetenv("CF_DOCKER_PASSWORD")
   738  					Expect(err).ToNot(HaveOccurred())
   739  				})
   740  
   741  				AfterEach(func() {
   742  					err := os.Setenv("CF_DOCKER_PASSWORD", oldPassword)
   743  					Expect(err).ToNot(HaveOccurred())
   744  				})
   745  
   746  				It("displays an error and exits 1", func() {
   747  					helpers.WithHelloWorldApp(func(appDir string) {
   748  						session := helpers.CF("v3-push", appName, "--docker-username", "some-username", "--docker-image", "some-image")
   749  						Eventually(session).Should(Say("FAILED"))
   750  						Eventually(session.Err).Should(Say("Environment variable CF_DOCKER_PASSWORD not set\\."))
   751  						Eventually(session).Should(Exit(1))
   752  					})
   753  				})
   754  			})
   755  
   756  			When("the -o and -p flags are provided together", func() {
   757  				It("displays an error and exits 1", func() {
   758  					helpers.WithHelloWorldApp(func(appDir string) {
   759  						session := helpers.CF("v3-push", appName, "-o", PublicDockerImage, "-p", appDir)
   760  						Eventually(session).Should(Say("FAILED"))
   761  						Eventually(session.Err).Should(Say("Incorrect Usage: The following arguments cannot be used together: --docker-image, -o, -p"))
   762  						Eventually(session).Should(Say("NAME:"))
   763  						Eventually(session).Should(Exit(1))
   764  					})
   765  				})
   766  			})
   767  
   768  			When("the -o and -b flags are provided together", func() {
   769  				It("displays an error and exits 1", func() {
   770  					helpers.WithHelloWorldApp(func(appDir string) {
   771  						session := helpers.CF("v3-push", appName, "-o", PublicDockerImage, "-b", "some-buildpack")
   772  						Eventually(session).Should(Say("FAILED"))
   773  						Eventually(session.Err).Should(Say("Incorrect Usage: The following arguments cannot be used together: -b, --docker-image, -o"))
   774  						Eventually(session).Should(Say("NAME:"))
   775  						Eventually(session).Should(Exit(1))
   776  					})
   777  				})
   778  			})
   779  		})
   780  	})
   781  })