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