github.com/cloudfoundry-attic/ltc@v0.0.0-20151123212628-098adc7919fc/app_examiner/command_factory/app_examiner_command_factory_test.go (about) 1 package command_factory_test 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 "time" 8 9 . "github.com/onsi/ginkgo" 10 . "github.com/onsi/gomega" 11 "github.com/onsi/gomega/gbytes" 12 13 "github.com/cloudfoundry-incubator/ltc/app_examiner" 14 "github.com/cloudfoundry-incubator/ltc/app_examiner/command_factory" 15 "github.com/cloudfoundry-incubator/ltc/app_examiner/command_factory/fake_terminal" 16 "github.com/cloudfoundry-incubator/ltc/app_examiner/command_factory/graphical/fake_graphical_visualizer" 17 "github.com/cloudfoundry-incubator/ltc/app_examiner/fake_app_examiner" 18 "github.com/cloudfoundry-incubator/ltc/exit_handler/exit_codes" 19 "github.com/cloudfoundry-incubator/ltc/exit_handler/fake_exit_handler" 20 "github.com/cloudfoundry-incubator/ltc/route_helpers" 21 "github.com/cloudfoundry-incubator/ltc/task_examiner" 22 "github.com/cloudfoundry-incubator/ltc/task_examiner/fake_task_examiner" 23 "github.com/cloudfoundry-incubator/ltc/terminal" 24 "github.com/cloudfoundry-incubator/ltc/terminal/colors" 25 "github.com/cloudfoundry-incubator/ltc/terminal/cursor" 26 "github.com/cloudfoundry-incubator/ltc/test_helpers" 27 "github.com/codegangsta/cli" 28 "github.com/pivotal-golang/clock/fakeclock" 29 ) 30 31 const TerminalEsc = "\033[" 32 33 var _ = Describe("CommandFactory", func() { 34 35 var ( 36 fakeAppExaminer *fake_app_examiner.FakeAppExaminer 37 outputBuffer *gbytes.Buffer 38 terminalUI terminal.UI 39 fakeTerm *fake_terminal.FakeTerminal 40 fakeClock *fakeclock.FakeClock 41 osSignalChan chan os.Signal 42 fakeExitHandler *fake_exit_handler.FakeExitHandler 43 fakeGraphicalVisualizer *fake_graphical_visualizer.FakeGraphicalVisualizer 44 fakeTaskExaminer *fake_task_examiner.FakeTaskExaminer 45 systemDomain string 46 ) 47 48 BeforeEach(func() { 49 fakeAppExaminer = &fake_app_examiner.FakeAppExaminer{} 50 fakeTaskExaminer = &fake_task_examiner.FakeTaskExaminer{} 51 outputBuffer = gbytes.NewBuffer() 52 terminalUI = terminal.NewUI(nil, outputBuffer, nil) 53 fakeTerm = &fake_terminal.FakeTerminal{} 54 osSignalChan = make(chan os.Signal, 1) 55 location, err := time.LoadLocation("Africa/Djibouti") 56 Expect(err).NotTo(HaveOccurred()) 57 fakeClock = fakeclock.NewFakeClock(time.Date(2012, time.February, 29, 6, 45, 30, 820, location)) 58 fakeExitHandler = &fake_exit_handler.FakeExitHandler{} 59 fakeGraphicalVisualizer = &fake_graphical_visualizer.FakeGraphicalVisualizer{} 60 systemDomain = "system.domain" 61 }) 62 63 Describe("ListAppsCommand", func() { 64 var listAppsCommand cli.Command 65 66 BeforeEach(func() { 67 commandFactory := command_factory.NewAppExaminerCommandFactory(fakeAppExaminer, terminalUI, fakeTerm, fakeClock, fakeExitHandler, nil, fakeTaskExaminer, systemDomain) 68 listAppsCommand = commandFactory.MakeListAppCommand() 69 }) 70 71 It("displays all the existing apps & tasks, making sure output spacing is correct", func() { 72 listApps := []app_examiner.AppInfo{ 73 { 74 ProcessGuid: "process1", 75 DesiredInstances: 21, 76 ActualRunningInstances: 0, 77 DiskMB: 100, 78 MemoryMB: 50, 79 Ports: []uint16{54321}, 80 Routes: route_helpers.Routes{ 81 AppRoutes: route_helpers.AppRoutes{ 82 {Hostnames: []string{"alldaylong.com"}, Port: 54321}, 83 }, 84 }, 85 }, 86 { 87 ProcessGuid: "process2", 88 DesiredInstances: 8, 89 ActualRunningInstances: 9, 90 DiskMB: 400, 91 MemoryMB: 30, 92 Ports: []uint16{1234}, 93 Routes: route_helpers.Routes{ 94 AppRoutes: route_helpers.AppRoutes{ 95 {Hostnames: []string{"never.io"}, Port: 1234}, 96 }, 97 }, 98 }, 99 { 100 ProcessGuid: "process3", 101 DesiredInstances: 5, 102 ActualRunningInstances: 5, 103 DiskMB: 600, 104 MemoryMB: 90, 105 Ports: []uint16{1234}, 106 Routes: route_helpers.Routes{ 107 AppRoutes: route_helpers.AppRoutes{ 108 {Hostnames: []string{"allthetime.com", "herewego.org"}, Port: 1234}, 109 }, 110 }, 111 }, 112 { 113 ProcessGuid: "process4", 114 DesiredInstances: 0, 115 ActualRunningInstances: 0, 116 DiskMB: 10, 117 MemoryMB: 10, 118 Routes: route_helpers.Routes{}, 119 }, 120 } 121 fakeAppExaminer.ListAppsReturns(listApps, nil) 122 listTasks := []task_examiner.TaskInfo{ 123 { 124 TaskGuid: "task-guid-1", 125 CellID: "cell-01", 126 Failed: false, 127 FailureReason: "", 128 Result: "Finished", 129 State: "COMPLETED", 130 }, 131 { 132 TaskGuid: "task-guid-2", 133 CellID: "cell-02", 134 Failed: true, 135 FailureReason: "No compatible container", 136 Result: "Finished", 137 State: "COMPLETED", 138 }, 139 { 140 TaskGuid: "task-guid-3", 141 CellID: "", 142 Failed: true, 143 FailureReason: "", 144 Result: "", 145 State: "COMPLETED", 146 }, 147 } 148 fakeTaskExaminer.ListTasksReturns(listTasks, nil) 149 150 test_helpers.ExecuteCommandWithArgs(listAppsCommand, []string{}) 151 152 Expect(outputBuffer).To(test_helpers.Say(colors.Bold("App Name"))) 153 Expect(outputBuffer).To(test_helpers.Say(colors.Bold("Instances"))) 154 Expect(outputBuffer).To(test_helpers.Say(colors.Bold("DiskMB"))) 155 Expect(outputBuffer).To(test_helpers.Say(colors.Bold("MemoryMB"))) 156 Expect(outputBuffer).To(test_helpers.Say(colors.Bold("Route"))) 157 158 Expect(outputBuffer).To(test_helpers.Say(colors.Bold("process1"))) 159 Expect(outputBuffer).To(test_helpers.Say(colors.Red("0/21"))) 160 Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("100"))) 161 Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("50"))) 162 Expect(outputBuffer).To(test_helpers.Say("alldaylong.com => 54321")) 163 164 Expect(outputBuffer).To(test_helpers.Say(colors.Bold("process2"))) 165 Expect(outputBuffer).To(test_helpers.Say(colors.Yellow("9/8"))) 166 Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("400"))) 167 Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("30"))) 168 Expect(outputBuffer).To(test_helpers.Say("never.io => 1234")) 169 170 Expect(outputBuffer).To(test_helpers.Say(colors.Bold("process3"))) 171 Expect(outputBuffer).To(test_helpers.Say(colors.Green("5/5"))) 172 Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("600"))) 173 Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("90"))) 174 Expect(outputBuffer).To(test_helpers.Say("allthetime.com => 1234, herewego.org => 1234")) 175 176 Expect(outputBuffer).To(test_helpers.Say(colors.Bold("process4"))) 177 Expect(outputBuffer).To(test_helpers.Say(colors.Green("0/0"))) 178 Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("10"))) 179 Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("10"))) 180 181 Expect(outputBuffer).To(test_helpers.Say(colors.Bold("Task GUID"))) 182 Expect(outputBuffer).To(test_helpers.Say(colors.Bold("Cell ID"))) 183 Expect(outputBuffer).To(test_helpers.Say(colors.Bold("Status"))) 184 Expect(outputBuffer).To(test_helpers.Say(colors.Bold("Result"))) 185 Expect(outputBuffer).To(test_helpers.Say(colors.Bold("Failure Reason"))) 186 187 Expect(outputBuffer).To(test_helpers.Say(colors.Bold("task-guid-1"))) 188 Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("cell-01"))) 189 Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("COMPLETED"))) 190 Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("Finished"))) 191 Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("N/A"))) 192 193 Expect(outputBuffer).To(test_helpers.Say(colors.Bold("task-guid-2"))) 194 Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("cell-02"))) 195 Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("COMPLETED"))) 196 Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("Finished"))) 197 Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("No compatible container"))) 198 199 Expect(outputBuffer).To(test_helpers.Say(colors.Bold("task-guid-3"))) 200 Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("N/A"))) 201 Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("COMPLETED"))) 202 Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("N/A"))) 203 Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("N/A"))) 204 }) 205 206 It("alerts the user if there are no apps or tasks", func() { 207 fakeAppExaminer.ListAppsReturns([]app_examiner.AppInfo{}, nil) 208 fakeTaskExaminer.ListTasksReturns([]task_examiner.TaskInfo{}, nil) 209 210 test_helpers.ExecuteCommandWithArgs(listAppsCommand, []string{}) 211 212 Expect(outputBuffer).To(test_helpers.SayLine("No apps to display.")) 213 Expect(outputBuffer).To(test_helpers.Say("No tasks to display.")) 214 }) 215 216 Context("when the app examiner returns an error", func() { 217 It("alerts the user fetching the app list returns an error", func() { 218 fakeAppExaminer.ListAppsReturns(nil, errors.New("The list was lost")) 219 listTasks := []task_examiner.TaskInfo{ 220 { 221 TaskGuid: "task-guid-1", 222 CellID: "cell-01", 223 Failed: false, 224 FailureReason: "", 225 Result: "Finished", 226 State: "COMPLETED", 227 }, 228 } 229 fakeTaskExaminer.ListTasksReturns(listTasks, nil) 230 231 test_helpers.ExecuteCommandWithArgs(listAppsCommand, []string{}) 232 233 Expect(outputBuffer).To(test_helpers.SayLine("Error listing apps: The list was lost")) 234 235 Expect(outputBuffer).To(test_helpers.Say(colors.Bold("Task GUID"))) 236 Expect(outputBuffer).To(test_helpers.Say(colors.Bold("Cell ID"))) 237 Expect(outputBuffer).To(test_helpers.Say(colors.Bold("Status"))) 238 Expect(outputBuffer).To(test_helpers.Say(colors.Bold("Result"))) 239 Expect(outputBuffer).To(test_helpers.Say(colors.Bold("Failure Reason"))) 240 241 Expect(outputBuffer).To(test_helpers.Say(colors.Bold("task-guid-1"))) 242 Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("cell-01"))) 243 Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("COMPLETED"))) 244 Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("Finished"))) 245 Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("N/A"))) 246 247 Expect(fakeExitHandler.ExitCalledWith).To(Equal([]int{exit_codes.CommandFailed})) 248 }) 249 250 It("alerts the user fetching the task list returns an error", func() { 251 listApps := []app_examiner.AppInfo{ 252 { 253 ProcessGuid: "process1", 254 DesiredInstances: 21, 255 ActualRunningInstances: 0, 256 DiskMB: 100, 257 MemoryMB: 50, 258 Ports: []uint16{54321}, 259 Routes: route_helpers.Routes{ 260 AppRoutes: route_helpers.AppRoutes{ 261 { 262 Hostnames: []string{"alldaylong.com"}, 263 Port: 54321, 264 }, 265 }, 266 }, 267 }, 268 } 269 fakeAppExaminer.ListAppsReturns(listApps, nil) 270 fakeTaskExaminer.ListTasksReturns(nil, errors.New("The list was lost")) 271 272 test_helpers.ExecuteCommandWithArgs(listAppsCommand, []string{}) 273 274 Expect(outputBuffer).To(test_helpers.Say(colors.Bold("App Name"))) 275 Expect(outputBuffer).To(test_helpers.Say(colors.Bold("Instances"))) 276 Expect(outputBuffer).To(test_helpers.Say(colors.Bold("DiskMB"))) 277 Expect(outputBuffer).To(test_helpers.Say(colors.Bold("MemoryMB"))) 278 Expect(outputBuffer).To(test_helpers.Say(colors.Bold("Route"))) 279 280 Expect(outputBuffer).To(test_helpers.Say(colors.Bold("process1"))) 281 Expect(outputBuffer).To(test_helpers.Say(colors.Red("0/21"))) 282 Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("100"))) 283 Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("50"))) 284 Expect(outputBuffer).To(test_helpers.Say("alldaylong.com => 54321")) 285 286 Expect(outputBuffer).To(test_helpers.SayLine("Error listing tasks: The list was lost")) 287 Expect(fakeExitHandler.ExitCalledWith).To(Equal([]int{exit_codes.CommandFailed})) 288 }) 289 }) 290 291 Context("when app has tcp routes", func() { 292 It("displays all tcp routes along with http routes", func() { 293 listApps := []app_examiner.AppInfo{ 294 { 295 ProcessGuid: "process1", 296 DesiredInstances: 21, 297 ActualRunningInstances: 0, 298 DiskMB: 100, 299 MemoryMB: 50, 300 Ports: []uint16{54321, 5222}, 301 Routes: route_helpers.Routes{ 302 AppRoutes: route_helpers.AppRoutes{ 303 {Hostnames: []string{"alldaylong.com"}, Port: 54321}, 304 }, 305 TcpRoutes: route_helpers.TcpRoutes{ 306 {ExternalPort: 51000, Port: 5222}, 307 }, 308 }, 309 }, 310 { 311 ProcessGuid: "process2", 312 DesiredInstances: 8, 313 ActualRunningInstances: 9, 314 DiskMB: 400, 315 MemoryMB: 30, 316 Ports: []uint16{1234, 7400}, 317 Routes: route_helpers.Routes{ 318 AppRoutes: route_helpers.AppRoutes{ 319 {Hostnames: []string{"never.io"}, Port: 1234}, 320 }, 321 TcpRoutes: route_helpers.TcpRoutes{ 322 {ExternalPort: 52000, Port: 7400}, 323 }, 324 }, 325 }, 326 { 327 ProcessGuid: "process3", 328 DesiredInstances: 5, 329 ActualRunningInstances: 5, 330 DiskMB: 600, 331 MemoryMB: 90, 332 Ports: []uint16{1234, 1883}, 333 Routes: route_helpers.Routes{ 334 AppRoutes: route_helpers.AppRoutes{ 335 {Hostnames: []string{"allthetime.com", "herewego.org"}, Port: 1234}, 336 }, 337 TcpRoutes: route_helpers.TcpRoutes{ 338 {ExternalPort: 53000, Port: 1883}, 339 }, 340 }, 341 }, 342 { 343 ProcessGuid: "process4", 344 DesiredInstances: 0, 345 ActualRunningInstances: 0, 346 DiskMB: 10, 347 MemoryMB: 10, 348 Routes: route_helpers.Routes{}, 349 }, 350 } 351 fakeAppExaminer.ListAppsReturns(listApps, nil) 352 353 test_helpers.ExecuteCommandWithArgs(listAppsCommand, []string{}) 354 355 Expect(outputBuffer).To(test_helpers.Say(colors.Bold("process1"))) 356 Expect(outputBuffer).To(test_helpers.Say("alldaylong.com => 54321, system.domain:51000 => 5222")) 357 358 Expect(outputBuffer).To(test_helpers.Say(colors.Bold("process2"))) 359 Expect(outputBuffer).To(test_helpers.Say("never.io => 1234, system.domain:52000 => 7400")) 360 361 Expect(outputBuffer).To(test_helpers.Say(colors.Bold("process3"))) 362 Expect(outputBuffer).To(test_helpers.Say("allthetime.com => 1234, herewego.org => 1234, system.domain:53000 => 1883")) 363 364 Expect(outputBuffer).To(test_helpers.Say(colors.Bold("process4"))) 365 }) 366 }) 367 368 Context("when app has routes with container ports not listed in ports section", func() { 369 It("displays all tcp routes along with http routes", func() { 370 listApps := []app_examiner.AppInfo{ 371 { 372 ProcessGuid: "process1", 373 DesiredInstances: 21, 374 ActualRunningInstances: 0, 375 DiskMB: 100, 376 MemoryMB: 50, 377 Ports: []uint16{54321}, 378 Routes: route_helpers.Routes{ 379 AppRoutes: route_helpers.AppRoutes{ 380 {Hostnames: []string{"alldaylong.com"}, Port: 54321}, 381 }, 382 TcpRoutes: route_helpers.TcpRoutes{ 383 {ExternalPort: 51000, Port: 5222}, 384 }, 385 }, 386 }, 387 { 388 ProcessGuid: "process2", 389 DesiredInstances: 8, 390 ActualRunningInstances: 9, 391 DiskMB: 400, 392 MemoryMB: 30, 393 Ports: []uint16{7400}, 394 Routes: route_helpers.Routes{ 395 AppRoutes: route_helpers.AppRoutes{ 396 {Hostnames: []string{"never.io"}, Port: 1234}, 397 }, 398 TcpRoutes: route_helpers.TcpRoutes{ 399 {ExternalPort: 52000, Port: 7400}, 400 }, 401 }, 402 }, 403 } 404 fakeAppExaminer.ListAppsReturns(listApps, nil) 405 406 test_helpers.ExecuteCommandWithArgs(listAppsCommand, []string{}) 407 408 Expect(outputBuffer).To(test_helpers.Say(colors.Bold("process1"))) 409 Expect(outputBuffer).To(test_helpers.Say("alldaylong.com => 54321")) 410 411 Expect(outputBuffer).To(test_helpers.Say(colors.Bold("process2"))) 412 Expect(outputBuffer).To(test_helpers.Say("system.domain:52000 => 7400")) 413 }) 414 }) 415 }) 416 417 Describe("VisualizeCommand", func() { 418 var visualizeCommand cli.Command 419 420 BeforeEach(func() { 421 commandFactory := command_factory.NewAppExaminerCommandFactory(fakeAppExaminer, terminalUI, fakeTerm, fakeClock, fakeExitHandler, fakeGraphicalVisualizer, fakeTaskExaminer, systemDomain) 422 visualizeCommand = commandFactory.MakeVisualizeCommand() 423 }) 424 425 It("displays a visualization of cells", func() { 426 listCells := []app_examiner.CellInfo{ 427 {CellID: "cell-1", RunningInstances: 3, ClaimedInstances: 2}, 428 {CellID: "cell-2", RunningInstances: 2, ClaimedInstances: 1}, 429 {CellID: "cell-3", RunningInstances: 0, ClaimedInstances: 0}, 430 } 431 fakeAppExaminer.ListCellsReturns(listCells, nil) 432 433 test_helpers.ExecuteCommandWithArgs(visualizeCommand, []string{}) 434 435 Expect(outputBuffer).To(test_helpers.SayLine(colors.Bold("Distribution"))) 436 Expect(outputBuffer).To(test_helpers.SayLine("cell-1: " + colors.Green("•••") + colors.Yellow("••") + cursor.ClearToEndOfLine())) 437 Expect(outputBuffer).To(test_helpers.SayLine("cell-2: " + colors.Green("••") + colors.Yellow("•") + cursor.ClearToEndOfLine())) 438 Expect(outputBuffer).To(test_helpers.SayLine("cell-3: " + colors.Red("empty") + cursor.ClearToEndOfLine())) 439 }) 440 441 Context("when the app examiner returns an error", func() { 442 It("alerts the user fetching the cells returns an error", func() { 443 fakeAppExaminer.ListCellsReturns(nil, errors.New("The list was lost")) 444 445 test_helpers.ExecuteCommandWithArgs(visualizeCommand, []string{}) 446 447 Expect(outputBuffer).To(test_helpers.Say("Error visualizing: The list was lost")) 448 Expect(outputBuffer).To(test_helpers.Say(cursor.ClearToEndOfLine())) 449 Expect(outputBuffer).To(test_helpers.SayNewLine()) 450 451 // TODO: this should return non-zero, but it's shared with refresh view 452 // which should continue to retry in the event on an error and not exit 453 }) 454 }) 455 456 Context("when a rate flag is provided", func() { 457 var closeChan chan struct{} 458 459 AfterEach(func() { 460 go fakeExitHandler.Exit(exit_codes.Signal) 461 Eventually(closeChan).Should(BeClosed()) 462 }) 463 464 Context("when term is set", func() { 465 var previousTerm string 466 467 BeforeEach(func() { 468 previousTerm = os.Getenv("TERM") 469 os.Setenv("TERM", "xterm") 470 471 }) 472 473 AfterEach(func() { 474 os.Setenv("TERM", previousTerm) 475 }) 476 477 It("dynamically displays the visualization", func() { 478 setNumberOfRunningInstances := func(count int) { 479 fakeAppExaminer.ListCellsReturns([]app_examiner.CellInfo{app_examiner.CellInfo{CellID: "cell-0", RunningInstances: count}, app_examiner.CellInfo{CellID: "cell-1", RunningInstances: count, Missing: true}}, nil) 480 } 481 setNumberOfRunningInstances(0) 482 483 closeChan = test_helpers.AsyncExecuteCommandWithArgs(visualizeCommand, []string{"--rate", "2s"}) 484 485 Eventually(outputBuffer).Should(test_helpers.SayLine("cell-0: " + colors.Red("empty") + cursor.ClearToEndOfLine())) 486 Eventually(outputBuffer).Should(test_helpers.SayLine("cell-1" + colors.Red("[MISSING]") + ": " + cursor.ClearToEndOfLine())) 487 488 setNumberOfRunningInstances(2) 489 490 fakeClock.IncrementBySeconds(1) 491 492 Consistently(outputBuffer).ShouldNot(test_helpers.Say("cell: \n")) // TODO: how would this happen 493 494 fakeClock.IncrementBySeconds(1) 495 496 Eventually(outputBuffer).Should(test_helpers.Say(cursor.Hide())) 497 Eventually(outputBuffer).Should(test_helpers.Say(cursor.Up(2))) 498 Eventually(outputBuffer).Should(test_helpers.SayLine("cell-0: " + colors.Green("••") + cursor.ClearToEndOfLine())) 499 Eventually(outputBuffer).Should(test_helpers.SayLine("cell-1" + colors.Red("[MISSING]") + ": " + colors.Green("••") + cursor.ClearToEndOfLine())) 500 Eventually(outputBuffer).Should(test_helpers.Say(cursor.ClearToEndOfDisplay())) 501 502 Consistently(closeChan).ShouldNot(BeClosed()) 503 }) 504 505 It("dynamically displays any errors", func() { 506 fakeAppExaminer.ListCellsReturns(nil, errors.New("Spilled the Paint")) 507 508 closeChan = test_helpers.AsyncExecuteCommandWithArgs(visualizeCommand, []string{"--rate", "1s"}) 509 510 Eventually(outputBuffer).Should(test_helpers.SayLine("Error visualizing: Spilled the Paint" + cursor.ClearToEndOfLine())) 511 512 fakeClock.IncrementBySeconds(1) 513 514 Eventually(outputBuffer).Should(test_helpers.Say(cursor.Up(1))) 515 Eventually(outputBuffer).Should(test_helpers.SayLine("Error visualizing: Spilled the Paint" + cursor.ClearToEndOfLine())) 516 Eventually(outputBuffer).Should(test_helpers.Say(cursor.ClearToEndOfDisplay())) 517 518 Consistently(closeChan).ShouldNot(BeClosed()) 519 }) 520 521 It("ensures the user's cursor is visible even if they interrupt ltc", func() { 522 closeChan = test_helpers.AsyncExecuteCommandWithArgs(visualizeCommand, []string{"--rate=1s"}) 523 524 Eventually(outputBuffer).Should(test_helpers.Say(cursor.Hide())) 525 fakeExitHandler.Exit(exit_codes.Signal) 526 Eventually(closeChan).Should(BeClosed()) 527 528 Expect(outputBuffer).To(test_helpers.Say(cursor.Show())) 529 Expect(fakeExitHandler.ExitCalledWith).To(Equal([]int{exit_codes.Signal})) 530 }) 531 }) 532 533 Context("when term is unset", func() { 534 var previousTerm string 535 536 BeforeEach(func() { 537 previousTerm = os.Getenv("TERM") 538 os.Unsetenv("TERM") 539 540 }) 541 542 AfterEach(func() { 543 os.Setenv("TERM", previousTerm) 544 }) 545 546 It("displays the visualization once", func() { 547 fakeAppExaminer.ListCellsReturns([]app_examiner.CellInfo{ 548 app_examiner.CellInfo{ 549 CellID: "cell-0", 550 RunningInstances: 2, 551 }, 552 app_examiner.CellInfo{ 553 CellID: "cell-1", 554 RunningInstances: 2, 555 Missing: true, 556 }, 557 }, nil) 558 559 closeChan = test_helpers.AsyncExecuteCommandWithArgs(visualizeCommand, []string{"--rate", "2s"}) 560 561 Eventually(outputBuffer).Should(test_helpers.SayLine("cell-0: ••")) 562 Eventually(outputBuffer).Should(test_helpers.SayLine("cell-1[MISSING]: ••")) 563 564 Expect(closeChan).To(BeClosed()) 565 }) 566 }) 567 }) 568 569 Context("when the graphical flag is passed", func() { 570 It("makes a successful call to the graphical visualizer and returns", func() { 571 test_helpers.ExecuteCommandWithArgs(visualizeCommand, []string{"--graphical"}) 572 573 Consistently(outputBuffer).ShouldNot(test_helpers.Say("Distribution")) 574 575 Expect(fakeGraphicalVisualizer.PrintDistributionChartCallCount()).To(Equal(1)) 576 Expect(fakeGraphicalVisualizer.PrintDistributionChartArgsForCall(0)).To(BeZero()) 577 }) 578 579 It("prints the error from an unsuccessful call to the graphical visualizer", func() { 580 fakeGraphicalVisualizer.PrintDistributionChartReturns(errors.New("errored")) 581 582 test_helpers.ExecuteCommandWithArgs(visualizeCommand, []string{"--graphical"}) 583 584 Consistently(outputBuffer).ShouldNot(test_helpers.Say("Distribution")) 585 Eventually(outputBuffer).Should(test_helpers.SayLine("Error Visualization: errored")) 586 587 Expect(fakeGraphicalVisualizer.PrintDistributionChartCallCount()).To(Equal(1)) 588 Expect(fakeExitHandler.ExitCalledWith).To(Equal([]int{exit_codes.CommandFailed})) 589 }) 590 591 Context("when the rate flag is also passed", func() { 592 It("sets the initial rate when calling the graphical visualizer", func() { 593 test_helpers.ExecuteCommandWithArgs(visualizeCommand, []string{"--graphical", "--rate=200ms"}) 594 595 Consistently(outputBuffer).ShouldNot(test_helpers.Say("Distribution")) 596 597 Expect(fakeGraphicalVisualizer.PrintDistributionChartCallCount()).To(Equal(1)) 598 duration, err := time.ParseDuration("200ms") 599 Expect(err).NotTo(HaveOccurred()) 600 Expect(fakeGraphicalVisualizer.PrintDistributionChartArgsForCall(0)).To(Equal(duration)) 601 }) 602 }) 603 }) 604 }) 605 606 Describe("StatusCommand", func() { 607 var ( 608 statusCommand cli.Command 609 sampleAppInfo app_examiner.AppInfo 610 ) 611 612 epochTime := int64(401120627) 613 roundTime := func(now, since time.Time) string { 614 uptime := (now.Sub(since) - (now.Sub(since) % time.Second)).String() 615 return uptime 616 } 617 618 BeforeEach(func() { 619 commandFactory := command_factory.NewAppExaminerCommandFactory(fakeAppExaminer, terminalUI, fakeTerm, fakeClock, fakeExitHandler, nil, fakeTaskExaminer, systemDomain) 620 statusCommand = commandFactory.MakeStatusCommand() 621 622 sampleAppInfo = app_examiner.AppInfo{ 623 ProcessGuid: "wompy-app", 624 RootFS: "preloaded:rootfs2", 625 DesiredInstances: 12, 626 ActualRunningInstances: 1, 627 EnvironmentVariables: []app_examiner.EnvironmentVariable{ 628 {Name: "WOMPY_APP_PASSWORD", Value: "seekreet pass"}, 629 {Name: "WOMPY_APP_USERNAME", Value: "mrbigglesworth54"}, 630 }, 631 StartTimeout: 600, 632 DiskMB: 2048, 633 MemoryMB: 256, 634 CPUWeight: 100, 635 Ports: []uint16{8887, 9000}, 636 Routes: route_helpers.Routes{ 637 AppRoutes: route_helpers.AppRoutes{ 638 {Hostnames: []string{"route-me.my-fun-domain.com"}, Port: 9000}, 639 {Hostnames: []string{"wompy-app.my-fun-domain.com", "cranky-app.my-fun-domain.com"}, Port: 8887}, 640 }, 641 }, 642 LogGuid: "a9s8dfa99023r", 643 LogSource: "wompy-app-logz", 644 Annotation: "I love this app. So wompy.", 645 ActualInstances: []app_examiner.InstanceInfo{ 646 app_examiner.InstanceInfo{ 647 InstanceGuid: "a0s9f-u9a8sf-aasdioasdjoi", 648 CellID: "cell-12", 649 Index: 3, 650 Ip: "10.85.12.100", 651 Ports: []app_examiner.PortMapping{ 652 {HostPort: 1234, ContainerPort: 3000}, 653 {HostPort: 5555, ContainerPort: 6666}, 654 }, 655 State: "RUNNING", 656 Since: epochTime * 1e9, 657 HasMetrics: true, 658 Metrics: app_examiner.InstanceMetrics{ 659 CpuPercentage: 23.45678, 660 MemoryBytes: 655360, 661 }, 662 }, 663 app_examiner.InstanceInfo{ 664 Index: 4, 665 State: "UNCLAIMED", 666 PlacementError: "insufficient resources.", 667 CrashCount: 2, 668 HasMetrics: false, 669 }, 670 app_examiner.InstanceInfo{ 671 Index: 5, 672 State: "CRASHED", 673 CrashCount: 7, 674 }, 675 }, 676 } 677 678 fakeTerm.GetWindowWidthReturns(9999, nil) 679 }) 680 681 It("emits a pretty representation of the DesiredLRP", func() { 682 fakeAppExaminer.AppStatusReturns(sampleAppInfo, nil) 683 684 test_helpers.ExecuteCommandWithArgs(statusCommand, []string{"wompy-app"}) 685 686 Expect(fakeAppExaminer.AppStatusCallCount()).To(Equal(1)) 687 Expect(fakeAppExaminer.AppStatusArgsForCall(0)).To(Equal("wompy-app")) 688 689 Expect(outputBuffer).To(test_helpers.Say("Instance")) 690 Expect(outputBuffer).To(test_helpers.Say("State")) 691 Expect(outputBuffer).To(test_helpers.Say("Crashes")) 692 Expect(outputBuffer).To(test_helpers.Say("CPU")) 693 Expect(outputBuffer).To(test_helpers.Say("Memory")) 694 Expect(outputBuffer).To(test_helpers.Say("Uptime")) 695 Expect(outputBuffer).To(test_helpers.SayNewLine()) 696 697 Expect(outputBuffer).To(test_helpers.Say("3")) 698 Expect(outputBuffer).To(test_helpers.Say("RUNNING")) 699 Expect(outputBuffer).To(test_helpers.Say("0")) 700 Expect(outputBuffer).To(test_helpers.Say("23.46%")) 701 Expect(outputBuffer).To(test_helpers.Say("640K")) 702 roundedTimeSince := roundTime(fakeClock.Now(), time.Unix(0, epochTime*1e9)) 703 Expect(outputBuffer).To(test_helpers.Say(roundedTimeSince)) 704 Expect(outputBuffer).To(test_helpers.SayNewLine()) 705 706 Expect(outputBuffer).To(test_helpers.Say("4")) 707 Expect(outputBuffer).To(test_helpers.Say("UNCLAIMED")) 708 Expect(outputBuffer).To(test_helpers.Say("2")) 709 Expect(outputBuffer).To(test_helpers.Say("N/A")) 710 Expect(outputBuffer).To(test_helpers.Say("N/A")) 711 Expect(outputBuffer).To(test_helpers.SayNewLine()) 712 713 Expect(outputBuffer).To(test_helpers.Say("5")) 714 Expect(outputBuffer).To(test_helpers.Say("CRASHED")) 715 Expect(outputBuffer).To(test_helpers.Say("7")) 716 Expect(outputBuffer).To(test_helpers.Say("N/A")) 717 Expect(outputBuffer).To(test_helpers.Say("N/A")) 718 Expect(outputBuffer).To(test_helpers.SayNewLine()) 719 }) 720 721 Context("when the --detailed flag is passed", func() { 722 It("prints the instance info in full mode", func() { 723 fakeAppExaminer.AppStatusReturns(sampleAppInfo, nil) 724 725 test_helpers.ExecuteCommandWithArgs(statusCommand, []string{"wompy-app", "--detailed"}) 726 727 Expect(fakeAppExaminer.AppStatusCallCount()).To(Equal(1)) 728 Expect(fakeAppExaminer.AppStatusArgsForCall(0)).To(Equal("wompy-app")) 729 730 Expect(outputBuffer).To(test_helpers.Say("wompy-app")) 731 732 Expect(outputBuffer).To(test_helpers.Say("Instances")) 733 Expect(outputBuffer).To(test_helpers.Say("1/12")) 734 Expect(outputBuffer).To(test_helpers.SayNewLine()) 735 736 Expect(outputBuffer).To(test_helpers.Say("Start Timeout")) 737 Expect(outputBuffer).To(test_helpers.SayLine("600")) 738 739 Expect(outputBuffer).To(test_helpers.Say("DiskMB")) 740 Expect(outputBuffer).To(test_helpers.SayLine("2048")) 741 742 Expect(outputBuffer).To(test_helpers.Say("MemoryMB")) 743 Expect(outputBuffer).To(test_helpers.SayLine("256")) 744 745 Expect(outputBuffer).To(test_helpers.Say("CPUWeight")) 746 Expect(outputBuffer).To(test_helpers.SayLine("100")) 747 748 Expect(outputBuffer).To(test_helpers.Say("Ports")) 749 Expect(outputBuffer).To(test_helpers.Say("8887")) 750 Expect(outputBuffer).To(test_helpers.Say("9000")) 751 Expect(outputBuffer).To(test_helpers.SayNewLine()) 752 753 Expect(outputBuffer).To(test_helpers.Say("Routes")) 754 Expect(outputBuffer).To(test_helpers.Say("wompy-app.my-fun-domain.com => 8887")) 755 Expect(outputBuffer).To(test_helpers.SayNewLine()) 756 Expect(outputBuffer).To(test_helpers.Say("cranky-app.my-fun-domain.com => 8887")) 757 Expect(outputBuffer).To(test_helpers.SayNewLine()) 758 Expect(outputBuffer).To(test_helpers.Say("route-me.my-fun-domain.com => 9000")) 759 Expect(outputBuffer).To(test_helpers.SayNewLine()) 760 761 Expect(outputBuffer).To(test_helpers.Say("Stack")) 762 Expect(outputBuffer).To(test_helpers.SayLine("rootfs2")) 763 764 Expect(outputBuffer).To(test_helpers.Say("Annotation")) 765 Expect(outputBuffer).To(test_helpers.SayLine("I love this app. So wompy.")) 766 767 Expect(outputBuffer).To(test_helpers.SayLine("Environment")) 768 Expect(outputBuffer).To(test_helpers.Say(`WOMPY_APP_PASSWORD="seekreet pass"`)) 769 Expect(outputBuffer).To(test_helpers.SayNewLine()) 770 Expect(outputBuffer).To(test_helpers.Say(`WOMPY_APP_USERNAME="mrbigglesworth54"`)) 771 Expect(outputBuffer).To(test_helpers.SayNewLine()) 772 773 Expect(outputBuffer).To(test_helpers.Say("Instance 3")) 774 Expect(outputBuffer).To(test_helpers.Say("RUNNING")) 775 Expect(outputBuffer).To(test_helpers.SayNewLine()) 776 777 Expect(outputBuffer).To(test_helpers.Say("InstanceGuid")) 778 Expect(outputBuffer).To(test_helpers.SayLine("a0s9f-u9a8sf-aasdioasdjoi")) 779 780 Expect(outputBuffer).To(test_helpers.Say("Cell ID")) 781 Expect(outputBuffer).To(test_helpers.SayLine("cell-12")) 782 783 Expect(outputBuffer).To(test_helpers.Say("Ip")) 784 Expect(outputBuffer).To(test_helpers.SayLine("10.85.12.100")) 785 786 Expect(outputBuffer).To(test_helpers.Say("Port Mapping")) 787 Expect(outputBuffer).To(test_helpers.Say("1234:3000")) 788 Expect(outputBuffer).To(test_helpers.Say("5555:6666")) 789 Expect(outputBuffer).To(test_helpers.SayNewLine()) 790 791 Expect(outputBuffer).To(test_helpers.Say("Uptime")) 792 roundedTimeSince := roundTime(fakeClock.Now(), time.Unix(0, epochTime*1e9)) 793 Expect(outputBuffer).To(test_helpers.Say(roundedTimeSince)) 794 Expect(outputBuffer).To(test_helpers.SayNewLine()) 795 796 Expect(outputBuffer).To(test_helpers.Say("Crash Count")) 797 Expect(outputBuffer).To(test_helpers.Say("0")) 798 Expect(outputBuffer).To(test_helpers.SayNewLine()) 799 800 Expect(outputBuffer).To(test_helpers.Say("CPU")) 801 Expect(outputBuffer).To(test_helpers.Say("23.46%")) 802 Expect(outputBuffer).To(test_helpers.SayNewLine()) 803 804 Expect(outputBuffer).To(test_helpers.Say("Memory")) 805 Expect(outputBuffer).To(test_helpers.Say("640K")) 806 Expect(outputBuffer).To(test_helpers.SayNewLine()) 807 808 Expect(outputBuffer).To(test_helpers.Say("Instance 4")) 809 Expect(outputBuffer).To(test_helpers.Say("UNCLAIMED")) 810 811 Expect(outputBuffer).NotTo(test_helpers.Say("InstanceGuid")) 812 Expect(outputBuffer).To(test_helpers.Say("Placement Error")) 813 Expect(outputBuffer).To(test_helpers.Say("insufficient resources.")) 814 Expect(outputBuffer).To(test_helpers.SayNewLine()) 815 816 Expect(outputBuffer).To(test_helpers.Say("Crash Count")) 817 Expect(outputBuffer).To(test_helpers.Say("2")) 818 Expect(outputBuffer).To(test_helpers.SayNewLine()) 819 820 Expect(outputBuffer).NotTo(test_helpers.Say("CPU")) 821 Expect(outputBuffer).NotTo(test_helpers.Say("Memory")) 822 823 Expect(outputBuffer).To(test_helpers.Say("Instance 5")) 824 Expect(outputBuffer).To(test_helpers.Say("CRASHED")) 825 Expect(outputBuffer).To(test_helpers.SayNewLine()) 826 827 Expect(outputBuffer).NotTo(test_helpers.Say("InstanceGuid")) 828 Expect(outputBuffer).To(test_helpers.Say("Crash Count")) 829 Expect(outputBuffer).To(test_helpers.Say("7")) 830 Expect(outputBuffer).To(test_helpers.SayNewLine()) 831 832 Expect(outputBuffer).NotTo(test_helpers.Say("CPU")) 833 Expect(outputBuffer).NotTo(test_helpers.Say("Memory")) 834 }) 835 }) 836 837 Context("when the app was launched from a docker image", func() { 838 It("emits a pretty representation of the image", func() { 839 sampleAppInfo.RootFS = "docker:///wompy/app#latest" 840 841 fakeAppExaminer.AppStatusReturns(sampleAppInfo, nil) 842 843 test_helpers.ExecuteCommandWithArgs(statusCommand, []string{"wompy-app"}) 844 845 Expect(fakeAppExaminer.AppStatusCallCount()).To(Equal(1)) 846 Expect(fakeAppExaminer.AppStatusArgsForCall(0)).To(Equal("wompy-app")) 847 848 Expect(outputBuffer).To(test_helpers.Say("Docker Image")) 849 Expect(outputBuffer).To(test_helpers.SayLine("wompy/app:latest")) 850 851 Expect(outputBuffer).To(test_helpers.Say("Annotation")) 852 }) 853 854 It("emits a pretty representation of the image without its library/ prefix", func() { 855 sampleAppInfo.RootFS = "docker:///library/wompy-app#latest" 856 857 fakeAppExaminer.AppStatusReturns(sampleAppInfo, nil) 858 859 test_helpers.ExecuteCommandWithArgs(statusCommand, []string{"wompy-app"}) 860 861 Expect(fakeAppExaminer.AppStatusCallCount()).To(Equal(1)) 862 Expect(fakeAppExaminer.AppStatusArgsForCall(0)).To(Equal("wompy-app")) 863 864 Expect(outputBuffer).To(test_helpers.Say("Docker Image")) 865 Expect(outputBuffer).NotTo(test_helpers.Say("library/")) 866 Expect(outputBuffer).To(test_helpers.SayLine("wompy-app:latest")) 867 868 Expect(outputBuffer).To(test_helpers.Say("Annotation")) 869 }) 870 871 It("emits a pretty representation of the image with source", func() { 872 sampleAppInfo.RootFS = "docker://dhub/library/wompy-app#latest" 873 874 fakeAppExaminer.AppStatusReturns(sampleAppInfo, nil) 875 876 test_helpers.ExecuteCommandWithArgs(statusCommand, []string{"wompy-app"}) 877 878 Expect(fakeAppExaminer.AppStatusCallCount()).To(Equal(1)) 879 Expect(fakeAppExaminer.AppStatusArgsForCall(0)).To(Equal("wompy-app")) 880 881 Expect(outputBuffer).To(test_helpers.Say("Docker Image")) 882 Expect(outputBuffer).To(test_helpers.SayLine("dhub/library/wompy-app:latest")) 883 884 Expect(outputBuffer).To(test_helpers.Say("Annotation")) 885 }) 886 }) 887 888 Describe("Monitors", func() { 889 It("prints out Monitor None when there is no monitor", func() { 890 sampleAppInfo.Monitor = app_examiner.Monitor{} 891 892 fakeAppExaminer.AppStatusReturns(sampleAppInfo, nil) 893 894 test_helpers.ExecuteCommandWithArgs(statusCommand, []string{"wompy-app"}) 895 896 Expect(outputBuffer).To(test_helpers.Say("Monitor")) 897 Expect(outputBuffer).To(test_helpers.SayLine("None")) 898 }) 899 900 It("prints out Monitor Port when there is port-only monitor", func() { 901 sampleAppInfo.Monitor = app_examiner.Monitor{ 902 Port: 1234, 903 } 904 905 fakeAppExaminer.AppStatusReturns(sampleAppInfo, nil) 906 907 test_helpers.ExecuteCommandWithArgs(statusCommand, []string{"wompy-app"}) 908 909 Expect(outputBuffer).To(test_helpers.Say("Monitor")) 910 Expect(outputBuffer).To(test_helpers.SayLine("Port (1234)")) 911 }) 912 913 It("prints out Monitor URL when there is a port- and uri- monitor", func() { 914 sampleAppInfo.Monitor = app_examiner.Monitor{ 915 Port: 1234, 916 URI: "/check", 917 } 918 919 fakeAppExaminer.AppStatusReturns(sampleAppInfo, nil) 920 921 test_helpers.ExecuteCommandWithArgs(statusCommand, []string{"wompy-app"}) 922 923 Expect(outputBuffer).To(test_helpers.Say("Monitor")) 924 Expect(outputBuffer).To(test_helpers.SayLine("URL (1234:/check)")) 925 }) 926 927 It("prints out Monitor Custom for other kinds of monitors", func() { 928 sampleAppInfo.Monitor = app_examiner.Monitor{ 929 Command: "/bin/sh", 930 CommandArgs: []string{"-c", "healthcheck", "-p"}, 931 } 932 933 fakeAppExaminer.AppStatusReturns(sampleAppInfo, nil) 934 935 test_helpers.ExecuteCommandWithArgs(statusCommand, []string{"wompy-app"}) 936 937 Expect(outputBuffer).To(test_helpers.Say("Monitor")) 938 Expect(outputBuffer).To(test_helpers.SayLine("Command (/bin/sh -c healthcheck -p)")) 939 }) 940 }) 941 942 It("prints out an unknown rootfs without parsing", func() { 943 sampleAppInfo.RootFS = "wuuuhhhhh" 944 945 fakeAppExaminer.AppStatusReturns(sampleAppInfo, nil) 946 947 test_helpers.ExecuteCommandWithArgs(statusCommand, []string{"wompy-app"}) 948 949 Expect(fakeAppExaminer.AppStatusCallCount()).To(Equal(1)) 950 Expect(fakeAppExaminer.AppStatusArgsForCall(0)).To(Equal("wompy-app")) 951 952 Expect(outputBuffer).To(test_helpers.Say("Routes")) 953 Expect(outputBuffer).To(test_helpers.Say("wompy-app.my-fun-domain.com => 8887")) 954 Expect(outputBuffer).To(test_helpers.SayNewLine()) 955 Expect(outputBuffer).To(test_helpers.Say("cranky-app.my-fun-domain.com => 8887")) 956 Expect(outputBuffer).To(test_helpers.SayNewLine()) 957 Expect(outputBuffer).To(test_helpers.Say("route-me.my-fun-domain.com => 9000")) 958 Expect(outputBuffer).To(test_helpers.SayNewLine()) 959 960 Expect(outputBuffer).To(test_helpers.Say("RootFS")) 961 Expect(outputBuffer).To(test_helpers.SayLine("wuuuhhhhh")) 962 963 Expect(outputBuffer).To(test_helpers.Say("Annotation")) 964 Expect(outputBuffer).To(test_helpers.SayLine("I love this app. So wompy.")) 965 }) 966 967 Context("when there are only tcp routes", func() { 968 BeforeEach(func() { 969 sampleAppInfo.Routes = route_helpers.Routes{ 970 TcpRoutes: route_helpers.TcpRoutes{ 971 {ExternalPort: 52000, Port: 9000}, 972 {ExternalPort: 51000, Port: 8887}, 973 }, 974 } 975 }) 976 977 It("emits a pretty representation of the DesiredLRP", func() { 978 fakeAppExaminer.AppStatusReturns(sampleAppInfo, nil) 979 980 test_helpers.ExecuteCommandWithArgs(statusCommand, []string{"wompy-app"}) 981 982 Expect(fakeAppExaminer.AppStatusCallCount()).To(Equal(1)) 983 Expect(fakeAppExaminer.AppStatusArgsForCall(0)).To(Equal("wompy-app")) 984 985 Expect(outputBuffer).To(test_helpers.Say("Ports")) 986 Expect(outputBuffer).To(test_helpers.Say("8887")) 987 Expect(outputBuffer).To(test_helpers.Say("9000")) 988 Expect(outputBuffer).To(test_helpers.SayNewLine()) 989 990 Expect(outputBuffer).To(test_helpers.Say("Routes")) 991 Expect(outputBuffer).To(test_helpers.Say("system.domain:51000 => 8887")) 992 Expect(outputBuffer).To(test_helpers.SayNewLine()) 993 Expect(outputBuffer).To(test_helpers.Say("system.domain:52000 => 9000")) 994 Expect(outputBuffer).To(test_helpers.SayNewLine()) 995 }) 996 }) 997 998 Context("when there are both http and tcp routes", func() { 999 BeforeEach(func() { 1000 sampleAppInfo.Routes = route_helpers.Routes{ 1001 TcpRoutes: route_helpers.TcpRoutes{ 1002 {ExternalPort: 51000, Port: 8887}, 1003 }, 1004 AppRoutes: route_helpers.AppRoutes{ 1005 {Hostnames: []string{"route-me.my-fun-domain.com"}, Port: 9000}, 1006 }, 1007 } 1008 }) 1009 1010 It("emits a pretty representation of the DesiredLRP", func() { 1011 fakeAppExaminer.AppStatusReturns(sampleAppInfo, nil) 1012 1013 test_helpers.ExecuteCommandWithArgs(statusCommand, []string{"wompy-app"}) 1014 1015 Expect(fakeAppExaminer.AppStatusCallCount()).To(Equal(1)) 1016 Expect(fakeAppExaminer.AppStatusArgsForCall(0)).To(Equal("wompy-app")) 1017 1018 Expect(outputBuffer).To(test_helpers.Say("Ports")) 1019 Expect(outputBuffer).To(test_helpers.Say("8887")) 1020 Expect(outputBuffer).To(test_helpers.Say("9000")) 1021 Expect(outputBuffer).To(test_helpers.SayNewLine()) 1022 1023 Expect(outputBuffer).To(test_helpers.Say("Routes")) 1024 Expect(outputBuffer).To(test_helpers.Say("system.domain:51000 => 8887")) 1025 Expect(outputBuffer).To(test_helpers.SayNewLine()) 1026 Expect(outputBuffer).To(test_helpers.Say("route-me.my-fun-domain.com => 9000")) 1027 Expect(outputBuffer).To(test_helpers.SayNewLine()) 1028 }) 1029 }) 1030 1031 Context("when there is a placement error on an actualLRP", func() { 1032 It("Displays UNCLAIMED in red, and outputs only the placement error", func() { 1033 fakeAppExaminer.AppStatusReturns( 1034 app_examiner.AppInfo{ 1035 ActualInstances: []app_examiner.InstanceInfo{ 1036 { 1037 Index: 7, 1038 State: "UNCLAIMED", 1039 PlacementError: "insufficient resources.", 1040 }, 1041 }, 1042 }, nil) 1043 1044 test_helpers.ExecuteCommandWithArgs(statusCommand, []string{"swanky-app", "--detailed"}) 1045 1046 Expect(outputBuffer).To(test_helpers.Say("Instance 7")) 1047 Expect(outputBuffer).To(test_helpers.Say("UNCLAIMED")) 1048 Expect(outputBuffer).To(test_helpers.SayNewLine()) 1049 1050 Expect(outputBuffer).NotTo(test_helpers.Say("InstanceGuid")) 1051 Expect(outputBuffer).NotTo(test_helpers.Say("Cell ID")) 1052 Expect(outputBuffer).NotTo(test_helpers.Say("Ip")) 1053 Expect(outputBuffer).NotTo(test_helpers.Say("Port Mapping")) 1054 Expect(outputBuffer).NotTo(test_helpers.Say("Uptime")) 1055 1056 Expect(outputBuffer).To(test_helpers.Say("Placement Error")) 1057 Expect(outputBuffer).To(test_helpers.Say("insufficient resources.")) 1058 Expect(outputBuffer).To(test_helpers.SayNewLine()) 1059 }) 1060 }) 1061 1062 Context("when a rate flag is passed", func() { 1063 var closeChan chan struct{} 1064 1065 AfterEach(func() { 1066 go fakeExitHandler.Exit(exit_codes.Signal) 1067 Eventually(closeChan).Should(BeClosed()) 1068 1069 _, err := fmt.Print(cursor.Show()) 1070 Expect(err).NotTo(HaveOccurred()) 1071 }) 1072 1073 Context("when term is set", func() { 1074 var previousTerm string 1075 1076 BeforeEach(func() { 1077 previousTerm = os.Getenv("TERM") 1078 os.Setenv("TERM", "xterm") 1079 1080 }) 1081 1082 AfterEach(func() { 1083 os.Setenv("TERM", previousTerm) 1084 }) 1085 1086 It("refreshes for the designated time", func() { 1087 fakeAppExaminer.AppStatusReturns(sampleAppInfo, nil) 1088 1089 closeChan = test_helpers.AsyncExecuteCommandWithArgs(statusCommand, []string{"wompy-app", "--rate", "2s"}) 1090 1091 Consistently(closeChan).ShouldNot(BeClosed()) 1092 Eventually(outputBuffer).Should(test_helpers.Say("wompy-app")) 1093 Expect(outputBuffer).To(test_helpers.SayNewLine()) 1094 1095 roundedTimeSince := roundTime(fakeClock.Now(), time.Unix(0, epochTime*1e9)) 1096 Expect(outputBuffer).To(test_helpers.Say(roundedTimeSince)) 1097 Expect(outputBuffer).To(test_helpers.SayNewLine()) 1098 1099 fakeClock.IncrementBySeconds(1) 1100 1101 Consistently(outputBuffer).ShouldNot(test_helpers.Say("wompy-app")) 1102 1103 refreshTime := int64(405234567) 1104 refreshAppInfo := app_examiner.AppInfo{ 1105 ProcessGuid: "wompy-app", 1106 DesiredInstances: 1, 1107 ActualRunningInstances: 1, 1108 ActualInstances: []app_examiner.InstanceInfo{ 1109 { 1110 InstanceGuid: "a0s9f-u9a8sf-aasdioasdjoi", 1111 Index: 1, 1112 State: "RUNNING", 1113 Since: refreshTime * 1e9, 1114 }, 1115 }, 1116 EnvironmentVariables: []app_examiner.EnvironmentVariable{ 1117 { 1118 Name: "VCAP_APPLICATION", 1119 Value: `{"application_name":"latty","application_uris":["latty.192.168.11.11.xip.io","latty-8080.192.168.11.11.xip.io"],"name":"latty","uris":["latty.192.168.11.11.xip.io","latty-8080.192.168.11.11.xip.io"],"limits":{"mem":128}}`, 1120 }, 1121 }, 1122 } 1123 1124 fakeAppExaminer.AppStatusReturns(refreshAppInfo, nil) 1125 1126 Eventually(outputBuffer).Should(test_helpers.Say(cursor.Hide())) 1127 1128 fakeClock.IncrementBySeconds(1) 1129 1130 Eventually(outputBuffer).Should(test_helpers.Say(cursor.Up(27))) 1131 Eventually(outputBuffer).Should(test_helpers.Say("wompy-app")) 1132 Eventually(outputBuffer).Should(test_helpers.SayNewLine()) 1133 roundedTimeSince = roundTime(fakeClock.Now(), time.Unix(0, refreshTime*1e9)) 1134 Eventually(outputBuffer).Should(test_helpers.Say(roundedTimeSince)) 1135 1136 Consistently(closeChan).ShouldNot(BeClosed()) 1137 }) 1138 1139 It("calculates the correct height based on terminal width", func() { 1140 fakeTerm.GetWindowWidthReturns(200, nil) 1141 1142 refreshTime := int64(405234567) 1143 wrappedAppInfo := app_examiner.AppInfo{ 1144 DesiredInstances: 1, 1145 ActualRunningInstances: 1, 1146 ActualInstances: []app_examiner.InstanceInfo{ 1147 { 1148 InstanceGuid: "a0s9f-u9a8sf-aasdioasdjoi", 1149 Index: 1, 1150 State: "RUNNING", 1151 Since: refreshTime * 1e9, 1152 }, 1153 }, 1154 EnvironmentVariables: []app_examiner.EnvironmentVariable{ 1155 { 1156 Name: "VCAP_APPLICATION", 1157 Value: `{"application_name":"latty","application_uris":["latty.192.168.11.11.xip.io","latty-8080.192.168.11.11.xip.io"],"name":"latty","uris":["latty.192.168.11.11.xip.io","latty-8080.192.168.11.11.xip.io"],"limits":{"mem":128}}`, 1158 }, 1159 }, 1160 } 1161 1162 fakeAppExaminer.AppStatusReturns(wrappedAppInfo, nil) 1163 1164 closeChan = test_helpers.AsyncExecuteCommandWithArgs(statusCommand, []string{"wompy-app", "--rate", "2s"}) 1165 1166 Eventually(outputBuffer).Should(test_helpers.Say(cursor.Hide())) 1167 1168 fakeClock.IncrementBySeconds(3) 1169 1170 Eventually(outputBuffer).Should(test_helpers.Say(cursor.Up(20))) 1171 1172 Consistently(closeChan).ShouldNot(BeClosed()) 1173 }) 1174 1175 It("dynamically displays any errors", func() { 1176 fakeAppExaminer.AppStatusReturns(sampleAppInfo, nil) 1177 1178 closeChan = test_helpers.AsyncExecuteCommandWithArgs(statusCommand, []string{"wompy-app", "--rate", "1s"}) 1179 1180 Eventually(outputBuffer).Should(test_helpers.Say("wompy-app")) 1181 Expect(outputBuffer).NotTo(test_helpers.Say("Error getting status")) 1182 1183 fakeAppExaminer.AppStatusReturns(app_examiner.AppInfo{}, errors.New("error fetching status")) 1184 fakeClock.IncrementBySeconds(1) 1185 Eventually(closeChan).Should(BeClosed()) 1186 1187 Expect(outputBuffer).NotTo(test_helpers.Say(TerminalEsc + "\\d+A")) 1188 Expect(outputBuffer).To(test_helpers.Say("Error getting status: error fetching status")) 1189 Expect(outputBuffer).To(test_helpers.Say(cursor.Show())) 1190 }) 1191 1192 Context("when the user interrupts ltc status with ctrl-c", func() { 1193 var previousTerm string 1194 1195 BeforeEach(func() { 1196 previousTerm = os.Getenv("TERM") 1197 Expect(os.Setenv("TERM", "xterm")).To(Succeed()) 1198 }) 1199 1200 AfterEach(func() { 1201 Expect(os.Setenv("TERM", previousTerm)).To(Succeed()) 1202 }) 1203 1204 It("ensures the user's cursor is still visible", func() { 1205 closeChan = test_helpers.AsyncExecuteCommandWithArgs(statusCommand, []string{"wompy-app", "--rate=1s"}) 1206 1207 Eventually(outputBuffer).Should(test_helpers.Say(cursor.Hide())) 1208 fakeExitHandler.Exit(exit_codes.Signal) 1209 Eventually(closeChan).Should(BeClosed()) 1210 1211 Expect(outputBuffer).To(test_helpers.Say(cursor.Show())) 1212 Expect(fakeExitHandler.ExitCalledWith).To(Equal([]int{exit_codes.Signal})) 1213 }) 1214 }) 1215 1216 Context("when the --detailed flag is also passed", func() { 1217 It("prints a warning message", func() { 1218 fakeAppExaminer.AppStatusReturns(sampleAppInfo, nil) 1219 1220 closeChan = test_helpers.AsyncExecuteCommandWithArgs(statusCommand, []string{"wompy-app", "--rate", "2s", "--detailed"}) 1221 1222 Consistently(closeChan).ShouldNot(BeClosed()) 1223 Eventually(outputBuffer).Should(test_helpers.SayLine("WARNING: flags '--detailed' and '--rate' are incompatible.")) 1224 }) 1225 1226 It("continues as normal with summary output", func() { 1227 fakeAppExaminer.AppStatusReturns(sampleAppInfo, nil) 1228 1229 closeChan = test_helpers.AsyncExecuteCommandWithArgs(statusCommand, []string{"wompy-app", "--rate", "2s", "--detailed"}) 1230 1231 Consistently(closeChan).ShouldNot(BeClosed()) 1232 Eventually(outputBuffer).Should(test_helpers.Say("wompy-app")) 1233 Expect(outputBuffer).To(test_helpers.SayNewLine()) 1234 1235 roundedTimeSince := roundTime(fakeClock.Now(), time.Unix(0, epochTime*1e9)) 1236 Expect(outputBuffer).To(test_helpers.Say(roundedTimeSince)) 1237 Expect(outputBuffer).To(test_helpers.SayNewLine()) 1238 1239 fakeClock.IncrementBySeconds(1) 1240 1241 Consistently(outputBuffer).ShouldNot(test_helpers.Say("wompy-app")) 1242 1243 refreshTime := int64(405234567) 1244 refreshAppInfo := app_examiner.AppInfo{ 1245 ProcessGuid: "wompy-app", 1246 DesiredInstances: 1, 1247 ActualRunningInstances: 1, 1248 ActualInstances: []app_examiner.InstanceInfo{ 1249 { 1250 InstanceGuid: "a0s9f-u9a8sf-aasdioasdjoi", 1251 Index: 1, 1252 State: "RUNNING", 1253 Since: refreshTime * 1e9, 1254 }, 1255 }, 1256 EnvironmentVariables: []app_examiner.EnvironmentVariable{ 1257 { 1258 Name: "VCAP_APPLICATION", 1259 Value: `{"application_name":"latty","application_uris":["latty.192.168.11.11.xip.io","latty-8080.192.168.11.11.xip.io"],"name":"latty","uris":["latty.192.168.11.11.xip.io","latty-8080.192.168.11.11.xip.io"],"limits":{"mem":128}}`, 1260 }, 1261 }, 1262 } 1263 1264 fakeAppExaminer.AppStatusReturns(refreshAppInfo, nil) 1265 1266 Eventually(outputBuffer).Should(test_helpers.Say(cursor.Hide())) 1267 1268 fakeClock.IncrementBySeconds(1) 1269 1270 Eventually(outputBuffer).Should(test_helpers.Say(cursor.Up(27))) 1271 Eventually(outputBuffer).Should(test_helpers.Say("wompy-app")) 1272 Eventually(outputBuffer).Should(test_helpers.SayNewLine()) 1273 roundedTimeSince = roundTime(fakeClock.Now(), time.Unix(0, refreshTime*1e9)) 1274 Eventually(outputBuffer).Should(test_helpers.Say(roundedTimeSince)) 1275 1276 Consistently(closeChan).ShouldNot(BeClosed()) 1277 }) 1278 }) 1279 1280 Context("when term is unset", func() { 1281 var previousTerm string 1282 1283 BeforeEach(func() { 1284 previousTerm = os.Getenv("TERM") 1285 os.Unsetenv("TERM") 1286 1287 }) 1288 1289 AfterEach(func() { 1290 os.Setenv("TERM", previousTerm) 1291 }) 1292 1293 It("prints once", func() { 1294 fakeAppExaminer.AppStatusReturns(sampleAppInfo, nil) 1295 1296 closeChan = test_helpers.AsyncExecuteCommandWithArgs(statusCommand, []string{"wompy-app", "--rate", "2s"}) 1297 1298 Eventually(outputBuffer).Should(test_helpers.Say("wompy-app")) 1299 Expect(outputBuffer).To(test_helpers.SayNewLine()) 1300 1301 roundedTimeSince := roundTime(fakeClock.Now(), time.Unix(0, epochTime*1e9)) 1302 Expect(outputBuffer).To(test_helpers.Say(roundedTimeSince)) 1303 Expect(outputBuffer).To(test_helpers.SayNewLine()) 1304 1305 Expect(closeChan).To(BeClosed()) 1306 }) 1307 }) 1308 }) 1309 }) 1310 1311 Context("when annotation is empty", func() { 1312 It("omits annotation from the output", func() { 1313 fakeAppExaminer.AppStatusReturns(app_examiner.AppInfo{ProcessGuid: "jumpy-app"}, nil) 1314 1315 test_helpers.ExecuteCommandWithArgs(statusCommand, []string{"jumpy-app"}) 1316 1317 Expect(outputBuffer).NotTo(test_helpers.Say("Annotation")) 1318 }) 1319 }) 1320 1321 Context("when no app name is specified", func() { 1322 It("prints usage information", func() { 1323 test_helpers.ExecuteCommandWithArgs(statusCommand, []string{}) 1324 1325 Expect(outputBuffer).To(test_helpers.SayIncorrectUsage()) 1326 Expect(fakeExitHandler.ExitCalledWith).To(Equal([]int{exit_codes.InvalidSyntax})) 1327 }) 1328 }) 1329 1330 It("prints any errors from app examiner", func() { 1331 fakeAppExaminer.AppStatusReturns(app_examiner.AppInfo{}, errors.New("You want the status?? ...YOU CAN'T HANDLE THE STATUS!!!")) 1332 1333 test_helpers.ExecuteCommandWithArgs(statusCommand, []string{"zany-app"}) 1334 1335 Expect(outputBuffer).To(test_helpers.SayLine("You want the status?? ...YOU CAN'T HANDLE THE STATUS!!!")) 1336 Expect(fakeExitHandler.ExitCalledWith).To(Equal([]int{exit_codes.CommandFailed})) 1337 }) 1338 }) 1339 1340 Describe("Cells", func() { 1341 var cellsCommand cli.Command 1342 1343 BeforeEach(func() { 1344 commandFactory := command_factory.NewAppExaminerCommandFactory(fakeAppExaminer, terminalUI, fakeTerm, fakeClock, fakeExitHandler, nil, fakeTaskExaminer, systemDomain) 1345 cellsCommand = commandFactory.MakeCellsCommand() 1346 }) 1347 1348 It("lists the cells", func() { 1349 fakeAppExaminer.ListCellsReturns([]app_examiner.CellInfo{ 1350 { 1351 CellID: "cell-one", 1352 RunningInstances: 37, 1353 ClaimedInstances: 12, 1354 Zone: "z1", 1355 MemoryMB: 1229, 1356 DiskMB: 4301, 1357 Containers: 256, 1358 }, 1359 { 1360 CellID: "cell-two", 1361 }, 1362 }, nil) 1363 1364 test_helpers.ExecuteCommandWithArgs(cellsCommand, []string{}) 1365 1366 Expect(outputBuffer).To(test_helpers.Say("Cells")) 1367 Expect(outputBuffer).To(test_helpers.Say("Zone")) 1368 Expect(outputBuffer).To(test_helpers.Say("Memory")) 1369 Expect(outputBuffer).To(test_helpers.Say("Disk")) 1370 Expect(outputBuffer).To(test_helpers.Say("Apps")) 1371 Expect(outputBuffer).To(test_helpers.SayNewLine()) 1372 1373 Expect(outputBuffer).To(test_helpers.Say("cell-one")) 1374 Expect(outputBuffer).To(test_helpers.Say("z1")) 1375 Expect(outputBuffer).To(test_helpers.Say("1229M")) 1376 Expect(outputBuffer).To(test_helpers.Say("4301M")) 1377 Expect(outputBuffer).To(test_helpers.Say("37/12")) 1378 Expect(outputBuffer).To(test_helpers.SayNewLine()) 1379 1380 Expect(outputBuffer).To(test_helpers.Say("cell-two")) 1381 Expect(outputBuffer).To(test_helpers.SayNewLine()) 1382 1383 Expect(fakeAppExaminer.ListCellsCallCount()).To(Equal(1)) 1384 }) 1385 1386 Context("when the receptor returns an error", func() { 1387 It("prints an error", func() { 1388 fakeAppExaminer.ListCellsReturns(nil, errors.New("these are not the cells you're looking for")) 1389 1390 test_helpers.ExecuteCommandWithArgs(cellsCommand, []string{}) 1391 1392 Expect(outputBuffer).To(test_helpers.SayLine("these are not the cells you're looking for")) 1393 Expect(outputBuffer).NotTo(test_helpers.Say("Cells")) 1394 1395 Expect(fakeExitHandler.ExitCalledWith).To(Equal([]int{exit_codes.CommandFailed})) 1396 }) 1397 }) 1398 }) 1399 })