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