github.com/Thanhphan1147/cloudfoundry-cli@v7.1.0+incompatible/actor/pushaction/application.go (about)

     1  package pushaction
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"code.cloudfoundry.org/cli/actor/actionerror"
     7  	"code.cloudfoundry.org/cli/actor/v2action"
     8  	"code.cloudfoundry.org/cli/actor/v3action"
     9  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccerror"
    10  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant"
    11  	"code.cloudfoundry.org/cli/types"
    12  	log "github.com/sirupsen/logrus"
    13  )
    14  
    15  type Application struct {
    16  	v2action.Application
    17  	Buildpacks []string
    18  	Stack      v2action.Stack
    19  }
    20  
    21  // CalculatedBuildpacks will return back the buildpacks for the application.
    22  func (app Application) CalculatedBuildpacks() []string {
    23  	buildpack := app.CalculatedBuildpack()
    24  	switch {
    25  	case app.Buildpacks != nil:
    26  		return app.Buildpacks
    27  	case len(buildpack) > 0:
    28  		return []string{buildpack}
    29  	default:
    30  		return nil
    31  	}
    32  }
    33  
    34  func (app Application) String() string {
    35  	return fmt.Sprintf("%s, Stack Name: '%s', Buildpacks: %s", app.Application, app.Stack.Name, app.Buildpacks)
    36  }
    37  
    38  func (app *Application) SetStack(stack v2action.Stack) {
    39  	app.Stack = stack
    40  	app.StackGUID = stack.GUID
    41  }
    42  
    43  func (actor Actor) CreateApplication(config ApplicationConfig) (ApplicationConfig, Event, Warnings, error) {
    44  	log.Debug("creating application")
    45  	v2App := config.DesiredApplication.Application
    46  	v2App.Buildpack = actor.setBuildpack(config)
    47  
    48  	log.WithField("application", v2App).Debug("creating application with these settings")
    49  	var warnings Warnings
    50  	newApp, v2warnings, err := actor.V2Actor.CreateApplication(v2App)
    51  	warnings = append(warnings, v2warnings...)
    52  	if err != nil {
    53  		log.Errorln("creating application:", err)
    54  		return ApplicationConfig{}, "", Warnings(warnings), err
    55  	}
    56  
    57  	if config.HasMultipleBuildpacks() {
    58  		v3Warnings, v3Err := actor.updateBuildpacks(config, newApp)
    59  		warnings = append(warnings, v3Warnings...)
    60  		if v3Err != nil {
    61  			return ApplicationConfig{}, "", warnings, v3Err
    62  		}
    63  	}
    64  
    65  	config.DesiredApplication.Application = newApp
    66  	config.CurrentApplication = config.DesiredApplication
    67  
    68  	return config, CreatedApplication, Warnings(warnings), nil
    69  }
    70  
    71  func (actor Actor) UpdateApplication(config ApplicationConfig) (ApplicationConfig, Event, Warnings, error) {
    72  	log.Debug("updating application")
    73  	v2App := config.DesiredApplication.Application
    74  	v2App.Buildpack = actor.setBuildpack(config)
    75  
    76  	v2App = actor.ignoreSameState(config, v2App)
    77  	v2App = actor.ignoreSameStackGUID(config, v2App)
    78  
    79  	log.WithField("application", v2App).Debug("updating application with these settings")
    80  	var warnings Warnings
    81  	updatedApp, v2Warnings, err := actor.V2Actor.UpdateApplication(v2App)
    82  	warnings = append(warnings, v2Warnings...)
    83  	if err != nil {
    84  		log.Errorln("updating application:", err)
    85  		return ApplicationConfig{}, "", Warnings(warnings), err
    86  	}
    87  
    88  	if config.HasMultipleBuildpacks() {
    89  		v3Warnings, v3Err := actor.updateBuildpacks(config, updatedApp)
    90  		warnings = append(warnings, v3Warnings...)
    91  		if v3Err != nil {
    92  			return ApplicationConfig{}, "", warnings, v3Err
    93  		}
    94  	}
    95  
    96  	config.DesiredApplication.Application = updatedApp
    97  	config.CurrentApplication = config.DesiredApplication
    98  
    99  	return config, UpdatedApplication, Warnings(warnings), err
   100  }
   101  
   102  func (actor Actor) FindOrReturnPartialApp(appName string, spaceGUID string) (bool, Application, v2action.Warnings, error) {
   103  	foundApp, appWarnings, err := actor.V2Actor.GetApplicationByNameAndSpace(appName, spaceGUID)
   104  	if _, ok := err.(actionerror.ApplicationNotFoundError); ok {
   105  		log.Warnf("unable to find app %s in current space (GUID: %s)", appName, spaceGUID)
   106  		return false, Application{
   107  			Application: v2action.Application{
   108  				Name:      appName,
   109  				SpaceGUID: spaceGUID,
   110  			},
   111  		}, appWarnings, nil
   112  	} else if err != nil {
   113  		log.WithField("appName", appName).Error("error retrieving app")
   114  		return false, Application{}, appWarnings, err
   115  	}
   116  
   117  	stack, stackWarnings, err := actor.V2Actor.GetStack(foundApp.StackGUID)
   118  	warnings := append(appWarnings, stackWarnings...)
   119  	if err != nil {
   120  		log.Warnf("unable to find app's stack (GUID: %s)", foundApp.StackGUID)
   121  		return false, Application{}, warnings, err
   122  	}
   123  
   124  	v3App, v3AppWarnings, err := actor.V3Actor.GetApplicationByNameAndSpace(appName, spaceGUID)
   125  	warnings = append(warnings, v3AppWarnings...)
   126  	if err != nil {
   127  		if _, ok := err.(ccerror.APINotFoundError); !ok {
   128  			return false, Application{}, warnings, err
   129  		}
   130  	}
   131  
   132  	// if err api don't exist - ignore, else return error
   133  	app := Application{
   134  		Application: foundApp,
   135  		Stack:       stack,
   136  		Buildpacks:  v3App.LifecycleBuildpacks,
   137  	}
   138  	return true, app, warnings, nil
   139  }
   140  
   141  // For some versions of CC, sending state will always result in CC
   142  // attempting to do perform that request (i.e. started -> start/restart).
   143  // In order to prevent repeated unintended restarts in the middle of a
   144  // push, don't send state. This will be fixed in capi-release 1.48.0.
   145  func (actor Actor) ignoreSameState(config ApplicationConfig, v2App v2action.Application) v2action.Application {
   146  	if config.CurrentApplication.State == config.DesiredApplication.State {
   147  		v2App.State = ""
   148  	}
   149  
   150  	return v2App
   151  }
   152  
   153  // Apps updates with both docker image and stack guids fail. So do not send
   154  // StackGUID unless it is necessary.
   155  func (actor Actor) ignoreSameStackGUID(config ApplicationConfig, v2App v2action.Application) v2action.Application {
   156  	if config.CurrentApplication.StackGUID == config.DesiredApplication.StackGUID {
   157  		v2App.StackGUID = ""
   158  	}
   159  
   160  	return v2App
   161  }
   162  
   163  // If 'buildpacks' is set with only one buildpack, set `buildpack` (singular)
   164  // on the application.
   165  func (Actor) setBuildpack(config ApplicationConfig) types.FilteredString {
   166  	buildpacks := config.DesiredApplication.Buildpacks
   167  
   168  	if len(buildpacks) >= 1 {
   169  		var filtered types.FilteredString
   170  		filtered.ParseValue(buildpacks[0])
   171  		return filtered
   172  	}
   173  
   174  	if buildpacks != nil && len(buildpacks) == 0 {
   175  		return types.FilteredString{IsSet: true}
   176  	}
   177  
   178  	return config.DesiredApplication.Buildpack
   179  }
   180  
   181  func (actor Actor) updateBuildpacks(config ApplicationConfig, v2App v2action.Application) (Warnings, error) {
   182  	log.WithField("buildpacks", config.DesiredApplication.Buildpacks).Debug("updating with multiple buildpacks")
   183  	v3App := v3action.Application{
   184  		Name:                v2App.Name,
   185  		GUID:                v2App.GUID,
   186  		StackName:           config.DesiredApplication.Stack.Name,
   187  		LifecycleBuildpacks: config.DesiredApplication.Buildpacks,
   188  		LifecycleType:       constant.AppLifecycleTypeBuildpack,
   189  	}
   190  
   191  	_, warnings, err := actor.V3Actor.UpdateApplication(v3App)
   192  	return Warnings(warnings), err
   193  }