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