github.com/jghiloni/cli@v6.28.1-0.20170628223758-0ce05fe032a2+incompatible/command/v3/shared/app_summary_displayer.go (about) 1 package shared 2 3 import ( 4 "fmt" 5 "sort" 6 "strings" 7 "time" 8 9 "code.cloudfoundry.org/cli/actor/v2action" 10 "code.cloudfoundry.org/cli/actor/v3action" 11 "code.cloudfoundry.org/cli/command" 12 sharedV2 "code.cloudfoundry.org/cli/command/v2/shared" 13 "github.com/cloudfoundry/bytefmt" 14 ) 15 16 type AppSummaryDisplayer struct { 17 UI command.UI 18 Config command.Config 19 Actor V3AppSummaryActor 20 V2AppRouteActor V2AppRouteActor 21 AppName string 22 } 23 24 //go:generate counterfeiter . V2AppRouteActor 25 26 type V2AppRouteActor interface { 27 GetApplicationRoutes(appGUID string) ([]v2action.Route, v2action.Warnings, error) 28 } 29 30 //go:generate counterfeiter . V3AppSummaryActor 31 32 type V3AppSummaryActor interface { 33 GetApplicationSummaryByNameAndSpace(appName string, spaceGUID string) (v3action.ApplicationSummary, v3action.Warnings, error) 34 } 35 36 func (cmd AppSummaryDisplayer) DisplayAppInfo() error { 37 user, err := cmd.Config.CurrentUser() 38 if err != nil { 39 return HandleError(err) 40 } 41 42 cmd.UI.DisplayTextWithFlavor("Showing health and status for app {{.AppName}} in org {{.OrgName}} / space {{.SpaceName}} as {{.Username}}...", map[string]interface{}{ 43 "AppName": cmd.AppName, 44 "OrgName": cmd.Config.TargetedOrganization().Name, 45 "SpaceName": cmd.Config.TargetedSpace().Name, 46 "Username": user.Name, 47 }) 48 cmd.UI.DisplayNewline() 49 50 summary, warnings, err := cmd.Actor.GetApplicationSummaryByNameAndSpace(cmd.AppName, cmd.Config.TargetedSpace().GUID) 51 cmd.UI.DisplayWarnings(warnings) 52 if err != nil { 53 return HandleError(err) 54 } 55 56 routes, routeWarnings, err := cmd.V2AppRouteActor.GetApplicationRoutes(summary.Application.GUID) 57 cmd.UI.DisplayWarnings(routeWarnings) 58 if err != nil { 59 return sharedV2.HandleError(err) 60 } 61 62 cmd.displayAppTable(summary, routes) 63 64 return nil 65 } 66 67 // Sort processes alphabetically and put web first. 68 func (cmd AppSummaryDisplayer) displayAppTable(summary v3action.ApplicationSummary, routes []v2action.Route) { 69 sort.Slice(summary.Processes, func(i int, j int) bool { 70 var iScore int 71 var jScore int 72 73 switch summary.Processes[i].Type { 74 case "web": 75 iScore = 0 76 default: 77 iScore = 1 78 } 79 80 switch summary.Processes[j].Type { 81 case "web": 82 jScore = 0 83 default: 84 jScore = 1 85 } 86 87 if iScore == 1 && jScore == 1 { 88 return summary.Processes[i].Type < summary.Processes[j].Type 89 } 90 return iScore < jScore 91 }) 92 93 keyValueTable := [][]string{ 94 {cmd.UI.TranslateText("name:"), summary.Application.Name}, 95 {cmd.UI.TranslateText("requested state:"), strings.ToLower(summary.State)}, 96 {cmd.UI.TranslateText("processes:"), cmd.processesSummary(summary.Processes)}, 97 {cmd.UI.TranslateText("memory usage:"), cmd.usageSummary(summary.Processes)}, 98 {cmd.UI.TranslateText("routes:"), cmd.routesSummary(routes)}, 99 {cmd.UI.TranslateText("stack:"), summary.CurrentDroplet.Stack}, 100 {cmd.UI.TranslateText("buildpacks:"), cmd.buildpackNames(summary.CurrentDroplet.Buildpacks)}, 101 } 102 103 crashedProcesses := []string{} 104 for i := range summary.Processes { 105 if cmd.processInstancesAreAllCrashed(&summary.Processes[i]) { 106 crashedProcesses = append(crashedProcesses, summary.Processes[i].Type) 107 } 108 } 109 110 cmd.UI.DisplayKeyValueTableForV3App(keyValueTable, crashedProcesses) 111 112 appHasARunningInstance := false 113 114 for processIdx := range summary.Processes { 115 if cmd.processHasAnInstance(&summary.Processes[processIdx]) { 116 appHasARunningInstance = true 117 break 118 } 119 } 120 121 if !appHasARunningInstance { 122 cmd.UI.DisplayNewline() 123 cmd.UI.DisplayText("There are no running instances of this app.") 124 return 125 } 126 127 for _, process := range summary.Processes { 128 cmd.UI.DisplayNewline() 129 130 cmd.UI.DisplayTextWithBold("{{.ProcessType}}:{{.HealthyInstanceCount}}/{{.TotalInstanceCount}}", map[string]interface{}{ 131 "ProcessType": process.Type, 132 "HealthyInstanceCount": process.HealthyInstanceCount(), 133 "TotalInstanceCount": process.TotalInstanceCount(), 134 }) 135 136 if !cmd.processHasAnInstance(&process) { 137 continue 138 } 139 140 table := [][]string{ 141 { 142 "", 143 cmd.UI.TranslateText("state"), 144 cmd.UI.TranslateText("since"), 145 cmd.UI.TranslateText("cpu"), 146 cmd.UI.TranslateText("memory"), 147 cmd.UI.TranslateText("disk"), 148 }, 149 } 150 151 for _, instance := range process.Instances { 152 table = append(table, []string{ 153 fmt.Sprintf("#%d", instance.Index), 154 cmd.UI.TranslateText(strings.ToLower(string(instance.State))), 155 cmd.appInstanceDate(instance.StartTime()), 156 fmt.Sprintf("%.1f%%", instance.CPU*100), 157 fmt.Sprintf("%s of %s", bytefmt.ByteSize(instance.MemoryUsage), bytefmt.ByteSize(instance.MemoryQuota)), 158 fmt.Sprintf("%s of %s", bytefmt.ByteSize(instance.DiskUsage), bytefmt.ByteSize(instance.DiskQuota)), 159 }) 160 } 161 162 cmd.UI.DisplayInstancesTableForApp(table) 163 } 164 } 165 166 func (_ AppSummaryDisplayer) processesSummary(processes []v3action.Process) string { 167 var processesStrings []string 168 for _, process := range processes { 169 processesStrings = append(processesStrings, fmt.Sprintf("%s:%d/%d", process.Type, process.HealthyInstanceCount(), process.TotalInstanceCount())) 170 } 171 172 return strings.Join(processesStrings, ", ") 173 } 174 175 func (_ AppSummaryDisplayer) routesSummary(routes []v2action.Route) string { 176 formattedRoutes := []string{} 177 for _, route := range routes { 178 formattedRoutes = append(formattedRoutes, route.String()) 179 } 180 return strings.Join(formattedRoutes, ", ") 181 } 182 183 func (_ AppSummaryDisplayer) usageSummary(processes []v3action.Process) string { 184 var usageStrings []string 185 for _, process := range processes { 186 if process.TotalInstanceCount() > 0 { 187 usageStrings = append(usageStrings, fmt.Sprintf("%dM x %d", process.MemoryInMB, process.TotalInstanceCount())) 188 } 189 } 190 191 return strings.Join(usageStrings, ", ") 192 } 193 194 func (_ AppSummaryDisplayer) buildpackNames(buildpacks []v3action.Buildpack) string { 195 var names []string 196 for _, buildpack := range buildpacks { 197 if buildpack.DetectOutput != "" { 198 names = append(names, buildpack.DetectOutput) 199 } else { 200 names = append(names, buildpack.Name) 201 } 202 } 203 204 return strings.Join(names, ", ") 205 } 206 207 func (_ AppSummaryDisplayer) appInstanceDate(input time.Time) string { 208 return input.Local().Format("2006-01-02 15:04:05 PM") 209 } 210 211 func (_ AppSummaryDisplayer) processHasAnInstance(process *v3action.Process) bool { 212 for instanceIdx := range process.Instances { 213 if process.Instances[instanceIdx].State != "DOWN" { 214 return true 215 } 216 } 217 218 return false 219 } 220 221 func (_ AppSummaryDisplayer) processInstancesAreAllCrashed(process *v3action.Process) bool { 222 if len(process.Instances) < 1 { 223 return false 224 } 225 226 for instanceIdx := range process.Instances { 227 if process.Instances[instanceIdx].State != "CRASHED" { 228 return false 229 } 230 } 231 232 return true 233 }