github.com/jenspinney/cli@v6.42.1-0.20190207184520-7450c600020e+incompatible/command/v7/shared/app_summary_displayer.go (about) 1 package shared 2 3 import ( 4 "fmt" 5 "strings" 6 "time" 7 8 "code.cloudfoundry.org/bytefmt" 9 "code.cloudfoundry.org/cli/actor/v7action" 10 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant" 11 "code.cloudfoundry.org/cli/command" 12 ) 13 14 type AppSummaryDisplayer struct { 15 UI command.UI 16 } 17 18 func NewAppSummaryDisplayer(ui command.UI) *AppSummaryDisplayer { 19 return &AppSummaryDisplayer{ 20 UI: ui, 21 } 22 } 23 24 func (display AppSummaryDisplayer) AppDisplay(summary v7action.ApplicationSummary, displayStartCommand bool) { 25 var isoRow []string 26 if name, exists := summary.GetIsolationSegmentName(); exists { 27 isoRow = append(isoRow, display.UI.TranslateText("isolation segment:"), name) 28 } 29 30 var lifecycleInfo []string 31 if summary.LifecycleType == constant.AppLifecycleTypeDocker { 32 lifecycleInfo = []string{display.UI.TranslateText("docker image:"), summary.CurrentDroplet.Image} 33 } else { 34 lifecycleInfo = []string{display.UI.TranslateText("buildpacks:"), display.buildpackNames(summary.CurrentDroplet.Buildpacks)} 35 } 36 37 keyValueTable := [][]string{ 38 {display.UI.TranslateText("name:"), summary.Application.Name}, 39 {display.UI.TranslateText("requested state:"), strings.ToLower(string(summary.State))}, 40 isoRow, 41 {display.UI.TranslateText("routes:"), summary.Routes.Summary()}, 42 {display.UI.TranslateText("last uploaded:"), display.getCreatedTime(summary)}, 43 {display.UI.TranslateText("stack:"), summary.CurrentDroplet.Stack}, 44 lifecycleInfo, 45 } 46 47 display.UI.DisplayKeyValueTable("", keyValueTable, 3) 48 49 display.displayProcessTable(summary, displayStartCommand) 50 } 51 52 func (display AppSummaryDisplayer) displayAppInstancesTable(processSummary v7action.ProcessSummary) { 53 table := [][]string{ 54 { 55 "", 56 display.UI.TranslateText("state"), 57 display.UI.TranslateText("since"), 58 display.UI.TranslateText("cpu"), 59 display.UI.TranslateText("memory"), 60 display.UI.TranslateText("disk"), 61 display.UI.TranslateText("details"), 62 }, 63 } 64 65 for _, instance := range processSummary.InstanceDetails { 66 table = append(table, []string{ 67 fmt.Sprintf("#%d", instance.Index), 68 display.UI.TranslateText(strings.ToLower(string(instance.State))), 69 display.appInstanceDate(instance.StartTime()), 70 fmt.Sprintf("%.1f%%", instance.CPU*100), 71 display.UI.TranslateText("{{.MemUsage}} of {{.MemQuota}}", map[string]interface{}{ 72 "MemUsage": bytefmt.ByteSize(instance.MemoryUsage), 73 "MemQuota": bytefmt.ByteSize(instance.MemoryQuota), 74 }), 75 display.UI.TranslateText("{{.DiskUsage}} of {{.DiskQuota}}", map[string]interface{}{ 76 "DiskUsage": bytefmt.ByteSize(instance.DiskUsage), 77 "DiskQuota": bytefmt.ByteSize(instance.DiskQuota), 78 }), 79 instance.Details, 80 }) 81 } 82 83 display.UI.DisplayInstancesTableForApp(table) 84 } 85 86 func (display AppSummaryDisplayer) displayProcessTable(summary v7action.ApplicationSummary, displayStartCommand bool) { 87 for _, process := range summary.ProcessSummaries { 88 display.UI.DisplayNewline() 89 90 var startCommandRow []string 91 if displayStartCommand && len(process.Command.Value) > 0 { 92 startCommandRow = append(startCommandRow, display.UI.TranslateText("start command:"), process.Command.Value) 93 } 94 95 keyValueTable := [][]string{ 96 {display.UI.TranslateText("type:"), process.Type}, 97 {display.UI.TranslateText("instances:"), fmt.Sprintf("%d/%d", process.HealthyInstanceCount(), process.TotalInstanceCount())}, 98 {display.UI.TranslateText("memory usage:"), fmt.Sprintf("%dM", process.MemoryInMB.Value)}, 99 startCommandRow, 100 } 101 102 display.UI.DisplayKeyValueTable("", keyValueTable, 3) 103 104 if len(process.InstanceDetails) == 0 { 105 display.UI.DisplayNewline() 106 display.UI.DisplayText("There are no running instances of this process.") 107 continue 108 } 109 display.displayAppInstancesTable(process) 110 } 111 } 112 113 func (display AppSummaryDisplayer) getCreatedTime(summary v7action.ApplicationSummary) string { 114 if summary.CurrentDroplet.CreatedAt != "" { 115 timestamp, _ := time.Parse(time.RFC3339, summary.CurrentDroplet.CreatedAt) 116 return display.UI.UserFriendlyDate(timestamp) 117 } 118 119 return "" 120 } 121 122 func (AppSummaryDisplayer) usageSummary(processSummaries v7action.ProcessSummaries) string { 123 var usageStrings []string 124 for _, summary := range processSummaries { 125 if summary.TotalInstanceCount() > 0 { 126 usageStrings = append(usageStrings, fmt.Sprintf("%dM x %d", summary.MemoryInMB.Value, summary.TotalInstanceCount())) 127 } 128 } 129 130 return strings.Join(usageStrings, ", ") 131 } 132 133 func (AppSummaryDisplayer) buildpackNames(buildpacks []v7action.DropletBuildpack) string { 134 var names []string 135 for _, buildpack := range buildpacks { 136 if buildpack.DetectOutput != "" { 137 names = append(names, buildpack.DetectOutput) 138 } else { 139 names = append(names, buildpack.Name) 140 } 141 } 142 143 return strings.Join(names, ", ") 144 } 145 146 func (AppSummaryDisplayer) appInstanceDate(input time.Time) string { 147 return input.UTC().Format(time.RFC3339) 148 } 149 150 func (AppSummaryDisplayer) processHasAnInstanceUp(processSummary *v7action.ProcessSummary) bool { 151 for _, processInstance := range processSummary.InstanceDetails { 152 if processInstance.State != constant.ProcessInstanceDown { 153 return true 154 } 155 } 156 return false 157 } 158 159 func (AppSummaryDisplayer) processInstancesAreAllCrashed(processSummary *v7action.ProcessSummary) bool { 160 if len(processSummary.InstanceDetails) < 1 { 161 return false 162 } 163 164 for _, processInstance := range processSummary.InstanceDetails { 165 if processInstance.State != constant.ProcessInstanceDown { 166 return false 167 } 168 } 169 return true 170 }