github.com/DaAlbrecht/cf-cli@v0.0.0-20231128151943-1fe19bb400b9/command/v7/shared/app_stager.go (about)

     1  package shared
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"code.cloudfoundry.org/cli/actor/sharedaction"
     8  	"code.cloudfoundry.org/cli/actor/v7action"
     9  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant"
    10  	"code.cloudfoundry.org/cli/command"
    11  	"code.cloudfoundry.org/cli/resources"
    12  	"code.cloudfoundry.org/cli/util/configv3"
    13  )
    14  
    15  /*
    16  	AppStager interface extracts the complexity of the asynchronous
    17      staging process, which is used by several commands (e.g. restage,
    18      copy-package).
    19  */
    20  
    21  //go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 . AppStager
    22  
    23  type AppStager interface {
    24  	StageAndStart(
    25  		app resources.Application,
    26  		space configv3.Space,
    27  		organization configv3.Organization,
    28  		packageGUID string,
    29  		strategy constant.DeploymentStrategy,
    30  		noWait bool,
    31  		appAction constant.ApplicationAction,
    32  	) error
    33  
    34  	StageApp(
    35  		app resources.Application,
    36  		packageGUID string,
    37  		space configv3.Space,
    38  	) (resources.Droplet, error)
    39  
    40  	StartApp(
    41  		app resources.Application,
    42  		resourceGuid string,
    43  		strategy constant.DeploymentStrategy,
    44  		noWait bool,
    45  		space configv3.Space,
    46  		organization configv3.Organization,
    47  		appAction constant.ApplicationAction,
    48  	) error
    49  }
    50  
    51  type Stager struct {
    52  	Actor    stagingAndStartActor
    53  	UI       command.UI
    54  	Config   command.Config
    55  	LogCache sharedaction.LogCacheClient
    56  }
    57  
    58  type stagingAndStartActor interface {
    59  	CreateDeploymentByApplicationAndDroplet(appGUID string, dropletGUID string) (string, v7action.Warnings, error)
    60  	CreateDeploymentByApplicationAndRevision(appGUID string, revisionGUID string) (string, v7action.Warnings, error)
    61  	GetCurrentUser() (configv3.User, error)
    62  	GetDetailedAppSummary(appName string, spaceGUID string, withObfuscatedValues bool) (v7action.DetailedApplicationSummary, v7action.Warnings, error)
    63  	GetStreamingLogsForApplicationByNameAndSpace(appName string, spaceGUID string, client sharedaction.LogCacheClient) (<-chan sharedaction.LogMessage, <-chan error, context.CancelFunc, v7action.Warnings, error)
    64  	PollStart(app resources.Application, noWait bool, handleProcessStats func(string)) (v7action.Warnings, error)
    65  	PollStartForRolling(app resources.Application, deploymentGUID string, noWait bool, handleProcessStats func(string)) (v7action.Warnings, error)
    66  	SetApplicationDroplet(appGUID string, dropletGUID string) (v7action.Warnings, error)
    67  	StagePackage(packageGUID, appName, spaceGUID string) (<-chan resources.Droplet, <-chan v7action.Warnings, <-chan error)
    68  	StartApplication(appGUID string) (v7action.Warnings, error)
    69  	StopApplication(appGUID string) (v7action.Warnings, error)
    70  }
    71  
    72  func NewAppStager(actor stagingAndStartActor, ui command.UI, config command.Config, logCache sharedaction.LogCacheClient) AppStager {
    73  	return &Stager{
    74  		Actor:    actor,
    75  		UI:       ui,
    76  		Config:   config,
    77  		LogCache: logCache,
    78  	}
    79  }
    80  
    81  func (stager *Stager) StageAndStart(
    82  	app resources.Application,
    83  	space configv3.Space,
    84  	organization configv3.Organization,
    85  	packageGUID string,
    86  	strategy constant.DeploymentStrategy,
    87  	noWait bool,
    88  	appAction constant.ApplicationAction,
    89  ) error {
    90  
    91  	droplet, err := stager.StageApp(app, packageGUID, space)
    92  	if err != nil {
    93  		return err
    94  	}
    95  
    96  	stager.UI.DisplayNewline()
    97  
    98  	err = stager.StartApp(app, droplet.GUID, strategy, noWait, space, organization, appAction)
    99  	if err != nil {
   100  		return err
   101  	}
   102  
   103  	return nil
   104  }
   105  
   106  func (stager *Stager) StageApp(app resources.Application, packageGUID string, space configv3.Space) (resources.Droplet, error) {
   107  	logStream, logErrStream, stopLogStreamFunc, logWarnings, logErr := stager.Actor.GetStreamingLogsForApplicationByNameAndSpace(app.Name, space.GUID, stager.LogCache)
   108  	stager.UI.DisplayWarnings(logWarnings)
   109  	if logErr != nil {
   110  		return resources.Droplet{}, logErr
   111  	}
   112  	defer stopLogStreamFunc()
   113  
   114  	stager.UI.DisplayText("Staging app and tracing logs...")
   115  	dropletStream, warningsStream, errStream := stager.Actor.StagePackage(
   116  		packageGUID,
   117  		app.Name,
   118  		space.GUID,
   119  	)
   120  
   121  	droplet, err := PollStage(dropletStream, warningsStream, errStream, logStream, logErrStream, stager.UI)
   122  	if err != nil {
   123  		return resources.Droplet{}, err
   124  	}
   125  
   126  	return droplet, nil
   127  }
   128  
   129  func (stager *Stager) StartApp(
   130  	app resources.Application,
   131  	resourceGuid string,
   132  	strategy constant.DeploymentStrategy,
   133  	noWait bool,
   134  	space configv3.Space,
   135  	organization configv3.Organization,
   136  	appAction constant.ApplicationAction,
   137  ) error {
   138  	if strategy == constant.DeploymentStrategyRolling {
   139  		stager.UI.DisplayText("Creating deployment for app {{.AppName}}...\n",
   140  			map[string]interface{}{
   141  				"AppName": app.Name,
   142  			},
   143  		)
   144  
   145  		var (
   146  			deploymentGUID string
   147  			warnings       v7action.Warnings
   148  			err            error
   149  		)
   150  
   151  		switch appAction {
   152  		case constant.ApplicationRollingBack:
   153  			deploymentGUID, warnings, err = stager.Actor.CreateDeploymentByApplicationAndRevision(app.GUID, resourceGuid)
   154  		default:
   155  			deploymentGUID, warnings, err = stager.Actor.CreateDeploymentByApplicationAndDroplet(app.GUID, resourceGuid)
   156  		}
   157  
   158  		stager.UI.DisplayWarnings(warnings)
   159  		if err != nil {
   160  			return err
   161  		}
   162  
   163  		stager.UI.DisplayText("Waiting for app to deploy...\n")
   164  
   165  		handleInstanceDetails := func(instanceDetails string) {
   166  			stager.UI.DisplayText(instanceDetails)
   167  		}
   168  
   169  		warnings, err = stager.Actor.PollStartForRolling(app, deploymentGUID, noWait, handleInstanceDetails)
   170  		stager.UI.DisplayNewline()
   171  		stager.UI.DisplayWarnings(warnings)
   172  		if err != nil {
   173  			return err
   174  		}
   175  		if noWait == true {
   176  			stager.UI.DisplayText("First instance restaged correctly, restaging remaining in the background")
   177  			return nil
   178  		}
   179  	} else {
   180  		user, err := stager.Actor.GetCurrentUser()
   181  		if err != nil {
   182  			return err
   183  		}
   184  
   185  		flavorText := fmt.Sprintf("%s app {{.App}} in org {{.Org}} / space {{.Space}} as {{.UserName}}...", appAction)
   186  		stager.UI.DisplayTextWithFlavor(flavorText,
   187  			map[string]interface{}{
   188  				"App":      app.Name,
   189  				"Org":      organization.Name,
   190  				"Space":    space.Name,
   191  				"UserName": user.Name,
   192  			},
   193  		)
   194  		stager.UI.DisplayNewline()
   195  
   196  		if app.Started() {
   197  			if appAction == constant.ApplicationStarting {
   198  				stager.UI.DisplayText("App '{{.AppName}}' is already started.",
   199  					map[string]interface{}{
   200  						"AppName": app.Name,
   201  					})
   202  				stager.UI.DisplayOK()
   203  				return nil
   204  
   205  			} else {
   206  				stager.UI.DisplayText("Stopping app...")
   207  				stager.UI.DisplayNewline()
   208  
   209  				warnings, err := stager.Actor.StopApplication(app.GUID)
   210  				stager.UI.DisplayWarnings(warnings)
   211  				if err != nil {
   212  					return err
   213  				}
   214  			}
   215  		}
   216  
   217  		if resourceGuid != "" {
   218  			// attach droplet to app
   219  			warnings, err := stager.Actor.SetApplicationDroplet(app.GUID, resourceGuid)
   220  			stager.UI.DisplayWarnings(warnings)
   221  			if err != nil {
   222  				return err
   223  			}
   224  		}
   225  
   226  		stager.UI.DisplayText("Waiting for app to start...")
   227  		stager.UI.DisplayNewline()
   228  
   229  		// start the application
   230  		warnings, err := stager.Actor.StartApplication(app.GUID)
   231  		stager.UI.DisplayWarnings(warnings)
   232  		if err != nil {
   233  			return err
   234  		}
   235  
   236  		handleInstanceDetails := func(instanceDetails string) {
   237  			stager.UI.DisplayText(instanceDetails)
   238  		}
   239  
   240  		warnings, err = stager.Actor.PollStart(app, noWait, handleInstanceDetails)
   241  		stager.UI.DisplayNewline()
   242  		stager.UI.DisplayWarnings(warnings)
   243  		if err != nil {
   244  			return err
   245  		}
   246  	}
   247  
   248  	summary, warnings, err := stager.Actor.GetDetailedAppSummary(
   249  		app.Name,
   250  		space.GUID,
   251  		false,
   252  	)
   253  	stager.UI.DisplayWarnings(warnings)
   254  	if err != nil {
   255  		return err
   256  	}
   257  
   258  	appSummaryDisplayer := NewAppSummaryDisplayer(stager.UI)
   259  	appSummaryDisplayer.AppDisplay(summary, false)
   260  
   261  	return nil
   262  }