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  }