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