github.com/DaAlbrecht/cf-cli@v0.0.0-20231128151943-1fe19bb400b9/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 "code.cloudfoundry.org/cli/resources" 13 "code.cloudfoundry.org/cli/util/ui" 14 log "github.com/sirupsen/logrus" 15 ) 16 17 type AppSummaryDisplayer struct { 18 UI command.UI 19 } 20 21 func NewAppSummaryDisplayer(ui command.UI) *AppSummaryDisplayer { 22 return &AppSummaryDisplayer{ 23 UI: ui, 24 } 25 } 26 27 func (display AppSummaryDisplayer) AppDisplay(summary v7action.DetailedApplicationSummary, displayStartCommand bool) { 28 var isoRow []string 29 var keyValueTable [][]string 30 if name, exists := summary.GetIsolationSegmentName(); exists { 31 isoRow = append(isoRow, display.UI.TranslateText("isolation segment:"), name) 32 } 33 34 if summary.LifecycleType == constant.AppLifecycleTypeDocker { 35 keyValueTable = [][]string{ 36 {display.UI.TranslateText("name:"), summary.Application.Name}, 37 {display.UI.TranslateText("requested state:"), strings.ToLower(string(summary.State))}, 38 isoRow, 39 {display.UI.TranslateText("routes:"), routeSummary(summary.Routes)}, 40 {display.UI.TranslateText("last uploaded:"), display.getCreatedTime(summary)}, 41 {display.UI.TranslateText("stack:"), summary.CurrentDroplet.Stack}, 42 {display.UI.TranslateText("docker image:"), summary.CurrentDroplet.Image}, 43 isoRow, 44 } 45 } else { 46 keyValueTable = [][]string{ 47 {display.UI.TranslateText("name:"), summary.Application.Name}, 48 {display.UI.TranslateText("requested state:"), strings.ToLower(string(summary.State))}, 49 isoRow, 50 {display.UI.TranslateText("routes:"), routeSummary(summary.Routes)}, 51 {display.UI.TranslateText("last uploaded:"), display.getCreatedTime(summary)}, 52 {display.UI.TranslateText("stack:"), summary.CurrentDroplet.Stack}, 53 {display.UI.TranslateText("buildpacks:"), ""}, 54 isoRow, 55 } 56 } 57 58 display.UI.DisplayKeyValueTable("", keyValueTable, 3) 59 60 if summary.LifecycleType == constant.AppLifecycleTypeBuildpack { 61 display.displayBuildpackTable(summary.CurrentDroplet.Buildpacks) 62 } 63 64 display.displayProcessTable(summary, displayStartCommand) 65 } 66 67 func routeSummary(rs []resources.Route) string { 68 formattedRoutes := []string{} 69 for _, route := range rs { 70 formattedRoutes = append(formattedRoutes, route.URL) 71 } 72 return strings.Join(formattedRoutes, ", ") 73 } 74 75 func formatLogRateLimit(limit int64) string { 76 if limit == -1 { 77 return "unlimited" 78 } else { 79 return bytefmt.ByteSize(uint64(limit)) + "/s" 80 } 81 } 82 83 func (display AppSummaryDisplayer) displayAppInstancesTable(processSummary v7action.ProcessSummary) { 84 table := [][]string{ 85 { 86 "", 87 display.UI.TranslateText("state"), 88 display.UI.TranslateText("since"), 89 display.UI.TranslateText("cpu"), 90 display.UI.TranslateText("memory"), 91 display.UI.TranslateText("disk"), 92 display.UI.TranslateText("logging"), 93 display.UI.TranslateText("details"), 94 }, 95 } 96 97 for _, instance := range processSummary.InstanceDetails { 98 table = append(table, []string{ 99 fmt.Sprintf("#%d", instance.Index), 100 display.UI.TranslateText(strings.ToLower(string(instance.State))), 101 display.appInstanceDate(instance.StartTime()), 102 fmt.Sprintf("%.1f%%", instance.CPU*100), 103 display.UI.TranslateText("{{.MemUsage}} of {{.MemQuota}}", map[string]interface{}{ 104 "MemUsage": bytefmt.ByteSize(instance.MemoryUsage), 105 "MemQuota": bytefmt.ByteSize(instance.MemoryQuota), 106 }), 107 display.UI.TranslateText("{{.DiskUsage}} of {{.DiskQuota}}", map[string]interface{}{ 108 "DiskUsage": bytefmt.ByteSize(instance.DiskUsage), 109 "DiskQuota": bytefmt.ByteSize(instance.DiskQuota), 110 }), 111 display.UI.TranslateText("{{.LogRate}}/s of {{.LogRateLimit}}", map[string]interface{}{ 112 "LogRate": bytefmt.ByteSize(instance.LogRate), 113 "LogRateLimit": formatLogRateLimit(instance.LogRateLimit), 114 }), 115 instance.Details, 116 }) 117 } 118 119 display.UI.DisplayInstancesTableForApp(table) 120 } 121 122 func (display AppSummaryDisplayer) displayProcessTable(summary v7action.DetailedApplicationSummary, displayStartCommand bool) { 123 for _, process := range summary.ProcessSummaries { 124 display.UI.DisplayNewline() 125 126 var startCommandRow []string 127 if displayStartCommand && len(process.Command.Value) > 0 { 128 startCommandRow = append(startCommandRow, display.UI.TranslateText("start command:"), process.Command.Value) 129 } 130 131 var processSidecars []string 132 for _, sidecar := range process.Sidecars { 133 processSidecars = append(processSidecars, sidecar.Name) 134 } 135 136 keyValueTable := [][]string{ 137 {display.UI.TranslateText("type:"), process.Type}, 138 {display.UI.TranslateText("sidecars:"), strings.Join(processSidecars, ", ")}, 139 {display.UI.TranslateText("instances:"), fmt.Sprintf("%d/%d", process.HealthyInstanceCount(), process.TotalInstanceCount())}, 140 {display.UI.TranslateText("memory usage:"), fmt.Sprintf("%dM", process.MemoryInMB.Value)}, 141 startCommandRow, 142 } 143 144 display.UI.DisplayKeyValueTable("", keyValueTable, 3) 145 146 if len(process.InstanceDetails) == 0 { 147 display.UI.DisplayText("There are no running instances of this process.") 148 continue 149 } 150 display.displayAppInstancesTable(process) 151 } 152 } 153 154 func (display AppSummaryDisplayer) getCreatedTime(summary v7action.DetailedApplicationSummary) string { 155 if summary.CurrentDroplet.CreatedAt != "" { 156 timestamp, err := time.Parse(time.RFC3339, summary.CurrentDroplet.CreatedAt) 157 if err != nil { 158 log.WithField("createdAt", summary.CurrentDroplet.CreatedAt).Errorln("error parsing created at:", err) 159 } 160 161 return display.UI.UserFriendlyDate(timestamp) 162 } 163 164 return "" 165 } 166 167 func (AppSummaryDisplayer) appInstanceDate(input time.Time) string { 168 return input.UTC().Format(time.RFC3339) 169 } 170 171 func (display AppSummaryDisplayer) displayBuildpackTable(buildpacks []resources.DropletBuildpack) { 172 if len(buildpacks) > 0 { 173 var keyValueTable = [][]string{ 174 { 175 display.UI.TranslateText("name"), 176 display.UI.TranslateText("version"), 177 display.UI.TranslateText("detect output"), 178 display.UI.TranslateText("buildpack name"), 179 }, 180 } 181 182 for _, buildpack := range buildpacks { 183 keyValueTable = append(keyValueTable, []string{ 184 buildpack.Name, 185 buildpack.Version, 186 buildpack.DetectOutput, 187 buildpack.BuildpackName, 188 }) 189 } 190 191 display.UI.DisplayTableWithHeader("\t", keyValueTable, ui.DefaultTableSpacePadding) 192 } 193 }