github.com/nimakaviani/cli@v6.37.1-0.20180619223813-e734901a73fa+incompatible/command/v3/v3_push_original_command.go (about)

     1  package v3
     2  
     3  import (
     4  	"net/http"
     5  
     6  	"code.cloudfoundry.org/cli/actor/actionerror"
     7  	"code.cloudfoundry.org/cli/actor/pushaction"
     8  	"code.cloudfoundry.org/cli/actor/sharedaction"
     9  	"code.cloudfoundry.org/cli/actor/v2action"
    10  	"code.cloudfoundry.org/cli/actor/v3action"
    11  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccerror"
    12  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant"
    13  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccversion"
    14  	"code.cloudfoundry.org/cli/command"
    15  	"code.cloudfoundry.org/cli/command/translatableerror"
    16  	sharedV2 "code.cloudfoundry.org/cli/command/v2/shared"
    17  	"code.cloudfoundry.org/cli/command/v3/shared"
    18  )
    19  
    20  //go:generate counterfeiter . OriginalV2PushActor
    21  
    22  type OriginalV2PushActor interface {
    23  	CreateAndMapDefaultApplicationRoute(orgGUID string, spaceGUID string, app v2action.Application) (pushaction.Warnings, error)
    24  }
    25  
    26  //go:generate counterfeiter . OriginalV3PushActor
    27  
    28  type OriginalV3PushActor interface {
    29  	CloudControllerAPIVersion() string
    30  	CreateAndUploadBitsPackageByApplicationNameAndSpace(appName string, spaceGUID string, bitsPath string) (v3action.Package, v3action.Warnings, error)
    31  	CreateDockerPackageByApplicationNameAndSpace(appName string, spaceGUID string, dockerImageCredentials v3action.DockerImageCredentials) (v3action.Package, v3action.Warnings, error)
    32  	CreateApplicationInSpace(app v3action.Application, spaceGUID string) (v3action.Application, v3action.Warnings, error)
    33  	GetApplicationByNameAndSpace(appName string, spaceGUID string) (v3action.Application, v3action.Warnings, error)
    34  	GetApplicationSummaryByNameAndSpace(appName string, spaceGUID string) (v3action.ApplicationSummary, v3action.Warnings, error)
    35  	GetStreamingLogsForApplicationByNameAndSpace(appName string, spaceGUID string, client v3action.NOAAClient) (<-chan *v3action.LogMessage, <-chan error, v3action.Warnings, error)
    36  	PollStart(appGUID string, warnings chan<- v3action.Warnings) error
    37  	SetApplicationDroplet(appName string, spaceGUID string, dropletGUID string) (v3action.Warnings, error)
    38  	StagePackage(packageGUID string, appName string) (<-chan v3action.Droplet, <-chan v3action.Warnings, <-chan error)
    39  	StartApplication(appGUID string) (v3action.Application, v3action.Warnings, error)
    40  	StopApplication(appGUID string) (v3action.Warnings, error)
    41  	UpdateApplication(app v3action.Application) (v3action.Application, v3action.Warnings, error)
    42  }
    43  
    44  func (cmd *V3PushCommand) OriginalSetup(config command.Config, ui command.UI) error {
    45  	cmd.UI = ui
    46  	cmd.Config = config
    47  	sharedActor := sharedaction.NewActor(config)
    48  
    49  	ccClient, uaaClient, err := shared.NewClients(config, ui, true)
    50  	if err != nil {
    51  		if v3Err, ok := err.(ccerror.V3UnexpectedResponseError); ok && v3Err.ResponseCode == http.StatusNotFound {
    52  			return translatableerror.MinimumAPIVersionNotMetError{MinimumVersion: ccversion.MinVersionV3}
    53  		}
    54  
    55  		return err
    56  	}
    57  	cmd.OriginalActor = v3action.NewActor(ccClient, config, sharedActor, nil)
    58  
    59  	ccClientV2, uaaClientV2, err := sharedV2.NewClients(config, ui, true)
    60  	if err != nil {
    61  		return err
    62  	}
    63  
    64  	v2Actor := v2action.NewActor(ccClientV2, uaaClientV2, config)
    65  
    66  	cmd.SharedActor = sharedActor
    67  	cmd.OriginalV2PushActor = pushaction.NewActor(v2Actor, cmd.OriginalActor, sharedActor)
    68  
    69  	v2AppActor := v2action.NewActor(ccClientV2, uaaClientV2, config)
    70  	cmd.NOAAClient = shared.NewNOAAClient(ccClient.Info.Logging(), config, uaaClient, ui)
    71  
    72  	cmd.AppSummaryDisplayer = shared.AppSummaryDisplayer{
    73  		UI:              cmd.UI,
    74  		Config:          cmd.Config,
    75  		Actor:           cmd.OriginalActor,
    76  		V2AppRouteActor: v2AppActor,
    77  		AppName:         cmd.RequiredArgs.AppName,
    78  	}
    79  	cmd.PackageDisplayer = shared.NewPackageDisplayer(cmd.UI, cmd.Config)
    80  
    81  	return nil
    82  }
    83  
    84  func (cmd V3PushCommand) OriginalExecute(args []string) error {
    85  	cmd.UI.DisplayWarning(command.ExperimentalWarning)
    86  
    87  	err := cmd.validateArgs()
    88  	if err != nil {
    89  		return err
    90  	}
    91  
    92  	err = command.MinimumAPIVersionCheck(cmd.OriginalActor.CloudControllerAPIVersion(), ccversion.MinVersionV3)
    93  	if err != nil {
    94  		return err
    95  	}
    96  
    97  	err = cmd.SharedActor.CheckTarget(true, true)
    98  	if err != nil {
    99  		return err
   100  	}
   101  
   102  	user, err := cmd.Config.CurrentUser()
   103  	if err != nil {
   104  		return err
   105  	}
   106  
   107  	if !verifyBuildpacks(cmd.Buildpacks) {
   108  		return translatableerror.ConflictingBuildpacksError{}
   109  	}
   110  
   111  	var app v3action.Application
   112  	app, err = cmd.getApplication()
   113  	if _, ok := err.(actionerror.ApplicationNotFoundError); ok {
   114  		app, err = cmd.createApplication(user.Name)
   115  		if err != nil {
   116  			return err
   117  		}
   118  	} else if err != nil {
   119  		return err
   120  	} else {
   121  		app, err = cmd.updateApplication(user.Name, app.GUID)
   122  		if err != nil {
   123  			return err
   124  		}
   125  	}
   126  
   127  	pkg, err := cmd.createPackage()
   128  	if err != nil {
   129  		return err
   130  	}
   131  
   132  	if app.Started() {
   133  		err = cmd.stopApplication(app.GUID, user.Name)
   134  		if err != nil {
   135  			return err
   136  		}
   137  	}
   138  
   139  	if cmd.NoStart {
   140  		return nil
   141  	}
   142  
   143  	dropletGUID, err := cmd.stagePackage(pkg, user.Name)
   144  	if err != nil {
   145  		return err
   146  	}
   147  
   148  	err = cmd.setApplicationDroplet(dropletGUID, user.Name)
   149  	if err != nil {
   150  		return err
   151  	}
   152  
   153  	if !cmd.NoRoute {
   154  		err = cmd.createAndMapRoutes(app)
   155  		if err != nil {
   156  			return err
   157  		}
   158  	}
   159  
   160  	err = cmd.startApplication(app.GUID, user.Name)
   161  	if err != nil {
   162  		return err
   163  	}
   164  
   165  	cmd.UI.DisplayText("Waiting for app to start...")
   166  
   167  	warnings := make(chan v3action.Warnings)
   168  	done := make(chan bool)
   169  	go func() {
   170  		for {
   171  			select {
   172  			case message := <-warnings:
   173  				cmd.UI.DisplayWarnings(message)
   174  			case <-done:
   175  				return
   176  			}
   177  		}
   178  	}()
   179  
   180  	err = cmd.OriginalActor.PollStart(app.GUID, warnings)
   181  	done <- true
   182  
   183  	if err != nil {
   184  		if _, ok := err.(actionerror.StartupTimeoutError); ok {
   185  			return translatableerror.StartupTimeoutError{
   186  				AppName:    cmd.RequiredArgs.AppName,
   187  				BinaryName: cmd.Config.BinaryName(),
   188  			}
   189  		}
   190  
   191  		return err
   192  	}
   193  
   194  	cmd.UI.DisplayTextWithFlavor("Showing health and status for app {{.AppName}} in org {{.OrgName}} / space {{.SpaceName}} as {{.Username}}...", map[string]interface{}{
   195  		"AppName":   cmd.RequiredArgs.AppName,
   196  		"OrgName":   cmd.Config.TargetedOrganization().Name,
   197  		"SpaceName": cmd.Config.TargetedSpace().Name,
   198  		"Username":  user.Name,
   199  	})
   200  	cmd.UI.DisplayNewline()
   201  
   202  	return cmd.AppSummaryDisplayer.DisplayAppInfo()
   203  }
   204  
   205  func (cmd V3PushCommand) validateArgs() error {
   206  	switch {
   207  	case cmd.DockerImage.Path != "" && cmd.AppPath != "":
   208  		return translatableerror.ArgumentCombinationError{
   209  			Args: []string{"--docker-image", "-o", "-p"},
   210  		}
   211  	case cmd.DockerImage.Path != "" && len(cmd.Buildpacks) > 0:
   212  		return translatableerror.ArgumentCombinationError{
   213  			Args: []string{"-b", "--docker-image", "-o"},
   214  		}
   215  	case cmd.DockerUsername != "" && cmd.DockerImage.Path == "":
   216  		return translatableerror.RequiredFlagsError{
   217  			Arg1: "--docker-image, -o", Arg2: "--docker-username",
   218  		}
   219  	case cmd.DockerUsername != "" && cmd.Config.DockerPassword() == "":
   220  		return translatableerror.DockerPasswordNotSetError{}
   221  	}
   222  	return nil
   223  }
   224  
   225  func (cmd V3PushCommand) createApplication(userName string) (v3action.Application, error) {
   226  	appToCreate := v3action.Application{
   227  		Name: cmd.RequiredArgs.AppName,
   228  	}
   229  
   230  	if cmd.DockerImage.Path != "" {
   231  		appToCreate.LifecycleType = constant.AppLifecycleTypeDocker
   232  	} else {
   233  		appToCreate.LifecycleType = constant.AppLifecycleTypeBuildpack
   234  		appToCreate.LifecycleBuildpacks = cmd.Buildpacks
   235  	}
   236  
   237  	app, warnings, err := cmd.OriginalActor.CreateApplicationInSpace(
   238  		appToCreate,
   239  		cmd.Config.TargetedSpace().GUID,
   240  	)
   241  	cmd.UI.DisplayWarnings(warnings)
   242  	if err != nil {
   243  		return v3action.Application{}, err
   244  	}
   245  
   246  	cmd.UI.DisplayTextWithFlavor("Creating app {{.AppName}} in org {{.CurrentOrg}} / space {{.CurrentSpace}} as {{.CurrentUser}}...", map[string]interface{}{
   247  		"AppName":      cmd.RequiredArgs.AppName,
   248  		"CurrentSpace": cmd.Config.TargetedSpace().Name,
   249  		"CurrentOrg":   cmd.Config.TargetedOrganization().Name,
   250  		"CurrentUser":  userName,
   251  	})
   252  
   253  	cmd.UI.DisplayOK()
   254  	cmd.UI.DisplayNewline()
   255  	return app, nil
   256  }
   257  
   258  func (cmd V3PushCommand) getApplication() (v3action.Application, error) {
   259  	app, warnings, err := cmd.OriginalActor.GetApplicationByNameAndSpace(cmd.RequiredArgs.AppName, cmd.Config.TargetedSpace().GUID)
   260  	cmd.UI.DisplayWarnings(warnings)
   261  	if err != nil {
   262  		return v3action.Application{}, err
   263  	}
   264  
   265  	return app, nil
   266  }
   267  
   268  func (cmd V3PushCommand) updateApplication(userName string, appGUID string) (v3action.Application, error) {
   269  	cmd.UI.DisplayTextWithFlavor("Updating app {{.AppName}} in org {{.CurrentOrg}} / space {{.CurrentSpace}} as {{.CurrentUser}}...", map[string]interface{}{
   270  		"AppName":      cmd.RequiredArgs.AppName,
   271  		"CurrentSpace": cmd.Config.TargetedSpace().Name,
   272  		"CurrentOrg":   cmd.Config.TargetedOrganization().Name,
   273  		"CurrentUser":  userName,
   274  	})
   275  
   276  	appToUpdate := v3action.Application{
   277  		GUID: appGUID,
   278  	}
   279  
   280  	if cmd.DockerImage.Path != "" {
   281  		appToUpdate.LifecycleType = constant.AppLifecycleTypeDocker
   282  
   283  	} else {
   284  		appToUpdate.LifecycleType = constant.AppLifecycleTypeBuildpack
   285  		appToUpdate.LifecycleBuildpacks = cmd.Buildpacks
   286  	}
   287  
   288  	app, warnings, err := cmd.OriginalActor.UpdateApplication(appToUpdate)
   289  	cmd.UI.DisplayWarnings(warnings)
   290  	if err != nil {
   291  		return v3action.Application{}, err
   292  	}
   293  
   294  	cmd.UI.DisplayOK()
   295  	cmd.UI.DisplayNewline()
   296  	return app, nil
   297  }
   298  
   299  func (cmd V3PushCommand) createAndMapRoutes(app v3action.Application) error {
   300  	cmd.UI.DisplayText("Mapping routes...")
   301  	routeWarnings, err := cmd.OriginalV2PushActor.CreateAndMapDefaultApplicationRoute(cmd.Config.TargetedOrganization().GUID, cmd.Config.TargetedSpace().GUID, v2action.Application{Name: app.Name, GUID: app.GUID})
   302  	cmd.UI.DisplayWarnings(routeWarnings)
   303  	if err != nil {
   304  		return err
   305  	}
   306  
   307  	cmd.UI.DisplayOK()
   308  	cmd.UI.DisplayNewline()
   309  	return nil
   310  }
   311  
   312  func (cmd V3PushCommand) createPackage() (v3action.Package, error) {
   313  	isDockerImage := (cmd.DockerImage.Path != "")
   314  	err := cmd.PackageDisplayer.DisplaySetupMessage(cmd.RequiredArgs.AppName, isDockerImage)
   315  	if err != nil {
   316  		return v3action.Package{}, err
   317  	}
   318  
   319  	var (
   320  		pkg      v3action.Package
   321  		warnings v3action.Warnings
   322  	)
   323  
   324  	if isDockerImage {
   325  		pkg, warnings, err = cmd.OriginalActor.CreateDockerPackageByApplicationNameAndSpace(cmd.RequiredArgs.AppName, cmd.Config.TargetedSpace().GUID, v3action.DockerImageCredentials{Path: cmd.DockerImage.Path, Username: cmd.DockerUsername, Password: cmd.Config.DockerPassword()})
   326  	} else {
   327  		pkg, warnings, err = cmd.OriginalActor.CreateAndUploadBitsPackageByApplicationNameAndSpace(cmd.RequiredArgs.AppName, cmd.Config.TargetedSpace().GUID, string(cmd.AppPath))
   328  	}
   329  
   330  	cmd.UI.DisplayWarnings(warnings)
   331  	if err != nil {
   332  		return v3action.Package{}, err
   333  	}
   334  
   335  	cmd.UI.DisplayOK()
   336  	cmd.UI.DisplayNewline()
   337  	return pkg, nil
   338  }
   339  
   340  func (cmd V3PushCommand) stagePackage(pkg v3action.Package, userName string) (string, error) {
   341  	cmd.UI.DisplayTextWithFlavor("Staging package for app {{.AppName}} in org {{.OrgName}} / space {{.SpaceName}} as {{.Username}}...", map[string]interface{}{
   342  		"AppName":   cmd.RequiredArgs.AppName,
   343  		"OrgName":   cmd.Config.TargetedOrganization().Name,
   344  		"SpaceName": cmd.Config.TargetedSpace().Name,
   345  		"Username":  userName,
   346  	})
   347  
   348  	logStream, logErrStream, logWarnings, logErr := cmd.OriginalActor.GetStreamingLogsForApplicationByNameAndSpace(cmd.RequiredArgs.AppName, cmd.Config.TargetedSpace().GUID, cmd.NOAAClient)
   349  	cmd.UI.DisplayWarnings(logWarnings)
   350  	if logErr != nil {
   351  		return "", logErr
   352  	}
   353  
   354  	buildStream, warningsStream, errStream := cmd.OriginalActor.StagePackage(pkg.GUID, cmd.RequiredArgs.AppName)
   355  	droplet, err := shared.PollStage(buildStream, warningsStream, errStream, logStream, logErrStream, cmd.UI)
   356  	if err != nil {
   357  		return "", err
   358  	}
   359  
   360  	cmd.UI.DisplayOK()
   361  	cmd.UI.DisplayNewline()
   362  	return droplet.GUID, nil
   363  }
   364  
   365  func (cmd V3PushCommand) setApplicationDroplet(dropletGUID string, userName string) error {
   366  	cmd.UI.DisplayTextWithFlavor("Setting app {{.AppName}} to droplet {{.DropletGUID}} in org {{.OrgName}} / space {{.SpaceName}} as {{.Username}}...", map[string]interface{}{
   367  		"AppName":     cmd.RequiredArgs.AppName,
   368  		"DropletGUID": dropletGUID,
   369  		"OrgName":     cmd.Config.TargetedOrganization().Name,
   370  		"SpaceName":   cmd.Config.TargetedSpace().Name,
   371  		"Username":    userName,
   372  	})
   373  
   374  	warnings, err := cmd.OriginalActor.SetApplicationDroplet(cmd.RequiredArgs.AppName, cmd.Config.TargetedSpace().GUID, dropletGUID)
   375  	cmd.UI.DisplayWarnings(warnings)
   376  	if err != nil {
   377  		return err
   378  	}
   379  
   380  	cmd.UI.DisplayOK()
   381  	cmd.UI.DisplayNewline()
   382  	return nil
   383  }
   384  
   385  func (cmd V3PushCommand) startApplication(appGUID string, userName string) error {
   386  	cmd.UI.DisplayTextWithFlavor("Starting app {{.AppName}} in org {{.OrgName}} / space {{.SpaceName}} as {{.Username}}...", map[string]interface{}{
   387  		"AppName":   cmd.RequiredArgs.AppName,
   388  		"OrgName":   cmd.Config.TargetedOrganization().Name,
   389  		"SpaceName": cmd.Config.TargetedSpace().Name,
   390  		"Username":  userName,
   391  	})
   392  
   393  	_, warnings, err := cmd.OriginalActor.StartApplication(appGUID)
   394  	cmd.UI.DisplayWarnings(warnings)
   395  	if err != nil {
   396  		return err
   397  	}
   398  	cmd.UI.DisplayOK()
   399  	cmd.UI.DisplayNewline()
   400  	return nil
   401  }
   402  
   403  func (cmd V3PushCommand) stopApplication(appGUID string, userName string) error {
   404  	cmd.UI.DisplayTextWithFlavor("Stopping app {{.AppName}} in org {{.CurrentOrg}} / space {{.CurrentSpace}} as {{.CurrentUser}}...", map[string]interface{}{
   405  		"AppName":      cmd.RequiredArgs.AppName,
   406  		"CurrentSpace": cmd.Config.TargetedSpace().Name,
   407  		"CurrentOrg":   cmd.Config.TargetedOrganization().Name,
   408  		"CurrentUser":  userName,
   409  	})
   410  
   411  	warnings, err := cmd.OriginalActor.StopApplication(appGUID)
   412  	cmd.UI.DisplayWarnings(warnings)
   413  	if err != nil {
   414  		return err
   415  	}
   416  	cmd.UI.DisplayOK()
   417  	cmd.UI.DisplayNewline()
   418  	return nil
   419  }
   420  
   421  func verifyBuildpacks(buildpacks []string) bool {
   422  	if len(buildpacks) < 2 {
   423  		return true
   424  	}
   425  
   426  	for _, buildpack := range buildpacks {
   427  		if buildpack == "default" || buildpack == "null" {
   428  			return false
   429  		}
   430  	}
   431  	return true
   432  }