github.com/mook-as/cf-cli@v7.0.0-beta.28.0.20200120190804-b91c115fae48+incompatible/command/v6/restart_command_test.go (about) 1 package v6_test 2 3 import ( 4 "errors" 5 "time" 6 7 "code.cloudfoundry.org/cli/actor/actionerror" 8 "code.cloudfoundry.org/cli/actor/v2action" 9 "code.cloudfoundry.org/cli/actor/v2v3action" 10 "code.cloudfoundry.org/cli/actor/v3action" 11 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv2/constant" 12 "code.cloudfoundry.org/cli/command/commandfakes" 13 "code.cloudfoundry.org/cli/command/translatableerror" 14 . "code.cloudfoundry.org/cli/command/v6" 15 "code.cloudfoundry.org/cli/command/v6/shared/sharedfakes" 16 "code.cloudfoundry.org/cli/command/v6/v6fakes" 17 "code.cloudfoundry.org/cli/types" 18 "code.cloudfoundry.org/cli/util/configv3" 19 "code.cloudfoundry.org/cli/util/ui" 20 . "github.com/onsi/ginkgo" 21 . "github.com/onsi/gomega" 22 . "github.com/onsi/gomega/gbytes" 23 ) 24 25 var _ = Describe("Restart Command", func() { 26 var ( 27 cmd RestartCommand 28 testUI *ui.UI 29 fakeConfig *commandfakes.FakeConfig 30 fakeSharedActor *commandfakes.FakeSharedActor 31 fakeApplicationSummaryActor *sharedfakes.FakeApplicationSummaryActor 32 fakeActor *v6fakes.FakeRestartActor 33 binaryName string 34 executeErr error 35 ) 36 37 BeforeEach(func() { 38 testUI = ui.NewTestUI(nil, NewBuffer(), NewBuffer()) 39 fakeConfig = new(commandfakes.FakeConfig) 40 fakeSharedActor = new(commandfakes.FakeSharedActor) 41 fakeActor = new(v6fakes.FakeRestartActor) 42 fakeApplicationSummaryActor = new(sharedfakes.FakeApplicationSummaryActor) 43 44 cmd = RestartCommand{ 45 UI: testUI, 46 Config: fakeConfig, 47 SharedActor: fakeSharedActor, 48 Actor: fakeActor, 49 ApplicationSummaryActor: fakeApplicationSummaryActor, 50 } 51 52 cmd.RequiredArgs.AppName = "some-app" 53 54 binaryName = "faceman" 55 fakeConfig.BinaryNameReturns(binaryName) 56 57 var err error 58 testUI.TimezoneLocation, err = time.LoadLocation("America/Los_Angeles") 59 Expect(err).NotTo(HaveOccurred()) 60 61 fakeActor.RestartApplicationStub = func(app v2action.Application, client v2action.NOAAClient) (<-chan *v2action.LogMessage, <-chan error, <-chan v2action.ApplicationStateChange, <-chan string, <-chan error) { 62 messages := make(chan *v2action.LogMessage) 63 logErrs := make(chan error) 64 appState := make(chan v2action.ApplicationStateChange) 65 warnings := make(chan string) 66 errs := make(chan error) 67 68 go func() { 69 appState <- v2action.ApplicationStateStopping 70 appState <- v2action.ApplicationStateStaging 71 appState <- v2action.ApplicationStateStarting 72 close(messages) 73 close(logErrs) 74 close(appState) 75 close(warnings) 76 close(errs) 77 }() 78 79 return messages, logErrs, appState, warnings, errs 80 } 81 }) 82 83 JustBeforeEach(func() { 84 executeErr = cmd.Execute(nil) 85 }) 86 87 When("checking target fails", func() { 88 BeforeEach(func() { 89 fakeSharedActor.CheckTargetReturns(actionerror.NotLoggedInError{BinaryName: binaryName}) 90 }) 91 92 It("returns an error if the check fails", func() { 93 Expect(executeErr).To(MatchError(actionerror.NotLoggedInError{BinaryName: "faceman"})) 94 95 Expect(fakeSharedActor.CheckTargetCallCount()).To(Equal(1)) 96 checkTargetedOrg, checkTargetedSpace := fakeSharedActor.CheckTargetArgsForCall(0) 97 Expect(checkTargetedOrg).To(BeTrue()) 98 Expect(checkTargetedSpace).To(BeTrue()) 99 }) 100 }) 101 102 When("the user is logged in, and org and space are targeted", func() { 103 BeforeEach(func() { 104 fakeConfig.HasTargetedOrganizationReturns(true) 105 fakeConfig.TargetedOrganizationReturns(configv3.Organization{Name: "some-org"}) 106 fakeConfig.HasTargetedSpaceReturns(true) 107 fakeConfig.TargetedSpaceReturns(configv3.Space{ 108 GUID: "some-space-guid", 109 Name: "some-space"}) 110 fakeConfig.CurrentUserReturns( 111 configv3.User{Name: "some-user"}, 112 nil) 113 }) 114 115 When("getting the current user returns an error", func() { 116 var expectedErr error 117 118 BeforeEach(func() { 119 expectedErr = errors.New("getting current user error") 120 fakeConfig.CurrentUserReturns( 121 configv3.User{}, 122 expectedErr) 123 }) 124 125 It("returns the error", func() { 126 Expect(executeErr).To(MatchError(expectedErr)) 127 }) 128 }) 129 130 It("displays flavor text", func() { 131 Expect(testUI.Out).To(Say("Restarting app some-app in org some-org / space some-space as some-user...")) 132 }) 133 134 When("the app exists", func() { 135 When("the app is started", func() { 136 BeforeEach(func() { 137 fakeActor.GetApplicationByNameAndSpaceReturns( 138 v2action.Application{State: constant.ApplicationStarted}, 139 v2action.Warnings{"warning-1", "warning-2"}, 140 nil, 141 ) 142 }) 143 144 It("stops and starts the app", func() { 145 Expect(executeErr).ToNot(HaveOccurred()) 146 147 Expect(testUI.Out).Should(Say("Stopping app...")) 148 Expect(testUI.Out).Should(Say("Staging app and tracing logs...")) 149 Expect(testUI.Out).Should(Say("Waiting for app to start...")) 150 Expect(testUI.Err).To(Say("warning-1")) 151 Expect(testUI.Err).To(Say("warning-2")) 152 153 Expect(fakeActor.RestartApplicationCallCount()).To(Equal(1)) 154 }) 155 }) 156 157 When("the app is not already started", func() { 158 BeforeEach(func() { 159 fakeActor.GetApplicationByNameAndSpaceReturns( 160 v2action.Application{GUID: "app-guid", State: constant.ApplicationStopped}, 161 v2action.Warnings{"warning-1", "warning-2"}, 162 nil, 163 ) 164 }) 165 166 It("starts the app", func() { 167 Expect(executeErr).ToNot(HaveOccurred()) 168 169 Expect(testUI.Err).To(Say("warning-1")) 170 Expect(testUI.Err).To(Say("warning-2")) 171 172 Expect(fakeActor.RestartApplicationCallCount()).To(Equal(1)) 173 app, _ := fakeActor.RestartApplicationArgsForCall(0) 174 Expect(app.GUID).To(Equal("app-guid")) 175 }) 176 177 When("passed an appStarting message", func() { 178 BeforeEach(func() { 179 fakeActor.RestartApplicationStub = func(app v2action.Application, client v2action.NOAAClient) (<-chan *v2action.LogMessage, <-chan error, <-chan v2action.ApplicationStateChange, <-chan string, <-chan error) { 180 messages := make(chan *v2action.LogMessage) 181 logErrs := make(chan error) 182 appState := make(chan v2action.ApplicationStateChange) 183 warnings := make(chan string) 184 errs := make(chan error) 185 186 go func() { 187 messages <- v2action.NewLogMessage("log message 1", 1, time.Unix(0, 0), "STG", "1") 188 messages <- v2action.NewLogMessage("log message 2", 1, time.Unix(0, 0), "STG", "1") 189 appState <- v2action.ApplicationStateStopping 190 appState <- v2action.ApplicationStateStaging 191 appState <- v2action.ApplicationStateStarting 192 close(messages) 193 close(logErrs) 194 close(appState) 195 close(warnings) 196 close(errs) 197 }() 198 199 return messages, logErrs, appState, warnings, errs 200 } 201 }) 202 203 It("displays the log", func() { 204 Expect(executeErr).ToNot(HaveOccurred()) 205 Expect(testUI.Out).To(Say("log message 1")) 206 Expect(testUI.Out).To(Say("log message 2")) 207 Expect(testUI.Out).To(Say("Waiting for app to start...")) 208 }) 209 }) 210 211 When("passed a log message", func() { 212 BeforeEach(func() { 213 fakeActor.RestartApplicationStub = func(app v2action.Application, client v2action.NOAAClient) (<-chan *v2action.LogMessage, <-chan error, <-chan v2action.ApplicationStateChange, <-chan string, <-chan error) { 214 messages := make(chan *v2action.LogMessage) 215 logErrs := make(chan error) 216 appState := make(chan v2action.ApplicationStateChange) 217 warnings := make(chan string) 218 errs := make(chan error) 219 220 go func() { 221 messages <- v2action.NewLogMessage("log message 1", 1, time.Unix(0, 0), "STG", "1") 222 messages <- v2action.NewLogMessage("log message 2", 1, time.Unix(0, 0), "STG", "1") 223 messages <- v2action.NewLogMessage("log message 3", 1, time.Unix(0, 0), "Something else", "1") 224 close(messages) 225 close(logErrs) 226 close(appState) 227 close(warnings) 228 close(errs) 229 }() 230 231 return messages, logErrs, appState, warnings, errs 232 } 233 }) 234 235 It("displays the log", func() { 236 Expect(executeErr).ToNot(HaveOccurred()) 237 Expect(testUI.Out).To(Say("log message 1")) 238 Expect(testUI.Out).To(Say("log message 2")) 239 Expect(testUI.Out).ToNot(Say("log message 3")) 240 }) 241 }) 242 243 When("passed an log err", func() { 244 Context("NOAA connection times out/closes", func() { 245 BeforeEach(func() { 246 fakeActor.RestartApplicationStub = func(app v2action.Application, client v2action.NOAAClient) (<-chan *v2action.LogMessage, <-chan error, <-chan v2action.ApplicationStateChange, <-chan string, <-chan error) { 247 messages := make(chan *v2action.LogMessage) 248 logErrs := make(chan error) 249 appState := make(chan v2action.ApplicationStateChange) 250 warnings := make(chan string) 251 errs := make(chan error) 252 253 go func() { 254 messages <- v2action.NewLogMessage("log message 1", 1, time.Unix(0, 0), "STG", "1") 255 messages <- v2action.NewLogMessage("log message 2", 1, time.Unix(0, 0), "STG", "1") 256 messages <- v2action.NewLogMessage("log message 3", 1, time.Unix(0, 0), "STG", "1") 257 logErrs <- actionerror.NOAATimeoutError{} 258 close(messages) 259 close(logErrs) 260 close(appState) 261 close(warnings) 262 close(errs) 263 }() 264 265 return messages, logErrs, appState, warnings, errs 266 } 267 }) 268 269 It("displays a warning and continues until app has started", func() { 270 Expect(executeErr).To(BeNil()) 271 Expect(testUI.Out).To(Say("message 1")) 272 Expect(testUI.Out).To(Say("message 2")) 273 Expect(testUI.Out).To(Say("message 3")) 274 Expect(testUI.Err).To(Say("timeout connecting to log server, no log will be shown")) 275 }) 276 }) 277 278 Context("an unexpected error occurs", func() { 279 var expectedErr error 280 281 BeforeEach(func() { 282 expectedErr = errors.New("err log message") 283 fakeActor.RestartApplicationStub = func(app v2action.Application, client v2action.NOAAClient) (<-chan *v2action.LogMessage, <-chan error, <-chan v2action.ApplicationStateChange, <-chan string, <-chan error) { 284 messages := make(chan *v2action.LogMessage) 285 logErrs := make(chan error) 286 appState := make(chan v2action.ApplicationStateChange) 287 warnings := make(chan string) 288 errs := make(chan error) 289 290 go func() { 291 logErrs <- expectedErr 292 close(messages) 293 close(logErrs) 294 close(appState) 295 close(warnings) 296 close(errs) 297 }() 298 299 return messages, logErrs, appState, warnings, errs 300 } 301 }) 302 303 It("displays the error and continues to poll", func() { 304 Expect(executeErr).NotTo(HaveOccurred()) 305 Expect(testUI.Err).To(Say(expectedErr.Error())) 306 }) 307 }) 308 }) 309 310 When("passed a warning", func() { 311 Context("while NOAA is still logging", func() { 312 BeforeEach(func() { 313 fakeActor.RestartApplicationStub = func(app v2action.Application, client v2action.NOAAClient) (<-chan *v2action.LogMessage, <-chan error, <-chan v2action.ApplicationStateChange, <-chan string, <-chan error) { 314 messages := make(chan *v2action.LogMessage) 315 logErrs := make(chan error) 316 appState := make(chan v2action.ApplicationStateChange) 317 warnings := make(chan string) 318 errs := make(chan error) 319 320 go func() { 321 warnings <- "warning 1" 322 warnings <- "warning 2" 323 close(messages) 324 close(logErrs) 325 close(appState) 326 close(warnings) 327 close(errs) 328 }() 329 330 return messages, logErrs, appState, warnings, errs 331 } 332 }) 333 334 It("displays the warnings to STDERR", func() { 335 Expect(executeErr).ToNot(HaveOccurred()) 336 Expect(testUI.Err).To(Say("warning 1")) 337 Expect(testUI.Err).To(Say("warning 2")) 338 }) 339 }) 340 341 Context("while NOAA is no longer logging", func() { 342 BeforeEach(func() { 343 fakeActor.RestartApplicationStub = func(app v2action.Application, client v2action.NOAAClient) (<-chan *v2action.LogMessage, <-chan error, <-chan v2action.ApplicationStateChange, <-chan string, <-chan error) { 344 messages := make(chan *v2action.LogMessage) 345 logErrs := make(chan error) 346 appState := make(chan v2action.ApplicationStateChange) 347 warnings := make(chan string) 348 errs := make(chan error) 349 350 go func() { 351 warnings <- "warning 1" 352 warnings <- "warning 2" 353 logErrs <- actionerror.NOAATimeoutError{} 354 close(messages) 355 close(logErrs) 356 warnings <- "warning 3" 357 warnings <- "warning 4" 358 close(appState) 359 close(warnings) 360 close(errs) 361 }() 362 363 return messages, logErrs, appState, warnings, errs 364 } 365 }) 366 367 It("displays the warnings to STDERR", func() { 368 Expect(executeErr).ToNot(HaveOccurred()) 369 Expect(testUI.Err).To(Say("warning 1")) 370 Expect(testUI.Err).To(Say("warning 2")) 371 Expect(testUI.Err).To(Say("timeout connecting to log server, no log will be shown")) 372 Expect(testUI.Err).To(Say("warning 3")) 373 Expect(testUI.Err).To(Say("warning 4")) 374 }) 375 }) 376 }) 377 378 When("passed an API err", func() { 379 var apiErr error 380 381 BeforeEach(func() { 382 fakeActor.RestartApplicationStub = func(app v2action.Application, client v2action.NOAAClient) (<-chan *v2action.LogMessage, <-chan error, <-chan v2action.ApplicationStateChange, <-chan string, <-chan error) { 383 messages := make(chan *v2action.LogMessage) 384 logErrs := make(chan error) 385 appState := make(chan v2action.ApplicationStateChange) 386 warnings := make(chan string) 387 errs := make(chan error) 388 389 go func() { 390 errs <- apiErr 391 close(messages) 392 close(logErrs) 393 close(appState) 394 close(warnings) 395 close(errs) 396 }() 397 398 return messages, logErrs, appState, warnings, errs 399 } 400 }) 401 402 Context("an unexpected error", func() { 403 BeforeEach(func() { 404 apiErr = errors.New("err log message") 405 }) 406 407 It("stops logging and returns the error", func() { 408 Expect(executeErr).To(MatchError(apiErr)) 409 }) 410 }) 411 412 Context("staging failed", func() { 413 BeforeEach(func() { 414 apiErr = actionerror.StagingFailedError{Reason: "Something, but not nothing"} 415 }) 416 417 It("stops logging and returns StagingFailedError", func() { 418 Expect(executeErr).To(MatchError(translatableerror.StagingFailedError{Message: "Something, but not nothing"})) 419 }) 420 }) 421 422 Context("staging timed out", func() { 423 BeforeEach(func() { 424 apiErr = actionerror.StagingTimeoutError{AppName: "some-app", Timeout: time.Nanosecond} 425 }) 426 427 It("stops logging and returns StagingTimeoutError", func() { 428 Expect(executeErr).To(MatchError(translatableerror.StagingTimeoutError{AppName: "some-app", Timeout: time.Nanosecond})) 429 }) 430 }) 431 432 When("the app instance crashes", func() { 433 BeforeEach(func() { 434 apiErr = actionerror.ApplicationInstanceCrashedError{Name: "some-app"} 435 }) 436 437 It("stops logging and returns ApplicationUnableToStartError", func() { 438 Expect(executeErr).To(MatchError(translatableerror.ApplicationUnableToStartError{AppName: "some-app", BinaryName: "faceman"})) 439 }) 440 }) 441 442 When("the app instance flaps", func() { 443 BeforeEach(func() { 444 apiErr = actionerror.ApplicationInstanceFlappingError{Name: "some-app"} 445 }) 446 447 It("stops logging and returns ApplicationUnableToStartError", func() { 448 Expect(executeErr).To(MatchError(translatableerror.ApplicationUnableToStartError{AppName: "some-app", BinaryName: "faceman"})) 449 }) 450 }) 451 452 Context("starting timeout", func() { 453 BeforeEach(func() { 454 apiErr = actionerror.StartupTimeoutError{Name: "some-app"} 455 }) 456 457 It("stops logging and returns StartupTimeoutError", func() { 458 Expect(executeErr).To(MatchError(translatableerror.StartupTimeoutError{AppName: "some-app", BinaryName: "faceman"})) 459 }) 460 }) 461 }) 462 463 When("the app finishes starting", func() { 464 BeforeEach(func() { 465 fakeApplicationSummaryActor.GetApplicationSummaryByNameAndSpaceReturns( 466 v2v3action.ApplicationSummary{ 467 ApplicationSummary: v3action.ApplicationSummary{ 468 Application: v3action.Application{ 469 Name: "some-app", 470 }, 471 ProcessSummaries: v3action.ProcessSummaries{ 472 { 473 Process: v3action.Process{ 474 Type: "aba", 475 Command: *types.NewFilteredString("some-command-1"), 476 MemoryInMB: types.NullUint64{Value: 32, IsSet: true}, 477 DiskInMB: types.NullUint64{Value: 1024, IsSet: true}, 478 }, 479 }, 480 { 481 Process: v3action.Process{ 482 Type: "console", 483 Command: *types.NewFilteredString("some-command-2"), 484 MemoryInMB: types.NullUint64{Value: 16, IsSet: true}, 485 DiskInMB: types.NullUint64{Value: 512, IsSet: true}, 486 }, 487 }, 488 }, 489 }, 490 }, 491 v2v3action.Warnings{"combo-summary-warning"}, 492 nil) 493 }) 494 495 It("displays process information", func() { 496 Expect(executeErr).ToNot(HaveOccurred()) 497 498 Expect(testUI.Out).To(Say(`name:\s+%s`, "some-app")) 499 Expect(testUI.Out).To(Say(`type:\s+aba`)) 500 Expect(testUI.Out).To(Say(`instances:\s+0/0`)) 501 Expect(testUI.Out).To(Say(`memory usage:\s+32M`)) 502 Expect(testUI.Out).To(Say(`start command:\s+some-command-1`)) 503 Expect(testUI.Out).To(Say(`type:\s+console`)) 504 Expect(testUI.Out).To(Say(`instances:\s+0/0`)) 505 Expect(testUI.Out).To(Say(`memory usage:\s+16M`)) 506 Expect(testUI.Out).To(Say(`start command:\s+some-command-2`)) 507 508 Expect(testUI.Err).To(Say("combo-summary-warning")) 509 510 Expect(fakeApplicationSummaryActor.GetApplicationSummaryByNameAndSpaceCallCount()).To(Equal(1)) 511 passedAppName, spaceGUID, withObfuscatedValues := fakeApplicationSummaryActor.GetApplicationSummaryByNameAndSpaceArgsForCall(0) 512 Expect(passedAppName).To(Equal("some-app")) 513 Expect(spaceGUID).To(Equal("some-space-guid")) 514 Expect(withObfuscatedValues).To(BeTrue()) 515 }) 516 }) 517 }) 518 }) 519 520 When("the app does *not* exists", func() { 521 BeforeEach(func() { 522 fakeActor.GetApplicationByNameAndSpaceReturns( 523 v2action.Application{}, 524 v2action.Warnings{"warning-1", "warning-2"}, 525 actionerror.ApplicationNotFoundError{Name: "some-app"}, 526 ) 527 }) 528 529 It("returns back an error", func() { 530 Expect(executeErr).To(MatchError(actionerror.ApplicationNotFoundError{Name: "some-app"})) 531 532 Expect(testUI.Err).To(Say("warning-1")) 533 Expect(testUI.Err).To(Say("warning-2")) 534 }) 535 }) 536 }) 537 })