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  }