github.com/franc20/ayesa_sap@v7.0.0-beta.28.0.20200124003224-302d4d52fa6c+incompatible/actor/v7action/application.go (about) 1 package v7action 2 3 import ( 4 "errors" 5 "time" 6 7 "code.cloudfoundry.org/cli/actor/actionerror" 8 "code.cloudfoundry.org/cli/api/cloudcontroller/ccerror" 9 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" 10 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant" 11 ) 12 13 // Application represents a V3 actor application. 14 type Application struct { 15 Name string 16 GUID string 17 StackName string 18 State constant.ApplicationState 19 LifecycleType constant.AppLifecycleType 20 LifecycleBuildpacks []string 21 Metadata *Metadata 22 } 23 24 func (app Application) Started() bool { 25 return app.State == constant.ApplicationStarted 26 } 27 28 func (app Application) Stopped() bool { 29 return app.State == constant.ApplicationStopped 30 } 31 32 func (actor Actor) DeleteApplicationByNameAndSpace(name, spaceGUID string, deleteRoutes bool) (Warnings, error) { 33 var allWarnings Warnings 34 35 app, getAppWarnings, err := actor.GetApplicationByNameAndSpace(name, spaceGUID) 36 allWarnings = append(allWarnings, getAppWarnings...) 37 if err != nil { 38 return allWarnings, err 39 } 40 41 var routes []Route 42 if deleteRoutes { 43 var getRoutesWarnings Warnings 44 routes, getRoutesWarnings, err = actor.GetApplicationRoutes(app.GUID) 45 allWarnings = append(allWarnings, getRoutesWarnings...) 46 if err != nil { 47 return allWarnings, err 48 } 49 50 for _, route := range routes { 51 if len(route.Destinations) > 1 { 52 for _, destination := range route.Destinations { 53 guid := destination.App.GUID 54 if guid != app.GUID { 55 return allWarnings, actionerror.RouteBoundToMultipleAppsError{AppName: app.Name, RouteURL: route.URL} 56 } 57 } 58 } 59 } 60 } 61 62 jobURL, deleteAppWarnings, err := actor.CloudControllerClient.DeleteApplication(app.GUID) 63 allWarnings = append(allWarnings, deleteAppWarnings...) 64 if err != nil { 65 return allWarnings, err 66 } 67 68 pollWarnings, err := actor.CloudControllerClient.PollJob(jobURL) 69 allWarnings = append(allWarnings, pollWarnings...) 70 if err != nil { 71 return allWarnings, err 72 } 73 74 if deleteRoutes { 75 for _, route := range routes { 76 jobURL, deleteRouteWarnings, err := actor.CloudControllerClient.DeleteRoute(route.GUID) 77 allWarnings = append(allWarnings, deleteRouteWarnings...) 78 if err != nil { 79 if _, ok := err.(ccerror.ResourceNotFoundError); ok { 80 continue 81 } 82 return allWarnings, err 83 } 84 85 pollWarnings, err := actor.CloudControllerClient.PollJob(jobURL) 86 allWarnings = append(allWarnings, pollWarnings...) 87 if err != nil { 88 return allWarnings, err 89 } 90 } 91 } 92 93 return allWarnings, err 94 } 95 96 func (actor Actor) GetApplicationsByGUIDs(appGUIDs []string) ([]Application, Warnings, error) { 97 uniqueAppGUIDs := map[string]bool{} 98 for _, appGUID := range appGUIDs { 99 uniqueAppGUIDs[appGUID] = true 100 } 101 102 apps, warnings, err := actor.CloudControllerClient.GetApplications( 103 ccv3.Query{Key: ccv3.GUIDFilter, Values: appGUIDs}, 104 ) 105 106 if err != nil { 107 return nil, Warnings(warnings), err 108 } 109 110 if len(apps) < len(uniqueAppGUIDs) { 111 return nil, Warnings(warnings), actionerror.ApplicationsNotFoundError{} 112 } 113 114 actorApps := []Application{} 115 for _, a := range apps { 116 actorApps = append(actorApps, actor.convertCCToActorApplication(a)) 117 } 118 119 return actorApps, Warnings(warnings), nil 120 } 121 122 func (actor Actor) GetApplicationsByNamesAndSpace(appNames []string, spaceGUID string) ([]Application, Warnings, error) { 123 uniqueAppNames := map[string]bool{} 124 for _, appName := range appNames { 125 uniqueAppNames[appName] = true 126 } 127 128 apps, warnings, err := actor.CloudControllerClient.GetApplications( 129 ccv3.Query{Key: ccv3.NameFilter, Values: appNames}, 130 ccv3.Query{Key: ccv3.SpaceGUIDFilter, Values: []string{spaceGUID}}, 131 ) 132 133 if err != nil { 134 return nil, Warnings(warnings), err 135 } 136 137 if len(apps) < len(uniqueAppNames) { 138 return nil, Warnings(warnings), actionerror.ApplicationsNotFoundError{} 139 } 140 141 actorApps := []Application{} 142 for _, a := range apps { 143 actorApps = append(actorApps, actor.convertCCToActorApplication(a)) 144 } 145 return actorApps, Warnings(warnings), nil 146 } 147 148 // GetApplicationByNameAndSpace returns the application with the given 149 // name in the given space. 150 func (actor Actor) GetApplicationByNameAndSpace(appName string, spaceGUID string) (Application, Warnings, error) { 151 apps, warnings, err := actor.GetApplicationsByNamesAndSpace([]string{appName}, spaceGUID) 152 153 if err != nil { 154 if _, ok := err.(actionerror.ApplicationsNotFoundError); ok { 155 return Application{}, warnings, actionerror.ApplicationNotFoundError{Name: appName} 156 } 157 return Application{}, warnings, err 158 } 159 160 return apps[0], warnings, nil 161 } 162 163 // GetApplicationsBySpace returns all applications in a space. 164 func (actor Actor) GetApplicationsBySpace(spaceGUID string) ([]Application, Warnings, error) { 165 ccApps, warnings, err := actor.CloudControllerClient.GetApplications( 166 ccv3.Query{Key: ccv3.SpaceGUIDFilter, Values: []string{spaceGUID}}, 167 ) 168 169 if err != nil { 170 return []Application{}, Warnings(warnings), err 171 } 172 173 var apps []Application 174 for _, ccApp := range ccApps { 175 apps = append(apps, actor.convertCCToActorApplication(ccApp)) 176 } 177 return apps, Warnings(warnings), nil 178 } 179 180 // CreateApplicationInSpace creates and returns the application with the given 181 // name in the given space. 182 func (actor Actor) CreateApplicationInSpace(app Application, spaceGUID string) (Application, Warnings, error) { 183 createdApp, warnings, err := actor.CloudControllerClient.CreateApplication( 184 ccv3.Application{ 185 LifecycleType: app.LifecycleType, 186 LifecycleBuildpacks: app.LifecycleBuildpacks, 187 StackName: app.StackName, 188 Name: app.Name, 189 Relationships: ccv3.Relationships{ 190 constant.RelationshipTypeSpace: ccv3.Relationship{GUID: spaceGUID}, 191 }, 192 }) 193 194 if err != nil { 195 return Application{}, Warnings(warnings), err 196 } 197 198 return actor.convertCCToActorApplication(createdApp), Warnings(warnings), nil 199 } 200 201 // SetApplicationProcessHealthCheckTypeByNameAndSpace sets the health check 202 // information of the provided processType for an application with the given 203 // name and space GUID. 204 func (actor Actor) SetApplicationProcessHealthCheckTypeByNameAndSpace( 205 appName string, 206 spaceGUID string, 207 healthCheckType constant.HealthCheckType, 208 httpEndpoint string, 209 processType string, 210 invocationTimeout int64, 211 ) (Application, Warnings, error) { 212 213 app, getWarnings, err := actor.GetApplicationByNameAndSpace(appName, spaceGUID) 214 if err != nil { 215 return Application{}, getWarnings, err 216 } 217 218 setWarnings, err := actor.UpdateProcessByTypeAndApplication( 219 processType, 220 app.GUID, 221 Process{ 222 HealthCheckType: healthCheckType, 223 HealthCheckEndpoint: httpEndpoint, 224 HealthCheckInvocationTimeout: invocationTimeout, 225 }) 226 return app, append(getWarnings, setWarnings...), err 227 } 228 229 // StopApplication stops an application. 230 func (actor Actor) StopApplication(appGUID string) (Warnings, error) { 231 _, warnings, err := actor.CloudControllerClient.UpdateApplicationStop(appGUID) 232 233 return Warnings(warnings), err 234 } 235 236 // StartApplication starts an application. 237 func (actor Actor) StartApplication(appGUID string) (Warnings, error) { 238 _, warnings, err := actor.CloudControllerClient.UpdateApplicationStart(appGUID) 239 return Warnings(warnings), err 240 } 241 242 // RestartApplication restarts an application and waits for it to start. 243 func (actor Actor) RestartApplication(appGUID string, noWait bool) (Warnings, error) { 244 // var allWarnings Warnings 245 _, warnings, err := actor.CloudControllerClient.UpdateApplicationRestart(appGUID) 246 return Warnings(warnings), err 247 } 248 249 func (actor Actor) GetUnstagedNewestPackageGUID(appGUID string) (string, Warnings, error) { 250 var err error 251 var allWarnings Warnings 252 packages, warnings, err := actor.CloudControllerClient.GetPackages( 253 ccv3.Query{Key: ccv3.AppGUIDFilter, Values: []string{appGUID}}, 254 ccv3.Query{Key: ccv3.OrderBy, Values: []string{ccv3.CreatedAtDescendingOrder}}, 255 ccv3.Query{Key: ccv3.PerPage, Values: []string{"1"}}) 256 allWarnings = append(allWarnings, warnings...) 257 if err != nil { 258 return "", allWarnings, err 259 } 260 if len(packages) == 0 { 261 return "", allWarnings, nil 262 } 263 264 newestPackage := packages[0] 265 266 droplets, warnings, err := actor.CloudControllerClient.GetPackageDroplets( 267 newestPackage.GUID, 268 ccv3.Query{Key: ccv3.StatesFilter, Values: []string{"STAGED"}}, 269 ccv3.Query{Key: ccv3.PerPage, Values: []string{"1"}}, 270 ) 271 allWarnings = append(allWarnings, warnings...) 272 if err != nil { 273 return "", allWarnings, err 274 } 275 276 if len(droplets) == 0 { 277 return newestPackage.GUID, allWarnings, nil 278 } 279 280 return "", allWarnings, nil 281 } 282 283 // PollStart polls an application's processes until some are started. If noWait is false, 284 // it waits for at least one instance of all processes to be running. If noWait is true, 285 // it only waits for an instance of the web process to be running. 286 func (actor Actor) PollStart(appGUID string, noWait bool) (Warnings, error) { 287 var allWarnings Warnings 288 processes, warnings, err := actor.CloudControllerClient.GetApplicationProcesses(appGUID) 289 allWarnings = append(allWarnings, warnings...) 290 if err != nil { 291 return allWarnings, err 292 } 293 294 var filteredProcesses []ccv3.Process 295 if noWait { 296 for _, process := range processes { 297 if process.Type == constant.ProcessTypeWeb { 298 filteredProcesses = append(filteredProcesses, process) 299 } 300 } 301 } else { 302 filteredProcesses = processes 303 } 304 305 timer := actor.Clock.NewTimer(time.Millisecond) 306 defer timer.Stop() 307 timeout := actor.Clock.After(actor.Config.StartupTimeout()) 308 309 for { 310 select { 311 case <-timeout: 312 return allWarnings, actionerror.StartupTimeoutError{} 313 case <-timer.C(): 314 stopPolling, warnings, err := actor.PollProcesses(filteredProcesses) 315 allWarnings = append(allWarnings, warnings...) 316 if stopPolling || err != nil { 317 return allWarnings, err 318 } 319 320 timer.Reset(actor.Config.PollingInterval()) 321 } 322 } 323 } 324 325 // PollStartForRolling polls a deploying application's processes until some are started. It does the same thing as PollStart, except it accounts for rolling deployments and whether 326 // they have failed or been canceled during polling. 327 func (actor Actor) PollStartForRolling(appGUID string, deploymentGUID string, noWait bool) (Warnings, error) { 328 var ( 329 deployment ccv3.Deployment 330 processes []ccv3.Process 331 allWarnings Warnings 332 ) 333 334 timer := actor.Clock.NewTimer(time.Millisecond) 335 defer timer.Stop() 336 timeout := actor.Clock.After(actor.Config.StartupTimeout()) 337 338 for { 339 select { 340 case <-timeout: 341 return allWarnings, actionerror.StartupTimeoutError{} 342 case <-timer.C(): 343 if !isDeployed(deployment) { 344 ccDeployment, warnings, err := actor.getDeployment(deploymentGUID) 345 allWarnings = append(allWarnings, warnings...) 346 if err != nil { 347 return allWarnings, err 348 } 349 deployment = ccDeployment 350 processes, warnings, err = actor.getProcesses(deployment, appGUID, noWait) 351 allWarnings = append(allWarnings, warnings...) 352 if err != nil { 353 return allWarnings, err 354 } 355 } 356 357 if noWait || isDeployed(deployment) { 358 stopPolling, warnings, err := actor.PollProcesses(processes) 359 allWarnings = append(allWarnings, warnings...) 360 if stopPolling || err != nil { 361 return allWarnings, err 362 } 363 } 364 365 timer.Reset(actor.Config.PollingInterval()) 366 } 367 } 368 } 369 370 func isDeployed(d ccv3.Deployment) bool { 371 return d.StatusValue == constant.DeploymentStatusValueFinalized && d.StatusReason == constant.DeploymentStatusReasonDeployed 372 } 373 374 // PollProcesses - return true if there's no need to keep polling 375 func (actor Actor) PollProcesses(processes []ccv3.Process) (bool, Warnings, error) { 376 numProcesses := len(processes) 377 numStableProcesses := 0 378 var allWarnings Warnings 379 for _, process := range processes { 380 ccInstances, ccWarnings, err := actor.CloudControllerClient.GetProcessInstances(process.GUID) 381 instances := ProcessInstances(ccInstances) 382 allWarnings = append(allWarnings, ccWarnings...) 383 if err != nil { 384 return true, allWarnings, err 385 } 386 387 if instances.Empty() || instances.AnyRunning() { 388 numStableProcesses += 1 389 continue 390 } 391 392 if instances.AllCrashed() { 393 return true, allWarnings, actionerror.AllInstancesCrashedError{} 394 } 395 396 //precondition: !instances.Empty() && no instances are running 397 // do not increment numStableProcesses 398 return false, allWarnings, nil 399 } 400 return numStableProcesses == numProcesses, allWarnings, nil 401 } 402 403 // UpdateApplication updates the buildpacks on an application 404 func (actor Actor) UpdateApplication(app Application) (Application, Warnings, error) { 405 ccApp := ccv3.Application{ 406 GUID: app.GUID, 407 StackName: app.StackName, 408 LifecycleType: app.LifecycleType, 409 LifecycleBuildpacks: app.LifecycleBuildpacks, 410 Metadata: (*ccv3.Metadata)(app.Metadata), 411 Name: app.Name, 412 } 413 414 updatedApp, warnings, err := actor.CloudControllerClient.UpdateApplication(ccApp) 415 if err != nil { 416 return Application{}, Warnings(warnings), err 417 } 418 419 return actor.convertCCToActorApplication(updatedApp), Warnings(warnings), nil 420 } 421 422 func (Actor) convertCCToActorApplication(app ccv3.Application) Application { 423 return Application{ 424 GUID: app.GUID, 425 StackName: app.StackName, 426 LifecycleType: app.LifecycleType, 427 LifecycleBuildpacks: app.LifecycleBuildpacks, 428 Name: app.Name, 429 State: app.State, 430 Metadata: (*Metadata)(app.Metadata), 431 } 432 } 433 434 func (actor Actor) getDeployment(deploymentGUID string) (ccv3.Deployment, Warnings, error) { 435 deployment, warnings, err := actor.CloudControllerClient.GetDeployment(deploymentGUID) 436 if err != nil { 437 return deployment, Warnings(warnings), err 438 } 439 440 if deployment.StatusValue == constant.DeploymentStatusValueFinalized { 441 switch deployment.StatusReason { 442 case constant.DeploymentStatusReasonCanceled: 443 return deployment, Warnings(warnings), errors.New("Deployment has been canceled") 444 case constant.DeploymentStatusReasonSuperseded: 445 return deployment, Warnings(warnings), errors.New("Deployment has been superseded") 446 } 447 } 448 449 return deployment, Warnings(warnings), err 450 } 451 452 func (actor Actor) getProcesses(deployment ccv3.Deployment, appGUID string, noWait bool) ([]ccv3.Process, Warnings, error) { 453 if noWait { 454 // these are only web processes for now so we can just use these 455 return deployment.NewProcesses, nil, nil 456 } 457 458 // if the deployment is deployed we know web are all running and PollProcesses will see those as stable 459 // so just getting all processes is equivalent to just getting non-web ones and polling those 460 if isDeployed(deployment) { 461 processes, warnings, err := actor.CloudControllerClient.GetApplicationProcesses(appGUID) 462 if err != nil { 463 return processes, Warnings(warnings), err 464 } 465 return processes, Warnings(warnings), nil 466 } 467 468 return nil, nil, nil 469 } 470 471 func (actor Actor) RenameApplicationByNameAndSpaceGUID(appName, newAppName, spaceGUID string) (Application, Warnings, error) { 472 allWarnings := Warnings{} 473 application, warnings, err := actor.GetApplicationByNameAndSpace(appName, spaceGUID) 474 allWarnings = append(allWarnings, warnings...) 475 if err != nil { 476 return Application{}, allWarnings, err 477 } 478 application.Name = newAppName 479 application, warnings, err = actor.UpdateApplication(application) 480 allWarnings = append(allWarnings, warnings...) 481 if err != nil { 482 return Application{}, allWarnings, err 483 } 484 485 return application, allWarnings, nil 486 }