github.com/liamawhite/cli-with-i18n@v6.32.1-0.20171122084555-dede0a5c3448+incompatible/actor/v3action/application.go (about)

     1  package v3action
     2  
     3  import (
     4  	"fmt"
     5  	"net/url"
     6  	"time"
     7  
     8  	"github.com/liamawhite/cli-with-i18n/api/cloudcontroller/ccerror"
     9  	"github.com/liamawhite/cli-with-i18n/api/cloudcontroller/ccv3"
    10  )
    11  
    12  // Application represents a V3 actor application.
    13  type Application struct {
    14  	Name      string
    15  	GUID      string
    16  	State     string
    17  	Lifecycle AppLifecycle
    18  }
    19  
    20  type AppLifecycle struct {
    21  	Type AppLifecycleType
    22  	Data AppLifecycleData
    23  }
    24  
    25  type AppLifecycleType ccv3.AppLifecycleType
    26  type AppLifecycleData ccv3.AppLifecycleData
    27  
    28  const (
    29  	BuildpackAppLifecycleType AppLifecycleType = AppLifecycleType(ccv3.BuildpackAppLifecycleType)
    30  	DockerAppLifecycleType    AppLifecycleType = AppLifecycleType(ccv3.DockerAppLifecycleType)
    31  )
    32  
    33  func (app Application) Started() bool {
    34  	return app.State == "STARTED"
    35  }
    36  
    37  // ApplicationNotFoundError represents the error that occurs when the
    38  // application is not found.
    39  type ApplicationNotFoundError struct {
    40  	Name string
    41  }
    42  
    43  func (e ApplicationNotFoundError) Error() string {
    44  	return fmt.Sprintf("Application '%s' not found.", e.Name)
    45  }
    46  
    47  // ApplicationAlreadyExistsError represents the error that occurs when the
    48  // application already exists.
    49  type ApplicationAlreadyExistsError struct {
    50  	Name string
    51  }
    52  
    53  func (e ApplicationAlreadyExistsError) Error() string {
    54  	return fmt.Sprintf("Application '%s' already exists.", e.Name)
    55  }
    56  
    57  func (actor Actor) DeleteApplicationByNameAndSpace(name string, spaceGUID string) (Warnings, error) {
    58  	var allWarnings Warnings
    59  
    60  	app, getAppWarnings, err := actor.GetApplicationByNameAndSpace(name, spaceGUID)
    61  	allWarnings = append(allWarnings, getAppWarnings...)
    62  	if err != nil {
    63  		return allWarnings, err
    64  	}
    65  
    66  	jobURL, deleteAppWarnings, err := actor.CloudControllerClient.DeleteApplication(app.GUID)
    67  	allWarnings = append(allWarnings, deleteAppWarnings...)
    68  	if err != nil {
    69  		return allWarnings, err
    70  	}
    71  
    72  	pollWarnings, err := actor.CloudControllerClient.PollJob(jobURL)
    73  	allWarnings = append(allWarnings, pollWarnings...)
    74  	return allWarnings, err
    75  }
    76  
    77  // GetApplicationByNameAndSpace returns the application with the given
    78  // name in the given space.
    79  func (actor Actor) GetApplicationByNameAndSpace(appName string, spaceGUID string) (Application, Warnings, error) {
    80  	apps, warnings, err := actor.CloudControllerClient.GetApplications(url.Values{
    81  		"space_guids": []string{spaceGUID},
    82  		"names":       []string{appName},
    83  	})
    84  	if err != nil {
    85  		return Application{}, Warnings(warnings), err
    86  	}
    87  
    88  	if len(apps) == 0 {
    89  		return Application{}, Warnings(warnings), ApplicationNotFoundError{Name: appName}
    90  	}
    91  
    92  	return Application{
    93  		Name:  apps[0].Name,
    94  		GUID:  apps[0].GUID,
    95  		State: apps[0].State,
    96  		Lifecycle: AppLifecycle{
    97  			Type: AppLifecycleType(apps[0].Lifecycle.Type),
    98  			Data: AppLifecycleData(apps[0].Lifecycle.Data),
    99  		},
   100  	}, Warnings(warnings), nil
   101  }
   102  
   103  // GetApplicationsBySpace returns all applications in a space.
   104  func (actor Actor) GetApplicationsBySpace(spaceGUID string) ([]Application, Warnings, error) {
   105  	ccv3Apps, warnings, err := actor.CloudControllerClient.GetApplications(url.Values{
   106  		"space_guids": []string{spaceGUID},
   107  	})
   108  
   109  	if err != nil {
   110  		return []Application{}, Warnings(warnings), err
   111  	}
   112  
   113  	apps := make([]Application, len(ccv3Apps))
   114  	for i, ccv3App := range ccv3Apps {
   115  		apps[i] = Application{
   116  			Name:  ccv3App.Name,
   117  			GUID:  ccv3App.GUID,
   118  			State: ccv3App.State,
   119  			Lifecycle: AppLifecycle{
   120  				Type: AppLifecycleType(ccv3App.Lifecycle.Type),
   121  				Data: AppLifecycleData(ccv3App.Lifecycle.Data),
   122  			},
   123  		}
   124  	}
   125  	return apps, Warnings(warnings), nil
   126  }
   127  
   128  // CreateApplicationInSpace creates and returns the application with the given
   129  // name in the given space.
   130  func (actor Actor) CreateApplicationInSpace(app Application, spaceGUID string) (Application, Warnings, error) {
   131  	createdApp, warnings, err := actor.CloudControllerClient.CreateApplication(
   132  		ccv3.Application{
   133  			Name: app.Name,
   134  			Relationships: ccv3.Relationships{
   135  				ccv3.SpaceRelationship: ccv3.Relationship{GUID: spaceGUID},
   136  			},
   137  			Lifecycle: ccv3.AppLifecycle{
   138  				Type: ccv3.AppLifecycleType(app.Lifecycle.Type),
   139  				Data: ccv3.AppLifecycleData{
   140  					Buildpacks: app.Lifecycle.Data.Buildpacks,
   141  				},
   142  			},
   143  		})
   144  
   145  	if err != nil {
   146  		if _, ok := err.(ccerror.NameNotUniqueInSpaceError); ok {
   147  			return Application{}, Warnings(warnings), ApplicationAlreadyExistsError{Name: app.Name}
   148  		}
   149  		return Application{}, Warnings(warnings), err
   150  	}
   151  
   152  	return Application{
   153  		Name:  createdApp.Name,
   154  		GUID:  createdApp.GUID,
   155  		State: createdApp.State,
   156  		Lifecycle: AppLifecycle{
   157  			Type: AppLifecycleType(createdApp.Lifecycle.Type),
   158  			Data: AppLifecycleData(createdApp.Lifecycle.Data),
   159  		},
   160  	}, Warnings(warnings), nil
   161  }
   162  
   163  // StopApplication stops an application.
   164  func (actor Actor) StopApplication(appGUID string) (Warnings, error) {
   165  	warnings, err := actor.CloudControllerClient.StopApplication(appGUID)
   166  
   167  	return Warnings(warnings), err
   168  }
   169  
   170  // StartApplication starts an application.
   171  func (actor Actor) StartApplication(appGUID string) (Application, Warnings, error) {
   172  	updatedApp, warnings, err := actor.CloudControllerClient.StartApplication(appGUID)
   173  	if err != nil {
   174  		return Application{}, Warnings(warnings), err
   175  	}
   176  
   177  	return Application{
   178  		Name:  updatedApp.Name,
   179  		GUID:  updatedApp.GUID,
   180  		State: updatedApp.State,
   181  		Lifecycle: AppLifecycle{
   182  			Type: AppLifecycleType(updatedApp.Lifecycle.Type),
   183  			Data: AppLifecycleData(updatedApp.Lifecycle.Data),
   184  		},
   185  	}, Warnings(warnings), nil
   186  }
   187  
   188  func (actor Actor) PollStart(appGUID string, warningsChannel chan<- Warnings) error {
   189  	processes, warnings, err := actor.CloudControllerClient.GetApplicationProcesses(appGUID)
   190  	warningsChannel <- Warnings(warnings)
   191  	if err != nil {
   192  		return err
   193  	}
   194  
   195  	timeout := time.Now().Add(actor.Config.StartupTimeout())
   196  	for time.Now().Before(timeout) {
   197  		readyProcs := 0
   198  		for _, process := range processes {
   199  			ready, err := actor.processStatus(process, warningsChannel)
   200  			if err != nil {
   201  				return err
   202  			}
   203  
   204  			if ready {
   205  				readyProcs++
   206  			}
   207  		}
   208  
   209  		if readyProcs == len(processes) {
   210  			return nil
   211  		}
   212  		time.Sleep(actor.Config.PollingInterval())
   213  	}
   214  
   215  	return StartupTimeoutError{}
   216  }
   217  
   218  // UpdateApplication updates the buildpacks on an application
   219  func (actor Actor) UpdateApplication(app Application) (Application, Warnings, error) {
   220  	ccApp := ccv3.Application{
   221  		GUID: app.GUID,
   222  		Lifecycle: ccv3.AppLifecycle{
   223  			Type: ccv3.AppLifecycleType(app.Lifecycle.Type),
   224  			Data: ccv3.AppLifecycleData(app.Lifecycle.Data),
   225  		},
   226  	}
   227  
   228  	updatedApp, warnings, err := actor.CloudControllerClient.UpdateApplication(ccApp)
   229  	if err != nil {
   230  		return Application{}, Warnings(warnings), err
   231  	}
   232  
   233  	return Application{
   234  		Name:  updatedApp.Name,
   235  		GUID:  updatedApp.GUID,
   236  		State: updatedApp.State,
   237  		Lifecycle: AppLifecycle{
   238  			Type: AppLifecycleType(updatedApp.Lifecycle.Type),
   239  			Data: AppLifecycleData(updatedApp.Lifecycle.Data),
   240  		},
   241  	}, Warnings(warnings), nil
   242  }
   243  
   244  // StartupTimeoutError is returned when startup timeout is reached waiting for
   245  // an application to start.
   246  type StartupTimeoutError struct {
   247  }
   248  
   249  func (e StartupTimeoutError) Error() string {
   250  	return fmt.Sprintf("Timed out waiting for application to start")
   251  }
   252  
   253  func (actor Actor) processStatus(process ccv3.Process, warningsChannel chan<- Warnings) (bool, error) {
   254  	instances, warnings, err := actor.CloudControllerClient.GetProcessInstances(process.GUID)
   255  	warningsChannel <- Warnings(warnings)
   256  	if err != nil {
   257  		return false, err
   258  	}
   259  	if len(instances) == 0 {
   260  		return true, nil
   261  	}
   262  
   263  	for _, instance := range instances {
   264  		if instance.State == "RUNNING" {
   265  			return true, nil
   266  		}
   267  	}
   268  
   269  	for _, instance := range instances {
   270  		if instance.State != "CRASHED" {
   271  			return false, nil
   272  		}
   273  	}
   274  
   275  	// all of the instances are crashed at this point
   276  	return true, nil
   277  }