github.com/randomtask1155/cli@v6.41.1-0.20181227003417-a98eed78cbde+incompatible/command/v6/v3_zdt_push_command.go (about)

     1  package v6
     2  
     3  import (
     4  	"fmt"
     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/ccv3/constant"
    12  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccversion"
    13  	"code.cloudfoundry.org/cli/command"
    14  	"code.cloudfoundry.org/cli/command/flag"
    15  	"code.cloudfoundry.org/cli/command/translatableerror"
    16  	"code.cloudfoundry.org/cli/command/v6/shared"
    17  )
    18  
    19  //go:generate counterfeiter . V3ZeroDowntimeVersionActor
    20  
    21  type V3ZeroDowntimeVersionActor interface {
    22  	ZeroDowntimePollStart(appGUID string, warningsChannel chan<- v3action.Warnings) error
    23  	CreateDeployment(appGUID string, deploymentGUID string) (string, v3action.Warnings, error)
    24  	PollDeployment(deploymentGUID string, warningsChannel chan<- v3action.Warnings) error
    25  	CloudControllerAPIVersion() string
    26  	CreateAndUploadBitsPackageByApplicationNameAndSpace(appName string, spaceGUID string, bitsPath string) (v3action.Package, v3action.Warnings, error)
    27  	CreateDockerPackageByApplicationNameAndSpace(appName string, spaceGUID string, dockerImageCredentials v3action.DockerImageCredentials) (v3action.Package, v3action.Warnings, error)
    28  	CreateApplicationInSpace(app v3action.Application, spaceGUID string) (v3action.Application, v3action.Warnings, error)
    29  	GetApplicationByNameAndSpace(appName string, spaceGUID string) (v3action.Application, v3action.Warnings, error)
    30  	GetCurrentDropletByApplication(appGUID string) (v3action.Droplet, v3action.Warnings, error)
    31  	GetStreamingLogsForApplicationByNameAndSpace(appName string, spaceGUID string, client v3action.NOAAClient) (<-chan *v3action.LogMessage, <-chan error, v3action.Warnings, error)
    32  	PollStart(appGUID string, warningsChannel chan<- v3action.Warnings) error
    33  	SetApplicationDropletByApplicationNameAndSpace(appName string, spaceGUID string, dropletGUID string) (v3action.Warnings, error)
    34  	StagePackage(packageGUID string, appName string) (<-chan v3action.Droplet, <-chan v3action.Warnings, <-chan error)
    35  	RestartApplication(appGUID string) (v3action.Warnings, error)
    36  	UpdateApplication(app v3action.Application) (v3action.Application, v3action.Warnings, error)
    37  }
    38  
    39  type V3ZeroDowntimePushCommand struct {
    40  	RequiredArgs        flag.AppName                `positional-args:"yes"`
    41  	Buildpacks          []string                    `short:"b" description:"Custom buildpack by name (e.g. my-buildpack) or Git URL (e.g. 'https://github.com/cloudfoundry/java-buildpack.git') or Git URL with a branch or tag (e.g. 'https://github.com/cloudfoundry/java-buildpack.git#v3.3.0' for 'v3.3.0' tag). To use built-in buildpacks only, specify 'default' or 'null'"`
    42  	StackName           string                      `short:"s" description:"Stack to use (a stack is a pre-built file system, including an operating system, that can run apps)"`
    43  	DockerImage         flag.DockerImage            `long:"docker-image" short:"o" description:"Docker image to use (e.g. user/docker-image-name)"`
    44  	DockerUsername      string                      `long:"docker-username" description:"Repository username; used with password from environment variable CF_DOCKER_PASSWORD"`
    45  	NoRoute             bool                        `long:"no-route" description:"Do not map a route to this app"`
    46  	NoStart             bool                        `long:"no-start" description:"Do not stage and start the app after pushing"`
    47  	WaitUntilDeployed   bool                        `long:"wait-for-deploy-complete" description:"Wait for the entire deployment to complete"`
    48  	AppPath             flag.PathWithExistenceCheck `short:"p" description:"Path to app directory or to a zip file of the contents of the app directory"`
    49  	dockerPassword      interface{}                 `environmentName:"CF_DOCKER_PASSWORD" environmentDescription:"Password used for private docker repository"`
    50  	usage               interface{}                 `usage:"CF_NAME v3-zdt-push APP_NAME [-b BUILDPACK]... [-p APP_PATH] [--no-route] [--no-start]\n   CF_NAME v3-zdt-push APP_NAME --docker-image [REGISTRY_HOST:PORT/]IMAGE[:TAG] [--docker-username USERNAME] [--no-route] [--no-start]"`
    51  	envCFStagingTimeout interface{}                 `environmentName:"CF_STAGING_TIMEOUT" environmentDescription:"Max wait time for buildpack staging, in minutes" environmentDefault:"15"`
    52  	envCFStartupTimeout interface{}                 `environmentName:"CF_STARTUP_TIMEOUT" environmentDescription:"Max wait time for app instance startup, in minutes" environmentDefault:"5"`
    53  
    54  	UI                  command.UI
    55  	Config              command.Config
    56  	NOAAClient          v3action.NOAAClient
    57  	Actor               V3PushActor
    58  	VersionActor        V3PushVersionActor
    59  	SharedActor         command.SharedActor
    60  	AppSummaryDisplayer shared.AppSummaryDisplayer
    61  	PackageDisplayer    shared.PackageDisplayer
    62  	ProgressBar         ProgressBar
    63  
    64  	ZdtActor            V3ZeroDowntimeVersionActor
    65  	OriginalV3PushActor OriginalV3PushActor
    66  	OriginalV2PushActor OriginalV2PushActor
    67  }
    68  
    69  func (cmd *V3ZeroDowntimePushCommand) Setup(config command.Config, ui command.UI) error {
    70  	cmd.UI = ui
    71  	cmd.Config = config
    72  	sharedActor := sharedaction.NewActor(config)
    73  
    74  	ccClient, uaaClient, err := shared.NewV3BasedClients(config, ui, true, "")
    75  	if err != nil {
    76  		return err
    77  	}
    78  	v3actor := v3action.NewActor(ccClient, config, sharedActor, nil)
    79  	cmd.ZdtActor = v3actor
    80  	cmd.OriginalV3PushActor = v3actor
    81  
    82  	ccClientV2, uaaClientV2, err := shared.NewClients(config, ui, true)
    83  	if err != nil {
    84  		return err
    85  	}
    86  
    87  	v2Actor := v2action.NewActor(ccClientV2, uaaClientV2, config)
    88  
    89  	cmd.SharedActor = sharedActor
    90  	cmd.OriginalV2PushActor = pushaction.NewActor(v2Actor, v3actor, sharedActor)
    91  
    92  	v2AppActor := v2action.NewActor(ccClientV2, uaaClientV2, config)
    93  	cmd.NOAAClient = shared.NewNOAAClient(ccClient.Info.Logging(), config, uaaClient, ui)
    94  
    95  	cmd.AppSummaryDisplayer = shared.AppSummaryDisplayer{
    96  		UI:         cmd.UI,
    97  		Config:     cmd.Config,
    98  		Actor:      cmd.OriginalV3PushActor,
    99  		V2AppActor: v2AppActor,
   100  		AppName:    cmd.RequiredArgs.AppName,
   101  	}
   102  	cmd.PackageDisplayer = shared.NewPackageDisplayer(cmd.UI, cmd.Config)
   103  
   104  	return nil
   105  }
   106  
   107  func (cmd V3ZeroDowntimePushCommand) Execute(args []string) error {
   108  	cmd.UI.DisplayWarning(command.ExperimentalWarning)
   109  
   110  	err := cmd.validateArgs()
   111  	if err != nil {
   112  		return err
   113  	}
   114  
   115  	err = command.MinimumCCAPIVersionCheck(cmd.ZdtActor.CloudControllerAPIVersion(), ccversion.MinVersionZeroDowntimePushV3)
   116  	if err != nil {
   117  		return err
   118  	}
   119  
   120  	err = cmd.SharedActor.CheckTarget(true, true)
   121  	if err != nil {
   122  		return err
   123  	}
   124  
   125  	user, err := cmd.Config.CurrentUser()
   126  	if err != nil {
   127  		return err
   128  	}
   129  
   130  	if !verifyBuildpacks(cmd.Buildpacks) {
   131  		return translatableerror.ConflictingBuildpacksError{}
   132  	}
   133  
   134  	var app v3action.Application
   135  	app, err = cmd.getApplication()
   136  	if _, ok := err.(actionerror.ApplicationNotFoundError); ok {
   137  		app, err = cmd.createApplication(user.Name)
   138  		if err != nil {
   139  			return err
   140  		}
   141  	} else if err != nil {
   142  		return err
   143  	} else {
   144  		app, err = cmd.updateApplication(user.Name, app.GUID)
   145  		if err != nil {
   146  			return err
   147  		}
   148  	}
   149  
   150  	pkg, err := cmd.createPackage()
   151  	if err != nil {
   152  		return err
   153  	}
   154  
   155  	if cmd.NoStart {
   156  		return nil
   157  	}
   158  
   159  	dropletGUID, err := cmd.stagePackage(pkg, user.Name)
   160  	if err != nil {
   161  		return err
   162  	}
   163  
   164  	if !cmd.NoRoute {
   165  		err = cmd.createAndMapRoutes(app)
   166  		if err != nil {
   167  			return err
   168  		}
   169  	}
   170  
   171  	warnings := make(chan v3action.Warnings)
   172  	done := make(chan bool)
   173  	go func() {
   174  		for {
   175  			select {
   176  			case message := <-warnings:
   177  				cmd.UI.DisplayWarnings(message)
   178  			case <-done:
   179  				return
   180  			}
   181  		}
   182  	}()
   183  
   184  	switch app.State {
   185  	case constant.ApplicationStopped:
   186  		err = cmd.setApplicationDroplet(dropletGUID, user.Name)
   187  		if err != nil {
   188  			return err
   189  		}
   190  
   191  		err = cmd.restartApplication(app.GUID, user.Name)
   192  		if err != nil {
   193  			return err
   194  		}
   195  
   196  		cmd.UI.DisplayText("Waiting for app to start...")
   197  		err = cmd.ZdtActor.PollStart(app.GUID, warnings)
   198  
   199  	case constant.ApplicationStarted:
   200  		var deploymentGUID string
   201  		deploymentGUID, err = cmd.createDeployment(app.GUID, user.Name, dropletGUID)
   202  		if err != nil {
   203  			return err
   204  		}
   205  
   206  		cmd.UI.DisplayText("Waiting for app to start...")
   207  		if cmd.WaitUntilDeployed {
   208  			err = cmd.ZdtActor.PollDeployment(deploymentGUID, warnings) //
   209  		} else {
   210  			err = cmd.ZdtActor.ZeroDowntimePollStart(app.GUID, warnings)
   211  		}
   212  	default:
   213  		return fmt.Errorf("inconceivable application state: %s", app.State)
   214  	}
   215  
   216  	done <- true
   217  	if err != nil {
   218  		if _, ok := err.(actionerror.StartupTimeoutError); ok {
   219  			return translatableerror.StartupTimeoutError{
   220  				AppName:    cmd.RequiredArgs.AppName,
   221  				BinaryName: cmd.Config.BinaryName(),
   222  			}
   223  		}
   224  
   225  		return err
   226  	}
   227  
   228  	cmd.UI.DisplayTextWithFlavor("Showing health and status for app {{.AppName}} in org {{.OrgName}} / space {{.SpaceName}} as {{.Username}}...", map[string]interface{}{
   229  		"AppName":   cmd.RequiredArgs.AppName,
   230  		"OrgName":   cmd.Config.TargetedOrganization().Name,
   231  		"SpaceName": cmd.Config.TargetedSpace().Name,
   232  		"Username":  user.Name,
   233  	})
   234  	cmd.UI.DisplayNewline()
   235  
   236  	return cmd.AppSummaryDisplayer.DisplayAppInfo()
   237  }
   238  
   239  func (cmd V3ZeroDowntimePushCommand) validateArgs() error {
   240  	switch {
   241  	case cmd.DockerImage.Path != "" && cmd.AppPath != "":
   242  		return translatableerror.ArgumentCombinationError{
   243  			Args: []string{"--docker-image", "-o", "-p"},
   244  		}
   245  	case cmd.DockerImage.Path != "" && len(cmd.Buildpacks) > 0:
   246  		return translatableerror.ArgumentCombinationError{
   247  			Args: []string{"-b", "--docker-image", "-o"},
   248  		}
   249  	case cmd.DockerUsername != "" && cmd.DockerImage.Path == "":
   250  		return translatableerror.RequiredFlagsError{
   251  			Arg1: "--docker-image, -o", Arg2: "--docker-username",
   252  		}
   253  	case cmd.DockerUsername != "" && cmd.Config.DockerPassword() == "":
   254  		return translatableerror.DockerPasswordNotSetError{}
   255  	}
   256  	return nil
   257  }
   258  
   259  func (cmd V3ZeroDowntimePushCommand) createApplication(userName string) (v3action.Application, error) {
   260  	appToCreate := v3action.Application{
   261  		Name: cmd.RequiredArgs.AppName,
   262  	}
   263  
   264  	if cmd.DockerImage.Path != "" {
   265  		appToCreate.LifecycleType = constant.AppLifecycleTypeDocker
   266  	} else {
   267  		appToCreate.LifecycleType = constant.AppLifecycleTypeBuildpack
   268  		appToCreate.LifecycleBuildpacks = cmd.Buildpacks
   269  		appToCreate.StackName = cmd.StackName
   270  	}
   271  
   272  	app, warnings, err := cmd.ZdtActor.CreateApplicationInSpace(
   273  		appToCreate,
   274  		cmd.Config.TargetedSpace().GUID,
   275  	)
   276  	cmd.UI.DisplayWarnings(warnings)
   277  	if err != nil {
   278  		return v3action.Application{}, err
   279  	}
   280  
   281  	cmd.UI.DisplayTextWithFlavor("Creating app {{.AppName}} in org {{.CurrentOrg}} / space {{.CurrentSpace}} as {{.CurrentUser}}...", map[string]interface{}{
   282  		"AppName":      cmd.RequiredArgs.AppName,
   283  		"CurrentSpace": cmd.Config.TargetedSpace().Name,
   284  		"CurrentOrg":   cmd.Config.TargetedOrganization().Name,
   285  		"CurrentUser":  userName,
   286  	})
   287  
   288  	cmd.UI.DisplayOK()
   289  	cmd.UI.DisplayNewline()
   290  	return app, nil
   291  }
   292  
   293  func (cmd V3ZeroDowntimePushCommand) getApplication() (v3action.Application, error) {
   294  	app, warnings, err := cmd.ZdtActor.GetApplicationByNameAndSpace(cmd.RequiredArgs.AppName, cmd.Config.TargetedSpace().GUID)
   295  	cmd.UI.DisplayWarnings(warnings)
   296  	if err != nil {
   297  		return v3action.Application{}, err
   298  	}
   299  
   300  	return app, nil
   301  }
   302  
   303  func (cmd V3ZeroDowntimePushCommand) updateApplication(userName string, appGUID string) (v3action.Application, error) {
   304  	cmd.UI.DisplayTextWithFlavor("Updating app {{.AppName}} in org {{.CurrentOrg}} / space {{.CurrentSpace}} as {{.CurrentUser}}...", map[string]interface{}{
   305  		"AppName":      cmd.RequiredArgs.AppName,
   306  		"CurrentSpace": cmd.Config.TargetedSpace().Name,
   307  		"CurrentOrg":   cmd.Config.TargetedOrganization().Name,
   308  		"CurrentUser":  userName,
   309  	})
   310  
   311  	appToUpdate := v3action.Application{
   312  		GUID: appGUID,
   313  	}
   314  
   315  	if cmd.DockerImage.Path != "" {
   316  		appToUpdate.LifecycleType = constant.AppLifecycleTypeDocker
   317  
   318  	} else {
   319  		appToUpdate.LifecycleType = constant.AppLifecycleTypeBuildpack
   320  		appToUpdate.LifecycleBuildpacks = cmd.Buildpacks
   321  		appToUpdate.StackName = cmd.StackName
   322  	}
   323  
   324  	app, warnings, err := cmd.ZdtActor.UpdateApplication(appToUpdate)
   325  	cmd.UI.DisplayWarnings(warnings)
   326  	if err != nil {
   327  		return v3action.Application{}, err
   328  	}
   329  
   330  	cmd.UI.DisplayOK()
   331  	cmd.UI.DisplayNewline()
   332  	return app, nil
   333  }
   334  
   335  func (cmd V3ZeroDowntimePushCommand) createAndMapRoutes(app v3action.Application) error {
   336  	cmd.UI.DisplayText("Mapping routes...")
   337  	routeWarnings, err := cmd.OriginalV2PushActor.CreateAndMapDefaultApplicationRoute(cmd.Config.TargetedOrganization().GUID, cmd.Config.TargetedSpace().GUID, v2action.Application{Name: app.Name, GUID: app.GUID})
   338  	cmd.UI.DisplayWarnings(routeWarnings)
   339  	if err != nil {
   340  		return err
   341  	}
   342  
   343  	cmd.UI.DisplayOK()
   344  	cmd.UI.DisplayNewline()
   345  	return nil
   346  }
   347  
   348  func (cmd V3ZeroDowntimePushCommand) createPackage() (v3action.Package, error) {
   349  	isDockerImage := cmd.DockerImage.Path != ""
   350  	err := cmd.PackageDisplayer.DisplaySetupMessage(cmd.RequiredArgs.AppName, isDockerImage)
   351  	if err != nil {
   352  		return v3action.Package{}, err
   353  	}
   354  
   355  	var (
   356  		pkg      v3action.Package
   357  		warnings v3action.Warnings
   358  	)
   359  
   360  	if isDockerImage {
   361  		pkg, warnings, err = cmd.ZdtActor.CreateDockerPackageByApplicationNameAndSpace(cmd.RequiredArgs.AppName, cmd.Config.TargetedSpace().GUID, v3action.DockerImageCredentials{Path: cmd.DockerImage.Path, Username: cmd.DockerUsername, Password: cmd.Config.DockerPassword()})
   362  	} else {
   363  		pkg, warnings, err = cmd.ZdtActor.CreateAndUploadBitsPackageByApplicationNameAndSpace(cmd.RequiredArgs.AppName, cmd.Config.TargetedSpace().GUID, string(cmd.AppPath))
   364  	}
   365  
   366  	cmd.UI.DisplayWarnings(warnings)
   367  	if err != nil {
   368  		return v3action.Package{}, err
   369  	}
   370  
   371  	cmd.UI.DisplayOK()
   372  	cmd.UI.DisplayNewline()
   373  	return pkg, nil
   374  }
   375  
   376  func (cmd V3ZeroDowntimePushCommand) stagePackage(pkg v3action.Package, userName string) (string, error) {
   377  	cmd.UI.DisplayTextWithFlavor("Staging package for app {{.AppName}} in org {{.OrgName}} / space {{.SpaceName}} as {{.Username}}...", map[string]interface{}{
   378  		"AppName":   cmd.RequiredArgs.AppName,
   379  		"OrgName":   cmd.Config.TargetedOrganization().Name,
   380  		"SpaceName": cmd.Config.TargetedSpace().Name,
   381  		"Username":  userName,
   382  	})
   383  
   384  	logStream, logErrStream, logWarnings, logErr := cmd.ZdtActor.GetStreamingLogsForApplicationByNameAndSpace(cmd.RequiredArgs.AppName, cmd.Config.TargetedSpace().GUID, cmd.NOAAClient)
   385  	cmd.UI.DisplayWarnings(logWarnings)
   386  	if logErr != nil {
   387  		return "", logErr
   388  	}
   389  
   390  	buildStream, warningsStream, errStream := cmd.ZdtActor.StagePackage(pkg.GUID, cmd.RequiredArgs.AppName)
   391  	droplet, err := shared.PollStage(buildStream, warningsStream, errStream, logStream, logErrStream, cmd.UI)
   392  	if err != nil {
   393  		return "", err
   394  	}
   395  
   396  	cmd.UI.DisplayOK()
   397  	cmd.UI.DisplayNewline()
   398  	return droplet.GUID, nil
   399  }
   400  
   401  func (cmd V3ZeroDowntimePushCommand) setApplicationDroplet(dropletGUID string, userName string) error {
   402  	cmd.UI.DisplayTextWithFlavor("Setting app {{.AppName}} to droplet {{.DropletGUID}} in org {{.OrgName}} / space {{.SpaceName}} as {{.Username}}...", map[string]interface{}{
   403  		"AppName":     cmd.RequiredArgs.AppName,
   404  		"DropletGUID": dropletGUID,
   405  		"OrgName":     cmd.Config.TargetedOrganization().Name,
   406  		"SpaceName":   cmd.Config.TargetedSpace().Name,
   407  		"Username":    userName,
   408  	})
   409  
   410  	warnings, err := cmd.ZdtActor.SetApplicationDropletByApplicationNameAndSpace(cmd.RequiredArgs.AppName, cmd.Config.TargetedSpace().GUID, dropletGUID)
   411  	cmd.UI.DisplayWarnings(warnings)
   412  	if err != nil {
   413  		return err
   414  	}
   415  
   416  	cmd.UI.DisplayOK()
   417  	cmd.UI.DisplayNewline()
   418  	return nil
   419  }
   420  
   421  func (cmd V3ZeroDowntimePushCommand) restartApplication(appGUID string, userName string) error {
   422  	cmd.UI.DisplayTextWithFlavor("Starting app {{.AppName}} in org {{.OrgName}} / space {{.SpaceName}} as {{.Username}}...", map[string]interface{}{
   423  		"AppName":   cmd.RequiredArgs.AppName,
   424  		"OrgName":   cmd.Config.TargetedOrganization().Name,
   425  		"SpaceName": cmd.Config.TargetedSpace().Name,
   426  		"Username":  userName,
   427  	})
   428  
   429  	warnings, err := cmd.ZdtActor.RestartApplication(appGUID)
   430  	cmd.UI.DisplayWarnings(warnings)
   431  	if err != nil {
   432  		return err
   433  	}
   434  	cmd.UI.DisplayOK()
   435  	cmd.UI.DisplayNewline()
   436  	return nil
   437  }
   438  
   439  func (cmd V3ZeroDowntimePushCommand) createDeployment(appGUID string, userName string, dropletGUID string) (string, error) {
   440  	cmd.UI.DisplayTextWithFlavor("Starting deployment for app {{.AppName}} in org {{.CurrentOrg}} / space {{.CurrentSpace}} as {{.CurrentUser}}...", map[string]interface{}{
   441  		"AppName":      cmd.RequiredArgs.AppName,
   442  		"CurrentSpace": cmd.Config.TargetedSpace().Name,
   443  		"CurrentOrg":   cmd.Config.TargetedOrganization().Name,
   444  		"CurrentUser":  userName,
   445  	})
   446  
   447  	deploymentGUID, warnings, err := cmd.ZdtActor.CreateDeployment(appGUID, dropletGUID)
   448  	cmd.UI.DisplayWarnings(warnings)
   449  	if err != nil {
   450  		return "", err
   451  	}
   452  	cmd.UI.DisplayOK()
   453  	cmd.UI.DisplayNewline()
   454  	return deploymentGUID, nil
   455  }