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

     1  package v6
     2  
     3  import (
     4  	"os"
     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/ccversion"
    12  	"code.cloudfoundry.org/cli/command"
    13  	"code.cloudfoundry.org/cli/command/flag"
    14  	"code.cloudfoundry.org/cli/command/translatableerror"
    15  	"code.cloudfoundry.org/cli/command/v6/shared"
    16  	"code.cloudfoundry.org/cli/util/progressbar"
    17  
    18  	log "github.com/sirupsen/logrus"
    19  )
    20  
    21  //go:generate counterfeiter . V3PushActor
    22  
    23  type V3PushActor interface {
    24  	Actualize(state pushaction.PushState, progressBar pushaction.ProgressBar) (<-chan pushaction.PushState, <-chan pushaction.Event, <-chan pushaction.Warnings, <-chan error)
    25  	Conceptualize(setting pushaction.CommandLineSettings, spaceGUID string) ([]pushaction.PushState, pushaction.Warnings, error)
    26  }
    27  
    28  //go:generate counterfeiter . V3PushVersionActor
    29  
    30  type V3PushVersionActor interface {
    31  	CloudControllerAPIVersion() string
    32  	GetStreamingLogsForApplicationByNameAndSpace(appName string, spaceGUID string, client v3action.NOAAClient) (<-chan *v3action.LogMessage, <-chan error, v3action.Warnings, error)
    33  	PollStart(appGUID string, warningsChannel chan<- v3action.Warnings) error
    34  	RestartApplication(appGUID string) (v3action.Warnings, error)
    35  }
    36  
    37  type V3PushCommand struct {
    38  	RequiredArgs flag.AppName `positional-args:"yes"`
    39  	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'"`
    40  	// Command flag.Command
    41  	// Domain string
    42  	DockerImage    flag.DockerImage `long:"docker-image" short:"o" description:"Docker image to use (e.g. user/docker-image-name)"`
    43  	DockerUsername string           `long:"docker-username" description:"Repository username; used with password from environment variable CF_DOCKER_PASSWORD"`
    44  	// DropletPath flag.PathWithExistenceCheck
    45  	// PathToManifest flag.PathWithExistenceCheck
    46  	// HealthCheckType flag.HealthCheckTypeWithDeprecatedValue
    47  	// Hostname string
    48  	// Instances flag.Instances
    49  	// DiskQuota           flag.Megabytes
    50  	// Memory              flag.Megabytes
    51  	// NoHostname          bool
    52  	// NoManifest          bool
    53  	NoRoute bool                        `long:"no-route" description:"Do not map a route to this app"`
    54  	NoStart bool                        `long:"no-start" description:"Do not stage and start the app after pushing"`
    55  	AppPath flag.PathWithExistenceCheck `short:"p" description:"Path to app directory or to a zip file of the contents of the app directory"`
    56  	// RandomRoute         bool
    57  	// RoutePath           flag.RoutePath
    58  	// StackName           string
    59  	// VarsFilePaths []flag.PathWithExistenceCheck
    60  	// Vars []template.VarKV
    61  	// HealthCheckTimeout int
    62  	dockerPassword      interface{} `environmentName:"CF_DOCKER_PASSWORD" environmentDescription:"Password used for private docker repository"`
    63  	usage               interface{} `usage:"CF_NAME v3-push APP_NAME [-b BUILDPACK]... [-p APP_PATH] [--no-route] [--no-start]\n   CF_NAME v3-push APP_NAME --docker-image [REGISTRY_HOST:PORT/]IMAGE[:TAG] [--docker-username USERNAME] [--no-route] [--no-start]"`
    64  	envCFStagingTimeout interface{} `environmentName:"CF_STAGING_TIMEOUT" environmentDescription:"Max wait time for buildpack staging, in minutes" environmentDefault:"15"`
    65  	envCFStartupTimeout interface{} `environmentName:"CF_STARTUP_TIMEOUT" environmentDescription:"Max wait time for app instance startup, in minutes" environmentDefault:"5"`
    66  
    67  	UI                  command.UI
    68  	Config              command.Config
    69  	NOAAClient          v3action.NOAAClient
    70  	Actor               V3PushActor
    71  	VersionActor        V3PushVersionActor
    72  	SharedActor         command.SharedActor
    73  	AppSummaryDisplayer shared.AppSummaryDisplayer
    74  	PackageDisplayer    shared.PackageDisplayer
    75  	ProgressBar         ProgressBar
    76  
    77  	OriginalActor       OriginalV3PushActor
    78  	OriginalV2PushActor OriginalV2PushActor
    79  }
    80  
    81  func (cmd *V3PushCommand) Setup(config command.Config, ui command.UI) error {
    82  	if !config.Experimental() {
    83  		return cmd.OriginalSetup(config, ui)
    84  	}
    85  
    86  	cmd.Config = config
    87  	cmd.UI = ui
    88  	cmd.ProgressBar = progressbar.NewProgressBar()
    89  
    90  	sharedActor := sharedaction.NewActor(config)
    91  	cmd.SharedActor = sharedActor
    92  
    93  	ccClient, uaaClient, err := shared.NewV3BasedClients(config, ui, true, "")
    94  	if err != nil {
    95  		return err
    96  	}
    97  	v3Actor := v3action.NewActor(ccClient, config, sharedActor, uaaClient)
    98  	cmd.VersionActor = v3Actor
    99  
   100  	ccClientV2, uaaClientV2, err := shared.NewClients(config, ui, true)
   101  	if err != nil {
   102  		return err
   103  	}
   104  
   105  	v2Actor := v2action.NewActor(ccClientV2, uaaClientV2, config)
   106  	cmd.Actor = pushaction.NewActor(v2Actor, v3Actor, sharedActor)
   107  
   108  	cmd.NOAAClient = shared.NewNOAAClient(ccClient.Info.Logging(), config, uaaClient, ui)
   109  
   110  	return nil
   111  }
   112  
   113  func (cmd V3PushCommand) Execute(args []string) error {
   114  	if !cmd.Config.Experimental() {
   115  		return cmd.OriginalExecute(args)
   116  	}
   117  
   118  	err := command.MinimumCCAPIVersionCheck(cmd.VersionActor.CloudControllerAPIVersion(), ccversion.MinVersionApplicationFlowV3)
   119  	if err != nil {
   120  		return err
   121  	}
   122  
   123  	cmd.UI.DisplayWarning(command.ExperimentalWarning)
   124  
   125  	err = cmd.SharedActor.CheckTarget(true, true)
   126  	if err != nil {
   127  		return err
   128  	}
   129  
   130  	user, err := cmd.Config.CurrentUser()
   131  	if err != nil {
   132  		return err
   133  	}
   134  
   135  	cliSettings, err := cmd.GetCommandLineSettings()
   136  	if err != nil {
   137  		return err
   138  	}
   139  
   140  	cmd.UI.DisplayTextWithFlavor("Pushing app {{.AppName}} to org {{.OrgName}} / space {{.SpaceName}} as {{.Username}}...", map[string]interface{}{
   141  		"AppName":   cliSettings.Name,
   142  		"OrgName":   cmd.Config.TargetedOrganization().Name,
   143  		"SpaceName": cmd.Config.TargetedSpace().Name,
   144  		"Username":  user.Name,
   145  	})
   146  
   147  	cmd.UI.DisplayText("Getting app info...")
   148  
   149  	log.Info("generating the app state")
   150  	pushState, warnings, err := cmd.Actor.Conceptualize(cliSettings, cmd.Config.TargetedSpace().GUID)
   151  	cmd.UI.DisplayWarnings(warnings)
   152  	if err != nil {
   153  		return err
   154  	}
   155  	log.WithField("number of states", len(pushState)).Debug("completed generating state")
   156  
   157  	for _, state := range pushState {
   158  		log.WithField("app_name", state.Application.Name).Info("actualizing")
   159  		stateStream, eventStream, warningsStream, errorStream := cmd.Actor.Actualize(state, cmd.ProgressBar)
   160  		updatedState, err := cmd.processApplyStreams(state.Application.Name, stateStream, eventStream, warningsStream, errorStream)
   161  		if err != nil {
   162  			return err
   163  		}
   164  
   165  		cmd.UI.DisplayNewline()
   166  		cmd.UI.DisplayText("Waiting for app to start...")
   167  		warnings, err := cmd.VersionActor.RestartApplication(updatedState.Application.GUID)
   168  		cmd.UI.DisplayWarnings(warnings)
   169  		if err != nil {
   170  			return err
   171  		}
   172  
   173  		pollWarnings := make(chan v3action.Warnings)
   174  		done := make(chan bool)
   175  		go func() {
   176  			for {
   177  				select {
   178  				case message := <-pollWarnings:
   179  					cmd.UI.DisplayWarnings(message)
   180  				case <-done:
   181  					return
   182  				}
   183  			}
   184  		}()
   185  
   186  		err = cmd.VersionActor.PollStart(updatedState.Application.GUID, pollWarnings)
   187  		done <- true
   188  
   189  		if err != nil {
   190  			if _, ok := err.(actionerror.StartupTimeoutError); ok {
   191  				return translatableerror.StartupTimeoutError{
   192  					AppName:    cmd.RequiredArgs.AppName,
   193  					BinaryName: cmd.Config.BinaryName(),
   194  				}
   195  			}
   196  
   197  			return err
   198  		}
   199  	}
   200  
   201  	return nil
   202  }
   203  
   204  func (cmd V3PushCommand) processApplyStreams(
   205  	appName string,
   206  	stateStream <-chan pushaction.PushState,
   207  	eventStream <-chan pushaction.Event,
   208  	warningsStream <-chan pushaction.Warnings,
   209  	errorStream <-chan error,
   210  ) (pushaction.PushState, error) {
   211  	var stateClosed, eventClosed, warningsClosed, complete bool
   212  	var updateState pushaction.PushState
   213  
   214  	for {
   215  		select {
   216  		case state, ok := <-stateStream:
   217  			if !ok {
   218  				log.Debug("processing config stream closed")
   219  				stateClosed = true
   220  				break
   221  			}
   222  			updateState = state
   223  		case event, ok := <-eventStream:
   224  			if !ok {
   225  				log.Debug("processing event stream closed")
   226  				eventClosed = true
   227  				break
   228  			}
   229  			complete = cmd.processEvent(appName, event)
   230  		case warnings, ok := <-warningsStream:
   231  			if !ok {
   232  				log.Debug("processing warnings stream closed")
   233  				warningsClosed = true
   234  				break
   235  			}
   236  			cmd.UI.DisplayWarnings(warnings)
   237  		case err, ok := <-errorStream:
   238  			if !ok {
   239  				log.Debug("processing error stream closed")
   240  				warningsClosed = true
   241  				break
   242  			}
   243  			return pushaction.PushState{}, err
   244  		}
   245  
   246  		if stateClosed && eventClosed && warningsClosed && complete {
   247  			break
   248  		}
   249  	}
   250  
   251  	return updateState, nil
   252  }
   253  
   254  func (cmd V3PushCommand) processEvent(appName string, event pushaction.Event) bool {
   255  	log.Infoln("received apply event:", event)
   256  
   257  	switch event {
   258  	case pushaction.SkippingApplicationCreation:
   259  		cmd.UI.DisplayTextWithFlavor("Updating app {{.AppName}}...", map[string]interface{}{
   260  			"AppName": appName,
   261  		})
   262  	case pushaction.CreatedApplication:
   263  		cmd.UI.DisplayTextWithFlavor("Creating app {{.AppName}}...", map[string]interface{}{
   264  			"AppName": appName,
   265  		})
   266  	case pushaction.CreatingArchive:
   267  		cmd.UI.DisplayTextWithFlavor("Packaging files to upload...")
   268  	case pushaction.UploadingApplicationWithArchive:
   269  		cmd.UI.DisplayTextWithFlavor("Uploading files...")
   270  		log.Debug("starting progress bar")
   271  		cmd.ProgressBar.Ready()
   272  	case pushaction.RetryUpload:
   273  		cmd.UI.DisplayText("Retrying upload due to an error...")
   274  	case pushaction.UploadWithArchiveComplete:
   275  		cmd.ProgressBar.Complete()
   276  		cmd.UI.DisplayNewline()
   277  		cmd.UI.DisplayText("Waiting for API to complete processing files...")
   278  	case pushaction.StartingStaging:
   279  		cmd.UI.DisplayNewline()
   280  		cmd.UI.DisplayText("Staging app and tracing logs...")
   281  		logStream, errStream, warnings, _ := cmd.VersionActor.GetStreamingLogsForApplicationByNameAndSpace(appName, cmd.Config.TargetedSpace().GUID, cmd.NOAAClient)
   282  		cmd.UI.DisplayWarnings(warnings)
   283  		go cmd.getLogs(logStream, errStream)
   284  	case pushaction.StagingComplete:
   285  		cmd.NOAAClient.Close()
   286  	case pushaction.Complete:
   287  		return true
   288  	default:
   289  		log.WithField("event", event).Debug("ignoring event")
   290  	}
   291  	return false
   292  }
   293  
   294  func (cmd V3PushCommand) getLogs(logStream <-chan *v3action.LogMessage, errStream <-chan error) {
   295  	for {
   296  		select {
   297  		case logMessage, open := <-logStream:
   298  			if !open {
   299  				return
   300  			}
   301  			if logMessage.Staging() {
   302  				cmd.UI.DisplayLogMessage(logMessage, false)
   303  			}
   304  		case err, open := <-errStream:
   305  			if !open {
   306  				return
   307  			}
   308  			_, ok := err.(actionerror.NOAATimeoutError)
   309  			if ok {
   310  				cmd.UI.DisplayWarning("timeout connecting to log server, no log will be shown")
   311  			}
   312  			cmd.UI.DisplayWarning(err.Error())
   313  		}
   314  	}
   315  }
   316  
   317  func (cmd V3PushCommand) GetCommandLineSettings() (pushaction.CommandLineSettings, error) {
   318  	pwd, err := os.Getwd()
   319  	if err != nil {
   320  		return pushaction.CommandLineSettings{}, err
   321  	}
   322  	return pushaction.CommandLineSettings{
   323  		Buildpacks:       cmd.Buildpacks,
   324  		CurrentDirectory: pwd,
   325  		Name:             cmd.RequiredArgs.AppName,
   326  		ProvidedAppPath:  string(cmd.AppPath),
   327  	}, nil
   328  }