github.com/arunkumar7540/cli@v6.45.0+incompatible/command/v7/push_command.go (about)

     1  package v7
     2  
     3  import (
     4  	"os"
     5  	"strings"
     6  
     7  	"code.cloudfoundry.org/cli/command/v7/shared"
     8  	"code.cloudfoundry.org/cli/util/configv3"
     9  
    10  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant"
    11  
    12  	"code.cloudfoundry.org/cli/actor/actionerror"
    13  	"code.cloudfoundry.org/cli/actor/sharedaction"
    14  	"code.cloudfoundry.org/cli/actor/v2action"
    15  	"code.cloudfoundry.org/cli/actor/v3action"
    16  	"code.cloudfoundry.org/cli/actor/v7action"
    17  	"code.cloudfoundry.org/cli/actor/v7pushaction"
    18  	"code.cloudfoundry.org/cli/command"
    19  	"code.cloudfoundry.org/cli/command/flag"
    20  	"code.cloudfoundry.org/cli/command/translatableerror"
    21  	v6shared "code.cloudfoundry.org/cli/command/v6/shared"
    22  	"code.cloudfoundry.org/cli/util/manifestparser"
    23  	"code.cloudfoundry.org/cli/util/progressbar"
    24  
    25  	"github.com/cloudfoundry/bosh-cli/director/template"
    26  	log "github.com/sirupsen/logrus"
    27  )
    28  
    29  //go:generate counterfeiter . ProgressBar
    30  
    31  type ProgressBar interface {
    32  	v7pushaction.ProgressBar
    33  	Complete()
    34  	Ready()
    35  }
    36  
    37  //go:generate counterfeiter . PushActor
    38  
    39  type PushActor interface {
    40  	CreatePushPlans(appNameArg string, spaceGUID string, orgGUID string, parser v7pushaction.ManifestParser, overrides v7pushaction.FlagOverrides) ([]v7pushaction.PushPlan, error)
    41  	// Prepare the space by creating needed apps/applying the manifest
    42  	PrepareSpace(pushPlans []v7pushaction.PushPlan, parser v7pushaction.ManifestParser) (<-chan []v7pushaction.PushPlan, <-chan v7pushaction.Event, <-chan v7pushaction.Warnings, <-chan error)
    43  	// UpdateApplicationSettings figures out the state of the world.
    44  	UpdateApplicationSettings(pushPlans []v7pushaction.PushPlan) ([]v7pushaction.PushPlan, v7pushaction.Warnings, error)
    45  	// Actualize applies any necessary changes.
    46  	Actualize(plan v7pushaction.PushPlan, progressBar v7pushaction.ProgressBar) (<-chan v7pushaction.PushPlan, <-chan v7pushaction.Event, <-chan v7pushaction.Warnings, <-chan error)
    47  }
    48  
    49  //go:generate counterfeiter . V7ActorForPush
    50  
    51  type V7ActorForPush interface {
    52  	AppActor
    53  	GetStreamingLogsForApplicationByNameAndSpace(appName string, spaceGUID string, client v7action.NOAAClient) (<-chan *v7action.LogMessage, <-chan error, v7action.Warnings, error)
    54  	RestartApplication(appGUID string) (v7action.Warnings, error)
    55  }
    56  
    57  //go:generate counterfeiter . ManifestParser
    58  
    59  type ManifestParser interface {
    60  	v7pushaction.ManifestParser
    61  	ContainsMultipleApps() bool
    62  	InterpolateAndParse(pathToManifest string, pathsToVarsFiles []string, vars []template.VarKV) error
    63  	ContainsPrivateDockerImages() bool
    64  }
    65  
    66  //go:generate counterfeiter . ManifestLocator
    67  
    68  type ManifestLocator interface {
    69  	Path(filepathOrDirectory string) (string, bool, error)
    70  }
    71  
    72  type PushCommand struct {
    73  	OptionalArgs            flag.OptionalAppName          `positional-args:"yes"`
    74  	HealthCheckTimeout      flag.PositiveInteger          `long:"app-start-timeout" short:"t" description:"Time (in seconds) allowed to elapse between starting up an app and the first healthy response from the app"`
    75  	Buildpacks              []string                      `long:"buildpack" 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'"`
    76  	Disk                    flag.Megabytes                `long:"disk" short:"k" description:"Disk limit (e.g. 256M, 1024M, 1G)"`
    77  	DockerImage             flag.DockerImage              `long:"docker-image" short:"o" description:"Docker image to use (e.g. user/docker-image-name)"`
    78  	DockerUsername          string                        `long:"docker-username" description:"Repository username; used with password from environment variable CF_DOCKER_PASSWORD"`
    79  	DropletPath             flag.PathWithExistenceCheck   `long:"droplet" description:"Path to a tgz file with a pre-staged app"`
    80  	HealthCheckHTTPEndpoint string                        `long:"endpoint"  description:"Valid path on the app for an HTTP health check. Only used when specifying --health-check-type=http"`
    81  	HealthCheckType         flag.HealthCheckType          `long:"health-check-type" short:"u" description:"Application health check type. Defaults to 'port'. 'http' requires a valid endpoint, for example, '/health'."`
    82  	Instances               flag.Instances                `long:"instances" short:"i" description:"Number of instances"`
    83  	PathToManifest          flag.PathWithExistenceCheck   `long:"manifest" short:"f" description:"Path to manifest"`
    84  	Memory                  flag.Megabytes                `long:"memory" short:"m" description:"Memory limit (e.g. 256M, 1024M, 1G)"`
    85  	NoManifest              bool                          `long:"no-manifest" description:""`
    86  	NoRoute                 bool                          `long:"no-route" description:"Do not map a route to this app"`
    87  	NoStart                 bool                          `long:"no-start" description:"Do not stage and start the app after pushing"`
    88  	AppPath                 flag.PathWithExistenceCheck   `long:"path" short:"p" description:"Path to app directory or to a zip file of the contents of the app directory"`
    89  	Stack                   string                        `long:"stack" short:"s" description:"Stack to use (a stack is a pre-built file system, including an operating system, that can run apps)"`
    90  	StartCommand            flag.Command                  `long:"start-command" short:"c" description:"Startup command, set to null to reset to default start command"`
    91  	Vars                    []template.VarKV              `long:"var" description:"Variable key value pair for variable substitution, (e.g., name=app1); can specify multiple times"`
    92  	PathsToVarsFiles        []flag.PathWithExistenceCheck `long:"vars-file" description:"Path to a variable substitution file for manifest; can specify multiple times"`
    93  	dockerPassword          interface{}                   `environmentName:"CF_DOCKER_PASSWORD" environmentDescription:"Password used for private docker repository"`
    94  	usage                   interface{}                   `usage:"CF_NAME push APP_NAME [-b BUILDPACK_NAME] [-c COMMAND]\n   [-f MANIFEST_PATH | --no-manifest] [--no-start] [-i NUM_INSTANCES]\n   [-k DISK] [-m MEMORY] [-p PATH] [-s STACK] [-t HEALTH_TIMEOUT]\n   [-u (process | port | http)]   [--no-route | --random-route]\n   [--var KEY=VALUE] [--vars-file VARS_FILE_PATH]...\n \n  CF_NAME push APP_NAME --docker-image [REGISTRY_HOST:PORT/]IMAGE[:TAG] [--docker-username USERNAME]\n   [-c COMMAND] [-f MANIFEST_PATH | --no-manifest] [--no-start]\n   [-i NUM_INSTANCES] [-k DISK] [-m MEMORY] [-p PATH] [-s STACK] [-t HEALTH_TIMEOUT] [-u (process | port | http)]\n   [--no-route | --random-route ] [--var KEY=VALUE] [--vars-file VARS_FILE_PATH]..."`
    95  	envCFStagingTimeout     interface{}                   `environmentName:"CF_STAGING_TIMEOUT" environmentDescription:"Max wait time for buildpack staging, in minutes" environmentDefault:"15"`
    96  	envCFStartupTimeout     interface{}                   `environmentName:"CF_STARTUP_TIMEOUT" environmentDescription:"Max wait time for app instance startup, in minutes" environmentDefault:"5"`
    97  
    98  	Config          command.Config
    99  	UI              command.UI
   100  	NOAAClient      v3action.NOAAClient
   101  	Actor           PushActor
   102  	VersionActor    V7ActorForPush
   103  	SharedActor     command.SharedActor
   104  	RouteActor      v7action.RouteActor
   105  	ProgressBar     ProgressBar
   106  	PWD             string
   107  	ManifestLocator ManifestLocator
   108  	ManifestParser  ManifestParser
   109  }
   110  
   111  func (cmd *PushCommand) Setup(config command.Config, ui command.UI) error {
   112  	cmd.Config = config
   113  	cmd.UI = ui
   114  	cmd.ProgressBar = progressbar.NewProgressBar()
   115  
   116  	sharedActor := sharedaction.NewActor(config)
   117  	cmd.SharedActor = sharedActor
   118  
   119  	ccClient, uaaClient, err := v6shared.NewV3BasedClients(config, ui, true, "")
   120  	if err != nil {
   121  		return err
   122  	}
   123  
   124  	v7actor := v7action.NewActor(ccClient, config, sharedActor, uaaClient)
   125  	cmd.VersionActor = v7actor
   126  	ccClientV2, uaaClientV2, err := v6shared.NewClients(config, ui, true)
   127  	if err != nil {
   128  		return err
   129  	}
   130  
   131  	v2Actor := v2action.NewActor(ccClientV2, uaaClientV2, config)
   132  	cmd.RouteActor = v2Actor
   133  	cmd.Actor = v7pushaction.NewActor(v2Actor, v7actor, sharedActor)
   134  
   135  	cmd.NOAAClient = v6shared.NewNOAAClient(ccClient.Info.Logging(), config, uaaClient, ui)
   136  
   137  	currentDir, err := os.Getwd()
   138  	cmd.PWD = currentDir
   139  
   140  	cmd.ManifestLocator = manifestparser.NewLocator()
   141  	cmd.ManifestParser = manifestparser.NewParser()
   142  
   143  	return err
   144  }
   145  
   146  func (cmd PushCommand) Execute(args []string) error {
   147  	cmd.UI.DisplayWarning(command.ExperimentalWarning)
   148  
   149  	err := cmd.SharedActor.CheckTarget(true, true)
   150  	if err != nil {
   151  		return err
   152  	}
   153  
   154  	if !cmd.NoManifest {
   155  		if err = cmd.ReadManifest(); err != nil {
   156  			return err
   157  		}
   158  	}
   159  
   160  	err = cmd.ValidateFlags()
   161  	if err != nil {
   162  		return err
   163  	}
   164  
   165  	flagOverrides, err := cmd.GetFlagOverrides()
   166  	if err != nil {
   167  		return err
   168  	}
   169  
   170  	err = cmd.ValidateAllowedFlagsForMultipleApps(cmd.ManifestParser.ContainsMultipleApps())
   171  	if err != nil {
   172  		return err
   173  	}
   174  
   175  	flagOverrides.DockerPassword, err = cmd.GetDockerPassword(flagOverrides.DockerUsername, cmd.ManifestParser.ContainsPrivateDockerImages())
   176  	if err != nil {
   177  		return err
   178  	}
   179  
   180  	pushPlans, err := cmd.Actor.CreatePushPlans(
   181  		cmd.OptionalArgs.AppName,
   182  		cmd.Config.TargetedSpace().GUID,
   183  		cmd.Config.TargetedOrganization().GUID,
   184  		cmd.ManifestParser,
   185  		flagOverrides,
   186  	)
   187  	if err != nil {
   188  		return err
   189  	}
   190  
   191  	pushPlansStream, eventStream, warningsStream, errorStream := cmd.Actor.PrepareSpace(pushPlans, cmd.ManifestParser)
   192  	appNames, err := cmd.processStreamsFromPrepareSpace(pushPlansStream, eventStream, warningsStream, errorStream)
   193  
   194  	if err != nil {
   195  		return err
   196  	}
   197  
   198  	if len(appNames) == 0 {
   199  		return translatableerror.AppNameOrManifestRequiredError{}
   200  	}
   201  
   202  	user, err := cmd.Config.CurrentUser()
   203  	if err != nil {
   204  		return err
   205  	}
   206  
   207  	cmd.announcePushing(appNames, user)
   208  
   209  	cmd.UI.DisplayText("Getting app info...")
   210  	log.Info("generating the app plan")
   211  
   212  	pushPlans, warnings, err := cmd.Actor.UpdateApplicationSettings(pushPlans)
   213  	cmd.UI.DisplayWarnings(warnings)
   214  	if err != nil {
   215  		return err
   216  	}
   217  	log.WithField("number of plans", len(pushPlans)).Debug("completed generating plan")
   218  
   219  	for _, plan := range pushPlans {
   220  		log.WithField("app_name", plan.Application.Name).Info("actualizing")
   221  		planStream, eventStream, warningsStream, errorStream := cmd.Actor.Actualize(plan, cmd.ProgressBar)
   222  		err := cmd.processApplyStreams(plan.Application.Name, planStream, eventStream, warningsStream, errorStream)
   223  
   224  		if cmd.shouldDisplaySummary(err) {
   225  			summaryErr := cmd.displayAppSummary(plan)
   226  			if summaryErr != nil {
   227  				return summaryErr
   228  			}
   229  		}
   230  		if err != nil {
   231  			return cmd.mapErr(plan.Application.Name, err)
   232  		}
   233  	}
   234  
   235  	return nil
   236  }
   237  
   238  func (cmd PushCommand) shouldDisplaySummary(err error) bool {
   239  	if err == nil {
   240  		return true
   241  	}
   242  	_, ok := err.(actionerror.AllInstancesCrashedError)
   243  	return ok
   244  }
   245  
   246  func (cmd PushCommand) mapErr(appName string, err error) error {
   247  	switch err.(type) {
   248  	case actionerror.AllInstancesCrashedError:
   249  		return translatableerror.ApplicationUnableToStartError{
   250  			AppName:    appName,
   251  			BinaryName: cmd.Config.BinaryName(),
   252  		}
   253  	case actionerror.StartupTimeoutError:
   254  		return translatableerror.StartupTimeoutError{
   255  			AppName:    appName,
   256  			BinaryName: cmd.Config.BinaryName(),
   257  		}
   258  	}
   259  	return err
   260  }
   261  
   262  func (cmd PushCommand) announcePushing(appNames []string, user configv3.User) {
   263  	tokens := map[string]interface{}{
   264  		"AppName":   strings.Join(appNames, ", "),
   265  		"OrgName":   cmd.Config.TargetedOrganization().Name,
   266  		"SpaceName": cmd.Config.TargetedSpace().Name,
   267  		"Username":  user.Name,
   268  	}
   269  	singular := "Pushing app {{.AppName}} to org {{.OrgName}} / space {{.SpaceName}} as {{.Username}}..."
   270  	plural := "Pushing apps {{.AppName}} to org {{.OrgName}} / space {{.SpaceName}} as {{.Username}}..."
   271  
   272  	if len(appNames) == 1 {
   273  		cmd.UI.DisplayTextWithFlavor(singular, tokens)
   274  	} else {
   275  		cmd.UI.DisplayTextWithFlavor(plural, tokens)
   276  	}
   277  }
   278  
   279  func (cmd PushCommand) displayAppSummary(plan v7pushaction.PushPlan) error {
   280  	log.Info("getting application summary info")
   281  	summary, warnings, err := cmd.VersionActor.GetApplicationSummaryByNameAndSpace(
   282  		plan.Application.Name,
   283  		cmd.Config.TargetedSpace().GUID,
   284  		true,
   285  		cmd.RouteActor,
   286  	)
   287  	cmd.UI.DisplayWarnings(warnings)
   288  	if err != nil {
   289  		return err
   290  	}
   291  	cmd.UI.DisplayNewline()
   292  	appSummaryDisplayer := shared.NewAppSummaryDisplayer(cmd.UI)
   293  	appSummaryDisplayer.AppDisplay(summary, true)
   294  	return nil
   295  }
   296  
   297  func (cmd PushCommand) processStreamsFromPrepareSpace(
   298  	pushPlansStream <-chan []v7pushaction.PushPlan,
   299  	eventStream <-chan v7pushaction.Event,
   300  	warningsStream <-chan v7pushaction.Warnings,
   301  	errorStream <-chan error,
   302  ) ([]string, error) {
   303  	var namesClosed, eventClosed, warningsClosed, errClosed bool
   304  	var appNames []string
   305  	var err error
   306  
   307  	for {
   308  		select {
   309  		case plans, ok := <-pushPlansStream:
   310  			if !ok {
   311  				if !namesClosed {
   312  					log.Debug("processing config stream closed")
   313  				}
   314  				namesClosed = true
   315  				break
   316  			}
   317  			for _, plan := range plans {
   318  				appNames = append(appNames, plan.Application.Name)
   319  			}
   320  		case event, ok := <-eventStream:
   321  			if !ok {
   322  				if !eventClosed {
   323  					log.Debug("processing event stream closed")
   324  				}
   325  				eventClosed = true
   326  				break
   327  			}
   328  			_, err := cmd.processEvent(event, cmd.OptionalArgs.AppName)
   329  			if err != nil {
   330  				return nil, err
   331  			}
   332  		case warnings, ok := <-warningsStream:
   333  			if !ok {
   334  				if !warningsClosed {
   335  					log.Debug("processing warnings stream closed")
   336  				}
   337  				warningsClosed = true
   338  				break
   339  			}
   340  			cmd.UI.DisplayWarnings(warnings)
   341  		case receivedError, ok := <-errorStream:
   342  			if !ok {
   343  				if !errClosed {
   344  					log.Debug("processing error stream closed")
   345  				}
   346  				errClosed = true
   347  				break
   348  			}
   349  			return nil, receivedError
   350  		}
   351  
   352  		if namesClosed && eventClosed && warningsClosed && errClosed {
   353  			break
   354  		}
   355  	}
   356  
   357  	return appNames, err
   358  }
   359  
   360  func (cmd PushCommand) processApplyStreams(
   361  	appName string,
   362  	planStream <-chan v7pushaction.PushPlan,
   363  	eventStream <-chan v7pushaction.Event,
   364  	warningsStream <-chan v7pushaction.Warnings,
   365  	errorStream <-chan error,
   366  ) error {
   367  	var planClosed, eventClosed, warningsClosed, errClosed, complete bool
   368  
   369  	for {
   370  		select {
   371  		case _, ok := <-planStream:
   372  			if !ok {
   373  				if !planClosed {
   374  					log.Debug("processing config stream closed")
   375  				}
   376  				planClosed = true
   377  				break
   378  			}
   379  		case event, ok := <-eventStream:
   380  			if !ok {
   381  				if !eventClosed {
   382  					log.Debug("processing event stream closed")
   383  				}
   384  				eventClosed = true
   385  				break
   386  			}
   387  			var err error
   388  			complete, err = cmd.processEvent(event, appName)
   389  			if err != nil {
   390  				return err
   391  			}
   392  		case warnings, ok := <-warningsStream:
   393  			if !ok {
   394  				if !warningsClosed {
   395  					log.Debug("processing warnings stream closed")
   396  				}
   397  				warningsClosed = true
   398  				break
   399  			}
   400  			cmd.UI.DisplayWarnings(warnings)
   401  		case err, ok := <-errorStream:
   402  			if !ok {
   403  				if !errClosed {
   404  					log.Debug("processing error stream closed")
   405  				}
   406  				errClosed = true
   407  				break
   408  			}
   409  			return err
   410  		}
   411  
   412  		if planClosed && eventClosed && warningsClosed && complete {
   413  			break
   414  		}
   415  	}
   416  
   417  	return nil
   418  }
   419  
   420  func (cmd PushCommand) processEvent(event v7pushaction.Event, appName string) (bool, error) {
   421  	switch event {
   422  	case v7pushaction.SkippingApplicationCreation:
   423  		cmd.UI.DisplayTextWithFlavor("Updating app {{.AppName}}...", map[string]interface{}{
   424  			"AppName": appName,
   425  		})
   426  	case v7pushaction.CreatingApplication:
   427  		cmd.UI.DisplayTextWithFlavor("Creating app {{.AppName}}...", map[string]interface{}{
   428  			"AppName": appName,
   429  		})
   430  	case v7pushaction.CreatingAndMappingRoutes:
   431  		cmd.UI.DisplayText("Mapping routes...")
   432  	case v7pushaction.CreatingArchive:
   433  		cmd.UI.DisplayText("Packaging files to upload...")
   434  	case v7pushaction.UploadingApplicationWithArchive:
   435  		cmd.UI.DisplayText("Uploading files...")
   436  		log.Debug("starting progress bar")
   437  		cmd.ProgressBar.Ready()
   438  	case v7pushaction.UploadingApplication:
   439  		cmd.UI.DisplayText("All files found in remote cache; nothing to upload.")
   440  		cmd.UI.DisplayText("Waiting for API to complete processing files...")
   441  	case v7pushaction.RetryUpload:
   442  		cmd.UI.DisplayText("Retrying upload due to an error...")
   443  	case v7pushaction.UploadWithArchiveComplete:
   444  		cmd.ProgressBar.Complete()
   445  		cmd.UI.DisplayNewline()
   446  		cmd.UI.DisplayText("Waiting for API to complete processing files...")
   447  	case v7pushaction.UploadingDroplet:
   448  		cmd.UI.DisplayText("Uploading droplet bits...")
   449  		cmd.ProgressBar.Ready()
   450  	case v7pushaction.UploadDropletComplete:
   451  		cmd.ProgressBar.Complete()
   452  		cmd.UI.DisplayNewline()
   453  		cmd.UI.DisplayText("Waiting for API to complete processing files...")
   454  	case v7pushaction.StoppingApplication:
   455  		cmd.UI.DisplayText("Stopping Application...")
   456  	case v7pushaction.StoppingApplicationComplete:
   457  		cmd.UI.DisplayText("Application Stopped")
   458  	case v7pushaction.ApplyManifest:
   459  		cmd.UI.DisplayText("Applying manifest...")
   460  	case v7pushaction.ApplyManifestComplete:
   461  		cmd.UI.DisplayText("Manifest applied")
   462  	case v7pushaction.StartingStaging:
   463  		cmd.UI.DisplayNewline()
   464  		cmd.UI.DisplayText("Staging app and tracing logs...")
   465  		logStream, errStream, warnings, err := cmd.VersionActor.GetStreamingLogsForApplicationByNameAndSpace(appName, cmd.Config.TargetedSpace().GUID, cmd.NOAAClient)
   466  		cmd.UI.DisplayWarnings(warnings)
   467  		if err != nil {
   468  			return false, err
   469  		}
   470  		go cmd.getLogs(logStream, errStream)
   471  	case v7pushaction.StagingComplete:
   472  		cmd.NOAAClient.Close()
   473  	case v7pushaction.RestartingApplication:
   474  		cmd.UI.DisplayNewline()
   475  		cmd.UI.DisplayTextWithFlavor(
   476  			"Waiting for app {{.AppName}} to start...",
   477  			map[string]interface{}{
   478  				"AppName": appName,
   479  			},
   480  		)
   481  	case v7pushaction.Complete:
   482  		return true, nil
   483  	default:
   484  		log.WithField("event", event).Debug("ignoring event")
   485  	}
   486  	return false, nil
   487  }
   488  
   489  func (cmd PushCommand) getLogs(logStream <-chan *v7action.LogMessage, errStream <-chan error) {
   490  	for {
   491  		select {
   492  		case logMessage, open := <-logStream:
   493  			if !open {
   494  				return
   495  			}
   496  			if logMessage.Staging() {
   497  				cmd.UI.DisplayLogMessage(logMessage, false)
   498  			}
   499  		case err, open := <-errStream:
   500  			if !open {
   501  				return
   502  			}
   503  			_, ok := err.(actionerror.NOAATimeoutError)
   504  			if ok {
   505  				cmd.UI.DisplayWarning("timeout connecting to log server, no log will be shown")
   506  			}
   507  			cmd.UI.DisplayWarning(err.Error())
   508  		}
   509  	}
   510  }
   511  
   512  func (cmd PushCommand) ReadManifest() error {
   513  	log.Info("reading manifest if exists")
   514  	pathsToVarsFiles := []string{}
   515  	for _, varfilepath := range cmd.PathsToVarsFiles {
   516  		pathsToVarsFiles = append(pathsToVarsFiles, string(varfilepath))
   517  	}
   518  
   519  	readPath := cmd.PWD
   520  	if len(cmd.PathToManifest) != 0 {
   521  		log.WithField("manifestPath", cmd.PathToManifest).Debug("reading '-f' provided manifest")
   522  		readPath = string(cmd.PathToManifest)
   523  	}
   524  
   525  	pathToManifest, exists, err := cmd.ManifestLocator.Path(readPath)
   526  	if err != nil {
   527  		return err
   528  	}
   529  
   530  	if exists {
   531  		log.WithField("manifestPath", pathToManifest).Debug("path to manifest")
   532  		err = cmd.ManifestParser.InterpolateAndParse(pathToManifest, pathsToVarsFiles, cmd.Vars)
   533  		if err != nil {
   534  			log.Errorln("reading manifest:", err)
   535  			return err
   536  		}
   537  
   538  		cmd.UI.DisplayText("Using manifest file {{.Path}}", map[string]interface{}{"Path": pathToManifest})
   539  	}
   540  
   541  	return nil
   542  }
   543  
   544  func (cmd PushCommand) GetFlagOverrides() (v7pushaction.FlagOverrides, error) {
   545  	return v7pushaction.FlagOverrides{
   546  		Buildpacks:          cmd.Buildpacks,
   547  		Stack:               cmd.Stack,
   548  		Disk:                cmd.Disk.NullUint64,
   549  		DropletPath:         string(cmd.DropletPath),
   550  		DockerImage:         cmd.DockerImage.Path,
   551  		DockerUsername:      cmd.DockerUsername,
   552  		HealthCheckEndpoint: cmd.HealthCheckHTTPEndpoint,
   553  		HealthCheckType:     cmd.HealthCheckType.Type,
   554  		HealthCheckTimeout:  cmd.HealthCheckTimeout.Value, Instances: cmd.Instances.NullInt,
   555  		Memory:            cmd.Memory.NullUint64,
   556  		NoStart:           cmd.NoStart,
   557  		ProvidedAppPath:   string(cmd.AppPath),
   558  		SkipRouteCreation: cmd.NoRoute,
   559  		StartCommand:      cmd.StartCommand.FilteredString,
   560  	}, nil
   561  }
   562  
   563  func (cmd PushCommand) ValidateAllowedFlagsForMultipleApps(containsMultipleApps bool) error {
   564  	if cmd.OptionalArgs.AppName != "" {
   565  		return nil
   566  	}
   567  
   568  	allowedFlagsMultipleApps := !(len(cmd.Buildpacks) > 0 ||
   569  		cmd.Disk.IsSet ||
   570  		cmd.DockerImage.Path != "" ||
   571  		cmd.DockerUsername != "" ||
   572  		cmd.DropletPath != "" ||
   573  		cmd.HealthCheckType.Type != "" ||
   574  		cmd.HealthCheckHTTPEndpoint != "" ||
   575  		cmd.HealthCheckTimeout.Value > 0 ||
   576  		cmd.Instances.IsSet ||
   577  		cmd.Stack != "" ||
   578  		cmd.Memory.IsSet ||
   579  		cmd.AppPath != "" ||
   580  		cmd.NoRoute ||
   581  		cmd.StartCommand.IsSet)
   582  
   583  	if containsMultipleApps && !allowedFlagsMultipleApps {
   584  		return translatableerror.CommandLineArgsWithMultipleAppsError{}
   585  	}
   586  
   587  	return nil
   588  }
   589  
   590  func (cmd PushCommand) ValidateFlags() error {
   591  	switch {
   592  	case cmd.DockerUsername != "" && cmd.DockerImage.Path == "":
   593  		return translatableerror.RequiredFlagsError{
   594  			Arg1: "--docker-image, -o",
   595  			Arg2: "--docker-username",
   596  		}
   597  
   598  	case cmd.DockerImage.Path != "" && cmd.Buildpacks != nil:
   599  		return translatableerror.ArgumentCombinationError{
   600  			Args: []string{
   601  				"--buildpack, -b",
   602  				"--docker-image, -o",
   603  			},
   604  		}
   605  
   606  	case cmd.DockerImage.Path != "" && cmd.AppPath != "":
   607  		return translatableerror.ArgumentCombinationError{
   608  			Args: []string{
   609  				"--docker-image, -o",
   610  				"--path, -p",
   611  			},
   612  		}
   613  	case cmd.DockerImage.Path != "" && cmd.Stack != "":
   614  		return translatableerror.ArgumentCombinationError{
   615  			Args: []string{
   616  				"--stack, -s",
   617  				"--docker-image, -o",
   618  			},
   619  		}
   620  	case cmd.NoManifest && cmd.PathToManifest != "":
   621  		return translatableerror.ArgumentCombinationError{
   622  			Args: []string{
   623  				"--no-manifest",
   624  				"--manifest, -f",
   625  			},
   626  		}
   627  	case cmd.NoManifest && len(cmd.PathsToVarsFiles) > 0:
   628  		return translatableerror.ArgumentCombinationError{
   629  			Args: []string{
   630  				"--no-manifest",
   631  				"--vars-file",
   632  			},
   633  		}
   634  	case cmd.NoManifest && len(cmd.Vars) > 0:
   635  		return translatableerror.ArgumentCombinationError{
   636  			Args: []string{
   637  				"--no-manifest",
   638  				"--vars",
   639  			},
   640  		}
   641  	case cmd.HealthCheckType.Type == constant.HTTP && cmd.HealthCheckHTTPEndpoint == "":
   642  		return translatableerror.RequiredFlagsError{
   643  			Arg1: "--endpoint",
   644  			Arg2: "--health-check-type=http, -u=http",
   645  		}
   646  	case 0 < len(cmd.HealthCheckHTTPEndpoint) && cmd.HealthCheckType.Type != constant.HTTP:
   647  		return translatableerror.RequiredFlagsError{
   648  			Arg1: "--health-check-type=http, -u=http",
   649  			Arg2: "--endpoint",
   650  		}
   651  
   652  	case cmd.DropletPath != "" && (cmd.DockerImage.Path != "" || cmd.DockerUsername != "" || cmd.AppPath != ""):
   653  		return translatableerror.ArgumentCombinationError{
   654  			Args: []string{
   655  				"--droplet",
   656  				"--docker-image, -o",
   657  				"--docker-username",
   658  				"-p",
   659  			},
   660  		}
   661  	}
   662  
   663  	return nil
   664  }