github.com/jenspinney/cli@v6.42.1-0.20190207184520-7450c600020e+incompatible/command/v6/v3_push_original_command.go (about)

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