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