github.com/wanddynosios/cli/v8@v8.7.9-0.20240221182337-1a92e3a7017f/command/v7/scale_command.go (about)

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