github.com/franc20/ayesa_sap@v7.0.0-beta.28.0.20200124003224-302d4d52fa6c+incompatible/command/v6/push_command.go (about) 1 package v6 2 3 import ( 4 "os" 5 "path/filepath" 6 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/v2v3action" 11 "code.cloudfoundry.org/cli/actor/v3action" 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 sharedV3 "code.cloudfoundry.org/cli/command/v6/shared" 17 "code.cloudfoundry.org/cli/util/configv3" 18 "code.cloudfoundry.org/cli/util/manifest" 19 "code.cloudfoundry.org/cli/util/progressbar" 20 "github.com/cloudfoundry/bosh-cli/director/template" 21 log "github.com/sirupsen/logrus" 22 ) 23 24 //go:generate counterfeiter . ProgressBar 25 26 type ProgressBar interface { 27 pushaction.ProgressBar 28 Complete() 29 Ready() 30 } 31 32 //go:generate counterfeiter . V2PushActor 33 34 type V2PushActor interface { 35 Apply(config pushaction.ApplicationConfig, progressBar pushaction.ProgressBar) (<-chan pushaction.ApplicationConfig, <-chan pushaction.Event, <-chan pushaction.Warnings, <-chan error) 36 CloudControllerV2APIVersion() string 37 CloudControllerV3APIVersion() string 38 ConvertToApplicationConfigs(orgGUID string, spaceGUID string, noStart bool, apps []manifest.Application) ([]pushaction.ApplicationConfig, pushaction.Warnings, error) 39 MergeAndValidateSettingsAndManifests(cmdSettings pushaction.CommandLineSettings, apps []manifest.Application) ([]manifest.Application, error) 40 ReadManifest(pathToManifest string, pathsToVarsFiles []string, vars []template.VarKV) ([]manifest.Application, pushaction.Warnings, error) 41 } 42 43 type PushCommand struct { 44 OptionalArgs flag.OptionalAppName `positional-args:"yes"` 45 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'"` 46 Command flag.Command `short:"c" description:"Startup command, set to null to reset to default start command"` 47 Domain string `short:"d" description:"Specify a custom domain (e.g. private-domain.example.com, apps.internal.com) to use instead of the default domain"` 48 DockerImage flag.DockerImage `long:"docker-image" short:"o" description:"Docker-image to be used (e.g. user/docker-image-name)"` 49 DockerUsername string `long:"docker-username" description:"Repository username; used with password from environment variable CF_DOCKER_PASSWORD"` 50 DropletPath flag.PathWithExistenceCheck `long:"droplet" description:"Path to a tgz file with a pre-staged app"` 51 PathToManifest flag.PathWithExistenceCheck `short:"f" description:"Path to manifest"` 52 HealthCheckType flag.HealthCheckTypeWithDeprecatedValue `long:"health-check-type" short:"u" description:"Application health check type (Default: 'port', 'none' accepted for 'process', 'http' implies endpoint '/')"` 53 Hostname string `long:"hostname" short:"n" description:"Hostname (e.g. my-subdomain)"` 54 Instances flag.Instances `short:"i" description:"Number of instances"` 55 DiskQuota flag.Megabytes `short:"k" description:"Disk limit (e.g. 256M, 1024M, 1G)"` 56 Memory flag.Megabytes `short:"m" description:"Memory limit (e.g. 256M, 1024M, 1G)"` 57 NoHostname bool `long:"no-hostname" description:"Map the root domain to this app"` 58 NoManifest bool `long:"no-manifest" description:"Ignore manifest file"` 59 NoRoute bool `long:"no-route" description:"Do not map a route to this app and remove routes from previous pushes of this app"` 60 NoStart bool `long:"no-start" description:"Do not start an app after pushing"` 61 AppPath flag.PathWithExistenceCheck `short:"p" description:"Path to app directory or to a zip file of the contents of the app directory"` 62 RandomRoute bool `long:"random-route" description:"Create a random route for this app"` 63 RoutePath flag.V6RoutePath `long:"route-path" description:"Path for the route"` 64 StackName string `short:"s" description:"Stack to use (a stack is a pre-built file system, including an operating system, that can run apps)"` 65 VarsFilePaths []flag.PathWithExistenceCheck `long:"vars-file" description:"Path to a variable substitution file for manifest; can specify multiple times"` 66 Vars []template.VarKV `long:"var" description:"Variable key value pair for variable substitution, (e.g., name=app1); can specify multiple times"` 67 HealthCheckTimeout uint64 `short:"t" description:"Time (in seconds) allowed to elapse between starting up an app and the first healthy response from the app"` 68 envCFStagingTimeout interface{} `environmentName:"CF_STAGING_TIMEOUT" environmentDescription:"Max wait time for buildpack staging, in minutes" environmentDefault:"15"` 69 envCFStartupTimeout interface{} `environmentName:"CF_STARTUP_TIMEOUT" environmentDescription:"Max wait time for app instance startup, in minutes" environmentDefault:"5"` 70 dockerPassword interface{} `environmentName:"CF_DOCKER_PASSWORD" environmentDescription:"Password used for private docker repository"` 71 72 usage interface{} `usage:"CF_NAME push APP_NAME [-b BUILDPACK_NAME] [-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 | --hostname HOST | --no-hostname] [-d DOMAIN] [--route-path ROUTE_PATH] [--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] [-t HEALTH_TIMEOUT] [-u (process | port | http)]\n [--no-route | --random-route | --hostname HOST | --no-hostname] [-d DOMAIN] [--route-path ROUTE_PATH] [--var KEY=VALUE]... [--vars-file VARS_FILE_PATH]...\n\n CF_NAME push APP_NAME --droplet DROPLET_PATH\n [-c COMMAND] [-f MANIFEST_PATH | --no-manifest] [--no-start]\n [-i NUM_INSTANCES] [-k DISK] [-m MEMORY] [-t HEALTH_TIMEOUT] [-u (process | port | http)]\n [--no-route | --random-route | --hostname HOST | --no-hostname] [-d DOMAIN] [--route-path ROUTE_PATH] [--var KEY=VALUE]... [--vars-file VARS_FILE_PATH]...\n\n CF_NAME push -f MANIFEST_WITH_MULTIPLE_APPS_PATH [APP_NAME] [--no-start]"` 73 relatedCommands interface{} `related_commands:"apps, create-app-manifest, logs, ssh, start"` 74 75 UI command.UI 76 Config command.Config 77 SharedActor command.SharedActor 78 Actor V2PushActor 79 ApplicationSummaryActor shared.ApplicationSummaryActor 80 ProgressBar ProgressBar 81 82 RestartActor RestartActor 83 LogCacheClient sharedaction.LogCacheClient 84 } 85 86 func (cmd *PushCommand) Setup(config command.Config, ui command.UI) error { 87 cmd.UI = ui 88 cmd.Config = config 89 sharedActor := sharedaction.NewActor(config) 90 cmd.SharedActor = sharedActor 91 92 ccClient, uaaClient, err := shared.GetNewClientsAndConnectToCF(config, ui) 93 if err != nil { 94 return err 95 } 96 97 ccClientV3, _, err := sharedV3.NewV3BasedClients(config, ui, true) 98 if err != nil { 99 return err 100 } 101 102 v2Actor := v2action.NewActor(ccClient, uaaClient, config) 103 v3Actor := v3action.NewActor(ccClientV3, config, sharedActor, nil) 104 105 cmd.RestartActor = v2Actor 106 cmd.Actor = pushaction.NewActor(v2Actor, v3Actor, sharedActor) 107 108 cmd.ApplicationSummaryActor = v2v3action.NewActor(v2Actor, v3Actor) 109 110 logcacheURL := v3Actor.LogCacheURL() 111 cmd.LogCacheClient = command.NewLogCacheClient(logcacheURL, config, ui) 112 113 cmd.ProgressBar = progressbar.NewProgressBar() 114 return nil 115 } 116 117 func (cmd PushCommand) Execute(args []string) error { 118 err := cmd.SharedActor.CheckTarget(true, true) 119 if err != nil { 120 return err 121 } 122 123 user, err := cmd.Config.CurrentUser() 124 if err != nil { 125 return err 126 } 127 128 log.Info("collating flags") 129 cliSettings, err := cmd.GetCommandLineSettings() 130 if err != nil { 131 log.Errorln("reading flags:", err) 132 return err 133 } 134 135 log.Info("checking manifest") 136 rawApps, err := cmd.findAndReadManifestWithFlavorText(cliSettings) 137 if err != nil { 138 log.Errorln("reading manifest:", err) 139 return err 140 } 141 142 log.Info("merging manifest and command flags") 143 manifestApplications, err := cmd.Actor.MergeAndValidateSettingsAndManifests(cliSettings, rawApps) 144 if err != nil { 145 log.Errorln("merging manifest:", err) 146 return err 147 } 148 149 cmd.UI.DisplayText("Getting app info...") 150 151 log.Info("converting manifests to ApplicationConfigs") 152 appConfigs, warnings, err := cmd.Actor.ConvertToApplicationConfigs( 153 cmd.Config.TargetedOrganization().GUID, 154 cmd.Config.TargetedSpace().GUID, 155 cmd.NoStart, 156 manifestApplications, 157 ) 158 cmd.UI.DisplayWarnings(warnings) 159 if err != nil { 160 log.Errorln("converting manifest:", err) 161 return err 162 } 163 164 for _, appConfig := range appConfigs { 165 if appConfig.CreatingApplication() { 166 cmd.UI.DisplayText("Creating app with these attributes...") 167 } else { 168 cmd.UI.DisplayText("Updating app with these attributes...") 169 } 170 log.Infoln("starting create/update:", appConfig.DesiredApplication.Name) 171 changes := shared.GetApplicationChanges(appConfig) 172 err := cmd.UI.DisplayChangesForPush(changes) 173 if err != nil { 174 log.Errorln("display changes:", err) 175 return err 176 } 177 cmd.UI.DisplayNewline() 178 } 179 180 for appNumber, appConfig := range appConfigs { 181 if appConfig.CreatingApplication() { 182 cmd.UI.DisplayTextWithFlavor("Creating app {{.AppName}}...", map[string]interface{}{ 183 "AppName": appConfig.DesiredApplication.Name, 184 }) 185 } else { 186 cmd.UI.DisplayTextWithFlavor("Updating app {{.AppName}}...", map[string]interface{}{ 187 "AppName": appConfig.DesiredApplication.Name, 188 }) 189 } 190 191 configStream, eventStream, warningsStream, errorStream := cmd.Actor.Apply(appConfig, cmd.ProgressBar) 192 updatedConfig, err := cmd.processApplyStreams(user, appConfig, configStream, eventStream, warningsStream, errorStream) 193 if err != nil { 194 log.Errorln("process apply stream:", err) 195 return err 196 } 197 198 if !cmd.NoStart { 199 appState, apiWarnings, errs := cmd.RestartActor.RestartApplication(updatedConfig.CurrentApplication.Application) 200 messages, logErrs, stopStreaming := cmd.RestartActor.GetStreamingLogs(updatedConfig.CurrentApplication.Application.GUID, cmd.LogCacheClient) 201 err = shared.PollStart(cmd.UI, cmd.Config, messages, logErrs, appState, apiWarnings, errs, stopStreaming) 202 if err != nil { 203 return err 204 } 205 } 206 207 cmd.UI.DisplayNewline() 208 209 log.WithField("v3_api_version", cmd.ApplicationSummaryActor.CloudControllerV3APIVersion()).Debug("using v3 for app display") 210 appSummary, warnings, err := cmd.ApplicationSummaryActor.GetApplicationSummaryByNameAndSpace(appConfig.DesiredApplication.Name, cmd.Config.TargetedSpace().GUID, true) 211 cmd.UI.DisplayWarnings(warnings) 212 if err != nil { 213 return err 214 } 215 shared.NewAppSummaryDisplayer2(cmd.UI).AppDisplay(appSummary, true) 216 217 if appNumber+1 <= len(appConfigs) { 218 cmd.UI.DisplayNewline() 219 } 220 } 221 222 return nil 223 } 224 225 // GetCommandLineSettings generates a push CommandLineSettings object from the 226 // command's command line flags. It also validates those settings, preventing 227 // contradictory flags. 228 func (cmd PushCommand) GetCommandLineSettings() (pushaction.CommandLineSettings, error) { 229 err := cmd.validateArgs() 230 if err != nil { 231 return pushaction.CommandLineSettings{}, err 232 } 233 234 pwd, err := os.Getwd() 235 if err != nil { 236 return pushaction.CommandLineSettings{}, err 237 } 238 239 cmd.warnOnAnyDeprecatedRouteFlags() 240 241 dockerPassword := cmd.Config.DockerPassword() 242 if dockerPassword != "" { 243 cmd.UI.DisplayText("Using docker repository password from environment variable CF_DOCKER_PASSWORD.") 244 } else if cmd.DockerUsername != "" { 245 cmd.UI.DisplayText("Environment variable CF_DOCKER_PASSWORD not set.") 246 dockerPassword, err = cmd.UI.DisplayPasswordPrompt("Docker password") 247 if err != nil { 248 return pushaction.CommandLineSettings{}, err 249 } 250 } 251 252 config := pushaction.CommandLineSettings{ 253 Buildpacks: cmd.Buildpacks, // -b 254 Command: cmd.Command.FilteredString, // -c 255 CurrentDirectory: pwd, 256 DefaultRouteDomain: cmd.Domain, // -d 257 DefaultRouteHostname: cmd.Hostname, // -n/--hostname 258 DiskQuota: cmd.DiskQuota.Value, // -k 259 DockerImage: cmd.DockerImage.Path, // -o 260 DockerPassword: dockerPassword, // ENV - CF_DOCKER_PASSWORD 261 DockerUsername: cmd.DockerUsername, // --docker-username 262 DropletPath: string(cmd.DropletPath), // --droplet 263 HealthCheckTimeout: cmd.HealthCheckTimeout, // -t 264 HealthCheckType: cmd.HealthCheckType.Type, // -u/--health-check-type 265 Instances: cmd.Instances.NullInt, // -i 266 Memory: cmd.Memory.Value, // -m 267 Name: cmd.OptionalArgs.AppName, // arg 268 NoHostname: cmd.NoHostname, // --no-hostname 269 NoRoute: cmd.NoRoute, // --no-route 270 ProvidedAppPath: string(cmd.AppPath), // -p 271 RandomRoute: cmd.RandomRoute, // --random-route 272 RoutePath: cmd.RoutePath.Path, // --route-path 273 StackName: cmd.StackName, // -s 274 } 275 276 log.Debugln("Command Line Settings:", config) 277 return config, nil 278 } 279 280 func (cmd PushCommand) warnOnAnyDeprecatedRouteFlags() { 281 var deprecatedFlag string 282 if cmd.NoHostname { 283 deprecatedFlag = "--no-hostname" 284 } else if cmd.Domain != "" { 285 deprecatedFlag = "-d" 286 } else if cmd.Hostname != "" { 287 deprecatedFlag = "--hostname" 288 } else if cmd.RoutePath.Path != "" { 289 deprecatedFlag = "--route-path" 290 } 291 if len(deprecatedFlag) > 0 { 292 cmd.UI.DisplayWarning("Deprecation warning: Use of the '{{.Flag}}' command-line flag option is deprecated in favor of the 'routes' property in the manifest. Please see https://docs.cloudfoundry.org/devguide/deploy-apps/manifest-attributes.html#routes for usage information. The '{{.Flag}}' command-line flag option will be removed in the future.", 293 map[string]interface{}{ 294 "Flag": deprecatedFlag, 295 }) 296 } 297 } 298 299 func (cmd PushCommand) findAndReadManifestWithFlavorText(settings pushaction.CommandLineSettings) ([]manifest.Application, error) { 300 var ( 301 pathToManifest string 302 ) 303 switch { 304 case cmd.NoManifest: 305 log.Debug("skipping reading of manifest") 306 case cmd.PathToManifest != "": 307 log.WithField("file", cmd.PathToManifest).Debug("using specified manifest file") 308 pathToManifest = string(cmd.PathToManifest) 309 310 fileInfo, err := os.Stat(pathToManifest) 311 if err != nil { 312 return nil, err 313 } 314 315 if fileInfo.IsDir() { 316 manifestPaths := []string{ 317 filepath.Join(pathToManifest, "manifest.yml"), 318 filepath.Join(pathToManifest, "manifest.yaml"), 319 } 320 for _, manifestPath := range manifestPaths { 321 if _, err = os.Stat(manifestPath); err == nil { 322 pathToManifest = manifestPath 323 break 324 } 325 } 326 } 327 328 if err != nil { 329 return nil, translatableerror.ManifestFileNotFoundInDirectoryError{ 330 PathToManifest: pathToManifest, 331 } 332 } 333 default: 334 log.Debug("searching for manifest file") 335 pathToManifest = filepath.Join(settings.CurrentDirectory, "manifest.yml") 336 if _, err := os.Stat(pathToManifest); os.IsNotExist(err) { 337 log.WithField("pathToManifest", pathToManifest).Debug("could not find") 338 339 // While this is unlikely to be used, it is kept for backwards 340 // compatibility. 341 pathToManifest = filepath.Join(settings.CurrentDirectory, "manifest.yaml") 342 if _, err := os.Stat(pathToManifest); os.IsNotExist(err) { 343 log.WithField("pathToManifest", pathToManifest).Debug("could not find") 344 pathToManifest = "" 345 } 346 } 347 } 348 349 user, err := cmd.Config.CurrentUser() 350 if err != nil { 351 return nil, err 352 } 353 354 if pathToManifest == "" { 355 cmd.UI.DisplayTextWithFlavor("Pushing app {{.AppName}} to org {{.OrgName}} / space {{.SpaceName}} as {{.Username}}...", map[string]interface{}{ 356 "AppName": settings.Name, 357 "OrgName": cmd.Config.TargetedOrganization().Name, 358 "SpaceName": cmd.Config.TargetedSpace().Name, 359 "Username": user.Name, 360 }) 361 return nil, nil 362 } 363 364 var pathsToVarsFiles []string 365 for _, path := range cmd.VarsFilePaths { 366 pathsToVarsFiles = append(pathsToVarsFiles, string(path)) 367 } 368 369 cmd.UI.DisplayTextWithFlavor("Pushing from manifest to org {{.OrgName}} / space {{.SpaceName}} as {{.Username}}...", map[string]interface{}{ 370 "OrgName": cmd.Config.TargetedOrganization().Name, 371 "SpaceName": cmd.Config.TargetedSpace().Name, 372 "Username": user.Name, 373 }) 374 log.WithField("pathToManifest", pathToManifest).Info("reading manifest") 375 cmd.UI.DisplayText("Using manifest file {{.Path}}", map[string]interface{}{ 376 "Path": pathToManifest, 377 }) 378 379 apps, warnings, err := cmd.Actor.ReadManifest(pathToManifest, pathsToVarsFiles, cmd.Vars) 380 cmd.UI.DisplayWarnings(warnings) 381 382 return apps, err 383 } 384 385 func (cmd PushCommand) processApplyStreams( 386 user configv3.User, 387 appConfig pushaction.ApplicationConfig, 388 configStream <-chan pushaction.ApplicationConfig, 389 eventStream <-chan pushaction.Event, 390 warningsStream <-chan pushaction.Warnings, 391 errorStream <-chan error, 392 ) (pushaction.ApplicationConfig, error) { 393 var configClosed, eventClosed, warningsClosed, complete bool 394 var updatedConfig pushaction.ApplicationConfig 395 396 for { 397 select { 398 case config, ok := <-configStream: 399 if !ok { 400 log.Debug("processing config stream closed") 401 configClosed = true 402 break 403 } 404 updatedConfig = config 405 log.Debugf("updated config received: %#v", updatedConfig) 406 case event, ok := <-eventStream: 407 if !ok { 408 log.Debug("processing event stream closed") 409 eventClosed = true 410 break 411 } 412 complete = cmd.processEvent(user, appConfig, event) 413 case warnings, ok := <-warningsStream: 414 if !ok { 415 log.Debug("processing warnings stream closed") 416 warningsClosed = true 417 break 418 } 419 cmd.UI.DisplayWarnings(warnings) 420 case err, ok := <-errorStream: 421 if !ok { 422 log.Debug("processing error stream closed") 423 warningsClosed = true 424 break 425 } 426 return pushaction.ApplicationConfig{}, err 427 } 428 429 if configClosed && eventClosed && warningsClosed && complete { 430 log.Debug("breaking apply display loop") 431 break 432 } 433 } 434 435 return updatedConfig, nil 436 } 437 438 func (cmd PushCommand) processEvent(user configv3.User, appConfig pushaction.ApplicationConfig, event pushaction.Event) bool { 439 log.Infoln("received apply event:", event) 440 441 switch event { 442 case pushaction.CreatingAndMappingRoutes: 443 cmd.UI.DisplayText("Mapping routes...") 444 case pushaction.UnmappingRoutes: 445 cmd.UI.DisplayText("Unmapping routes...") 446 case pushaction.ConfiguringServices: 447 cmd.UI.DisplayText("Binding services...") 448 case pushaction.ResourceMatching: 449 cmd.UI.DisplayText("Comparing local files to remote cache...") 450 case pushaction.CreatingArchive: 451 cmd.UI.DisplayText("Packaging files to upload...") 452 case pushaction.UploadingApplication: 453 cmd.UI.DisplayText("All files found in remote cache; nothing to upload.") 454 cmd.UI.DisplayText("Waiting for API to complete processing files...") 455 case pushaction.UploadingDroplet: 456 cmd.UI.DisplayText("Uploading droplet...") 457 log.Debug("starting progress bar") 458 cmd.ProgressBar.Ready() 459 case pushaction.UploadingApplicationWithArchive: 460 cmd.UI.DisplayText("Uploading files...") 461 log.Debug("starting progress bar") 462 cmd.ProgressBar.Ready() 463 case pushaction.RetryUpload: 464 cmd.UI.DisplayText("Retrying upload due to an error...") 465 case pushaction.UploadWithArchiveComplete: 466 cmd.ProgressBar.Complete() 467 cmd.UI.DisplayNewline() 468 cmd.UI.DisplayText("Waiting for API to complete processing files...") 469 case pushaction.UploadDropletComplete: 470 cmd.ProgressBar.Complete() 471 cmd.UI.DisplayNewline() 472 cmd.UI.DisplayText("Waiting for API to complete processing droplet...") 473 case pushaction.Complete: 474 return true 475 default: 476 log.WithField("event", event).Debug("ignoring event") 477 } 478 return false 479 } 480 481 func (cmd PushCommand) validateArgs() error { 482 switch { 483 case cmd.DropletPath != "" && cmd.AppPath != "": 484 return translatableerror.ArgumentCombinationError{ 485 Args: []string{"--droplet", "-p"}, 486 } 487 case cmd.DropletPath != "" && cmd.DockerImage.Path != "": 488 return translatableerror.ArgumentCombinationError{ 489 Args: []string{"--droplet", "--docker-image", "-o"}, 490 } 491 // ArgumentCombinationError trumps RequiredArgsError in this case (when 492 // DockerImage unspecified) 493 case cmd.DropletPath != "" && cmd.DockerUsername != "": 494 return translatableerror.ArgumentCombinationError{ 495 Args: []string{"--droplet", "--docker-username", "-p"}, 496 } 497 case cmd.DockerImage.Path != "" && cmd.AppPath != "": 498 return translatableerror.ArgumentCombinationError{ 499 Args: []string{"--docker-image, -o", "-p"}, 500 } 501 case cmd.DockerImage.Path != "" && cmd.Buildpacks != nil: 502 return translatableerror.ArgumentCombinationError{ 503 Args: []string{"-b", "--docker-image, -o"}, 504 } 505 case cmd.DockerUsername != "" && cmd.DockerImage.Path == "": 506 return translatableerror.RequiredFlagsError{ 507 Arg1: "--docker-image, -o", 508 Arg2: "--docker-username", 509 } 510 case cmd.Domain != "" && cmd.NoRoute: 511 return translatableerror.ArgumentCombinationError{ 512 Args: []string{"-d", "--no-route"}, 513 } 514 case cmd.Hostname != "" && cmd.NoHostname: 515 return translatableerror.ArgumentCombinationError{ 516 Args: []string{"--hostname", "-n", "--no-hostname"}, 517 } 518 case cmd.Hostname != "" && cmd.NoRoute: 519 return translatableerror.ArgumentCombinationError{ 520 Args: []string{"--hostname", "-n", "--no-route"}, 521 } 522 case cmd.NoHostname && cmd.NoRoute: 523 return translatableerror.ArgumentCombinationError{ 524 Args: []string{"--no-hostname", "--no-route"}, 525 } 526 case cmd.PathToManifest != "" && cmd.NoManifest: 527 return translatableerror.ArgumentCombinationError{ 528 Args: []string{"-f", "--no-manifest"}, 529 } 530 case cmd.RandomRoute && cmd.Hostname != "": 531 return translatableerror.ArgumentCombinationError{ 532 Args: []string{"--hostname", "-n", "--random-route"}, 533 } 534 case cmd.RandomRoute && cmd.NoHostname: 535 return translatableerror.ArgumentCombinationError{ 536 Args: []string{"--no-hostname", "--random-route"}, 537 } 538 case cmd.RandomRoute && cmd.NoRoute: 539 return translatableerror.ArgumentCombinationError{ 540 Args: []string{"--no-route", "--random-route"}, 541 } 542 case cmd.RandomRoute && cmd.RoutePath.Path != "": 543 return translatableerror.ArgumentCombinationError{ 544 Args: []string{"--random-route", "--route-path"}, 545 } 546 case cmd.RoutePath.Path != "" && cmd.NoRoute: 547 return translatableerror.ArgumentCombinationError{ 548 Args: []string{"--route-path", "--no-route"}, 549 } 550 } 551 552 return nil 553 }