github.com/randomtask1155/cli@v6.41.1-0.20181227003417-a98eed78cbde+incompatible/command/v6/v3_zdt_push_command.go (about) 1 package v6 2 3 import ( 4 "fmt" 5 6 "code.cloudfoundry.org/cli/actor/actionerror" 7 "code.cloudfoundry.org/cli/actor/pushaction" 8 "code.cloudfoundry.org/cli/actor/sharedaction" 9 "code.cloudfoundry.org/cli/actor/v2action" 10 "code.cloudfoundry.org/cli/actor/v3action" 11 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant" 12 "code.cloudfoundry.org/cli/api/cloudcontroller/ccversion" 13 "code.cloudfoundry.org/cli/command" 14 "code.cloudfoundry.org/cli/command/flag" 15 "code.cloudfoundry.org/cli/command/translatableerror" 16 "code.cloudfoundry.org/cli/command/v6/shared" 17 ) 18 19 //go:generate counterfeiter . V3ZeroDowntimeVersionActor 20 21 type V3ZeroDowntimeVersionActor interface { 22 ZeroDowntimePollStart(appGUID string, warningsChannel chan<- v3action.Warnings) error 23 CreateDeployment(appGUID string, deploymentGUID string) (string, v3action.Warnings, error) 24 PollDeployment(deploymentGUID string, warningsChannel chan<- v3action.Warnings) error 25 CloudControllerAPIVersion() string 26 CreateAndUploadBitsPackageByApplicationNameAndSpace(appName string, spaceGUID string, bitsPath string) (v3action.Package, v3action.Warnings, error) 27 CreateDockerPackageByApplicationNameAndSpace(appName string, spaceGUID string, dockerImageCredentials v3action.DockerImageCredentials) (v3action.Package, v3action.Warnings, error) 28 CreateApplicationInSpace(app v3action.Application, spaceGUID string) (v3action.Application, v3action.Warnings, error) 29 GetApplicationByNameAndSpace(appName string, spaceGUID string) (v3action.Application, v3action.Warnings, error) 30 GetCurrentDropletByApplication(appGUID string) (v3action.Droplet, v3action.Warnings, error) 31 GetStreamingLogsForApplicationByNameAndSpace(appName string, spaceGUID string, client v3action.NOAAClient) (<-chan *v3action.LogMessage, <-chan error, v3action.Warnings, error) 32 PollStart(appGUID string, warningsChannel chan<- v3action.Warnings) error 33 SetApplicationDropletByApplicationNameAndSpace(appName string, spaceGUID string, dropletGUID string) (v3action.Warnings, error) 34 StagePackage(packageGUID string, appName string) (<-chan v3action.Droplet, <-chan v3action.Warnings, <-chan error) 35 RestartApplication(appGUID string) (v3action.Warnings, error) 36 UpdateApplication(app v3action.Application) (v3action.Application, v3action.Warnings, error) 37 } 38 39 type V3ZeroDowntimePushCommand struct { 40 RequiredArgs flag.AppName `positional-args:"yes"` 41 Buildpacks []string `short:"b" description:"Custom buildpack by name (e.g. my-buildpack) or Git URL (e.g. 'https://github.com/cloudfoundry/java-buildpack.git') or Git URL with a branch or tag (e.g. 'https://github.com/cloudfoundry/java-buildpack.git#v3.3.0' for 'v3.3.0' tag). To use built-in buildpacks only, specify 'default' or 'null'"` 42 StackName string `short:"s" description:"Stack to use (a stack is a pre-built file system, including an operating system, that can run apps)"` 43 DockerImage flag.DockerImage `long:"docker-image" short:"o" description:"Docker image to use (e.g. user/docker-image-name)"` 44 DockerUsername string `long:"docker-username" description:"Repository username; used with password from environment variable CF_DOCKER_PASSWORD"` 45 NoRoute bool `long:"no-route" description:"Do not map a route to this app"` 46 NoStart bool `long:"no-start" description:"Do not stage and start the app after pushing"` 47 WaitUntilDeployed bool `long:"wait-for-deploy-complete" description:"Wait for the entire deployment to complete"` 48 AppPath flag.PathWithExistenceCheck `short:"p" description:"Path to app directory or to a zip file of the contents of the app directory"` 49 dockerPassword interface{} `environmentName:"CF_DOCKER_PASSWORD" environmentDescription:"Password used for private docker repository"` 50 usage interface{} `usage:"CF_NAME v3-zdt-push APP_NAME [-b BUILDPACK]... [-p APP_PATH] [--no-route] [--no-start]\n CF_NAME v3-zdt-push APP_NAME --docker-image [REGISTRY_HOST:PORT/]IMAGE[:TAG] [--docker-username USERNAME] [--no-route] [--no-start]"` 51 envCFStagingTimeout interface{} `environmentName:"CF_STAGING_TIMEOUT" environmentDescription:"Max wait time for buildpack staging, in minutes" environmentDefault:"15"` 52 envCFStartupTimeout interface{} `environmentName:"CF_STARTUP_TIMEOUT" environmentDescription:"Max wait time for app instance startup, in minutes" environmentDefault:"5"` 53 54 UI command.UI 55 Config command.Config 56 NOAAClient v3action.NOAAClient 57 Actor V3PushActor 58 VersionActor V3PushVersionActor 59 SharedActor command.SharedActor 60 AppSummaryDisplayer shared.AppSummaryDisplayer 61 PackageDisplayer shared.PackageDisplayer 62 ProgressBar ProgressBar 63 64 ZdtActor V3ZeroDowntimeVersionActor 65 OriginalV3PushActor OriginalV3PushActor 66 OriginalV2PushActor OriginalV2PushActor 67 } 68 69 func (cmd *V3ZeroDowntimePushCommand) Setup(config command.Config, ui command.UI) error { 70 cmd.UI = ui 71 cmd.Config = config 72 sharedActor := sharedaction.NewActor(config) 73 74 ccClient, uaaClient, err := shared.NewV3BasedClients(config, ui, true, "") 75 if err != nil { 76 return err 77 } 78 v3actor := v3action.NewActor(ccClient, config, sharedActor, nil) 79 cmd.ZdtActor = v3actor 80 cmd.OriginalV3PushActor = v3actor 81 82 ccClientV2, uaaClientV2, err := shared.NewClients(config, ui, true) 83 if err != nil { 84 return err 85 } 86 87 v2Actor := v2action.NewActor(ccClientV2, uaaClientV2, config) 88 89 cmd.SharedActor = sharedActor 90 cmd.OriginalV2PushActor = pushaction.NewActor(v2Actor, v3actor, sharedActor) 91 92 v2AppActor := v2action.NewActor(ccClientV2, uaaClientV2, config) 93 cmd.NOAAClient = shared.NewNOAAClient(ccClient.Info.Logging(), config, uaaClient, ui) 94 95 cmd.AppSummaryDisplayer = shared.AppSummaryDisplayer{ 96 UI: cmd.UI, 97 Config: cmd.Config, 98 Actor: cmd.OriginalV3PushActor, 99 V2AppActor: v2AppActor, 100 AppName: cmd.RequiredArgs.AppName, 101 } 102 cmd.PackageDisplayer = shared.NewPackageDisplayer(cmd.UI, cmd.Config) 103 104 return nil 105 } 106 107 func (cmd V3ZeroDowntimePushCommand) Execute(args []string) error { 108 cmd.UI.DisplayWarning(command.ExperimentalWarning) 109 110 err := cmd.validateArgs() 111 if err != nil { 112 return err 113 } 114 115 err = command.MinimumCCAPIVersionCheck(cmd.ZdtActor.CloudControllerAPIVersion(), ccversion.MinVersionZeroDowntimePushV3) 116 if err != nil { 117 return err 118 } 119 120 err = cmd.SharedActor.CheckTarget(true, true) 121 if err != nil { 122 return err 123 } 124 125 user, err := cmd.Config.CurrentUser() 126 if err != nil { 127 return err 128 } 129 130 if !verifyBuildpacks(cmd.Buildpacks) { 131 return translatableerror.ConflictingBuildpacksError{} 132 } 133 134 var app v3action.Application 135 app, err = cmd.getApplication() 136 if _, ok := err.(actionerror.ApplicationNotFoundError); ok { 137 app, err = cmd.createApplication(user.Name) 138 if err != nil { 139 return err 140 } 141 } else if err != nil { 142 return err 143 } else { 144 app, err = cmd.updateApplication(user.Name, app.GUID) 145 if err != nil { 146 return err 147 } 148 } 149 150 pkg, err := cmd.createPackage() 151 if err != nil { 152 return err 153 } 154 155 if cmd.NoStart { 156 return nil 157 } 158 159 dropletGUID, err := cmd.stagePackage(pkg, user.Name) 160 if err != nil { 161 return err 162 } 163 164 if !cmd.NoRoute { 165 err = cmd.createAndMapRoutes(app) 166 if err != nil { 167 return err 168 } 169 } 170 171 warnings := make(chan v3action.Warnings) 172 done := make(chan bool) 173 go func() { 174 for { 175 select { 176 case message := <-warnings: 177 cmd.UI.DisplayWarnings(message) 178 case <-done: 179 return 180 } 181 } 182 }() 183 184 switch app.State { 185 case constant.ApplicationStopped: 186 err = cmd.setApplicationDroplet(dropletGUID, user.Name) 187 if err != nil { 188 return err 189 } 190 191 err = cmd.restartApplication(app.GUID, user.Name) 192 if err != nil { 193 return err 194 } 195 196 cmd.UI.DisplayText("Waiting for app to start...") 197 err = cmd.ZdtActor.PollStart(app.GUID, warnings) 198 199 case constant.ApplicationStarted: 200 var deploymentGUID string 201 deploymentGUID, err = cmd.createDeployment(app.GUID, user.Name, dropletGUID) 202 if err != nil { 203 return err 204 } 205 206 cmd.UI.DisplayText("Waiting for app to start...") 207 if cmd.WaitUntilDeployed { 208 err = cmd.ZdtActor.PollDeployment(deploymentGUID, warnings) // 209 } else { 210 err = cmd.ZdtActor.ZeroDowntimePollStart(app.GUID, warnings) 211 } 212 default: 213 return fmt.Errorf("inconceivable application state: %s", app.State) 214 } 215 216 done <- true 217 if err != nil { 218 if _, ok := err.(actionerror.StartupTimeoutError); ok { 219 return translatableerror.StartupTimeoutError{ 220 AppName: cmd.RequiredArgs.AppName, 221 BinaryName: cmd.Config.BinaryName(), 222 } 223 } 224 225 return err 226 } 227 228 cmd.UI.DisplayTextWithFlavor("Showing health and status for 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": user.Name, 233 }) 234 cmd.UI.DisplayNewline() 235 236 return cmd.AppSummaryDisplayer.DisplayAppInfo() 237 } 238 239 func (cmd V3ZeroDowntimePushCommand) validateArgs() error { 240 switch { 241 case cmd.DockerImage.Path != "" && cmd.AppPath != "": 242 return translatableerror.ArgumentCombinationError{ 243 Args: []string{"--docker-image", "-o", "-p"}, 244 } 245 case cmd.DockerImage.Path != "" && len(cmd.Buildpacks) > 0: 246 return translatableerror.ArgumentCombinationError{ 247 Args: []string{"-b", "--docker-image", "-o"}, 248 } 249 case cmd.DockerUsername != "" && cmd.DockerImage.Path == "": 250 return translatableerror.RequiredFlagsError{ 251 Arg1: "--docker-image, -o", Arg2: "--docker-username", 252 } 253 case cmd.DockerUsername != "" && cmd.Config.DockerPassword() == "": 254 return translatableerror.DockerPasswordNotSetError{} 255 } 256 return nil 257 } 258 259 func (cmd V3ZeroDowntimePushCommand) createApplication(userName string) (v3action.Application, error) { 260 appToCreate := v3action.Application{ 261 Name: cmd.RequiredArgs.AppName, 262 } 263 264 if cmd.DockerImage.Path != "" { 265 appToCreate.LifecycleType = constant.AppLifecycleTypeDocker 266 } else { 267 appToCreate.LifecycleType = constant.AppLifecycleTypeBuildpack 268 appToCreate.LifecycleBuildpacks = cmd.Buildpacks 269 appToCreate.StackName = cmd.StackName 270 } 271 272 app, warnings, err := cmd.ZdtActor.CreateApplicationInSpace( 273 appToCreate, 274 cmd.Config.TargetedSpace().GUID, 275 ) 276 cmd.UI.DisplayWarnings(warnings) 277 if err != nil { 278 return v3action.Application{}, err 279 } 280 281 cmd.UI.DisplayTextWithFlavor("Creating app {{.AppName}} in org {{.CurrentOrg}} / space {{.CurrentSpace}} as {{.CurrentUser}}...", map[string]interface{}{ 282 "AppName": cmd.RequiredArgs.AppName, 283 "CurrentSpace": cmd.Config.TargetedSpace().Name, 284 "CurrentOrg": cmd.Config.TargetedOrganization().Name, 285 "CurrentUser": userName, 286 }) 287 288 cmd.UI.DisplayOK() 289 cmd.UI.DisplayNewline() 290 return app, nil 291 } 292 293 func (cmd V3ZeroDowntimePushCommand) getApplication() (v3action.Application, error) { 294 app, warnings, err := cmd.ZdtActor.GetApplicationByNameAndSpace(cmd.RequiredArgs.AppName, cmd.Config.TargetedSpace().GUID) 295 cmd.UI.DisplayWarnings(warnings) 296 if err != nil { 297 return v3action.Application{}, err 298 } 299 300 return app, nil 301 } 302 303 func (cmd V3ZeroDowntimePushCommand) updateApplication(userName string, appGUID string) (v3action.Application, error) { 304 cmd.UI.DisplayTextWithFlavor("Updating app {{.AppName}} in org {{.CurrentOrg}} / space {{.CurrentSpace}} as {{.CurrentUser}}...", map[string]interface{}{ 305 "AppName": cmd.RequiredArgs.AppName, 306 "CurrentSpace": cmd.Config.TargetedSpace().Name, 307 "CurrentOrg": cmd.Config.TargetedOrganization().Name, 308 "CurrentUser": userName, 309 }) 310 311 appToUpdate := v3action.Application{ 312 GUID: appGUID, 313 } 314 315 if cmd.DockerImage.Path != "" { 316 appToUpdate.LifecycleType = constant.AppLifecycleTypeDocker 317 318 } else { 319 appToUpdate.LifecycleType = constant.AppLifecycleTypeBuildpack 320 appToUpdate.LifecycleBuildpacks = cmd.Buildpacks 321 appToUpdate.StackName = cmd.StackName 322 } 323 324 app, warnings, err := cmd.ZdtActor.UpdateApplication(appToUpdate) 325 cmd.UI.DisplayWarnings(warnings) 326 if err != nil { 327 return v3action.Application{}, err 328 } 329 330 cmd.UI.DisplayOK() 331 cmd.UI.DisplayNewline() 332 return app, nil 333 } 334 335 func (cmd V3ZeroDowntimePushCommand) createAndMapRoutes(app v3action.Application) error { 336 cmd.UI.DisplayText("Mapping routes...") 337 routeWarnings, err := cmd.OriginalV2PushActor.CreateAndMapDefaultApplicationRoute(cmd.Config.TargetedOrganization().GUID, cmd.Config.TargetedSpace().GUID, v2action.Application{Name: app.Name, GUID: app.GUID}) 338 cmd.UI.DisplayWarnings(routeWarnings) 339 if err != nil { 340 return err 341 } 342 343 cmd.UI.DisplayOK() 344 cmd.UI.DisplayNewline() 345 return nil 346 } 347 348 func (cmd V3ZeroDowntimePushCommand) createPackage() (v3action.Package, error) { 349 isDockerImage := cmd.DockerImage.Path != "" 350 err := cmd.PackageDisplayer.DisplaySetupMessage(cmd.RequiredArgs.AppName, isDockerImage) 351 if err != nil { 352 return v3action.Package{}, err 353 } 354 355 var ( 356 pkg v3action.Package 357 warnings v3action.Warnings 358 ) 359 360 if isDockerImage { 361 pkg, warnings, err = cmd.ZdtActor.CreateDockerPackageByApplicationNameAndSpace(cmd.RequiredArgs.AppName, cmd.Config.TargetedSpace().GUID, v3action.DockerImageCredentials{Path: cmd.DockerImage.Path, Username: cmd.DockerUsername, Password: cmd.Config.DockerPassword()}) 362 } else { 363 pkg, warnings, err = cmd.ZdtActor.CreateAndUploadBitsPackageByApplicationNameAndSpace(cmd.RequiredArgs.AppName, cmd.Config.TargetedSpace().GUID, string(cmd.AppPath)) 364 } 365 366 cmd.UI.DisplayWarnings(warnings) 367 if err != nil { 368 return v3action.Package{}, err 369 } 370 371 cmd.UI.DisplayOK() 372 cmd.UI.DisplayNewline() 373 return pkg, nil 374 } 375 376 func (cmd V3ZeroDowntimePushCommand) stagePackage(pkg v3action.Package, userName string) (string, error) { 377 cmd.UI.DisplayTextWithFlavor("Staging package for app {{.AppName}} in org {{.OrgName}} / space {{.SpaceName}} as {{.Username}}...", map[string]interface{}{ 378 "AppName": cmd.RequiredArgs.AppName, 379 "OrgName": cmd.Config.TargetedOrganization().Name, 380 "SpaceName": cmd.Config.TargetedSpace().Name, 381 "Username": userName, 382 }) 383 384 logStream, logErrStream, logWarnings, logErr := cmd.ZdtActor.GetStreamingLogsForApplicationByNameAndSpace(cmd.RequiredArgs.AppName, cmd.Config.TargetedSpace().GUID, cmd.NOAAClient) 385 cmd.UI.DisplayWarnings(logWarnings) 386 if logErr != nil { 387 return "", logErr 388 } 389 390 buildStream, warningsStream, errStream := cmd.ZdtActor.StagePackage(pkg.GUID, cmd.RequiredArgs.AppName) 391 droplet, err := shared.PollStage(buildStream, warningsStream, errStream, logStream, logErrStream, cmd.UI) 392 if err != nil { 393 return "", err 394 } 395 396 cmd.UI.DisplayOK() 397 cmd.UI.DisplayNewline() 398 return droplet.GUID, nil 399 } 400 401 func (cmd V3ZeroDowntimePushCommand) setApplicationDroplet(dropletGUID string, userName string) error { 402 cmd.UI.DisplayTextWithFlavor("Setting app {{.AppName}} to droplet {{.DropletGUID}} in org {{.OrgName}} / space {{.SpaceName}} as {{.Username}}...", map[string]interface{}{ 403 "AppName": cmd.RequiredArgs.AppName, 404 "DropletGUID": dropletGUID, 405 "OrgName": cmd.Config.TargetedOrganization().Name, 406 "SpaceName": cmd.Config.TargetedSpace().Name, 407 "Username": userName, 408 }) 409 410 warnings, err := cmd.ZdtActor.SetApplicationDropletByApplicationNameAndSpace(cmd.RequiredArgs.AppName, cmd.Config.TargetedSpace().GUID, dropletGUID) 411 cmd.UI.DisplayWarnings(warnings) 412 if err != nil { 413 return err 414 } 415 416 cmd.UI.DisplayOK() 417 cmd.UI.DisplayNewline() 418 return nil 419 } 420 421 func (cmd V3ZeroDowntimePushCommand) restartApplication(appGUID string, userName string) error { 422 cmd.UI.DisplayTextWithFlavor("Starting app {{.AppName}} in org {{.OrgName}} / space {{.SpaceName}} as {{.Username}}...", map[string]interface{}{ 423 "AppName": cmd.RequiredArgs.AppName, 424 "OrgName": cmd.Config.TargetedOrganization().Name, 425 "SpaceName": cmd.Config.TargetedSpace().Name, 426 "Username": userName, 427 }) 428 429 warnings, err := cmd.ZdtActor.RestartApplication(appGUID) 430 cmd.UI.DisplayWarnings(warnings) 431 if err != nil { 432 return err 433 } 434 cmd.UI.DisplayOK() 435 cmd.UI.DisplayNewline() 436 return nil 437 } 438 439 func (cmd V3ZeroDowntimePushCommand) createDeployment(appGUID string, userName string, dropletGUID string) (string, error) { 440 cmd.UI.DisplayTextWithFlavor("Starting deployment for app {{.AppName}} in org {{.CurrentOrg}} / space {{.CurrentSpace}} as {{.CurrentUser}}...", map[string]interface{}{ 441 "AppName": cmd.RequiredArgs.AppName, 442 "CurrentSpace": cmd.Config.TargetedSpace().Name, 443 "CurrentOrg": cmd.Config.TargetedOrganization().Name, 444 "CurrentUser": userName, 445 }) 446 447 deploymentGUID, warnings, err := cmd.ZdtActor.CreateDeployment(appGUID, dropletGUID) 448 cmd.UI.DisplayWarnings(warnings) 449 if err != nil { 450 return "", err 451 } 452 cmd.UI.DisplayOK() 453 cmd.UI.DisplayNewline() 454 return deploymentGUID, nil 455 }