github.com/cloudfoundry-community/cloudfoundry-cli@v6.44.1-0.20240130060226-cda5ed8e89a5+incompatible/api/cloudcontroller/ccv2/application.go (about) 1 package ccv2 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "net/url" 8 "time" 9 10 "code.cloudfoundry.org/cli/api/cloudcontroller" 11 "code.cloudfoundry.org/cli/api/cloudcontroller/ccerror" 12 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv2/constant" 13 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv2/internal" 14 "code.cloudfoundry.org/cli/types" 15 ) 16 17 // Application represents a Cloud Controller Application. 18 type Application struct { 19 // Buildpack is the buildpack set by the user. 20 Buildpack types.FilteredString 21 22 // Command is the user specified start command. 23 Command types.FilteredString 24 25 // DetectedBuildpack is the buildpack automatically detected. 26 DetectedBuildpack types.FilteredString 27 28 // DetectedStartCommand is the command used to start the application. 29 DetectedStartCommand types.FilteredString 30 31 // DiskQuota is the disk given to each instance, in megabytes. 32 DiskQuota types.NullByteSizeInMb 33 34 // DockerCredentials is the authentication information for the provided 35 // DockerImage. 36 DockerCredentials DockerCredentials 37 38 // DockerImage is the docker image location. 39 DockerImage string 40 41 // EnvironmentVariables are the environment variables passed to the app. 42 EnvironmentVariables map[string]string 43 44 // GUID is the unique application identifier. 45 GUID string 46 47 // HealthCheckTimeout is the number of seconds for health checking of an 48 // staged app when starting up. 49 HealthCheckTimeout uint64 50 51 // HealthCheckType is the type of health check that will be done to the app. 52 HealthCheckType constant.ApplicationHealthCheckType 53 54 // HealthCheckHTTPEndpoint is the url of the http health check endpoint. 55 HealthCheckHTTPEndpoint string 56 57 // Instances is the total number of app instances. 58 Instances types.NullInt 59 60 // Memory is the memory given to each instance, in megabytes. 61 Memory types.NullByteSizeInMb 62 63 // Name is the name given to the application. 64 Name string 65 66 // PackageState represents the staging state of the application bits. 67 PackageState constant.ApplicationPackageState 68 69 // PackageUpdatedAt is the last time the app bits were updated. In RFC3339. 70 PackageUpdatedAt time.Time 71 72 // SpaceGUID is the GUID of the app's space. 73 SpaceGUID string 74 75 // StackGUID is the GUID for the Stack the application is running on. 76 StackGUID string 77 78 // StagingFailedDescription is the verbose description of why the package 79 // failed to stage. 80 StagingFailedDescription string 81 82 // StagingFailedReason is the reason why the package failed to stage. 83 StagingFailedReason string 84 85 // State is the desired state of the application. 86 State constant.ApplicationState 87 88 // EnableSSH specifies whether SSH is enabled for this app. 89 EnableSSH types.NullBool 90 91 // Ports on which application may listen. 92 Ports []int 93 } 94 95 // MarshalJSON converts an application into a Cloud Controller Application. 96 func (application Application) MarshalJSON() ([]byte, error) { 97 ccApp := struct { 98 Buildpack *string `json:"buildpack,omitempty"` 99 Command *string `json:"command,omitempty"` 100 DiskQuota *uint64 `json:"disk_quota,omitempty"` 101 DockerCredentials *DockerCredentials `json:"docker_credentials,omitempty"` 102 DockerImage string `json:"docker_image,omitempty"` 103 EnvironmentVariables map[string]string `json:"environment_json,omitempty"` 104 HealthCheckHTTPEndpoint *string `json:"health_check_http_endpoint,omitempty"` 105 HealthCheckTimeout uint64 `json:"health_check_timeout,omitempty"` 106 HealthCheckType constant.ApplicationHealthCheckType `json:"health_check_type,omitempty"` 107 Instances *int `json:"instances,omitempty"` 108 Memory *uint64 `json:"memory,omitempty"` 109 Name string `json:"name,omitempty"` 110 SpaceGUID string `json:"space_guid,omitempty"` 111 StackGUID string `json:"stack_guid,omitempty"` 112 State constant.ApplicationState `json:"state,omitempty"` 113 EnableSSH *bool `json:"enable_ssh,omitempty"` 114 Ports []int `json:"ports,omitempty"` 115 }{ 116 DockerImage: application.DockerImage, 117 EnvironmentVariables: application.EnvironmentVariables, 118 HealthCheckTimeout: application.HealthCheckTimeout, 119 HealthCheckType: application.HealthCheckType, 120 Name: application.Name, 121 SpaceGUID: application.SpaceGUID, 122 StackGUID: application.StackGUID, 123 State: application.State, 124 Ports: application.Ports, 125 } 126 127 if application.Buildpack.IsSet { 128 ccApp.Buildpack = &application.Buildpack.Value 129 } 130 131 if application.Command.IsSet { 132 ccApp.Command = &application.Command.Value 133 } 134 135 if application.DiskQuota.IsSet { 136 ccApp.DiskQuota = &application.DiskQuota.Value 137 } 138 139 if application.DockerCredentials.Username != "" || application.DockerCredentials.Password != "" { 140 ccApp.DockerCredentials = &DockerCredentials{ 141 Username: application.DockerCredentials.Username, 142 Password: application.DockerCredentials.Password, 143 } 144 } 145 146 if application.Instances.IsSet { 147 ccApp.Instances = &application.Instances.Value 148 } 149 150 if application.HealthCheckType != "" { 151 ccApp.HealthCheckHTTPEndpoint = &application.HealthCheckHTTPEndpoint 152 } 153 154 if application.Memory.IsSet { 155 ccApp.Memory = &application.Memory.Value 156 } 157 158 if application.EnableSSH.IsSet { 159 ccApp.EnableSSH = &application.EnableSSH.Value 160 } 161 162 return json.Marshal(ccApp) 163 } 164 165 // UnmarshalJSON helps unmarshal a Cloud Controller Application response. 166 func (application *Application) UnmarshalJSON(data []byte) error { 167 var ccApp struct { 168 Metadata internal.Metadata `json:"metadata"` 169 Entity struct { 170 Buildpack string `json:"buildpack"` 171 Command string `json:"command"` 172 DetectedBuildpack string `json:"detected_buildpack"` 173 DetectedStartCommand string `json:"detected_start_command"` 174 DiskQuota *uint64 `json:"disk_quota"` 175 DockerImage string `json:"docker_image"` 176 DockerCredentials DockerCredentials `json:"docker_credentials"` 177 // EnvironmentVariables' values can be any type, so we must accept 178 // interface{}, but we convert to string. 179 EnvironmentVariables map[string]interface{} `json:"environment_json"` 180 HealthCheckHTTPEndpoint string `json:"health_check_http_endpoint"` 181 HealthCheckTimeout uint64 `json:"health_check_timeout"` 182 HealthCheckType string `json:"health_check_type"` 183 Instances json.Number `json:"instances"` 184 Memory *uint64 `json:"memory"` 185 Name string `json:"name"` 186 PackageState string `json:"package_state"` 187 PackageUpdatedAt *time.Time `json:"package_updated_at"` 188 SpaceGUID string `json:"space_guid"` 189 StackGUID string `json:"stack_guid"` 190 StagingFailedDescription string `json:"staging_failed_description"` 191 StagingFailedReason string `json:"staging_failed_reason"` 192 State string `json:"state"` 193 EnableSSH *bool `json:"enable_ssh"` 194 Ports []int `json:"ports"` 195 } `json:"entity"` 196 } 197 err := cloudcontroller.DecodeJSON(data, &ccApp) 198 if err != nil { 199 return err 200 } 201 202 application.Ports = ccApp.Entity.Ports 203 application.Buildpack.ParseValue(ccApp.Entity.Buildpack) 204 application.Command.ParseValue(ccApp.Entity.Command) 205 application.DetectedBuildpack.ParseValue(ccApp.Entity.DetectedBuildpack) 206 application.DetectedStartCommand.ParseValue(ccApp.Entity.DetectedStartCommand) 207 application.DiskQuota.ParseUint64Value(ccApp.Entity.DiskQuota) 208 application.DockerCredentials = ccApp.Entity.DockerCredentials 209 application.DockerImage = ccApp.Entity.DockerImage 210 application.GUID = ccApp.Metadata.GUID 211 application.HealthCheckHTTPEndpoint = ccApp.Entity.HealthCheckHTTPEndpoint 212 application.HealthCheckTimeout = ccApp.Entity.HealthCheckTimeout 213 application.HealthCheckType = constant.ApplicationHealthCheckType(ccApp.Entity.HealthCheckType) 214 application.Memory.ParseUint64Value(ccApp.Entity.Memory) 215 application.Name = ccApp.Entity.Name 216 application.PackageState = constant.ApplicationPackageState(ccApp.Entity.PackageState) 217 application.SpaceGUID = ccApp.Entity.SpaceGUID 218 application.StackGUID = ccApp.Entity.StackGUID 219 application.StagingFailedDescription = ccApp.Entity.StagingFailedDescription 220 application.StagingFailedReason = ccApp.Entity.StagingFailedReason 221 application.State = constant.ApplicationState(ccApp.Entity.State) 222 application.EnableSSH.ParseBoolValue(ccApp.Entity.EnableSSH) 223 224 if len(ccApp.Entity.EnvironmentVariables) > 0 { 225 envVariableValues := map[string]string{} 226 for key, value := range ccApp.Entity.EnvironmentVariables { 227 envVariableValues[key] = fmt.Sprint(value) 228 } 229 application.EnvironmentVariables = envVariableValues 230 } 231 232 err = application.Instances.ParseStringValue(ccApp.Entity.Instances.String()) 233 if err != nil { 234 return err 235 } 236 237 if ccApp.Entity.PackageUpdatedAt != nil { 238 application.PackageUpdatedAt = *ccApp.Entity.PackageUpdatedAt 239 } 240 return nil 241 } 242 243 // CreateApplication creates a cloud controller application in with the given 244 // settings. SpaceGUID and Name are the only required fields. 245 func (client *Client) CreateApplication(app Application) (Application, Warnings, error) { 246 body, err := json.Marshal(app) 247 if err != nil { 248 return Application{}, nil, err 249 } 250 251 request, err := client.newHTTPRequest(requestOptions{ 252 RequestName: internal.PostAppRequest, 253 Body: bytes.NewReader(body), 254 }) 255 if err != nil { 256 return Application{}, nil, err 257 } 258 259 var updatedApp Application 260 response := cloudcontroller.Response{ 261 DecodeJSONResponseInto: &updatedApp, 262 } 263 264 err = client.connection.Make(request, &response) 265 return updatedApp, response.Warnings, err 266 } 267 268 // GetApplication returns back an Application. 269 func (client *Client) GetApplication(guid string) (Application, Warnings, error) { 270 request, err := client.newHTTPRequest(requestOptions{ 271 RequestName: internal.GetAppRequest, 272 URIParams: Params{"app_guid": guid}, 273 }) 274 if err != nil { 275 return Application{}, nil, err 276 } 277 278 var app Application 279 response := cloudcontroller.Response{ 280 DecodeJSONResponseInto: &app, 281 } 282 283 err = client.connection.Make(request, &response) 284 return app, response.Warnings, err 285 } 286 287 // DeleteApplication delete an application 288 func (client *Client) DeleteApplication(guid string) (Warnings, error) { 289 request, err := client.newHTTPRequest(requestOptions{ 290 RequestName: internal.DeleteAppRequest, 291 URIParams: Params{"app_guid": guid}, 292 Query: url.Values{ 293 "recursive": {"true"}, 294 "async": {"true"}, 295 }, 296 }) 297 if err != nil { 298 return nil, err 299 } 300 301 response := cloudcontroller.Response{ 302 } 303 304 err = client.connection.Make(request, &response) 305 return response.Warnings, err 306 } 307 308 // GetApplications returns back a list of Applications based off of the 309 // provided filters. 310 func (client *Client) GetApplications(filters ...Filter) ([]Application, Warnings, error) { 311 request, err := client.newHTTPRequest(requestOptions{ 312 RequestName: internal.GetAppsRequest, 313 Query: ConvertFilterParameters(filters), 314 }) 315 if err != nil { 316 return nil, nil, err 317 } 318 319 var fullAppsList []Application 320 warnings, err := client.paginate(request, Application{}, func(item interface{}) error { 321 if app, ok := item.(Application); ok { 322 fullAppsList = append(fullAppsList, app) 323 } else { 324 return ccerror.UnknownObjectInListError{ 325 Expected: Application{}, 326 Unexpected: item, 327 } 328 } 329 return nil 330 }) 331 332 return fullAppsList, warnings, err 333 } 334 335 // GetRouteApplications returns a list of Applications based off a route 336 // GUID and the provided filters. 337 func (client *Client) GetRouteApplications(routeGUID string, filters ...Filter) ([]Application, Warnings, error) { 338 request, err := client.newHTTPRequest(requestOptions{ 339 RequestName: internal.GetRouteAppsRequest, 340 URIParams: map[string]string{"route_guid": routeGUID}, 341 Query: ConvertFilterParameters(filters), 342 }) 343 if err != nil { 344 return nil, nil, err 345 } 346 347 var fullAppsList []Application 348 warnings, err := client.paginate(request, Application{}, func(item interface{}) error { 349 if app, ok := item.(Application); ok { 350 fullAppsList = append(fullAppsList, app) 351 } else { 352 return ccerror.UnknownObjectInListError{ 353 Expected: Application{}, 354 Unexpected: item, 355 } 356 } 357 return nil 358 }) 359 360 return fullAppsList, warnings, err 361 } 362 363 // RestageApplication restages the application with the given GUID. 364 func (client *Client) RestageApplication(app Application) (Application, Warnings, error) { 365 request, err := client.newHTTPRequest(requestOptions{ 366 RequestName: internal.PostAppRestageRequest, 367 URIParams: Params{"app_guid": app.GUID}, 368 }) 369 if err != nil { 370 return Application{}, nil, err 371 } 372 373 var restagedApp Application 374 response := cloudcontroller.Response{ 375 DecodeJSONResponseInto: &restagedApp, 376 } 377 378 err = client.connection.Make(request, &response) 379 return restagedApp, response.Warnings, err 380 } 381 382 // UpdateApplication updates the application with the given GUID. Note: Sending 383 // DockerImage and StackGUID at the same time will result in an API error. 384 func (client *Client) UpdateApplication(app Application) (Application, Warnings, error) { 385 body, err := json.Marshal(app) 386 if err != nil { 387 return Application{}, nil, err 388 } 389 390 request, err := client.newHTTPRequest(requestOptions{ 391 RequestName: internal.PutAppRequest, 392 URIParams: Params{"app_guid": app.GUID}, 393 Body: bytes.NewReader(body), 394 }) 395 if err != nil { 396 return Application{}, nil, err 397 } 398 399 var updatedApp Application 400 response := cloudcontroller.Response{ 401 DecodeJSONResponseInto: &updatedApp, 402 } 403 404 err = client.connection.Make(request, &response) 405 return updatedApp, response.Warnings, err 406 }