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