github.com/orange-cloudfoundry/cli@v7.1.0+incompatible/command/v6/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/v2action" 10 "code.cloudfoundry.org/cli/actor/v3action" 11 "code.cloudfoundry.org/cli/api/cloudcontroller/ccerror" 12 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant" 13 "code.cloudfoundry.org/cli/command" 14 ) 15 16 type AppSummaryDisplayer struct { 17 UI command.UI 18 Config command.Config 19 Actor V3AppSummaryActor 20 V2AppActor V2AppActor 21 AppName string 22 } 23 24 //go:generate counterfeiter . V2AppActor 25 26 type V2AppActor interface { 27 GetApplicationRoutes(appGUID string) (v2action.Routes, v2action.Warnings, error) 28 GetApplicationInstancesWithStatsByApplication(guid string) ([]v2action.ApplicationInstanceWithStats, v2action.Warnings, error) 29 } 30 31 //go:generate counterfeiter . V3AppSummaryActor 32 33 type V3AppSummaryActor interface { 34 GetApplicationSummaryByNameAndSpace(appName string, spaceGUID string, withObfuscatedValues bool) (v3action.ApplicationSummary, v3action.Warnings, error) 35 } 36 37 func (display AppSummaryDisplayer) DisplayAppInfo() error { 38 summary, warnings, err := display.Actor.GetApplicationSummaryByNameAndSpace(display.AppName, display.Config.TargetedSpace().GUID, false) 39 display.UI.DisplayWarnings(warnings) 40 if err != nil { 41 return err 42 } 43 summary.ProcessSummaries.Sort() 44 45 var routes v2action.Routes 46 var appStats []v2action.ApplicationInstanceWithStats 47 if len(summary.ProcessSummaries) > 0 { 48 var routeWarnings v2action.Warnings 49 routes, routeWarnings, err = display.V2AppActor.GetApplicationRoutes(summary.Application.GUID) 50 display.UI.DisplayWarnings(routeWarnings) 51 if _, ok := err.(ccerror.ResourceNotFoundError); err != nil && !ok { 52 return err 53 } 54 55 if summary.State == constant.ApplicationStarted { 56 var instanceWarnings v2action.Warnings 57 appStats, instanceWarnings, err = display.V2AppActor.GetApplicationInstancesWithStatsByApplication(summary.Application.GUID) 58 display.UI.DisplayWarnings(instanceWarnings) 59 if _, ok := err.(ccerror.ResourceNotFoundError); err != nil && !ok { 60 return err 61 } 62 } 63 } 64 65 display.displayAppTable(summary, routes, appStats) 66 67 return nil 68 } 69 70 func (display AppSummaryDisplayer) DisplayAppProcessInfo() error { 71 summary, warnings, err := display.Actor.GetApplicationSummaryByNameAndSpace(display.AppName, display.Config.TargetedSpace().GUID, false) 72 display.UI.DisplayWarnings(warnings) 73 if err != nil { 74 return err 75 } 76 summary.ProcessSummaries.Sort() 77 78 display.displayProcessTable(summary) 79 return nil 80 } 81 82 func GetCreatedTime(summary v3action.ApplicationSummary) time.Time { 83 timestamp, _ := time.Parse(time.RFC3339, summary.CurrentDroplet.CreatedAt) 84 return timestamp 85 } 86 87 func (display AppSummaryDisplayer) displayAppTable(summary v3action.ApplicationSummary, routes v2action.Routes, appStats []v2action.ApplicationInstanceWithStats) { 88 var isoRow []string 89 if len(appStats) > 0 && len(appStats[0].IsolationSegment) > 0 { 90 isoRow = append(isoRow, display.UI.TranslateText("isolation segment:"), appStats[0].IsolationSegment) 91 } 92 93 var lifecycleInfo []string 94 if summary.LifecycleType == constant.AppLifecycleTypeDocker { 95 lifecycleInfo = []string{display.UI.TranslateText("docker image:"), summary.CurrentDroplet.Image} 96 } else { 97 lifecycleInfo = []string{display.UI.TranslateText("buildpacks:"), display.buildpackNames(summary.CurrentDroplet.Buildpacks)} 98 } 99 100 keyValueTable := [][]string{ 101 {display.UI.TranslateText("name:"), summary.Application.Name}, 102 {display.UI.TranslateText("requested state:"), strings.ToLower(string(summary.State))}, 103 isoRow, 104 {display.UI.TranslateText("routes:"), routes.Summary()}, 105 {display.UI.TranslateText("last uploaded:"), display.UI.UserFriendlyDate(GetCreatedTime(summary))}, 106 {display.UI.TranslateText("stack:"), summary.CurrentDroplet.Stack}, 107 lifecycleInfo, 108 } 109 110 display.UI.DisplayKeyValueTable("", keyValueTable, 3) 111 112 display.displayProcessTable(summary) 113 } 114 115 func (display AppSummaryDisplayer) displayAppInstancesTable(processSummary v3action.ProcessSummary) { 116 display.UI.DisplayNewline() 117 118 // TODO: figure out how to align key-value output 119 keyValueTable := [][]string{ 120 {display.UI.TranslateText("type:"), processSummary.Type}, 121 {display.UI.TranslateText("instances:"), fmt.Sprintf("%d/%d", processSummary.HealthyInstanceCount(), processSummary.TotalInstanceCount())}, 122 {display.UI.TranslateText("memory usage:"), fmt.Sprintf("%dM", processSummary.MemoryInMB.Value)}, 123 } 124 125 display.UI.DisplayKeyValueTable("", keyValueTable, 3) 126 127 if !display.processHasAnInstance(&processSummary) { 128 return 129 } 130 131 table := [][]string{ 132 { 133 "", 134 display.UI.TranslateText("state"), 135 display.UI.TranslateText("since"), 136 display.UI.TranslateText("cpu"), 137 display.UI.TranslateText("memory"), 138 display.UI.TranslateText("disk"), 139 }, 140 } 141 142 for _, instance := range processSummary.InstanceDetails { 143 table = append(table, []string{ 144 fmt.Sprintf("#%d", instance.Index), 145 display.UI.TranslateText(strings.ToLower(string(instance.State))), 146 display.appInstanceDate(instance.StartTime()), 147 fmt.Sprintf("%.1f%%", instance.CPU*100), 148 display.UI.TranslateText("{{.MemUsage}} of {{.MemQuota}}", map[string]interface{}{ 149 "MemUsage": bytefmt.ByteSize(instance.MemoryUsage), 150 "MemQuota": bytefmt.ByteSize(instance.MemoryQuota), 151 }), 152 display.UI.TranslateText("{{.DiskUsage}} of {{.DiskQuota}}", map[string]interface{}{ 153 "DiskUsage": bytefmt.ByteSize(instance.DiskUsage), 154 "DiskQuota": bytefmt.ByteSize(instance.DiskQuota), 155 }), 156 }) 157 } 158 159 display.UI.DisplayInstancesTableForApp(table) 160 } 161 162 func (display AppSummaryDisplayer) displayProcessTable(summary v3action.ApplicationSummary) { 163 appHasARunningInstance := false 164 165 for processIdx := range summary.ProcessSummaries { 166 if display.processHasAnInstance(&summary.ProcessSummaries[processIdx]) { 167 appHasARunningInstance = true 168 break 169 } 170 } 171 172 if !appHasARunningInstance { 173 display.UI.DisplayNewline() 174 display.UI.DisplayText("There are no running instances of this app.") 175 return 176 } 177 178 for _, process := range summary.ProcessSummaries { 179 display.displayAppInstancesTable(process) 180 } 181 } 182 183 func (AppSummaryDisplayer) buildpackNames(buildpacks []v3action.Buildpack) string { 184 var names []string 185 for _, buildpack := range buildpacks { 186 if buildpack.DetectOutput != "" { 187 names = append(names, buildpack.DetectOutput) 188 } else { 189 names = append(names, buildpack.Name) 190 } 191 } 192 193 return strings.Join(names, ", ") 194 } 195 196 func (AppSummaryDisplayer) appInstanceDate(input time.Time) string { 197 return input.Local().Format("2006-01-02 15:04:05 PM") 198 } 199 200 func (AppSummaryDisplayer) processHasAnInstance(processSummary *v3action.ProcessSummary) bool { 201 for instanceIdx := range processSummary.InstanceDetails { 202 if processSummary.InstanceDetails[instanceIdx].State != constant.ProcessInstanceDown { 203 return true 204 } 205 } 206 207 return false 208 }