github.com/nimakaviani/cli@v6.37.1-0.20180619223813-e734901a73fa+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.Err).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.StartAndTargetServerWithAPIVersions(helpers.DefaultV2Version, "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  			helpers.SetupCF(orgName, spaceName)
   205  			domainName = helpers.DefaultSharedDomain()
   206  		})
   207  
   208  		AfterEach(func() {
   209  			helpers.QuickDeleteOrg(orgName)
   210  		})
   211  
   212  		Context("when the app exists", func() {
   213  			var session *Session
   214  			BeforeEach(func() {
   215  				helpers.WithHelloWorldApp(func(appDir string) {
   216  					Eventually(helpers.CustomCF(helpers.CFEnv{WorkingDirectory: appDir}, "v3-push", appName)).Should(Exit(0))
   217  				})
   218  
   219  				helpers.WithHelloWorldApp(func(appDir string) {
   220  					session = helpers.CustomCF(helpers.CFEnv{WorkingDirectory: appDir}, "v3-push", appName, "-b", "https://github.com/cloudfoundry/staticfile-buildpack")
   221  					Eventually(session).Should(Exit(0))
   222  				})
   223  			})
   224  
   225  			It("pushes the app", func() {
   226  				Eventually(session).Should(Say("Updating app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   227  				Eventually(session).Should(Say("OK"))
   228  				Eventually(session).Should(Say(""))
   229  				Eventually(session).Should(Say("Uploading and creating bits package for app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   230  				Eventually(session).Should(Say("OK"))
   231  				Eventually(session).Should(Say(""))
   232  				Eventually(session).Should(Say("Stopping app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   233  				Eventually(session).Should(Say("OK"))
   234  				Eventually(session).Should(Say(""))
   235  				Eventually(session).Should(Say("Staging package for app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   236  				Eventually(session).Should(Say("OK"))
   237  				Eventually(session).Should(Say("Setting app %s to droplet .+ in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   238  				Eventually(session).Should(Say("OK"))
   239  				Eventually(session).Should(Say(""))
   240  				Eventually(session).Should(Say("Mapping routes\\.\\.\\."))
   241  				Eventually(session).Should(Say("OK"))
   242  				Eventually(session).Should(Say(""))
   243  				Eventually(session).Should(Say("Starting 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("Waiting for app to start\\.\\.\\."))
   247  				Eventually(session).Should(Say("Showing health and status for app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   248  				Eventually(session).Should(Say(""))
   249  				Eventually(session).Should(Say("name:\\s+%s", appName))
   250  				Eventually(session).Should(Say("requested state:\\s+started"))
   251  				Eventually(session).Should(Say("processes:\\s+web:1/1"))
   252  				Eventually(session).Should(Say("memory usage:\\s+\\d+M x 1"))
   253  				Eventually(session).Should(Say("routes:\\s+%s\\.%s", appName, domainName))
   254  				Eventually(session).Should(Say("stack:\\s+cflinuxfs2"))
   255  
   256  				// TODO: Uncomment when capi sorts out droplet buildpack name/detectoutput
   257  				// Eventually(session).Should(Say("buildpacks:\\s+https://github.com/cloudfoundry/staticfile-buildpack"))
   258  				Eventually(session).Should(Say(""))
   259  				Eventually(session).Should(Say("web:1/1"))
   260  				Eventually(session).Should(Say(`state\s+since\s+cpu\s+memory\s+disk`))
   261  				Eventually(session).Should(Say("#0\\s+running\\s+\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} [AP]M"))
   262  			})
   263  		})
   264  
   265  		Context("when the app does not already exist", func() {
   266  			var session *Session
   267  
   268  			BeforeEach(func() {
   269  				helpers.WithHelloWorldApp(func(appDir string) {
   270  					session = helpers.CustomCF(helpers.CFEnv{WorkingDirectory: appDir}, "v3-push", appName)
   271  					Eventually(session).Should(Exit(0))
   272  				})
   273  			})
   274  
   275  			It("pushes the app", func() {
   276  				Eventually(session).Should(Say("Creating app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   277  				Eventually(session).Should(Say("OK"))
   278  				Eventually(session).Should(Say(""))
   279  				Eventually(session).Should(Say("Uploading and creating bits package for app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   280  				Eventually(session).Should(Say("OK"))
   281  				Eventually(session).Should(Say(""))
   282  				Consistently(session).ShouldNot(Say("Stopping app %s", appName))
   283  				Eventually(session).Should(Say("Staging package for app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   284  				Eventually(session).Should(Say("OK"))
   285  				Eventually(session).Should(Say("Setting app %s to droplet .+ in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   286  				Eventually(session).Should(Say("OK"))
   287  				Eventually(session).Should(Say(""))
   288  				Eventually(session).Should(Say("Mapping routes\\.\\.\\."))
   289  				Eventually(session).Should(Say("OK"))
   290  				Eventually(session).Should(Say(""))
   291  				Eventually(session).Should(Say("Starting app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   292  				Eventually(session).Should(Say("OK"))
   293  				Eventually(session).Should(Say(""))
   294  				Eventually(session).Should(Say("Waiting for app to start\\.\\.\\."))
   295  				Eventually(session).Should(Say("Showing health and status for app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   296  				Eventually(session).Should(Say(""))
   297  				Eventually(session).Should(Say("name:\\s+%s", appName))
   298  				Eventually(session).Should(Say("requested state:\\s+started"))
   299  				Eventually(session).Should(Say("processes:\\s+web:1/1"))
   300  				Eventually(session).Should(Say("memory usage:\\s+\\d+M x 1"))
   301  				Eventually(session).Should(Say("routes:\\s+%s\\.%s", appName, domainName))
   302  				Eventually(session).Should(Say("stack:\\s+cflinuxfs2"))
   303  				Eventually(session).Should(Say("buildpacks:\\s+staticfile"))
   304  				Eventually(session).Should(Say(""))
   305  				Eventually(session).Should(Say("web:1/1"))
   306  				Eventually(session).Should(Say(`state\s+since\s+cpu\s+memory\s+disk`))
   307  				Eventually(session).Should(Say("#0\\s+running\\s+\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} [AP]M"))
   308  			})
   309  		})
   310  
   311  		Context("when the app crashes", func() {
   312  			var session *Session
   313  
   314  			BeforeEach(func() {
   315  				helpers.WithCrashingApp(func(appDir string) {
   316  					session = helpers.CustomCF(helpers.CFEnv{WorkingDirectory: appDir}, "v3-push", appName)
   317  					Eventually(session).Should(Exit(0))
   318  				})
   319  			})
   320  
   321  			It("pushes the app", func() {
   322  				Eventually(session).Should(Say("Creating app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   323  				Eventually(session).Should(Say("OK"))
   324  				Eventually(session).Should(Say(""))
   325  				Eventually(session).Should(Say("Uploading and creating bits package for app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   326  				Eventually(session).Should(Say("OK"))
   327  				Eventually(session).Should(Say(""))
   328  				Consistently(session).ShouldNot(Say("Stopping app %s", appName))
   329  				Eventually(session).Should(Say("Staging package for app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   330  				Eventually(session).Should(Say("OK"))
   331  				Eventually(session).Should(Say("Setting app %s to droplet .+ in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   332  				Eventually(session).Should(Say("OK"))
   333  				Eventually(session).Should(Say(""))
   334  				Eventually(session).Should(Say("Mapping routes\\.\\.\\."))
   335  				Eventually(session).Should(Say("OK"))
   336  				Eventually(session).Should(Say(""))
   337  				Eventually(session).Should(Say("Starting app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   338  				Eventually(session).Should(Say("OK"))
   339  				Eventually(session).Should(Say(""))
   340  				Eventually(session).Should(Say("Waiting for app to start\\.\\.\\."))
   341  				Eventually(session).Should(Say("Showing health and status for app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   342  				Eventually(session).Should(Say(""))
   343  				Eventually(session).Should(Say("name:\\s+%s", appName))
   344  				Eventually(session).Should(Say("requested state:\\s+started"))
   345  				Eventually(session).Should(Say("processes:\\s+web:0/1"))
   346  				Eventually(session).Should(Say("memory usage:\\s+\\d+M x 1"))
   347  				Eventually(session).Should(Say("routes:\\s+%s\\.%s", appName, domainName))
   348  				Eventually(session).Should(Say("stack:\\s+cflinuxfs2"))
   349  				Eventually(session).Should(Say("buildpacks:\\s+ruby"))
   350  				Eventually(session).Should(Say(""))
   351  				Eventually(session).Should(Say("web:0/1"))
   352  				Eventually(session).Should(Say(`state\s+since\s+cpu\s+memory\s+disk`))
   353  				Eventually(session).Should(Say("#0\\s+crashed\\s+\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} [AP]M"))
   354  			})
   355  		})
   356  
   357  		Context("when the -p flag is provided", func() {
   358  			Context("when the path is a directory", func() {
   359  				Context("when the directory contains files", func() {
   360  					It("pushes the app from the directory", func() {
   361  						helpers.WithHelloWorldApp(func(appDir string) {
   362  							session := helpers.CF("v3-push", appName, "-p", appDir)
   363  
   364  							Eventually(session).Should(Say("Starting app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   365  							Eventually(session).Should(Say("Waiting for app to start\\.\\.\\."))
   366  							Eventually(session).Should(Say("Showing health and status for app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   367  							Eventually(session).Should(Say(""))
   368  							Eventually(session).Should(Say("name:\\s+%s", appName))
   369  							Eventually(session).Should(Say("requested state:\\s+started"))
   370  							Eventually(session).Should(Say("processes:\\s+web:1/1"))
   371  							Eventually(session).Should(Say("memory usage:\\s+\\d+M x 1"))
   372  							Eventually(session).Should(Say("routes:\\s+%s\\.%s", appName, domainName))
   373  							Eventually(session).Should(Say("stack:\\s+cflinuxfs2"))
   374  							Eventually(session).Should(Say("buildpacks:\\s+staticfile"))
   375  							Eventually(session).Should(Say(""))
   376  							Eventually(session).Should(Say("web:1/1"))
   377  							Eventually(session).Should(Say(`state\s+since\s+cpu\s+memory\s+disk`))
   378  							Eventually(session).Should(Say("#0\\s+running\\s+\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} [AP]M"))
   379  
   380  							Eventually(session).Should(Exit(0))
   381  						})
   382  					})
   383  				})
   384  
   385  				Context("when the directory is empty", func() {
   386  					var emptyDir string
   387  
   388  					BeforeEach(func() {
   389  						var err error
   390  						emptyDir, err = ioutil.TempDir("", "integration-push-path-empty")
   391  						Expect(err).ToNot(HaveOccurred())
   392  					})
   393  
   394  					AfterEach(func() {
   395  						Expect(os.RemoveAll(emptyDir)).ToNot(HaveOccurred())
   396  					})
   397  
   398  					It("returns an error", func() {
   399  						session := helpers.CF("v3-push", appName, "-p", emptyDir)
   400  						Eventually(session.Err).Should(Say("No app files found in '%s'", regexp.QuoteMeta(emptyDir)))
   401  						Eventually(session).Should(Exit(1))
   402  					})
   403  				})
   404  			})
   405  
   406  			Context("when the path is a zip file", func() {
   407  				Context("pushing a zip file", func() {
   408  					var archive string
   409  
   410  					BeforeEach(func() {
   411  						helpers.WithHelloWorldApp(func(appDir string) {
   412  							tmpfile, err := ioutil.TempFile("", "push-archive-integration")
   413  							Expect(err).ToNot(HaveOccurred())
   414  							archive = tmpfile.Name()
   415  							Expect(tmpfile.Close())
   416  
   417  							err = helpers.Zipit(appDir, archive, "")
   418  							Expect(err).ToNot(HaveOccurred())
   419  						})
   420  					})
   421  
   422  					AfterEach(func() {
   423  						Expect(os.RemoveAll(archive)).ToNot(HaveOccurred())
   424  					})
   425  
   426  					It("pushes the app from the zip file", func() {
   427  						session := helpers.CF("v3-push", appName, "-p", archive)
   428  
   429  						Eventually(session).Should(Say("Starting app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   430  						Eventually(session).Should(Say("Waiting for app to start\\.\\.\\."))
   431  						Eventually(session).Should(Say("Showing health and status for app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   432  						Eventually(session).Should(Say(""))
   433  						Eventually(session).Should(Say("name:\\s+%s", appName))
   434  						Eventually(session).Should(Say("requested state:\\s+started"))
   435  						Eventually(session).Should(Say("processes:\\s+web:1/1"))
   436  						Eventually(session).Should(Say("memory usage:\\s+\\d+M x 1"))
   437  						Eventually(session).Should(Say("routes:\\s+%s\\.%s", appName, domainName))
   438  						Eventually(session).Should(Say("stack:\\s+cflinuxfs2"))
   439  						Eventually(session).Should(Say("buildpacks:\\s+staticfile"))
   440  						Eventually(session).Should(Say(""))
   441  						Eventually(session).Should(Say("web:1/1"))
   442  						Eventually(session).Should(Say(`state\s+since\s+cpu\s+memory\s+disk`))
   443  						Eventually(session).Should(Say("#0\\s+running\\s+\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} [AP]M"))
   444  
   445  						Eventually(session).Should(Exit(0))
   446  					})
   447  				})
   448  			})
   449  
   450  			Context("when the path is a symlink to a directory", func() {
   451  				var symlinkPath string
   452  
   453  				BeforeEach(func() {
   454  					tempFile, err := ioutil.TempFile("", "symlink-")
   455  					Expect(err).ToNot(HaveOccurred())
   456  					Expect(tempFile.Close()).To(Succeed())
   457  
   458  					symlinkPath = tempFile.Name()
   459  					Expect(os.Remove(symlinkPath)).To(Succeed())
   460  				})
   461  
   462  				AfterEach(func() {
   463  					Expect(os.Remove(symlinkPath)).To(Succeed())
   464  				})
   465  
   466  				It("creates and uploads the package from the directory", func() {
   467  					helpers.WithHelloWorldApp(func(appDir string) {
   468  						Expect(os.Symlink(appDir, symlinkPath)).To(Succeed())
   469  
   470  						session := helpers.CF("v3-push", appName, "-p", symlinkPath)
   471  
   472  						Eventually(session).Should(Say("Starting app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   473  						Eventually(session).Should(Say("Waiting for app to start\\.\\.\\."))
   474  						Eventually(session).Should(Say("Showing health and status for app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   475  						Eventually(session).Should(Say(""))
   476  						Eventually(session).Should(Say("name:\\s+%s", appName))
   477  						Eventually(session).Should(Say("requested state:\\s+started"))
   478  						Eventually(session).Should(Say("processes:\\s+web:1/1"))
   479  						Eventually(session).Should(Say("memory usage:\\s+\\d+M x 1"))
   480  						Eventually(session).Should(Say("routes:\\s+%s\\.%s", appName, domainName))
   481  						Eventually(session).Should(Say("stack:\\s+cflinuxfs2"))
   482  						Eventually(session).Should(Say("buildpacks:\\s+staticfile"))
   483  						Eventually(session).Should(Say(""))
   484  						Eventually(session).Should(Say("web:1/1"))
   485  						Eventually(session).Should(Say(`state\s+since\s+cpu\s+memory\s+disk`))
   486  						Eventually(session).Should(Say("#0\\s+running\\s+\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} [AP]M"))
   487  
   488  						Eventually(session).Should(Exit(0))
   489  					})
   490  				})
   491  			})
   492  
   493  			Context("when the path is a symlink to a zip file", func() {
   494  				var (
   495  					archive     string
   496  					symlinkPath string
   497  				)
   498  
   499  				BeforeEach(func() {
   500  					helpers.WithHelloWorldApp(func(appDir string) {
   501  						tmpfile, err := ioutil.TempFile("", "package-archive-integration")
   502  						Expect(err).ToNot(HaveOccurred())
   503  						archive = tmpfile.Name()
   504  						Expect(tmpfile.Close())
   505  
   506  						err = helpers.Zipit(appDir, archive, "")
   507  						Expect(err).ToNot(HaveOccurred())
   508  					})
   509  
   510  					tempFile, err := ioutil.TempFile("", "symlink-to-archive-")
   511  					Expect(err).ToNot(HaveOccurred())
   512  					Expect(tempFile.Close()).To(Succeed())
   513  
   514  					symlinkPath = tempFile.Name()
   515  					Expect(os.Remove(symlinkPath)).To(Succeed())
   516  					Expect(os.Symlink(archive, symlinkPath)).To(Succeed())
   517  				})
   518  
   519  				AfterEach(func() {
   520  					Expect(os.Remove(archive)).To(Succeed())
   521  					Expect(os.Remove(symlinkPath)).To(Succeed())
   522  				})
   523  
   524  				It("creates and uploads the package from the zip file", func() {
   525  					session := helpers.CF("v3-push", appName, "-p", symlinkPath)
   526  
   527  					Eventually(session).Should(Say("Starting app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   528  					Eventually(session).Should(Say("Waiting for app to start\\.\\.\\."))
   529  					Eventually(session).Should(Say("Showing health and status for app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName))
   530  					Eventually(session).Should(Say(""))
   531  					Eventually(session).Should(Say("name:\\s+%s", appName))
   532  					Eventually(session).Should(Say("requested state:\\s+started"))
   533  					Eventually(session).Should(Say("processes:\\s+web:1/1"))
   534  					Eventually(session).Should(Say("memory usage:\\s+\\d+M x 1"))
   535  					Eventually(session).Should(Say("routes:\\s+%s\\.%s", appName, domainName))
   536  					Eventually(session).Should(Say("stack:\\s+cflinuxfs2"))
   537  					Eventually(session).Should(Say("buildpacks:\\s+staticfile"))
   538  					Eventually(session).Should(Say(""))
   539  					Eventually(session).Should(Say("web:1/1"))
   540  					Eventually(session).Should(Say(`state\s+since\s+cpu\s+memory\s+disk`))
   541  					Eventually(session).Should(Say("#0\\s+running\\s+\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} [AP]M"))
   542  
   543  					Eventually(session).Should(Exit(0))
   544  				})
   545  			})
   546  		})
   547  
   548  		Context("when the --no-route flag is set", func() {
   549  			var session *Session
   550  
   551  			BeforeEach(func() {
   552  				helpers.WithHelloWorldApp(func(appDir string) {
   553  					session = helpers.CustomCF(helpers.CFEnv{WorkingDirectory: appDir}, "v3-push", appName, "--no-route")
   554  					Eventually(session).Should(Exit(0))
   555  				})
   556  			})
   557  
   558  			It("does not map any routes to the app", func() {
   559  				Consistently(session).ShouldNot(Say("Mapping routes\\.\\.\\."))
   560  				Eventually(session).Should(Say("name:\\s+%s", appName))
   561  				Eventually(session).Should(Say("requested state:\\s+started"))
   562  				Eventually(session).Should(Say("processes:\\s+web:1/1"))
   563  				Eventually(session).Should(Say("memory usage:\\s+\\d+M x 1"))
   564  				Eventually(session).Should(Say("routes:\\s+\n"))
   565  				Eventually(session).Should(Say("stack:\\s+cflinuxfs2"))
   566  				Eventually(session).Should(Say("buildpacks:\\s+staticfile"))
   567  				Eventually(session).Should(Say(""))
   568  				Eventually(session).Should(Say("web:1/1"))
   569  				Eventually(session).Should(Say(`state\s+since\s+cpu\s+memory\s+disk`))
   570  				Eventually(session).Should(Say("#0\\s+running\\s+\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} [AP]M"))
   571  			})
   572  		})
   573  
   574  		Context("when the -b flag is set", func() {
   575  			var session *Session
   576  
   577  			Context("when pushing a multi-buildpack app", func() {
   578  				BeforeEach(func() {
   579  					helpers.WithMultiBuildpackApp(func(appDir string) {
   580  						session = helpers.CustomCF(helpers.CFEnv{WorkingDirectory: appDir}, "v3-push", appName, "-b", "ruby_buildpack", "-b", "go_buildpack")
   581  
   582  						// TODO: uncomment this expectation once capi-release displays all buildpacks on droplet
   583  						// Story: https://www.pivotaltracker.com/story/show/150425459
   584  						// Eventually(session).Should(Say("buildpacks:.*ruby_buildpack, go_buildpack"))
   585  
   586  						Eventually(session).Should(Exit(0))
   587  					})
   588  				})
   589  
   590  				It("successfully compiles and runs the app", func() {
   591  					resp, err := http.Get(fmt.Sprintf("http://%s.%s", appName, helpers.DefaultSharedDomain()))
   592  					Expect(err).ToNot(HaveOccurred())
   593  					Expect(resp.StatusCode).To(Equal(http.StatusOK))
   594  				})
   595  			})
   596  
   597  			Context("when resetting the buildpack to default", func() {
   598  				BeforeEach(func() {
   599  					helpers.WithHelloWorldApp(func(appDir string) {
   600  						Eventually(helpers.CustomCF(helpers.CFEnv{WorkingDirectory: appDir}, "v3-push", appName, "-b", "java_buildpack")).Should(Exit(1))
   601  						session = helpers.CustomCF(helpers.CFEnv{WorkingDirectory: appDir}, "v3-push", appName, "-b", "default")
   602  						Eventually(session).Should(Exit(0))
   603  					})
   604  				})
   605  
   606  				It("successfully pushes the app", func() {
   607  					Eventually(session).Should(Say("name:\\s+%s", appName))
   608  					Eventually(session).Should(Say(`state\s+since\s+cpu\s+memory\s+disk`))
   609  					Eventually(session).Should(Say("#0\\s+running\\s+\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} [AP]M"))
   610  				})
   611  			})
   612  
   613  			Context("when omitting the buildpack", func() {
   614  				BeforeEach(func() {
   615  					helpers.WithHelloWorldApp(func(appDir string) {
   616  						Eventually(helpers.CustomCF(helpers.CFEnv{WorkingDirectory: appDir}, "v3-push", appName, "-b", "java_buildpack")).Should(Exit(1))
   617  						session = helpers.CustomCF(helpers.CFEnv{WorkingDirectory: appDir}, "v3-push", appName)
   618  						Eventually(session).Should(Exit(1))
   619  					})
   620  				})
   621  
   622  				It("continues using previously set buildpack", func() {
   623  					Eventually(session).Should(Say("FAILED"))
   624  				})
   625  			})
   626  
   627  			Context("when the buildpack is invalid", func() {
   628  				BeforeEach(func() {
   629  					helpers.WithHelloWorldApp(func(appDir string) {
   630  						session = helpers.CustomCF(helpers.CFEnv{WorkingDirectory: appDir}, "v3-push", appName, "-b", "wut")
   631  						Eventually(session).Should(Exit(1))
   632  					})
   633  				})
   634  
   635  				It("errors and does not push the app", func() {
   636  					Consistently(session).ShouldNot(Say("Creating app"))
   637  					Eventually(session).Should(Say("FAILED"))
   638  					Eventually(session.Err).Should(Say(`Buildpack "wut" must be an existing admin buildpack or a valid git URI`))
   639  				})
   640  			})
   641  
   642  			Context("when the buildpack is valid", func() {
   643  				BeforeEach(func() {
   644  					helpers.WithHelloWorldApp(func(appDir string) {
   645  						session = helpers.CustomCF(helpers.CFEnv{WorkingDirectory: appDir}, "v3-push", appName, "-b", "https://github.com/cloudfoundry/staticfile-buildpack")
   646  						Eventually(session).Should(Exit(0))
   647  					})
   648  				})
   649  
   650  				It("uses the specified buildpack", func() {
   651  					Eventually(session).Should(Say("name:\\s+%s", appName))
   652  					Eventually(session).Should(Say("requested state:\\s+started"))
   653  					Eventually(session).Should(Say("processes:\\s+web:1/1"))
   654  					Eventually(session).Should(Say("memory usage:\\s+\\d+M x 1"))
   655  					Eventually(session).Should(Say("routes:\\s+%s\\.%s", appName, domainName))
   656  					Eventually(session).Should(Say("stack:\\s+cflinuxfs2"))
   657  
   658  					// TODO: Uncomment when capi sorts out droplet buildpack name/detectoutput
   659  					// Eventually(session).Should(Say("buildpacks:\\s+https://github.com/cloudfoundry/staticfile-buildpack"))
   660  					Eventually(session).Should(Say(""))
   661  					Eventually(session).Should(Say("web:1/1"))
   662  					Eventually(session).Should(Say(`state\s+since\s+cpu\s+memory\s+disk`))
   663  					Eventually(session).Should(Say("#0\\s+running\\s+\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} [AP]M"))
   664  				})
   665  			})
   666  		})
   667  
   668  		Context("when the -o flag is set", func() {
   669  			Context("when the docker image is valid", func() {
   670  				It("uses the specified docker image", func() {
   671  					session := helpers.CF("v3-push", appName, "-o", PublicDockerImage)
   672  
   673  					Eventually(session).Should(Say("name:\\s+%s", appName))
   674  					Eventually(session).Should(Say("requested state:\\s+started"))
   675  					Eventually(session).Should(Say("processes:\\s+web:1/1"))
   676  					Eventually(session).Should(Say("memory usage:\\s+\\d+M x 1"))
   677  					Eventually(session).Should(Say("routes:\\s+%s\\.%s", appName, domainName))
   678  					Eventually(session).Should(Say("stack:"))
   679  					Eventually(session).ShouldNot(Say("buildpacks:"))
   680  					Eventually(session).Should(Say("docker image:\\s+%s", PublicDockerImage))
   681  					Eventually(session).Should(Say(""))
   682  					Eventually(session).Should(Say("web:1/1"))
   683  					Eventually(session).Should(Say(`state\s+since\s+cpu\s+memory\s+disk`))
   684  					Eventually(session).Should(Say("#0\\s+running\\s+\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} [AP]M"))
   685  					Eventually(session).Should(Exit(0))
   686  				})
   687  			})
   688  
   689  			Context("when the docker image is invalid", func() {
   690  				It("displays an error and exits 1", func() {
   691  					session := helpers.CF("v3-push", appName, "-o", "some-invalid-docker-image")
   692  					Eventually(session).Should(Say("FAILED"))
   693  					Eventually(session.Err).Should(Say("StagingError - Staging error: staging failed"))
   694  					Eventually(session).Should(Exit(1))
   695  				})
   696  			})
   697  
   698  			Context("when a docker username and password are provided with a private image", func() {
   699  				var (
   700  					privateDockerImage    string
   701  					privateDockerUsername string
   702  					privateDockerPassword string
   703  				)
   704  
   705  				BeforeEach(func() {
   706  					privateDockerImage = os.Getenv("CF_INT_DOCKER_IMAGE")
   707  					privateDockerUsername = os.Getenv("CF_INT_DOCKER_USERNAME")
   708  					privateDockerPassword = os.Getenv("CF_INT_DOCKER_PASSWORD")
   709  
   710  					if privateDockerImage == "" || privateDockerUsername == "" || privateDockerPassword == "" {
   711  						Skip("CF_INT_DOCKER_IMAGE, CF_INT_DOCKER_USERNAME, or CF_INT_DOCKER_PASSWORD is not set")
   712  					}
   713  				})
   714  
   715  				It("uses the specified private docker image", func() {
   716  					session := helpers.CustomCF(
   717  						helpers.CFEnv{
   718  							EnvVars: map[string]string{"CF_DOCKER_PASSWORD": privateDockerPassword},
   719  						},
   720  						"v3-push", "--docker-username", privateDockerUsername, "--docker-image", privateDockerImage, appName,
   721  					)
   722  
   723  					Eventually(session).Should(Say("name:\\s+%s", appName))
   724  					Eventually(session).Should(Say("requested state:\\s+started"))
   725  					Eventually(session).Should(Say("processes:\\s+web:1/1"))
   726  					Eventually(session).Should(Say("memory usage:\\s+\\d+M x 1"))
   727  					Eventually(session).Should(Say("routes:\\s+%s\\.%s", appName, domainName))
   728  					Eventually(session).Should(Say("stack:"))
   729  					Eventually(session).ShouldNot(Say("buildpacks:"))
   730  					Eventually(session).Should(Say("docker image:\\s+%s", privateDockerImage))
   731  					Eventually(session).Should(Say(""))
   732  					Eventually(session).Should(Say("web:1/1"))
   733  					Eventually(session).Should(Say(`state\s+since\s+cpu\s+memory\s+disk`))
   734  					Eventually(session).Should(Say("#0\\s+running\\s+\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} [AP]M"))
   735  					Eventually(session).Should(Exit(0))
   736  				})
   737  			})
   738  		})
   739  
   740  		Describe("argument combination errors", func() {
   741  			Context("when the --docker-username is provided without the -o flag", func() {
   742  				It("displays an error and exits 1", func() {
   743  					helpers.WithHelloWorldApp(func(appDir string) {
   744  						session := helpers.CF("v3-push", appName, "--docker-username", "some-username")
   745  						Eventually(session).Should(Say("FAILED"))
   746  						Eventually(session.Err).Should(Say("Incorrect Usage: '--docker-image, -o' and '--docker-username' must be used together."))
   747  						Eventually(session).Should(Say("NAME:"))
   748  						Eventually(session).Should(Exit(1))
   749  					})
   750  				})
   751  			})
   752  
   753  			Context("when the --docker-username and -p flags are provided together", func() {
   754  				It("displays an error and exits 1", func() {
   755  					helpers.WithHelloWorldApp(func(appDir string) {
   756  						session := helpers.CF("v3-push", appName, "--docker-username", "some-username", "-p", appDir)
   757  						Eventually(session).Should(Say("FAILED"))
   758  						Eventually(session.Err).Should(Say("Incorrect Usage: '--docker-image, -o' and '--docker-username' must be used together."))
   759  						Eventually(session).Should(Say("NAME:"))
   760  						Eventually(session).Should(Exit(1))
   761  					})
   762  				})
   763  			})
   764  
   765  			Context("when the --docker-username is provided without a password", func() {
   766  				var oldPassword string
   767  
   768  				BeforeEach(func() {
   769  					oldPassword = os.Getenv("CF_DOCKER_PASSWORD")
   770  					err := os.Unsetenv("CF_DOCKER_PASSWORD")
   771  					Expect(err).ToNot(HaveOccurred())
   772  				})
   773  
   774  				AfterEach(func() {
   775  					err := os.Setenv("CF_DOCKER_PASSWORD", oldPassword)
   776  					Expect(err).ToNot(HaveOccurred())
   777  				})
   778  
   779  				It("displays an error and exits 1", func() {
   780  					helpers.WithHelloWorldApp(func(appDir string) {
   781  						session := helpers.CF("v3-push", appName, "--docker-username", "some-username", "--docker-image", "some-image")
   782  						Eventually(session).Should(Say("FAILED"))
   783  						Eventually(session.Err).Should(Say("Environment variable CF_DOCKER_PASSWORD not set\\."))
   784  						Eventually(session).Should(Exit(1))
   785  					})
   786  				})
   787  			})
   788  
   789  			Context("when the -o and -p flags are provided together", func() {
   790  				It("displays an error and exits 1", func() {
   791  					helpers.WithHelloWorldApp(func(appDir string) {
   792  						session := helpers.CF("v3-push", appName, "-o", PublicDockerImage, "-p", appDir)
   793  						Eventually(session).Should(Say("FAILED"))
   794  						Eventually(session.Err).Should(Say("Incorrect Usage: The following arguments cannot be used together: --docker-image, -o, -p"))
   795  						Eventually(session).Should(Say("NAME:"))
   796  						Eventually(session).Should(Exit(1))
   797  					})
   798  				})
   799  			})
   800  
   801  			Context("when the -o and -b flags are provided together", func() {
   802  				It("displays an error and exits 1", func() {
   803  					helpers.WithHelloWorldApp(func(appDir string) {
   804  						session := helpers.CF("v3-push", appName, "-o", PublicDockerImage, "-b", "some-buildpack")
   805  						Eventually(session).Should(Say("FAILED"))
   806  						Eventually(session.Err).Should(Say("Incorrect Usage: The following arguments cannot be used together: -b, --docker-image, -o"))
   807  						Eventually(session).Should(Say("NAME:"))
   808  						Eventually(session).Should(Exit(1))
   809  					})
   810  				})
   811  			})
   812  		})
   813  	})
   814  })