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