func (factory *AppExaminerCommandFactory) printDistribution() int { defer factory.ui.Say(cursor.ClearToEndOfDisplay()) cells, err := factory.appExaminer.ListCells() if err != nil { factory.ui.Say("Error visualizing: " + err.Error()) factory.ui.Say(cursor.ClearToEndOfLine()) factory.ui.SayNewLine() return 1 } for _, cell := range cells { factory.ui.Say(cell.CellID) if cell.Missing { factory.ui.Say(colors.Red("[MISSING]")) } factory.ui.Say(": ") if cell.RunningInstances == 0 && cell.ClaimedInstances == 0 && !cell.Missing { factory.ui.Say(colors.Red("empty")) } else { factory.ui.Say(colors.Green(strings.Repeat("•", cell.RunningInstances))) factory.ui.Say(colors.Yellow(strings.Repeat("•", cell.ClaimedInstances))) } factory.ui.Say(cursor.ClearToEndOfLine()) factory.ui.SayNewLine() } return len(cells) }
func (factory *AppRunnerCommandFactory) pollUntilAllInstancesRunning(pollTimeout time.Duration, appName string, instances int, action pollingAction) bool { var placementErrorOccurred bool ok := factory.pollUntilSuccess(pollTimeout, func() bool { numberOfRunningInstances, placementError, _ := factory.AppExaminer.RunningAppInstancesInfo(appName) if placementError { factory.UI.SayLine(colors.Red("Error, could not place all instances: insufficient resources. Try requesting fewer instances or reducing the requested memory or disk capacity.")) placementErrorOccurred = true return true } return numberOfRunningInstances == instances }, true) if placementErrorOccurred { factory.ExitHandler.Exit(exit_codes.PlacementError) return false } if !ok { if action == pollingStart { factory.UI.SayLine(colors.Red("Timed out waiting for the container to come up.")) factory.UI.SayLine("This typically happens because docker layers can take time to download.") factory.UI.SayLine("Lattice is still downloading your application in the background.") } else { factory.UI.SayLine(colors.Red("Timed out waiting for the container to scale.")) factory.UI.SayLine("Lattice is still scaling your application in the background.") } factory.UI.SayLine(fmt.Sprintf("To view logs:\n\tltc logs %s", appName)) factory.UI.SayLine(fmt.Sprintf("To view status:\n\tltc status %s", appName)) factory.UI.SayNewLine() } return ok }
func colorInstances(appInfo app_examiner.AppInfo) string { instances := fmt.Sprintf("%d/%d", appInfo.ActualRunningInstances, appInfo.DesiredInstances) if appInfo.ActualRunningInstances == appInfo.DesiredInstances { return colors.Green(instances) } else if appInfo.ActualRunningInstances == 0 { return colors.Red(instances) } return colors.Yellow(instances) }
func (factory *DropletRunnerCommandFactory) waitForBuildTask(pollTimeout time.Duration, taskName string) (bool, task_examiner.TaskInfo) { var taskInfo task_examiner.TaskInfo ok := factory.pollUntilSuccess(pollTimeout, func() bool { var err error taskInfo, err = factory.taskExaminer.TaskStatus(taskName) if err != nil { factory.UI.SayLine(colors.Red("Error requesting task status: %s"), err) return true } return taskInfo.State != "RUNNING" && taskInfo.State != "PENDING" }) return ok, taskInfo }
func (factory *TaskRunnerCommandFactory) deleteTask(context *cli.Context) { taskGuid := context.Args().First() if taskGuid == "" { factory.ui.SayIncorrectUsage("Please input a valid TASK_GUID") factory.exitHandler.Exit(exit_codes.InvalidSyntax) return } if err := factory.taskRunner.DeleteTask(taskGuid); err != nil { factory.ui.SayLine(fmt.Sprintf(colors.Red("Error deleting %s: %s"), taskGuid, err.Error())) factory.exitHandler.Exit(exit_codes.CommandFailed) return } factory.ui.SayLine(colors.Green("OK")) }
func (factory *TaskExaminerCommandFactory) task(context *cli.Context) { taskName := context.Args().First() if taskName == "" { factory.ui.SayIncorrectUsage("Please input a valid TASK_GUID") factory.exitHandler.Exit(exit_codes.InvalidSyntax) return } taskInfo, err := factory.taskExaminer.TaskStatus(taskName) if err != nil { if err.Error() == task_examiner.TaskNotFoundErrorMessage { factory.ui.SayLine(colors.Red(fmt.Sprintf("No task '%s' was found", taskName))) factory.exitHandler.Exit(exit_codes.CommandFailed) return } factory.ui.SayLine(colors.Red("Error fetching task result: " + err.Error())) factory.exitHandler.Exit(exit_codes.CommandFailed) return } w := tabwriter.NewWriter(factory.ui, 9, 8, 1, '\t', 0) fmt.Fprintf(w, "%s\t%s\n", "Task GUID", taskInfo.TaskGuid) fmt.Fprintf(w, "%s\t%s\n", "Cell ID", taskInfo.CellID) if taskInfo.State == "PENDING" || taskInfo.State == "CLAIMED" || taskInfo.State == "RUNNING" { fmt.Fprintf(w, "%s\t%s\n", "Status", colors.Yellow(taskInfo.State)) } else if (taskInfo.State == "COMPLETED" || taskInfo.State == "RESOLVING") && !taskInfo.Failed { fmt.Fprintf(w, "%s\t%s\n", "Status", colors.Green(taskInfo.State)) fmt.Fprintf(w, "%s\t%s\n", "Result", taskInfo.Result) } else if taskInfo.Failed { fmt.Fprintf(w, "%s\t%s\n", "Status", colors.Red(taskInfo.State)) fmt.Fprintf(w, "%s\t%s\n", "Failure Reason", taskInfo.FailureReason) } w.Flush() }
func (factory *DropletRunnerCommandFactory) buildDroplet(context *cli.Context) { pathFlag := context.String("path") cpuWeightFlag := context.Int("cpu-weight") memoryMBFlag := context.Int("memory-mb") diskMBFlag := context.Int("disk-mb") envFlag := context.StringSlice("env") timeoutFlag := context.Duration("timeout") dropletName := context.Args().First() buildpack := context.Args().Get(1) if dropletName == "" || buildpack == "" { factory.UI.SayIncorrectUsage("<droplet-name> and <buildpack-uri> are required") factory.ExitHandler.Exit(exit_codes.InvalidSyntax) return } var buildpackUrl string if knownBuildpackUrl, ok := knownBuildpacks[buildpack]; ok { buildpackUrl = knownBuildpackUrl } else if _, err := url.ParseRequestURI(buildpack); err == nil { buildpackUrl = buildpack } else { factory.UI.SayIncorrectUsage(fmt.Sprintf("invalid buildpack %s", buildpack)) factory.ExitHandler.Exit(exit_codes.InvalidSyntax) return } if cpuWeightFlag < 1 || cpuWeightFlag > 100 { factory.UI.SayIncorrectUsage("invalid CPU Weight") factory.ExitHandler.Exit(exit_codes.InvalidSyntax) return } if !factory.ensureBlobStoreVerified() { return } var archivePath string var err error if factory.zipper.IsZipFile(pathFlag) { tmpDir, err := ioutil.TempDir("", "rezip") if err != nil { factory.UI.SayLine(fmt.Sprintf("Error re-archiving %s: %s", pathFlag, err)) factory.ExitHandler.Exit(exit_codes.FileSystemError) return } defer os.RemoveAll(tmpDir) if err := factory.zipper.Unzip(pathFlag, tmpDir); err != nil { factory.UI.SayLine(fmt.Sprintf("Error unarchiving %s: %s", pathFlag, err)) factory.ExitHandler.Exit(exit_codes.FileSystemError) return } archivePath, err = factory.zipper.Zip(tmpDir, factory.cfIgnore) if err != nil { factory.UI.SayLine(fmt.Sprintf("Error re-archiving %s: %s", pathFlag, err)) factory.ExitHandler.Exit(exit_codes.FileSystemError) return } defer os.Remove(archivePath) } else { archivePath, err = factory.zipper.Zip(pathFlag, factory.cfIgnore) if err != nil { factory.UI.SayLine(fmt.Sprintf("Error archiving %s: %s", pathFlag, err)) factory.ExitHandler.Exit(exit_codes.FileSystemError) return } defer os.Remove(archivePath) } factory.UI.SayLine("Uploading application bits...") if err := factory.dropletRunner.UploadBits(dropletName, archivePath); err != nil { factory.UI.SayLine(fmt.Sprintf("Error uploading %s: %s", dropletName, err)) factory.ExitHandler.Exit(exit_codes.CommandFailed) return } factory.UI.SayLine("Uploaded.") environment := factory.AppRunnerCommandFactory.BuildEnvironment(envFlag) taskName := "build-droplet-" + dropletName if err := factory.dropletRunner.BuildDroplet(taskName, dropletName, buildpackUrl, environment, memoryMBFlag, cpuWeightFlag, diskMBFlag); err != nil { factory.UI.SayLine(fmt.Sprintf("Error submitting build of %s: %s", dropletName, err)) factory.ExitHandler.Exit(exit_codes.CommandFailed) return } factory.UI.SayLine("Submitted build of " + dropletName) go factory.TailedLogsOutputter.OutputTailedLogs(taskName) defer factory.TailedLogsOutputter.StopOutputting() ok, taskState := factory.waitForBuildTask(timeoutFlag, taskName) if ok { if taskState.Failed { factory.UI.SayLine("Build failed: " + taskState.FailureReason) factory.ExitHandler.Exit(exit_codes.CommandFailed) } else { factory.UI.SayLine("Build completed") } } else { factory.UI.SayLine(colors.Red("Timed out waiting for the build to complete.")) factory.UI.SayLine("Lattice is still building your application in the background.") factory.UI.SayLine(fmt.Sprintf("To view logs:\n\tltc logs %s", taskName)) factory.UI.SayLine(fmt.Sprintf("To view status:\n\tltc status %s", taskName)) factory.UI.SayNewLine() } }
test_helpers.ExecuteCommandWithArgs(deleteTaskCommand, []string{"task-guid-1"}) Expect(outputBuffer).To(test_helpers.SayLine(colors.Green("OK"))) }) It("returns error when fail to delete the task", func() { taskInfo := task_examiner.TaskInfo{ TaskGuid: "task-guid-1", State: "COMPLETED", } fakeTaskExaminer.TaskStatusReturns(taskInfo, nil) fakeTaskRunner.DeleteTaskReturns(errors.New("task in unknown state")) test_helpers.ExecuteCommandWithArgs(deleteTaskCommand, []string{"task-guid-1"}) Expect(outputBuffer).To(test_helpers.SayLine(colors.Red("Error deleting task-guid-1: " + "task in unknown state"))) Expect(fakeExitHandler.ExitCalledWith).To(Equal([]int{exit_codes.CommandFailed})) }) It("fails with usage", func() { test_helpers.ExecuteCommandWithArgs(deleteTaskCommand, []string{}) Expect(outputBuffer).To(test_helpers.SayLine("Please input a valid <task-guid>")) Expect(fakeExitHandler.ExitCalledWith).To(Equal([]int{exit_codes.InvalidSyntax})) }) }) Describe("CancelTaskCommand", func() { var cancelTaskCommand cli.Command BeforeEach(func() {
args := []string{ "cool-web-app", "superfun/app", "--", "/start-me-please", } doneChan := test_helpers.AsyncExecuteCommandWithArgs(createCommand, args) Eventually(outputBuffer).Should(test_helpers.SayLine("Creating App: cool-web-app")) fakeClock.IncrementBySeconds(120) Eventually(doneChan, 3).Should(BeClosed()) Expect(outputBuffer).To(test_helpers.SayLine(colors.Red("Timed out waiting for the container to come up."))) 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:")) Expect(outputBuffer).To(test_helpers.SayLine("ltc logs cool-web-app")) Expect(outputBuffer).To(test_helpers.SayLine("To view status:")) Expect(outputBuffer).To(test_helpers.SayLine("ltc status cool-web-app")) Expect(outputBuffer).To(test_helpers.SayLine("App will be reachable at:")) Expect(outputBuffer).To(test_helpers.SayLine(colors.Green("http://cool-web-app.192.168.11.11.xip.io"))) }) }) 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)
State: "COMPLETED", CellID: "cell-01", Failed: true, FailureReason: "womp womp", Result: "", } fakeTaskExaminer.TaskStatusReturns(taskInfo, nil) test_helpers.ExecuteCommandWithArgs(taskCommand, []string{"boop"}) Expect(outputBuffer).To(test_helpers.Say("Task GUID")) Expect(outputBuffer).To(test_helpers.Say("boop")) Expect(outputBuffer).To(test_helpers.Say("Cell ID")) Expect(outputBuffer).To(test_helpers.Say("cell-01")) Expect(outputBuffer).To(test_helpers.Say("Status")) Expect(outputBuffer).To(test_helpers.Say(colors.Red("COMPLETED"))) Expect(outputBuffer).NotTo(test_helpers.Say("Result")) Expect(outputBuffer).To(test_helpers.Say("Failure Reason")) Expect(outputBuffer).To(test_helpers.Say("womp womp")) Expect(fakeTaskExaminer.TaskStatusCallCount()).To(Equal(1)) Expect(fakeTaskExaminer.TaskStatusArgsForCall(0)).To(Equal("boop")) }) It("bails out when no task name passed", func() { test_helpers.ExecuteCommandWithArgs(taskCommand, []string{}) Expect(outputBuffer).To(test_helpers.SayLine("Please input a valid TASK_GUID")) Expect(fakeTaskExaminer.TaskStatusCallCount()).To(Equal(0)) Expect(fakeExitHandler.ExitCalledWith).To(Equal([]int{exit_codes.InvalidSyntax})) })
AfterEach(func() { Expect(os.Setenv("TERM", previousTerm)).To(Succeed()) }) itShouldNotColorizeWhitespace := func(colorizer func(text string) string) { It("returns a string without color codes when only whitespace is passed in", func() { Expect(colorizer(" ")).To(Equal(" ")) Expect(colorizer("\n")).To(Equal("\n")) Expect(colorizer("\t")).To(Equal("\t")) Expect(colorizer("\r")).To(Equal("\r")) }) } Describe("Red", func() { It("adds the red color code", func() { Expect(colors.Red("ERROR NOT GOOD")).To(Equal("\x1b[91mERROR NOT GOOD\x1b[0m")) }) itShouldNotColorizeWhitespace(colors.Red) }) Describe("Green", func() { It("adds the green color code", func() { Expect(colors.Green("TOO GOOD")).To(Equal("\x1b[32mTOO GOOD\x1b[0m")) }) itShouldNotColorizeWhitespace(colors.Green) }) Describe("Cyan", func() { It("adds the cyan color code", func() {
args := []string{ "droppo-the-clown", "http://some.url/for/buildpack", "-t", "17s", } doneChan := test_helpers.AsyncExecuteCommandWithArgs(buildDropletCommand, args) Eventually(outputBuffer).Should(test_helpers.SayLine("Submitted build of droppo-the-clown")) fakeClock.IncrementBySeconds(17) Eventually(doneChan, 5).Should(BeClosed()) Expect(outputBuffer).To(test_helpers.SayLine(colors.Red("Timed out waiting for the build to complete."))) Expect(outputBuffer).To(test_helpers.SayLine("Lattice is still building your application in the background.")) Expect(outputBuffer).To(test_helpers.SayLine("To view logs:")) Expect(outputBuffer).To(test_helpers.SayLine("ltc logs build-droplet-droppo-the-clown")) Expect(outputBuffer).To(test_helpers.SayLine("To view status:")) Expect(outputBuffer).To(test_helpers.SayLine("ltc status build-droplet-droppo-the-clown")) }) }) Context("when the build completes", func() { It("alerts the user of a complete but failed build", func() { fakeTaskExaminer.TaskStatusReturns(task_examiner.TaskInfo{State: "PENDING"}, nil) args := []string{"droppo-the-clown", "http://some.url/for/buildpack"} doneChan := test_helpers.AsyncExecuteCommandWithArgs(buildDropletCommand, args)
Result: "", State: "COMPLETED", }, } fakeTaskExaminer.ListTasksReturns(listTasks, nil) test_helpers.ExecuteCommandWithArgs(listAppsCommand, []string{}) Expect(outputBuffer).To(test_helpers.Say(colors.Bold("App Name"))) Expect(outputBuffer).To(test_helpers.Say(colors.Bold("Instances"))) Expect(outputBuffer).To(test_helpers.Say(colors.Bold("DiskMB"))) Expect(outputBuffer).To(test_helpers.Say(colors.Bold("MemoryMB"))) Expect(outputBuffer).To(test_helpers.Say(colors.Bold("Route"))) Expect(outputBuffer).To(test_helpers.Say(colors.Bold("process1"))) Expect(outputBuffer).To(test_helpers.Say(colors.Red("0/21"))) Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("100"))) Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("50"))) Expect(outputBuffer).To(test_helpers.Say("alldaylong.com => 54321")) Expect(outputBuffer).To(test_helpers.Say(colors.Bold("process2"))) Expect(outputBuffer).To(test_helpers.Say(colors.Yellow("9/8"))) Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("400"))) Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("30"))) Expect(outputBuffer).To(test_helpers.Say("never.io => 1234")) Expect(outputBuffer).To(test_helpers.Say(colors.Bold("process3"))) Expect(outputBuffer).To(test_helpers.Say(colors.Green("5/5"))) Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("600"))) Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("90"))) Expect(outputBuffer).To(test_helpers.Say("allthetime.com => 1234, herewego.org => 1234"))
Expect(presentation.ColorInstanceState(instanceInfo)).To(Equal(colors.Green(string(receptor.ActualLRPStateRunning)))) }) It("colors CLAIMED yellow", func() { instanceInfo := app_examiner.InstanceInfo{State: string(receptor.ActualLRPStateClaimed)} Expect(presentation.ColorInstanceState(instanceInfo)).To(Equal(colors.Yellow(string(receptor.ActualLRPStateClaimed)))) }) Context("when there is a placement error", func() { It("colors UNCLAIMED red", func() { instanceInfo := app_examiner.InstanceInfo{ State: string(receptor.ActualLRPStateUnclaimed), PlacementError: "I misplaced my cells. Uh oh.", } Expect(presentation.ColorInstanceState(instanceInfo)).To(Equal(colors.Red(string(receptor.ActualLRPStateUnclaimed)))) }) }) Context("when there is not a placement error", func() { It("colors UNCLAIMED cyan", func() { instanceInfo := app_examiner.InstanceInfo{State: string(receptor.ActualLRPStateUnclaimed)} Expect(presentation.ColorInstanceState(instanceInfo)).To(Equal(colors.Cyan(string(receptor.ActualLRPStateUnclaimed)))) }) }) It("colors INVALID red", func() { instanceInfo := app_examiner.InstanceInfo{State: string(receptor.ActualLRPStateInvalid)} Expect(presentation.ColorInstanceState(instanceInfo)).To(Equal(colors.Red(string(receptor.ActualLRPStateInvalid)))) })