github.com/DaAlbrecht/cf-cli@v0.0.0-20231128151943-1fe19bb400b9/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 go run github.com/maxbrunsfeld/counterfeiter/v6 . 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 resourceGuid string, 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 CreateDeploymentByApplicationAndRevision(appGUID string, revisionGUID string) (string, v7action.Warnings, error) 61 GetCurrentUser() (configv3.User, error) 62 GetDetailedAppSummary(appName string, spaceGUID string, withObfuscatedValues bool) (v7action.DetailedApplicationSummary, v7action.Warnings, error) 63 GetStreamingLogsForApplicationByNameAndSpace(appName string, spaceGUID string, client sharedaction.LogCacheClient) (<-chan sharedaction.LogMessage, <-chan error, context.CancelFunc, v7action.Warnings, error) 64 PollStart(app resources.Application, noWait bool, handleProcessStats func(string)) (v7action.Warnings, error) 65 PollStartForRolling(app resources.Application, deploymentGUID string, noWait bool, handleProcessStats func(string)) (v7action.Warnings, error) 66 SetApplicationDroplet(appGUID string, dropletGUID string) (v7action.Warnings, error) 67 StagePackage(packageGUID, appName, spaceGUID string) (<-chan resources.Droplet, <-chan v7action.Warnings, <-chan error) 68 StartApplication(appGUID string) (v7action.Warnings, error) 69 StopApplication(appGUID string) (v7action.Warnings, error) 70 } 71 72 func NewAppStager(actor stagingAndStartActor, ui command.UI, config command.Config, logCache sharedaction.LogCacheClient) AppStager { 73 return &Stager{ 74 Actor: actor, 75 UI: ui, 76 Config: config, 77 LogCache: logCache, 78 } 79 } 80 81 func (stager *Stager) StageAndStart( 82 app resources.Application, 83 space configv3.Space, 84 organization configv3.Organization, 85 packageGUID string, 86 strategy constant.DeploymentStrategy, 87 noWait bool, 88 appAction constant.ApplicationAction, 89 ) error { 90 91 droplet, err := stager.StageApp(app, packageGUID, space) 92 if err != nil { 93 return err 94 } 95 96 stager.UI.DisplayNewline() 97 98 err = stager.StartApp(app, droplet.GUID, strategy, noWait, space, organization, appAction) 99 if err != nil { 100 return err 101 } 102 103 return nil 104 } 105 106 func (stager *Stager) StageApp(app resources.Application, packageGUID string, space configv3.Space) (resources.Droplet, error) { 107 logStream, logErrStream, stopLogStreamFunc, logWarnings, logErr := stager.Actor.GetStreamingLogsForApplicationByNameAndSpace(app.Name, space.GUID, stager.LogCache) 108 stager.UI.DisplayWarnings(logWarnings) 109 if logErr != nil { 110 return resources.Droplet{}, logErr 111 } 112 defer stopLogStreamFunc() 113 114 stager.UI.DisplayText("Staging app and tracing logs...") 115 dropletStream, warningsStream, errStream := stager.Actor.StagePackage( 116 packageGUID, 117 app.Name, 118 space.GUID, 119 ) 120 121 droplet, err := PollStage(dropletStream, warningsStream, errStream, logStream, logErrStream, stager.UI) 122 if err != nil { 123 return resources.Droplet{}, err 124 } 125 126 return droplet, nil 127 } 128 129 func (stager *Stager) StartApp( 130 app resources.Application, 131 resourceGuid string, 132 strategy constant.DeploymentStrategy, 133 noWait bool, 134 space configv3.Space, 135 organization configv3.Organization, 136 appAction constant.ApplicationAction, 137 ) error { 138 if strategy == constant.DeploymentStrategyRolling { 139 stager.UI.DisplayText("Creating deployment for app {{.AppName}}...\n", 140 map[string]interface{}{ 141 "AppName": app.Name, 142 }, 143 ) 144 145 var ( 146 deploymentGUID string 147 warnings v7action.Warnings 148 err error 149 ) 150 151 switch appAction { 152 case constant.ApplicationRollingBack: 153 deploymentGUID, warnings, err = stager.Actor.CreateDeploymentByApplicationAndRevision(app.GUID, resourceGuid) 154 default: 155 deploymentGUID, warnings, err = stager.Actor.CreateDeploymentByApplicationAndDroplet(app.GUID, resourceGuid) 156 } 157 158 stager.UI.DisplayWarnings(warnings) 159 if err != nil { 160 return err 161 } 162 163 stager.UI.DisplayText("Waiting for app to deploy...\n") 164 165 handleInstanceDetails := func(instanceDetails string) { 166 stager.UI.DisplayText(instanceDetails) 167 } 168 169 warnings, err = stager.Actor.PollStartForRolling(app, deploymentGUID, noWait, handleInstanceDetails) 170 stager.UI.DisplayNewline() 171 stager.UI.DisplayWarnings(warnings) 172 if err != nil { 173 return err 174 } 175 if noWait == true { 176 stager.UI.DisplayText("First instance restaged correctly, restaging remaining in the background") 177 return nil 178 } 179 } else { 180 user, err := stager.Actor.GetCurrentUser() 181 if err != nil { 182 return err 183 } 184 185 flavorText := fmt.Sprintf("%s app {{.App}} in org {{.Org}} / space {{.Space}} as {{.UserName}}...", appAction) 186 stager.UI.DisplayTextWithFlavor(flavorText, 187 map[string]interface{}{ 188 "App": app.Name, 189 "Org": organization.Name, 190 "Space": space.Name, 191 "UserName": user.Name, 192 }, 193 ) 194 stager.UI.DisplayNewline() 195 196 if app.Started() { 197 if appAction == constant.ApplicationStarting { 198 stager.UI.DisplayText("App '{{.AppName}}' is already started.", 199 map[string]interface{}{ 200 "AppName": app.Name, 201 }) 202 stager.UI.DisplayOK() 203 return nil 204 205 } else { 206 stager.UI.DisplayText("Stopping app...") 207 stager.UI.DisplayNewline() 208 209 warnings, err := stager.Actor.StopApplication(app.GUID) 210 stager.UI.DisplayWarnings(warnings) 211 if err != nil { 212 return err 213 } 214 } 215 } 216 217 if resourceGuid != "" { 218 // attach droplet to app 219 warnings, err := stager.Actor.SetApplicationDroplet(app.GUID, resourceGuid) 220 stager.UI.DisplayWarnings(warnings) 221 if err != nil { 222 return err 223 } 224 } 225 226 stager.UI.DisplayText("Waiting for app to start...") 227 stager.UI.DisplayNewline() 228 229 // start the application 230 warnings, err := stager.Actor.StartApplication(app.GUID) 231 stager.UI.DisplayWarnings(warnings) 232 if err != nil { 233 return err 234 } 235 236 handleInstanceDetails := func(instanceDetails string) { 237 stager.UI.DisplayText(instanceDetails) 238 } 239 240 warnings, err = stager.Actor.PollStart(app, noWait, handleInstanceDetails) 241 stager.UI.DisplayNewline() 242 stager.UI.DisplayWarnings(warnings) 243 if err != nil { 244 return err 245 } 246 } 247 248 summary, warnings, err := stager.Actor.GetDetailedAppSummary( 249 app.Name, 250 space.GUID, 251 false, 252 ) 253 stager.UI.DisplayWarnings(warnings) 254 if err != nil { 255 return err 256 } 257 258 appSummaryDisplayer := NewAppSummaryDisplayer(stager.UI) 259 appSummaryDisplayer.AppDisplay(summary, false) 260 261 return nil 262 }