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