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  }