code.cloudfoundry.org/cli@v7.1.0+incompatible/command/v7/scale_command.go (about)

     1  package v7
     2  
     3  import (
     4  	"code.cloudfoundry.org/cli/actor/actionerror"
     5  	"code.cloudfoundry.org/cli/actor/v7action"
     6  	"code.cloudfoundry.org/cli/command/flag"
     7  	"code.cloudfoundry.org/cli/command/translatableerror"
     8  	"code.cloudfoundry.org/cli/command/v7/shared"
     9  )
    10  
    11  type ScaleCommand struct {
    12  	BaseCommand
    13  
    14  	RequiredArgs        flag.AppName   `positional-args:"yes"`
    15  	Force               bool           `short:"f" description:"Force restart of app without prompt"`
    16  	Instances           flag.Instances `short:"i" required:"false" description:"Number of instances"`
    17  	DiskLimit           flag.Megabytes `short:"k" required:"false" description:"Disk limit (e.g. 256M, 1024M, 1G)"`
    18  	MemoryLimit         flag.Megabytes `short:"m" required:"false" description:"Memory limit (e.g. 256M, 1024M, 1G)"`
    19  	ProcessType         string         `long:"process" default:"web" description:"App process to scale"`
    20  	usage               interface{}    `usage:"CF_NAME scale APP_NAME [--process PROCESS] [-i INSTANCES] [-k DISK] [-m MEMORY] [-f]\n\n   Modifying the app's disk or memory will cause the app to restart."`
    21  	relatedCommands     interface{}    `related_commands:"push"`
    22  	envCFStartupTimeout interface{}    `environmentName:"CF_STARTUP_TIMEOUT" environmentDescription:"Max wait time for app instance startup, in minutes" environmentDefault:"5"`
    23  }
    24  
    25  func (cmd ScaleCommand) Execute(args []string) error {
    26  	err := cmd.SharedActor.CheckTarget(true, true)
    27  	if err != nil {
    28  		return err
    29  	}
    30  
    31  	user, err := cmd.Config.CurrentUser()
    32  	if err != nil {
    33  		return err
    34  	}
    35  
    36  	app, warnings, err := cmd.Actor.GetApplicationByNameAndSpace(cmd.RequiredArgs.AppName, cmd.Config.TargetedSpace().GUID)
    37  	cmd.UI.DisplayWarnings(warnings)
    38  	if err != nil {
    39  		return err
    40  	}
    41  
    42  	if !cmd.Instances.IsSet && !cmd.DiskLimit.IsSet && !cmd.MemoryLimit.IsSet {
    43  		return cmd.showCurrentScale(user.Name, err)
    44  	}
    45  
    46  	scaled, err := cmd.scaleProcess(app.GUID, user.Name)
    47  	if err != nil {
    48  		return err
    49  	}
    50  	if !scaled {
    51  		return nil
    52  	}
    53  
    54  	handleInstanceDetails := func(instanceDetails string) {
    55  		cmd.UI.DisplayText(instanceDetails)
    56  	}
    57  
    58  	warnings, err = cmd.Actor.PollStart(app, false, handleInstanceDetails)
    59  	cmd.UI.DisplayNewline()
    60  	cmd.UI.DisplayWarnings(warnings)
    61  
    62  	showErr := cmd.showCurrentScale(user.Name, err)
    63  	if showErr != nil {
    64  		return showErr
    65  	}
    66  
    67  	return cmd.translateErrors(err)
    68  }
    69  
    70  func (cmd ScaleCommand) translateErrors(err error) error {
    71  	if _, ok := err.(actionerror.StartupTimeoutError); ok {
    72  		return translatableerror.StartupTimeoutError{
    73  			AppName:    cmd.RequiredArgs.AppName,
    74  			BinaryName: cmd.Config.BinaryName(),
    75  		}
    76  	} else if _, ok := err.(actionerror.AllInstancesCrashedError); ok {
    77  		return translatableerror.ApplicationUnableToStartError{
    78  			AppName:    cmd.RequiredArgs.AppName,
    79  			BinaryName: cmd.Config.BinaryName(),
    80  		}
    81  	}
    82  
    83  	return err
    84  }
    85  
    86  func (cmd ScaleCommand) scaleProcess(appGUID string, username string) (bool, error) {
    87  	cmd.UI.DisplayTextWithFlavor("Scaling app {{.AppName}} in org {{.OrgName}} / space {{.SpaceName}} as {{.Username}}...", map[string]interface{}{
    88  		"AppName":   cmd.RequiredArgs.AppName,
    89  		"OrgName":   cmd.Config.TargetedOrganization().Name,
    90  		"SpaceName": cmd.Config.TargetedSpace().Name,
    91  		"Username":  username,
    92  	})
    93  	cmd.UI.DisplayNewline()
    94  
    95  	shouldRestart := cmd.DiskLimit.IsSet || cmd.MemoryLimit.IsSet
    96  	if shouldRestart && !cmd.Force {
    97  		shouldScale, err := cmd.UI.DisplayBoolPrompt(
    98  			false,
    99  			"This will cause the app to restart. Are you sure you want to scale {{.AppName}}?",
   100  			map[string]interface{}{"AppName": cmd.RequiredArgs.AppName})
   101  		if err != nil {
   102  			return false, err
   103  		}
   104  
   105  		if !shouldScale {
   106  			cmd.UI.DisplayText("Scaling cancelled")
   107  			return false, nil
   108  		}
   109  		cmd.UI.DisplayNewline()
   110  	}
   111  
   112  	warnings, err := cmd.Actor.ScaleProcessByApplication(appGUID, v7action.Process{
   113  		Type:       cmd.ProcessType,
   114  		Instances:  cmd.Instances.NullInt,
   115  		MemoryInMB: cmd.MemoryLimit.NullUint64,
   116  		DiskInMB:   cmd.DiskLimit.NullUint64,
   117  	})
   118  	cmd.UI.DisplayWarnings(warnings)
   119  	if err != nil {
   120  		return false, err
   121  	}
   122  
   123  	if shouldRestart {
   124  		err := cmd.restartApplication(appGUID, username)
   125  		if err != nil {
   126  			return false, err
   127  		}
   128  	}
   129  
   130  	return true, nil
   131  }
   132  
   133  func (cmd ScaleCommand) restartApplication(appGUID string, username string) error {
   134  	cmd.UI.DisplayTextWithFlavor("Stopping app {{.AppName}} in org {{.OrgName}} / space {{.SpaceName}} as {{.Username}}...", map[string]interface{}{
   135  		"AppName":   cmd.RequiredArgs.AppName,
   136  		"OrgName":   cmd.Config.TargetedOrganization().Name,
   137  		"SpaceName": cmd.Config.TargetedSpace().Name,
   138  		"Username":  username,
   139  	})
   140  	cmd.UI.DisplayNewline()
   141  
   142  	warnings, err := cmd.Actor.StopApplication(appGUID)
   143  	cmd.UI.DisplayWarnings(warnings)
   144  	if err != nil {
   145  		return err
   146  	}
   147  
   148  	cmd.UI.DisplayTextWithFlavor("Starting app {{.AppName}} in org {{.OrgName}} / space {{.SpaceName}} as {{.Username}}...", map[string]interface{}{
   149  		"AppName":   cmd.RequiredArgs.AppName,
   150  		"OrgName":   cmd.Config.TargetedOrganization().Name,
   151  		"SpaceName": cmd.Config.TargetedSpace().Name,
   152  		"Username":  username,
   153  	})
   154  	cmd.UI.DisplayNewline()
   155  
   156  	warnings, err = cmd.Actor.StartApplication(appGUID)
   157  	cmd.UI.DisplayWarnings(warnings)
   158  	if err != nil {
   159  		return err
   160  	}
   161  
   162  	return nil
   163  }
   164  
   165  func (cmd ScaleCommand) showCurrentScale(userName string, runningErr error) error {
   166  	if !shouldShowCurrentScale(runningErr) {
   167  		return nil
   168  	}
   169  
   170  	cmd.UI.DisplayTextWithFlavor("Showing current scale of app {{.AppName}} in org {{.OrgName}} / space {{.SpaceName}} as {{.Username}}...", map[string]interface{}{
   171  		"AppName":   cmd.RequiredArgs.AppName,
   172  		"OrgName":   cmd.Config.TargetedOrganization().Name,
   173  		"SpaceName": cmd.Config.TargetedSpace().Name,
   174  		"Username":  userName,
   175  	})
   176  
   177  	cmd.UI.DisplayNewline()
   178  
   179  	summary, warnings, err := cmd.Actor.GetDetailedAppSummary(cmd.RequiredArgs.AppName, cmd.Config.TargetedSpace().GUID, false)
   180  	cmd.UI.DisplayWarnings(warnings)
   181  	if err != nil {
   182  		return err
   183  	}
   184  
   185  	appSummaryDisplayer := shared.NewAppSummaryDisplayer(cmd.UI)
   186  	appSummaryDisplayer.AppDisplay(summary, false)
   187  	return nil
   188  }
   189  
   190  func shouldShowCurrentScale(err error) bool {
   191  	if err == nil {
   192  		return true
   193  	}
   194  
   195  	if _, ok := err.(actionerror.AllInstancesCrashedError); ok {
   196  		return true
   197  	}
   198  
   199  	return false
   200  }