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  }