github.com/sleungcy/cli@v7.1.0+incompatible/command/v7/shared/app_stager.go (about) 1 package shared 2 3 import ( 4 "context" 5 "fmt" 6 7 "code.cloudfoundry.org/cli/actor/sharedaction" 8 "code.cloudfoundry.org/cli/actor/v7action" 9 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant" 10 "code.cloudfoundry.org/cli/command" 11 "code.cloudfoundry.org/cli/resources" 12 "code.cloudfoundry.org/cli/util/configv3" 13 ) 14 15 /* 16 AppStager interface extracts the complexity of the asynchronous 17 staging process, which is used by several commands (e.g. restage, 18 copy-package). 19 */ 20 21 //go:generate counterfeiter . AppStager 22 23 type AppStager interface { 24 StageAndStart( 25 app resources.Application, 26 space configv3.Space, 27 organization configv3.Organization, 28 packageGUID string, 29 strategy constant.DeploymentStrategy, 30 noWait bool, 31 appAction constant.ApplicationAction, 32 ) error 33 34 StageApp( 35 app resources.Application, 36 packageGUID string, 37 space configv3.Space, 38 ) (resources.Droplet, error) 39 40 StartApp( 41 app resources.Application, 42 droplet resources.Droplet, 43 strategy constant.DeploymentStrategy, 44 noWait bool, 45 space configv3.Space, 46 organization configv3.Organization, 47 appAction constant.ApplicationAction, 48 ) error 49 } 50 51 type Stager struct { 52 Actor stagingAndStartActor 53 UI command.UI 54 Config command.Config 55 LogCache sharedaction.LogCacheClient 56 } 57 58 type stagingAndStartActor interface { 59 CreateDeploymentByApplicationAndDroplet(appGUID string, dropletGUID string) (string, v7action.Warnings, error) 60 GetDetailedAppSummary(appName string, spaceGUID string, withObfuscatedValues bool) (v7action.DetailedApplicationSummary, v7action.Warnings, error) 61 GetStreamingLogsForApplicationByNameAndSpace(appName string, spaceGUID string, client sharedaction.LogCacheClient) (<-chan sharedaction.LogMessage, <-chan error, context.CancelFunc, v7action.Warnings, error) 62 PollStart(app resources.Application, noWait bool, handleProcessStats func(string)) (v7action.Warnings, error) 63 PollStartForRolling(app resources.Application, deploymentGUID string, noWait bool, handleProcessStats func(string)) (v7action.Warnings, error) 64 SetApplicationDroplet(appGUID string, dropletGUID string) (v7action.Warnings, error) 65 StagePackage(packageGUID, appName, spaceGUID string) (<-chan resources.Droplet, <-chan v7action.Warnings, <-chan error) 66 StartApplication(appGUID string) (v7action.Warnings, error) 67 StopApplication(appGUID string) (v7action.Warnings, error) 68 } 69 70 func NewAppStager(actor stagingAndStartActor, ui command.UI, config command.Config, logCache sharedaction.LogCacheClient) AppStager { 71 return &Stager{ 72 Actor: actor, 73 UI: ui, 74 Config: config, 75 LogCache: logCache, 76 } 77 } 78 79 func (stager *Stager) StageAndStart( 80 app resources.Application, 81 space configv3.Space, 82 organization configv3.Organization, 83 packageGUID string, 84 strategy constant.DeploymentStrategy, 85 noWait bool, 86 appAction constant.ApplicationAction, 87 ) error { 88 89 droplet, err := stager.StageApp(app, packageGUID, space) 90 if err != nil { 91 return err 92 } 93 94 stager.UI.DisplayNewline() 95 96 err = stager.StartApp(app, droplet, strategy, noWait, space, organization, appAction) 97 if err != nil { 98 return err 99 } 100 101 return nil 102 } 103 104 func (stager *Stager) StageApp(app resources.Application, packageGUID string, space configv3.Space) (resources.Droplet, error) { 105 logStream, logErrStream, stopLogStreamFunc, logWarnings, logErr := stager.Actor.GetStreamingLogsForApplicationByNameAndSpace(app.Name, space.GUID, stager.LogCache) 106 stager.UI.DisplayWarnings(logWarnings) 107 if logErr != nil { 108 return resources.Droplet{}, logErr 109 } 110 defer stopLogStreamFunc() 111 112 stager.UI.DisplayText("Staging app and tracing logs...") 113 dropletStream, warningsStream, errStream := stager.Actor.StagePackage( 114 packageGUID, 115 app.Name, 116 space.GUID, 117 ) 118 119 droplet, err := PollStage(dropletStream, warningsStream, errStream, logStream, logErrStream, stager.UI) 120 if err != nil { 121 return resources.Droplet{}, err 122 } 123 124 return droplet, nil 125 } 126 127 func (stager *Stager) StartApp( 128 app resources.Application, 129 droplet resources.Droplet, 130 strategy constant.DeploymentStrategy, 131 noWait bool, 132 space configv3.Space, 133 organization configv3.Organization, 134 appAction constant.ApplicationAction, 135 ) error { 136 if strategy == constant.DeploymentStrategyRolling { 137 stager.UI.DisplayText("Creating deployment for app {{.AppName}}...\n", 138 map[string]interface{}{ 139 "AppName": app.Name, 140 }, 141 ) 142 deploymentGUID, warnings, err := stager.Actor.CreateDeploymentByApplicationAndDroplet(app.GUID, droplet.GUID) 143 stager.UI.DisplayWarnings(warnings) 144 if err != nil { 145 return err 146 } 147 148 stager.UI.DisplayText("Waiting for app to deploy...\n") 149 150 handleInstanceDetails := func(instanceDetails string) { 151 stager.UI.DisplayText(instanceDetails) 152 } 153 154 warnings, err = stager.Actor.PollStartForRolling(app, deploymentGUID, noWait, handleInstanceDetails) 155 stager.UI.DisplayNewline() 156 stager.UI.DisplayWarnings(warnings) 157 if err != nil { 158 return err 159 } 160 } else { 161 user, err := stager.Config.CurrentUser() 162 if err != nil { 163 return err 164 } 165 166 flavorText := fmt.Sprintf("%s app {{.App}} in org {{.Org}} / space {{.Space}} as {{.UserName}}...", appAction) 167 stager.UI.DisplayTextWithFlavor(flavorText, 168 map[string]interface{}{ 169 "App": app.Name, 170 "Org": organization.Name, 171 "Space": space.Name, 172 "UserName": user.Name, 173 }, 174 ) 175 stager.UI.DisplayNewline() 176 177 if app.Started() { 178 if appAction == constant.ApplicationStarting { 179 stager.UI.DisplayText("App '{{.AppName}}' is already started.", 180 map[string]interface{}{ 181 "AppName": app.Name, 182 }) 183 stager.UI.DisplayOK() 184 return nil 185 186 } else { 187 stager.UI.DisplayText("Stopping app...") 188 stager.UI.DisplayNewline() 189 190 warnings, err := stager.Actor.StopApplication(app.GUID) 191 stager.UI.DisplayWarnings(warnings) 192 if err != nil { 193 return err 194 } 195 } 196 } 197 198 if droplet.GUID != "" { 199 // attach droplet to app 200 warnings, err := stager.Actor.SetApplicationDroplet(app.GUID, droplet.GUID) 201 stager.UI.DisplayWarnings(warnings) 202 if err != nil { 203 return err 204 } 205 } 206 207 stager.UI.DisplayText("Waiting for app to start...") 208 stager.UI.DisplayNewline() 209 210 // start the application 211 warnings, err := stager.Actor.StartApplication(app.GUID) 212 stager.UI.DisplayWarnings(warnings) 213 if err != nil { 214 return err 215 } 216 217 handleInstanceDetails := func(instanceDetails string) { 218 stager.UI.DisplayText(instanceDetails) 219 } 220 221 warnings, err = stager.Actor.PollStart(app, noWait, handleInstanceDetails) 222 stager.UI.DisplayNewline() 223 stager.UI.DisplayWarnings(warnings) 224 if err != nil { 225 return err 226 } 227 } 228 229 summary, warnings, err := stager.Actor.GetDetailedAppSummary( 230 app.Name, 231 space.GUID, 232 false, 233 ) 234 stager.UI.DisplayWarnings(warnings) 235 if err != nil { 236 return err 237 } 238 239 appSummaryDisplayer := NewAppSummaryDisplayer(stager.UI) 240 appSummaryDisplayer.AppDisplay(summary, false) 241 242 return nil 243 }