github.com/cloudfoundry-community/cloudfoundry-cli@v6.44.1-0.20240130060226-cda5ed8e89a5+incompatible/actor/v3action/application.go (about) 1 package v3action 2 3 import ( 4 "time" 5 6 "code.cloudfoundry.org/cli/actor/actionerror" 7 "code.cloudfoundry.org/cli/api/cloudcontroller/ccerror" 8 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" 9 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant" 10 ) 11 12 // Application represents a V3 actor application. 13 type Application struct { 14 Name string 15 GUID string 16 StackName string 17 State constant.ApplicationState 18 LifecycleType constant.AppLifecycleType 19 LifecycleBuildpacks []string 20 SpaceGUID string 21 } 22 23 func (app Application) Started() bool { 24 return app.State == constant.ApplicationStarted 25 } 26 27 func (app Application) Stopped() bool { 28 return app.State == constant.ApplicationStopped 29 } 30 31 func (actor Actor) DeleteApplicationByNameAndSpace(name string, spaceGUID string) (Warnings, error) { 32 var allWarnings Warnings 33 34 app, getAppWarnings, err := actor.GetApplicationByNameAndSpace(name, spaceGUID) 35 allWarnings = append(allWarnings, getAppWarnings...) 36 if err != nil { 37 return allWarnings, err 38 } 39 40 jobURL, deleteAppWarnings, err := actor.CloudControllerClient.DeleteApplication(app.GUID) 41 allWarnings = append(allWarnings, deleteAppWarnings...) 42 if err != nil { 43 return allWarnings, err 44 } 45 46 pollWarnings, err := actor.CloudControllerClient.PollJob(jobURL) 47 allWarnings = append(allWarnings, pollWarnings...) 48 return allWarnings, err 49 } 50 51 // GetApplicationByNameAndSpace returns the application with the given 52 // name in the given space. 53 func (actor Actor) GetApplicationByNameAndSpace(appName string, spaceGUID string) (Application, Warnings, error) { 54 apps, warnings, err := actor.CloudControllerClient.GetApplications( 55 ccv3.Query{Key: ccv3.NameFilter, Values: []string{appName}}, 56 ccv3.Query{Key: ccv3.SpaceGUIDFilter, Values: []string{spaceGUID}}, 57 ) 58 if err != nil { 59 return Application{}, Warnings(warnings), err 60 } 61 62 if len(apps) == 0 { 63 return Application{}, Warnings(warnings), actionerror.ApplicationNotFoundError{Name: appName} 64 } 65 66 return actor.convertCCToActorApplication(apps[0]), Warnings(warnings), nil 67 } 68 69 // GetApplicationsBySpace returns all applications in a space. 70 func (actor Actor) GetApplicationsBySpace(spaceGUID string) ([]Application, Warnings, error) { 71 ccApps, warnings, err := actor.CloudControllerClient.GetApplications( 72 ccv3.Query{Key: ccv3.SpaceGUIDFilter, Values: []string{spaceGUID}}, 73 ) 74 75 if err != nil { 76 return []Application{}, Warnings(warnings), err 77 } 78 79 var apps []Application 80 for _, ccApp := range ccApps { 81 apps = append(apps, actor.convertCCToActorApplication(ccApp)) 82 } 83 return apps, Warnings(warnings), nil 84 } 85 86 // GetApplicationsByGUIDs returns all applications with the provided GUIDs. 87 func (actor Actor) GetApplicationsByGUIDs(appGUIDs ...string) ([]Application, Warnings, error) { 88 ccApps, warnings, err := actor.CloudControllerClient.GetApplications( 89 ccv3.Query{Key: ccv3.GUIDFilter, Values: appGUIDs}, 90 ) 91 92 if err != nil { 93 return []Application{}, Warnings(warnings), err 94 } 95 96 var apps []Application 97 for _, ccApp := range ccApps { 98 apps = append(apps, actor.convertCCToActorApplication(ccApp)) 99 } 100 return apps, Warnings(warnings), nil 101 } 102 103 // CreateApplicationInSpace creates and returns the application with the given 104 // name in the given space. 105 func (actor Actor) CreateApplicationInSpace(app Application, spaceGUID string) (Application, Warnings, error) { 106 createdApp, warnings, err := actor.CloudControllerClient.CreateApplication( 107 ccv3.Application{ 108 LifecycleType: app.LifecycleType, 109 LifecycleBuildpacks: app.LifecycleBuildpacks, 110 StackName: app.StackName, 111 Name: app.Name, 112 Relationships: ccv3.Relationships{ 113 constant.RelationshipTypeSpace: ccv3.Relationship{GUID: spaceGUID}, 114 }, 115 }) 116 117 if err != nil { 118 if _, ok := err.(ccerror.NameNotUniqueInSpaceError); ok { 119 return Application{}, Warnings(warnings), actionerror.ApplicationAlreadyExistsError{Name: app.Name} 120 } 121 return Application{}, Warnings(warnings), err 122 } 123 124 return actor.convertCCToActorApplication(createdApp), Warnings(warnings), nil 125 } 126 127 // StopApplication stops an application. 128 func (actor Actor) StopApplication(appGUID string) (Warnings, error) { 129 _, warnings, err := actor.CloudControllerClient.UpdateApplicationStop(appGUID) 130 131 return Warnings(warnings), err 132 } 133 134 // StartApplication starts an application. 135 func (actor Actor) StartApplication(appGUID string) (Application, Warnings, error) { 136 updatedApp, warnings, err := actor.CloudControllerClient.UpdateApplicationStart(appGUID) 137 if err != nil { 138 return Application{}, Warnings(warnings), err 139 } 140 141 return actor.convertCCToActorApplication(updatedApp), Warnings(warnings), nil 142 } 143 144 // RestartApplication restarts an application. 145 func (actor Actor) RestartApplication(appGUID string) (Warnings, error) { 146 _, warnings, err := actor.CloudControllerClient.UpdateApplicationRestart(appGUID) 147 148 return Warnings(warnings), err 149 } 150 151 func (actor Actor) PollStart(appGUID string, warningsChannel chan<- Warnings) error { 152 processes, warnings, err := actor.CloudControllerClient.GetApplicationProcesses(appGUID) 153 warningsChannel <- Warnings(warnings) 154 if err != nil { 155 return err 156 } 157 158 timeout := time.Now().Add(actor.Config.StartupTimeout()) 159 for time.Now().Before(timeout) { 160 readyProcs := 0 161 for _, process := range processes { 162 ready, err := actor.processStatus(process, warningsChannel) 163 if err != nil { 164 return err 165 } 166 167 if ready { 168 readyProcs++ 169 } 170 } 171 172 if readyProcs == len(processes) { 173 return nil 174 } 175 time.Sleep(actor.Config.PollingInterval()) 176 } 177 178 return actionerror.StartupTimeoutError{} 179 } 180 181 // UpdateApplication updates the buildpacks on an application 182 func (actor Actor) UpdateApplication(app Application) (Application, Warnings, error) { 183 ccApp := ccv3.Application{ 184 GUID: app.GUID, 185 StackName: app.StackName, 186 LifecycleType: app.LifecycleType, 187 LifecycleBuildpacks: app.LifecycleBuildpacks, 188 } 189 190 updatedApp, warnings, err := actor.CloudControllerClient.UpdateApplication(ccApp) 191 if err != nil { 192 return Application{}, Warnings(warnings), err 193 } 194 195 return actor.convertCCToActorApplication(updatedApp), Warnings(warnings), nil 196 } 197 198 func (Actor) convertCCToActorApplication(app ccv3.Application) Application { 199 return Application{ 200 GUID: app.GUID, 201 StackName: app.StackName, 202 LifecycleType: app.LifecycleType, 203 LifecycleBuildpacks: app.LifecycleBuildpacks, 204 Name: app.Name, 205 State: app.State, 206 SpaceGUID: app.Relationships[constant.RelationshipTypeSpace].GUID, 207 } 208 } 209 210 func (actor Actor) processStatus(process ccv3.Process, warningsChannel chan<- Warnings) (bool, error) { 211 instances, warnings, err := actor.CloudControllerClient.GetProcessInstances(process.GUID) 212 warningsChannel <- Warnings(warnings) 213 if err != nil { 214 return false, err 215 } 216 if len(instances) == 0 { 217 return true, nil 218 } 219 220 for _, instance := range instances { 221 if instance.State == constant.ProcessInstanceRunning { 222 return true, nil 223 } 224 } 225 226 for _, instance := range instances { 227 if instance.State != constant.ProcessInstanceCrashed { 228 return false, nil 229 } 230 } 231 232 // all of the instances are crashed at this point 233 return true, nil 234 }