BeforeEach(func() {
			commandFactory := command_factory.NewConfigCommandFactory(config, terminalUI, fakeTargetVerifier, fakeBlobStoreVerifier, fakeExitHandler)
			targetCommand = commandFactory.MakeTargetCommand()

			config.SetTarget("oldtarget.com")
			config.SetLogin("olduser", "oldpass")
			Expect(config.Save()).To(Succeed())
		})

		Context("displaying the target", func() {
			JustBeforeEach(func() {
				test_helpers.ExecuteCommandWithArgs(targetCommand, []string{})
			})

			It("outputs the current user and target host", func() {
				Expect(outputBuffer).To(test_helpers.SayLine("Target:\t\[email protected]"))
			})

			Context("when no username is set", func() {
				BeforeEach(func() {
					config.SetLogin("", "")
					Expect(config.Save()).To(Succeed())
				})

				It("only prints the target", func() {
					Expect(outputBuffer).To(test_helpers.SayLine("Target:\t\toldtarget.com"))
				})
			})

			Context("when no target is set", func() {
				BeforeEach(func() {
		fakeExitHandler = &fake_exit_handler.FakeExitHandler{}
		fakeSecureShell = &fake_secure_shell.FakeSecureShell{}
	})

	Describe("SSHCommand", func() {
		var sshCommand cli.Command

		BeforeEach(func() {
			commandFactory := command_factory.NewSSHCommandFactory(config, terminalUI, fakeExitHandler, fakeSecureShell)
			sshCommand = commandFactory.MakeSSHCommand()
		})

		It("should ssh to instance 0 given an app name", func() {
			test_helpers.ExecuteCommandWithArgs(sshCommand, []string{"app-name"})

			Expect(outputBuffer).To(test_helpers.SayLine("Connecting to app-name/0 at %s", config.Target()))

			Expect(fakeSecureShell.ConnectToShellCallCount()).To(Equal(1))
			appName, instanceIndex, command, actualConfig := fakeSecureShell.ConnectToShellArgsForCall(0)
			Expect(appName).To(Equal("app-name"))
			Expect(instanceIndex).To(Equal(0))
			Expect(command).To(BeEmpty())
			Expect(actualConfig).To(Equal(config))
		})

		It("should ssh to instance index specified", func() {
			test_helpers.ExecuteCommandWithArgs(sshCommand, []string{"--instance", "2", "app-name"})

			Expect(outputBuffer).To(test_helpers.SayLine("Connecting to app-name/2 at %s", config.Target()))

			Expect(fakeSecureShell.ConnectToShellCallCount()).To(Equal(1))
			Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("No compatible container")))

			Expect(outputBuffer).To(test_helpers.Say(colors.Bold("task-guid-3")))
			Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("N/A")))
			Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("COMPLETED")))
			Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("N/A")))
			Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("N/A")))
		})

		It("alerts the user if there are no apps or tasks", func() {
			fakeAppExaminer.ListAppsReturns([]app_examiner.AppInfo{}, nil)
			fakeTaskExaminer.ListTasksReturns([]task_examiner.TaskInfo{}, nil)

			test_helpers.ExecuteCommandWithArgs(listAppsCommand, []string{})

			Expect(outputBuffer).To(test_helpers.SayLine("No apps to display."))
			Expect(outputBuffer).To(test_helpers.Say("No tasks to display."))
		})

		Context("when the app examiner returns an error", func() {
			It("alerts the user fetching the app list returns an error", func() {
				fakeAppExaminer.ListAppsReturns(nil, errors.New("The list was lost"))
				listTasks := []task_examiner.TaskInfo{
					task_examiner.TaskInfo{
						TaskGuid:      "task-guid-1",
						CellID:        "cell-01",
						Failed:        false,
						FailureReason: "",
						Result:        "Finished",
						State:         "COMPLETED",
					},
				fakeClock.IncrementBySeconds(1)
				Expect(commandFinishChan).ShouldNot(BeClosed())
				Expect(fakeTailedLogsOutputter.StopOutputtingCallCount()).To(Equal(0))

				fakeTaskExaminer.TaskStatusReturns(task_examiner.TaskInfo{State: "RUNNING"}, nil)

				fakeClock.IncrementBySeconds(1)
				Expect(commandFinishChan).ShouldNot(BeClosed())
				Expect(fakeTailedLogsOutputter.StopOutputtingCallCount()).To(Equal(0))

				fakeTaskExaminer.TaskStatusReturns(task_examiner.TaskInfo{State: "COMPLETED"}, nil)

				fakeClock.IncrementBySeconds(1)
				Eventually(commandFinishChan).Should(BeClosed())

				Expect(outputBuffer).To(test_helpers.SayLine("Build completed"))
				Expect(fakeTailedLogsOutputter.StopOutputtingCallCount()).To(Equal(1))
			})

			Context("when the build doesn't complete before the timeout elapses", func() {
				It("alerts the user the build took too long", func() {
					args := []string{
						"droppo-the-clown",
						"http://some.url/for/buildpack",
					}

					fakeTaskExaminer.TaskStatusReturns(task_examiner.TaskInfo{State: "RUNNING"}, nil)

					commandFinishChan := test_helpers.AsyncExecuteCommandWithArgs(buildDropletCommand, args)

					Eventually(outputBuffer).Should(test_helpers.Say("Submitted build of droppo-the-clown"))
Пример #5
0
			Expect(gbytesBuffer).To(test_helpers.Say(`match this \|?-^$.(){}`))
		})

		It("negated match", func() {
			gbytesBuffer.Write([]byte("say that"))

			Expect(gbytesBuffer).ToNot(test_helpers.Say("different"))
		})
	})

	Describe("SayLine", func() {
		It("matches with regex-escaped characters", func() {
			gbytesBuffer.Write([]byte("sample\n"))

			Expect(gbytesBuffer).To(test_helpers.SayLine("sample"))
		})

		It("negated match", func() {
			gbytesBuffer.Write([]byte("no match"))

			Expect(gbytesBuffer).ToNot(test_helpers.SayLine("no match"))
		})
	})

	Describe("SayIncorrectUsage", func() {
		It("matches", func() {
			gbytesBuffer.Write([]byte("Incorrect Usage"))

			Expect(gbytesBuffer).To(test_helpers.SayIncorrectUsage())
		})
			BeforeEach(func() {
				var err error
				tmpFile, err = ioutil.TempFile("", "tmp_json")
				Expect(err).ToNot(HaveOccurred())

				jsonContents = []byte(`{"Value":"test value"}`)
				Expect(ioutil.WriteFile(tmpFile.Name(), jsonContents, 0700)).To(Succeed())
			})

			It("submits a task from json", func() {
				fakeTaskRunner.SubmitTaskReturns("some-task", nil)

				args := []string{tmpFile.Name()}
				test_helpers.ExecuteCommandWithArgs(submitTaskCommand, args)

				Expect(outputBuffer).To(test_helpers.SayLine(colors.Green("Successfully submitted some-task")))
				Expect(fakeTaskRunner.SubmitTaskCallCount()).To(Equal(1))
				Expect(fakeTaskRunner.SubmitTaskArgsForCall(0)).To(Equal(jsonContents))
			})

			It("prints an error returned by the task_runner", func() {
				fakeTaskRunner.SubmitTaskReturns("some-task", errors.New("taskypoo"))

				args := []string{tmpFile.Name()}
				test_helpers.ExecuteCommandWithArgs(submitTaskCommand, args)

				Expect(fakeTaskRunner.SubmitTaskCallCount()).To(Equal(1))
				Expect(fakeTaskRunner.SubmitTaskArgsForCall(0)).To(Equal(jsonContents))

				Expect(outputBuffer).To(test_helpers.SayLine("Error submitting some-task: taskypoo"))
				Expect(fakeExitHandler.ExitCalledWith).To(Equal([]int{exit_codes.CommandFailed}))
				fakeClock.IncrementBySeconds(1)
				Expect(doneChan).ShouldNot(BeClosed())
				Expect(fakeTailedLogsOutputter.StopOutputtingCallCount()).To(Equal(0))

				fakeTaskExaminer.TaskStatusReturns(task_examiner.TaskInfo{State: "RUNNING"}, nil)

				fakeClock.IncrementBySeconds(1)
				Expect(doneChan).ShouldNot(BeClosed())
				Expect(fakeTailedLogsOutputter.StopOutputtingCallCount()).To(Equal(0))

				fakeTaskExaminer.TaskStatusReturns(task_examiner.TaskInfo{State: "COMPLETED"}, nil)

				fakeClock.IncrementBySeconds(1)
				Eventually(doneChan).Should(BeClosed())

				Expect(outputBuffer).To(test_helpers.SayLine("Build completed"))
				Expect(fakeTailedLogsOutputter.StopOutputtingCallCount()).To(Equal(1))
			})

			Context("when the build doesn't complete before the timeout elapses", func() {
				It("alerts the user the build took too long", func() {
					args := []string{
						"droppo-the-clown",
						"http://some.url/for/buildpack",
						"-t",
						"17s",
					}

					fakeTaskExaminer.TaskStatusReturns(task_examiner.TaskInfo{State: "RUNNING"}, nil)

					doneChan := test_helpers.AsyncExecuteCommandWithArgs(buildDropletCommand, args)
					"cool-web-app",
					"22",
				}

				doneChan := test_helpers.AsyncExecuteCommandWithArgs(scaleCommand, args)

				Eventually(outputBuffer).Should(test_helpers.Say("Scaling cool-web-app to 22 instances"))
				Eventually(outputBuffer).Should(test_helpers.SayNewLine())

				fakeClock.IncrementBySeconds(120)

				Eventually(doneChan).Should(BeClosed())

				Expect(outputBuffer).To(test_helpers.Say(colors.Red("Timed out waiting for the container to scale.")))
				Expect(outputBuffer).To(test_helpers.SayNewLine())
				Expect(outputBuffer).To(test_helpers.SayLine("Lattice is still scaling your application in the background."))
				Expect(outputBuffer).To(test_helpers.SayLine("To view logs:\n\tltc logs cool-web-app"))
				Expect(outputBuffer).To(test_helpers.SayLine("To view status:\n\tltc status cool-web-app"))
				Expect(outputBuffer).To(test_helpers.SayNewLine())
			})
		})

		Context("when the receptor returns errors", func() {
			It("outputs error messages", func() {
				fakeAppRunner.ScaleAppReturns(errors.New("Major Fault"))
				args := []string{
					"cool-web-app",
					"22",
				}

				test_helpers.ExecuteCommandWithArgs(scaleCommand, args)
		It("negated match", func() {
			Expect(outputBuffer).NotTo(test_helpers.Say("match that"))
		})
		Context("when format string is passed with arguments", func() {
			It("matches with regex-escaped characters", func() {
				Expect(outputBuffer).To(test_helpers.Say(`match %s \|?-^$.(){}`, "this"))
			})
		})
	})

	Describe("SayLine", func() {
		BeforeEach(func() {
			outputBuffer.Write([]byte(`match this \|?-^$.(){}` + "\n"))
		})
		It("matches with regex-escaped characters", func() {
			Expect(outputBuffer).To(test_helpers.SayLine(`match this \|?-^$.(){}`))
		})
		It("negated match", func() {
			Expect(outputBuffer).NotTo(test_helpers.SayLine("match that"))
		})
		Context("when format string is passed with arguments", func() {
			It("matches with regex-escaped characters", func() {
				Expect(outputBuffer).To(test_helpers.SayLine(`match %s \|?-^$.(){}`, "this"))
			})
		})
	})

	Describe("SayIncorrectUsage", func() {
		It("matches", func() {
			outputBuffer.Write([]byte("Incorrect Usage"))
			Expect(outputBuffer).To(test_helpers.SayIncorrectUsage())
	})

	Describe("SSHCommand", func() {
		var sshCommand cli.Command

		BeforeEach(func() {
			commandFactory := command_factory.NewSSHCommandFactory(config, terminalUI, fakeExitHandler, fakeAppExaminer, fakeSecureShell)
			sshCommand = commandFactory.MakeSSHCommand()
		})

		It("should ssh to instance 0 given an app name", func() {
			fakeAppExaminer.AppStatusReturns(app_examiner.AppInfo{ActualRunningInstances: 1}, nil)

			test_helpers.ExecuteCommandWithArgs(sshCommand, []string{"app-name"})

			Expect(outputBuffer).To(test_helpers.SayLine("Connecting to app-name/0 at %s", config.Target()))

			Expect(fakeSecureShell.ConnectToShellCallCount()).To(Equal(1))
			appName, instanceIndex, command, actualConfig := fakeSecureShell.ConnectToShellArgsForCall(0)
			Expect(appName).To(Equal("app-name"))
			Expect(instanceIndex).To(Equal(0))
			Expect(command).To(BeEmpty())
			Expect(actualConfig).To(Equal(config))

			Expect(fakeAppExaminer.AppStatusCallCount()).To(Equal(1))
			Expect(fakeAppExaminer.AppStatusArgsForCall(0)).To(Equal("app-name"))
		})

		It("should ssh to instance index specified", func() {
			fakeAppExaminer.AppStatusReturns(app_examiner.AppInfo{ActualRunningInstances: 3}, nil)
			BeforeEach(func() {
				tmpDir = os.TempDir()
				var err error
				tmpFile, err = ioutil.TempFile(tmpDir, "tmp_json")
				Expect(err).NotTo(HaveOccurred())

				Expect(ioutil.WriteFile(tmpFile.Name(), []byte(`{"Value":"test value"}`), 0700)).To(Succeed())
			})

			It("creates an app from json", func() {
				fakeAppRunner.SubmitLrpReturns("my-json-app", nil)

				args := []string{tmpFile.Name()}
				test_helpers.ExecuteCommandWithArgs(submitLrpCommand, args)

				Expect(outputBuffer).To(test_helpers.SayLine(colors.Green("Successfully submitted my-json-app.")))
				Expect(outputBuffer).To(test_helpers.SayLine("To view the status of your application: ltc status my-json-app"))
				Expect(fakeAppRunner.SubmitLrpCallCount()).To(Equal(1))
				Expect(fakeAppRunner.SubmitLrpArgsForCall(0)).To(Equal([]byte(`{"Value":"test value"}`)))
			})

			It("prints an error returned by the app_runner", func() {
				fakeAppRunner.SubmitLrpReturns("app-that-broke", errors.New("some error"))
				args := []string{tmpFile.Name()}
				test_helpers.ExecuteCommandWithArgs(submitLrpCommand, args)

				Expect(outputBuffer).To(test_helpers.SayLine("Error creating app-that-broke: some error"))
				Expect(fakeAppRunner.SubmitLrpCallCount()).To(Equal(1))
				Expect(fakeExitHandler.ExitCalledWith).To(Equal([]int{exit_codes.CommandFailed}))
			})
		})
			Expect(createAppParams.RouteOverrides).To(ContainExactly(app_runner.RouteOverrides{
				app_runner.RouteOverride{HostnamePrefix: "route-3000-yay", Port: 3000},
				app_runner.RouteOverride{HostnamePrefix: "route-1111-wahoo", Port: 1111},
				app_runner.RouteOverride{HostnamePrefix: "route-1111-me-too", Port: 1111},
			}))
			Expect(createAppParams.NoRoutes).To(BeFalse())
			Expect(createAppParams.WorkingDir).To(Equal("/applications"))

			Expect(createAppParams.Setup).To(BeAssignableToTypeOf(&models.DownloadAction{}))
			reqSetup, ok := createAppParams.Setup.(*models.DownloadAction)
			Expect(ok).To(BeTrue())
			Expect(reqSetup.From).To(Equal("http://file_server.service.dc1.consul:8080/v1/static/healthcheck.tgz"))
			Expect(reqSetup.To).To(Equal("/tmp"))
			Expect(reqSetup.User).To(Equal("vcap"))

			Expect(outputBuffer).To(test_helpers.SayLine("Creating App: cool-web-app"))
			Expect(outputBuffer).To(test_helpers.SayLine(colors.Green("cool-web-app is now running.")))
			Expect(outputBuffer).To(test_helpers.SayLine("App is reachable at:"))
			Expect(outputBuffer).To(test_helpers.SayLine(colors.Green("http://route-3000-yay.192.168.11.11.xip.io")))
			Expect(outputBuffer).To(test_helpers.SayLine(colors.Green("http://route-1111-wahoo.192.168.11.11.xip.io")))
			Expect(outputBuffer).To(test_helpers.SayLine(colors.Green("http://route-1111-me-too.192.168.11.11.xip.io")))
		})

		Context("when the PROCESS_GUID is passed in as --env", func() {
			It("sets the PROCESS_GUID to the value passed in", func() {
				fakeDockerMetadataFetcher.FetchMetadataReturns(&docker_metadata_fetcher.ImageMetadata{StartCommand: []string{""}}, nil)
				fakeAppExaminer.RunningAppInstancesInfoReturns(1, false, nil)
				args := []string{
					"app-to-start",
					"fun-org/app",
					"--env=PROCESS_GUID=MyHappyGuid",
		BeforeEach(func() {
			commandFactory := command_factory.NewAppExaminerCommandFactory(fakeAppExaminer, terminalUI, fakeClock, fakeExitHandler, fakeGraphicalVisualizer, fakeTaskExaminer)
			visualizeCommand = commandFactory.MakeVisualizeCommand()
		})

		It("displays a visualization of cells", func() {
			listCells := []app_examiner.CellInfo{
				app_examiner.CellInfo{CellID: "cell-1", RunningInstances: 3, ClaimedInstances: 2},
				app_examiner.CellInfo{CellID: "cell-2", RunningInstances: 2, ClaimedInstances: 1},
				app_examiner.CellInfo{CellID: "cell-3", RunningInstances: 0, ClaimedInstances: 0},
			}
			fakeAppExaminer.ListCellsReturns(listCells, nil)

			test_helpers.ExecuteCommandWithArgs(visualizeCommand, []string{})

			Expect(outputBuffer).To(test_helpers.SayLine(colors.Bold("Distribution")))
			Expect(outputBuffer).To(test_helpers.SayLine("cell-1: " + colors.Green("•••") + colors.Yellow("••") + cursor.ClearToEndOfLine()))
			Expect(outputBuffer).To(test_helpers.SayLine("cell-2: " + colors.Green("••") + colors.Yellow("•") + cursor.ClearToEndOfLine()))
			Expect(outputBuffer).To(test_helpers.SayLine("cell-3: " + colors.Red("empty") + cursor.ClearToEndOfLine()))
		})

		Context("when the app examiner returns an error", func() {
			It("alerts the user fetching the cells returns an error", func() {
				fakeAppExaminer.ListCellsReturns(nil, errors.New("The list was lost"))

				test_helpers.ExecuteCommandWithArgs(visualizeCommand, []string{})

				Expect(outputBuffer).To(test_helpers.Say("Error visualizing: The list was lost"))

				// TODO: this should return non-zero, but it's shared with refresh view
				//   which should continue to retry in the event on an error and not exit
				test_helpers.ExecuteCommandWithArgs(targetBlobCommand, []string{})

				Expect(outputBuffer).To(test_helpers.Say("Blob Target:\t192.168.11.11:8980\n"))
				Expect(outputBuffer).To(test_helpers.Say("Access Key:\tdatkeyyo"))
				Expect(outputBuffer).To(test_helpers.Say("Secret Key:\tsupersecretJKJK"))
				Expect(outputBuffer).To(test_helpers.Say("Bucket Name:\tbucket"))
			})

			It("alerts the user if no target is set", func() {
				config.SetBlobTarget("", 0, "", "", "")
				config.Save()

				test_helpers.ExecuteCommandWithArgs(targetBlobCommand, []string{})

				Expect(outputBuffer).To(test_helpers.SayLine("Blob target not set"))
			})
		})

		Context("setting the blob target", func() {
			It("sets the blob target and credentials", func() {
				fakeTargetVerifier.VerifyBlobTargetReturns(true, nil)

				commandFinishChan := test_helpers.AsyncExecuteCommandWithArgs(targetBlobCommand, []string{"192.168.11.11:8980"})

				Eventually(outputBuffer).Should(test_helpers.Say("Access Key: "))
				stdinWriter.Write([]byte("yaykey\n"))
				Eventually(outputBuffer).Should(test_helpers.Say("Secret Key: "))
				stdinWriter.Write([]byte("superserial\n"))
				Eventually(outputBuffer).Should(test_helpers.Say("Bucket Name [condenser-bucket]: "))
				stdinWriter.Write([]byte("bhuket\n"))
				Expect(cliApp).NotTo(BeNil())
				Expect(cliApp.Version).To(Equal("development (not versioned) (diego unknown)"))
			})
		})

		Describe("App.Action", func() {
			Context("when ltc is run without argument(s)", func() {
				It("prints app help", func() {
					cli.AppHelpTemplate = "HELP_TEMPLATE"
					flagSet := flag.NewFlagSet("flag_set", flag.ContinueOnError)
					flagSet.Parse([]string{})
					testContext := cli.NewContext(cliApp, flagSet, nil)

					cliApp.Action(testContext)

					Expect(outputBuffer).To(test_helpers.SayLine("ltc - Command line interface for Lattice."))
				})
			})

			Context("when ltc is run with argument(s)", func() {
				It("prints unknown command message", func() {
					flagSet := flag.NewFlagSet("flag_set", flag.ContinueOnError)
					flagSet.Parse([]string{"one_arg"})
					testContext := cli.NewContext(cliApp, flagSet, nil)

					cliApp.Action(testContext)

					Expect(outputBuffer).To(test_helpers.SayLine("ltc: 'one_arg' is not a registered command. See 'ltc help'"))
				})
			})
		})
		})

		Context("when the archive path is a folder and exists", func() {
			BeforeEach(func() {
				fakeCFIgnore.ShouldIgnoreStub = func(path string) bool {
					return path == "some-ignored-file"
				}
			})

			It("zips up current working folder and uploads as the droplet name", func() {
				fakeZipper.IsZipFileReturns(false)
				fakeZipper.ZipReturns("xyz.zip", nil)

				test_helpers.ExecuteCommandWithArgs(buildDropletCommand, []string{"droplet-name", "http://some.url/for/buildpack"})

				Expect(outputBuffer).To(test_helpers.SayLine("Uploading application bits..."))
				Expect(outputBuffer).To(test_helpers.SayLine("Uploaded."))

				Expect(fakeBlobStoreVerifier.VerifyCallCount()).To(Equal(1))
				Expect(fakeBlobStoreVerifier.VerifyArgsForCall(0)).To(Equal(config))

				Expect(outputBuffer).To(test_helpers.SayLine("Submitted build of droplet-name"))
				Expect(fakeDropletRunner.UploadBitsCallCount()).To(Equal(1))
				dropletName, uploadPath := fakeDropletRunner.UploadBitsArgsForCall(0)
				Expect(dropletName).To(Equal("droplet-name"))

				Expect(uploadPath).NotTo(BeNil())
				Expect(uploadPath).To(Equal("xyz.zip"))

				Expect(fakeZipper.ZipCallCount()).To(Equal(1))
				_, cfIgnore := fakeZipper.ZipArgsForCall(0)
						"--",
						"/start-me-please",
					}

					doneChan := test_helpers.AsyncExecuteCommandWithArgs(createCommand, args)

					Eventually(outputBuffer).Should(test_helpers.Say("Creating App: cool-web-app"))
					Expect(outputBuffer).To(test_helpers.SayNewLine())

					fakeClock.IncrementBySeconds(120)

					Eventually(doneChan).Should(BeClosed())

					Expect(outputBuffer).To(test_helpers.Say(colors.Red("Timed out waiting for the container to come up.")))
					Expect(outputBuffer).To(test_helpers.SayNewLine())
					Expect(outputBuffer).To(test_helpers.SayLine("This typically happens because docker layers can take time to download."))
					Expect(outputBuffer).To(test_helpers.SayLine("Lattice is still downloading your application in the background."))
					Expect(outputBuffer).To(test_helpers.SayLine("To view logs:\n\tltc logs cool-web-app"))
					Expect(outputBuffer).To(test_helpers.SayLine("To view status:\n\tltc status cool-web-app"))
					Expect(outputBuffer).To(test_helpers.Say("App will be reachable at:\n"))
					Expect(outputBuffer).To(test_helpers.Say(colors.Green("http://cool-web-app.192.168.11.11.xip.io\n")))
				})
			})

			Context("when there is a placement error when polling for the app to start", func() {
				It("prints an error message and exits", func() {
					fakeDockerMetadataFetcher.FetchMetadataReturns(&docker_metadata_fetcher.ImageMetadata{}, nil)
					fakeAppExaminer.RunningAppInstancesInfoReturns(0, false, nil)
					args := []string{
						"--instances=10",
						"--ports=3000",
			Expect(createAppParams.RouteOverrides).To(ContainExactly(app_runner.RouteOverrides{
				{HostnamePrefix: "route-3000-yay", Port: 3000},
				{HostnamePrefix: "route-1111-wahoo", Port: 1111},
				{HostnamePrefix: "route-1111-me-too", Port: 1111},
			}))
			Expect(createAppParams.NoRoutes).To(BeFalse())
			Expect(createAppParams.WorkingDir).To(Equal("/applications"))

			Expect(createAppParams.Setup).To(BeAssignableToTypeOf(&models.DownloadAction{}))
			reqSetup, ok := createAppParams.Setup.(*models.DownloadAction)
			Expect(ok).To(BeTrue())
			Expect(reqSetup.From).To(Equal("http://file_server.service.dc1.consul:8080/v1/static/healthcheck.tgz"))
			Expect(reqSetup.To).To(Equal("/tmp"))
			Expect(reqSetup.User).To(Equal("vcap"))

			Expect(outputBuffer).To(test_helpers.SayLine("Creating App: cool-web-app"))
			Expect(outputBuffer).To(test_helpers.SayLine(colors.Green("cool-web-app is now running.")))
			Expect(outputBuffer).To(test_helpers.SayLine("App is reachable at:"))
			Expect(outputBuffer).To(test_helpers.SayLine(colors.Green("http://route-3000-yay.192.168.11.11.xip.io")))
			Expect(outputBuffer).To(test_helpers.SayLine(colors.Green("http://route-1111-wahoo.192.168.11.11.xip.io")))
			Expect(outputBuffer).To(test_helpers.SayLine(colors.Green("http://route-1111-me-too.192.168.11.11.xip.io")))
		})

		Context("when the PROCESS_GUID is passed in as --env", func() {
			It("sets the PROCESS_GUID to the value passed in", func() {
				fakeDockerMetadataFetcher.FetchMetadataReturns(&docker_metadata_fetcher.ImageMetadata{StartCommand: []string{""}}, nil)
				fakeAppExaminer.RunningAppInstancesInfoReturns(1, false, nil)

				args := []string{
					"app-to-start",
					"fun-org/app",
	Describe("SSHCommand", func() {
		var sshCommand cli.Command

		BeforeEach(func() {
			commandFactory := command_factory.NewSSHCommandFactory(config, terminalUI, fakeExitHandler, fakeAppExaminer, fakeSSH)
			sshCommand = commandFactory.MakeSSHCommand()
		})

		Context("when connecting fails", func() {
			It("should print an error", func() {
				fakeAppExaminer.AppStatusReturns(app_examiner.AppInfo{ActualRunningInstances: 1}, nil)
				fakeSSH.ConnectReturns(errors.New("connection failed"))

				test_helpers.ExecuteCommandWithArgs(sshCommand, []string{"good-name"})

				Expect(outputBuffer).To(test_helpers.SayLine("Error connecting to good-name/0: connection failed"))

				Expect(fakeSSH.ConnectCallCount()).To(Equal(1))
				Expect(fakeSSH.ForwardCallCount()).To(Equal(0))
				Expect(fakeSSH.ShellCallCount()).To(Equal(0))
				Expect(fakeExitHandler.ExitCalledWith).To(Equal([]int{exit_codes.CommandFailed}))
			})
		})

		Describe("port forwarding", func() {
			It("should forward a local port to a remote host and port", func() {
				fakeAppExaminer.AppStatusReturns(app_examiner.AppInfo{ActualRunningInstances: 1}, nil)

				test_helpers.ExecuteCommandWithArgs(sshCommand, []string{"app-name", "-N", "-L", "mrlocalhost:1234:remotehost:5678"})

				Expect(outputBuffer).To(test_helpers.SayLine("Forwarding mrlocalhost:1234 to remotehost:5678 via app-name/0 at %s", config.Target()))