github.com/wanddynosios/cli@v7.1.0+incompatible/integration/v6/experimental/v3_zdt_push_command_test.go (about)

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