github.com/cloudfoundry-community/cloudfoundry-cli@v6.44.1-0.20240130060226-cda5ed8e89a5+incompatible/actor/v7action/application.go (about)

     1  package v7action
     2  
     3  import (
     4  	"code.cloudfoundry.org/cli/resources"
     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 string, spaceGUID string) (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  	jobURL, deleteAppWarnings, err := actor.CloudControllerClient.DeleteApplication(app.GUID)
    42  	allWarnings = append(allWarnings, deleteAppWarnings...)
    43  	if err != nil {
    44  		return allWarnings, err
    45  	}
    46  
    47  	pollWarnings, err := actor.CloudControllerClient.PollJob(jobURL)
    48  	allWarnings = append(allWarnings, pollWarnings...)
    49  	return allWarnings, err
    50  }
    51  
    52  func (actor Actor) GetApplicationsByNamesAndSpace(appNames []string, spaceGUID string) ([]Application, Warnings, error) {
    53  	apps, warnings, err := actor.CloudControllerClient.GetApplications(
    54  		ccv3.Query{Key: ccv3.NameFilter, Values: appNames},
    55  		ccv3.Query{Key: ccv3.SpaceGUIDFilter, Values: []string{spaceGUID}},
    56  	)
    57  
    58  	if err != nil {
    59  		return nil, Warnings(warnings), err
    60  	}
    61  
    62  	if len(apps) < len(appNames) {
    63  		return nil, Warnings(warnings), actionerror.ApplicationsNotFoundError{}
    64  	}
    65  
    66  	actorApps := []Application{}
    67  	for _, a := range apps {
    68  		actorApps = append(actorApps, actor.convertCCToActorApplication(a))
    69  	}
    70  	return actorApps, Warnings(warnings), nil
    71  }
    72  
    73  // GetApplicationByNameAndSpace returns the application with the given
    74  // name in the given space.
    75  func (actor Actor) GetApplicationByNameAndSpace(appName string, spaceGUID string) (Application, Warnings, error) {
    76  	apps, warnings, err := actor.GetApplicationsByNamesAndSpace([]string{appName}, spaceGUID)
    77  
    78  	if err != nil {
    79  		if _, ok := err.(actionerror.ApplicationsNotFoundError); ok {
    80  			return Application{}, warnings, actionerror.ApplicationNotFoundError{Name: appName}
    81  		}
    82  		return Application{}, warnings, err
    83  	}
    84  
    85  	return apps[0], warnings, nil
    86  }
    87  
    88  // GetApplicationsBySpace returns all applications in a space.
    89  func (actor Actor) GetApplicationsBySpace(spaceGUID string) ([]Application, Warnings, error) {
    90  	ccApps, warnings, err := actor.CloudControllerClient.GetApplications(
    91  		ccv3.Query{Key: ccv3.SpaceGUIDFilter, Values: []string{spaceGUID}},
    92  	)
    93  
    94  	if err != nil {
    95  		return []Application{}, Warnings(warnings), err
    96  	}
    97  
    98  	var apps []Application
    99  	for _, ccApp := range ccApps {
   100  		apps = append(apps, actor.convertCCToActorApplication(ccApp))
   101  	}
   102  	return apps, Warnings(warnings), nil
   103  }
   104  
   105  // CreateApplicationInSpace creates and returns the application with the given
   106  // name in the given space.
   107  func (actor Actor) CreateApplicationInSpace(app Application, spaceGUID string) (Application, Warnings, error) {
   108  	createdApp, warnings, err := actor.CloudControllerClient.CreateApplication(
   109  		resources.Application{
   110  			LifecycleType:       app.LifecycleType,
   111  			LifecycleBuildpacks: app.LifecycleBuildpacks,
   112  			StackName:           app.StackName,
   113  			Name:                app.Name,
   114  			SpaceGUID:           spaceGUID,
   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  // SetApplicationProcessHealthCheckTypeByNameAndSpace sets the health check
   128  // information of the provided processType for an application with the given
   129  // name and space GUID.
   130  func (actor Actor) SetApplicationProcessHealthCheckTypeByNameAndSpace(
   131  	appName string,
   132  	spaceGUID string,
   133  	healthCheckType constant.HealthCheckType,
   134  	httpEndpoint string,
   135  	processType string,
   136  	invocationTimeout int64,
   137  ) (Application, Warnings, error) {
   138  
   139  	app, getWarnings, err := actor.GetApplicationByNameAndSpace(appName, spaceGUID)
   140  	if err != nil {
   141  		return Application{}, getWarnings, err
   142  	}
   143  
   144  	setWarnings, err := actor.UpdateProcessByTypeAndApplication(
   145  		processType,
   146  		app.GUID,
   147  		Process{
   148  			HealthCheckType:              healthCheckType,
   149  			HealthCheckEndpoint:          httpEndpoint,
   150  			HealthCheckInvocationTimeout: invocationTimeout,
   151  		})
   152  	return app, append(getWarnings, setWarnings...), err
   153  }
   154  
   155  // StopApplication stops an application.
   156  func (actor Actor) StopApplication(appGUID string) (Warnings, error) {
   157  	_, warnings, err := actor.CloudControllerClient.UpdateApplicationStop(appGUID)
   158  
   159  	return Warnings(warnings), err
   160  }
   161  
   162  // StartApplication starts an application.
   163  func (actor Actor) StartApplication(appGUID string) (Application, Warnings, error) {
   164  	updatedApp, warnings, err := actor.CloudControllerClient.UpdateApplicationStart(appGUID)
   165  	if err != nil {
   166  		return Application{}, Warnings(warnings), err
   167  	}
   168  
   169  	return actor.convertCCToActorApplication(updatedApp), Warnings(warnings), nil
   170  }
   171  
   172  // RestartApplication restarts an application and waits for it to start.
   173  func (actor Actor) RestartApplication(appGUID string) (Warnings, error) {
   174  	var allWarnings Warnings
   175  	_, warnings, err := actor.CloudControllerClient.UpdateApplicationRestart(appGUID)
   176  	allWarnings = append(allWarnings, warnings...)
   177  	if err != nil {
   178  		return allWarnings, err
   179  	}
   180  
   181  	pollingWarnings, err := actor.PollStart(appGUID)
   182  	allWarnings = append(allWarnings, pollingWarnings...)
   183  	return allWarnings, err
   184  }
   185  
   186  func (actor Actor) PollStart(appGUID string) (Warnings, error) {
   187  	var allWarnings Warnings
   188  	processes, warnings, err := actor.CloudControllerClient.GetApplicationProcesses(appGUID)
   189  	allWarnings = append(allWarnings, warnings...)
   190  	if err != nil {
   191  		return allWarnings, err
   192  	}
   193  
   194  	timeout := time.Now().Add(actor.Config.StartupTimeout())
   195  	for time.Now().Before(timeout) {
   196  		allProcessesDone := true
   197  		for _, process := range processes {
   198  			shouldContinuePolling, warnings, err := actor.shouldContinuePollingProcessStatus(process)
   199  			allWarnings = append(allWarnings, warnings...)
   200  			if err != nil {
   201  				return allWarnings, err
   202  			}
   203  
   204  			if shouldContinuePolling {
   205  				allProcessesDone = false
   206  				break
   207  			}
   208  		}
   209  
   210  		if allProcessesDone {
   211  			return allWarnings, nil
   212  		}
   213  		time.Sleep(actor.Config.PollingInterval())
   214  	}
   215  
   216  	return allWarnings, actionerror.StartupTimeoutError{}
   217  }
   218  
   219  // UpdateApplication updates the buildpacks on an application
   220  func (actor Actor) UpdateApplication(app Application) (Application, Warnings, error) {
   221  	ccApp := resources.Application{
   222  		GUID:                app.GUID,
   223  		StackName:           app.StackName,
   224  		LifecycleType:       app.LifecycleType,
   225  		LifecycleBuildpacks: app.LifecycleBuildpacks,
   226  		Metadata:            (*resources.Metadata)(app.Metadata),
   227  	}
   228  
   229  	updatedApp, warnings, err := actor.CloudControllerClient.UpdateApplication(ccApp)
   230  	if err != nil {
   231  		return Application{}, Warnings(warnings), err
   232  	}
   233  
   234  	return actor.convertCCToActorApplication(updatedApp), Warnings(warnings), nil
   235  }
   236  
   237  func (Actor) convertCCToActorApplication(app resources.Application) Application {
   238  	return Application{
   239  		GUID:                app.GUID,
   240  		StackName:           app.StackName,
   241  		LifecycleType:       app.LifecycleType,
   242  		LifecycleBuildpacks: app.LifecycleBuildpacks,
   243  		Name:                app.Name,
   244  		State:               app.State,
   245  		Metadata:            (*Metadata)(app.Metadata),
   246  	}
   247  }
   248  
   249  func (actor Actor) shouldContinuePollingProcessStatus(process resources.Process) (bool, Warnings, error) {
   250  	ccInstances, ccWarnings, err := actor.CloudControllerClient.GetProcessInstances(process.GUID)
   251  	instances := ProcessInstances(ccInstances)
   252  	warnings := Warnings(ccWarnings)
   253  	if err != nil {
   254  		return true, warnings, err
   255  	}
   256  
   257  	if instances.Empty() || instances.AnyRunning() {
   258  		return false, warnings, nil
   259  	} else if instances.AllCrashed() {
   260  		return false, warnings, actionerror.AllInstancesCrashedError{}
   261  	}
   262  
   263  	return true, warnings, nil
   264  }