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