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