github.com/mook-as/cf-cli@v7.0.0-beta.28.0.20200120190804-b91c115fae48+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) usageSummary(processSummaries v3action.ProcessSummaries) string {
   184  	var usageStrings []string
   185  	for _, summary := range processSummaries {
   186  		if summary.TotalInstanceCount() > 0 {
   187  			usageStrings = append(usageStrings, fmt.Sprintf("%dM x %d", summary.MemoryInMB.Value, summary.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(processSummary *v3action.ProcessSummary) bool {
   212  	for instanceIdx := range processSummary.InstanceDetails {
   213  		if processSummary.InstanceDetails[instanceIdx].State != constant.ProcessInstanceDown {
   214  			return true
   215  		}
   216  	}
   217  
   218  	return false
   219  }
   220  
   221  func (AppSummaryDisplayer) processInstancesAreAllCrashed(processSummary *v3action.ProcessSummary) bool {
   222  	if len(processSummary.InstanceDetails) < 1 {
   223  		return false
   224  	}
   225  
   226  	for instanceIdx := range processSummary.InstanceDetails {
   227  		if processSummary.InstanceDetails[instanceIdx].State != constant.ProcessInstanceCrashed {
   228  			return false
   229  		}
   230  	}
   231  
   232  	return true
   233  }