github.com/LukasHeimann/cloudfoundrycli/v8@v8.4.4/command/v7/scale_command.go (about)

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