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 }