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