github.com/orange-cloudfoundry/cli@v7.1.0+incompatible/command/v7/shared/app_summary_displayer_test.go (about) 1 package shared_test 2 3 import ( 4 "time" 5 6 "code.cloudfoundry.org/cli/actor/v7action" 7 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant" 8 . "code.cloudfoundry.org/cli/command/v7/shared" 9 "code.cloudfoundry.org/cli/integration/helpers" 10 "code.cloudfoundry.org/cli/resources" 11 "code.cloudfoundry.org/cli/types" 12 "code.cloudfoundry.org/cli/util/ui" 13 . "github.com/onsi/ginkgo" 14 . "github.com/onsi/gomega" 15 . "github.com/onsi/gomega/gbytes" 16 ) 17 18 var _ = Describe("app summary displayer", func() { 19 var ( 20 appSummaryDisplayer *AppSummaryDisplayer 21 output *Buffer 22 testUI *ui.UI 23 ) 24 25 BeforeEach(func() { 26 output = NewBuffer() 27 testUI = ui.NewTestUI(nil, output, NewBuffer()) 28 29 appSummaryDisplayer = NewAppSummaryDisplayer(testUI) 30 }) 31 32 Describe("AppDisplay", func() { 33 var ( 34 summary v7action.DetailedApplicationSummary 35 displayStartCommand bool 36 ) 37 38 JustBeforeEach(func() { 39 appSummaryDisplayer.AppDisplay(summary, displayStartCommand) 40 }) 41 42 When("the app has instances", func() { 43 When("the process instances are running", func() { 44 var uptime time.Duration 45 46 BeforeEach(func() { 47 uptime = time.Since(time.Unix(267321600, 0)) 48 summary = v7action.DetailedApplicationSummary{ 49 ApplicationSummary: v7action.ApplicationSummary{ 50 Application: resources.Application{ 51 GUID: "some-app-guid", 52 State: constant.ApplicationStarted, 53 }, 54 ProcessSummaries: v7action.ProcessSummaries{ 55 { 56 Process: v7action.Process{ 57 Type: constant.ProcessTypeWeb, 58 MemoryInMB: types.NullUint64{Value: 32, IsSet: true}, 59 DiskInMB: types.NullUint64{Value: 1024, IsSet: true}, 60 }, 61 Sidecars: []v7action.Sidecar{}, 62 InstanceDetails: []v7action.ProcessInstance{ 63 v7action.ProcessInstance{ 64 Index: 0, 65 State: constant.ProcessInstanceRunning, 66 MemoryUsage: 1000000, 67 DiskUsage: 1000000, 68 MemoryQuota: 33554432, 69 DiskQuota: 2000000, 70 Uptime: uptime, 71 Details: "Some Details 1", 72 }, 73 v7action.ProcessInstance{ 74 Index: 1, 75 State: constant.ProcessInstanceRunning, 76 MemoryUsage: 2000000, 77 DiskUsage: 2000000, 78 MemoryQuota: 33554432, 79 DiskQuota: 4000000, 80 Uptime: time.Since(time.Unix(330480000, 0)), 81 Details: "Some Details 2", 82 }, 83 v7action.ProcessInstance{ 84 Index: 2, 85 State: constant.ProcessInstanceRunning, 86 MemoryUsage: 3000000, 87 DiskUsage: 3000000, 88 MemoryQuota: 33554432, 89 DiskQuota: 6000000, 90 Uptime: time.Since(time.Unix(1277164800, 0)), 91 }, 92 }, 93 }, 94 { 95 Process: v7action.Process{ 96 Type: "console", 97 MemoryInMB: types.NullUint64{Value: 16, IsSet: true}, 98 DiskInMB: types.NullUint64{Value: 512, IsSet: true}, 99 }, 100 Sidecars: []v7action.Sidecar{}, 101 InstanceDetails: []v7action.ProcessInstance{ 102 v7action.ProcessInstance{ 103 Index: 0, 104 State: constant.ProcessInstanceRunning, 105 MemoryUsage: 1000000, 106 DiskUsage: 1000000, 107 MemoryQuota: 33554432, 108 DiskQuota: 8000000, 109 Uptime: time.Since(time.Unix(167572800, 0)), 110 }, 111 }, 112 }, 113 }, 114 }, 115 } 116 }) 117 118 It("lists information for each of the processes", func() { 119 processTable := helpers.ParseV3AppProcessTable(output.Contents()) 120 Expect(len(processTable.Processes)).To(Equal(2)) 121 122 webProcessSummary := processTable.Processes[0] 123 Expect(webProcessSummary.Type).To(Equal("web")) 124 Expect(webProcessSummary.Sidecars).To(Equal("")) 125 Expect(webProcessSummary.InstanceCount).To(Equal("3/3")) 126 Expect(webProcessSummary.MemUsage).To(Equal("32M")) 127 128 Expect(webProcessSummary.Instances[0].Memory).To(Equal("976.6K of 32M")) 129 Expect(webProcessSummary.Instances[0].Since).To(MatchRegexp(`\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z`)) 130 Expect(time.Parse(time.RFC3339, webProcessSummary.Instances[0].Since)).To(BeTemporally("~", time.Now().Add(-uptime), 2*time.Second)) 131 Expect(webProcessSummary.Instances[0].Disk).To(Equal("976.6K of 1.9M")) 132 Expect(webProcessSummary.Instances[0].CPU).To(Equal("0.0%")) 133 Expect(webProcessSummary.Instances[0].Details).To(Equal("Some Details 1")) 134 135 Expect(webProcessSummary.Instances[1].Memory).To(Equal("1.9M of 32M")) 136 Expect(webProcessSummary.Instances[1].Disk).To(Equal("1.9M of 3.8M")) 137 Expect(webProcessSummary.Instances[1].CPU).To(Equal("0.0%")) 138 Expect(webProcessSummary.Instances[1].Details).To(Equal("Some Details 2")) 139 140 Expect(webProcessSummary.Instances[2].Memory).To(Equal("2.9M of 32M")) 141 Expect(webProcessSummary.Instances[2].Disk).To(Equal("2.9M of 5.7M")) 142 Expect(webProcessSummary.Instances[2].CPU).To(Equal("0.0%")) 143 144 consoleProcessSummary := processTable.Processes[1] 145 Expect(consoleProcessSummary.Type).To(Equal("console")) 146 Expect(consoleProcessSummary.Sidecars).To(Equal("")) 147 Expect(consoleProcessSummary.InstanceCount).To(Equal("1/1")) 148 Expect(consoleProcessSummary.MemUsage).To(Equal("16M")) 149 150 Expect(consoleProcessSummary.Instances[0].Memory).To(Equal("976.6K of 32M")) 151 Expect(consoleProcessSummary.Instances[0].Disk).To(Equal("976.6K of 7.6M")) 152 Expect(consoleProcessSummary.Instances[0].CPU).To(Equal("0.0%")) 153 }) 154 }) 155 156 When("some processes have > 0 instances and others have 0 instances", func() { 157 BeforeEach(func() { 158 summary = v7action.DetailedApplicationSummary{ 159 ApplicationSummary: v7action.ApplicationSummary{ 160 Application: resources.Application{ 161 GUID: "some-app-guid", 162 State: constant.ApplicationStarted, 163 }, 164 ProcessSummaries: v7action.ProcessSummaries{ 165 { 166 Process: v7action.Process{ 167 Type: constant.ProcessTypeWeb, 168 MemoryInMB: types.NullUint64{Value: 32, IsSet: true}, 169 DiskInMB: types.NullUint64{Value: 1024, IsSet: true}, 170 }, 171 Sidecars: []v7action.Sidecar{}, 172 InstanceDetails: []v7action.ProcessInstance{ 173 v7action.ProcessInstance{ 174 Index: 0, 175 State: constant.ProcessInstanceRunning, 176 MemoryUsage: 1000000, 177 DiskUsage: 1000000, 178 MemoryQuota: 33554432, 179 DiskQuota: 2000000, 180 Uptime: time.Since(time.Unix(267321600, 0)), 181 }, 182 }, 183 }, 184 { 185 Process: v7action.Process{ 186 Type: "console", 187 MemoryInMB: types.NullUint64{Value: 16, IsSet: true}, 188 DiskInMB: types.NullUint64{Value: 512, IsSet: true}, 189 }, 190 Sidecars: []v7action.Sidecar{}, 191 }, 192 }, 193 }, 194 } 195 }) 196 197 It("lists instance stats for process types that have > 0 instances", func() { 198 Expect(testUI.Out).To(Say(`type:\s+web`)) 199 Expect(testUI.Out).To(Say(`sidecars: `)) 200 Expect(testUI.Out).To(Say(`state\s+since\s+cpu\s+memory\s+disk\s+details`)) 201 }) 202 203 It("does not show the instance stats table for process types with 0 instances", func() { 204 Expect(testUI.Out).To(Say(`type:\s+console`)) 205 Expect(testUI.Out).To(Say(`sidecars: `)) 206 Expect(testUI.Out).To(Say("There are no running instances of this process.")) 207 }) 208 }) 209 210 When("all the instances for a process are down (but scaled to > 0 instances)", func() { 211 BeforeEach(func() { 212 summary = v7action.DetailedApplicationSummary{ 213 ApplicationSummary: v7action.ApplicationSummary{ 214 ProcessSummaries: []v7action.ProcessSummary{ 215 { 216 Process: v7action.Process{ 217 Type: constant.ProcessTypeWeb, 218 MemoryInMB: types.NullUint64{Value: 32, IsSet: true}, 219 }, 220 Sidecars: []v7action.Sidecar{}, 221 InstanceDetails: []v7action.ProcessInstance{{State: constant.ProcessInstanceDown}}, 222 }}, 223 }, 224 } 225 }) 226 227 It("displays the instances table", func() { 228 Expect(testUI.Out).To(Say(`type:\s+web`)) 229 Expect(testUI.Out).To(Say(`sidecars: `)) 230 Expect(testUI.Out).To(Say(`state\s+since\s+cpu\s+memory\s+disk\s+details`)) 231 }) 232 }) 233 234 Describe("start command", func() { 235 BeforeEach(func() { 236 summary = v7action.DetailedApplicationSummary{ 237 ApplicationSummary: v7action.ApplicationSummary{ 238 Application: resources.Application{ 239 GUID: "some-app-guid", 240 State: constant.ApplicationStarted, 241 }, 242 ProcessSummaries: v7action.ProcessSummaries{ 243 { 244 Process: v7action.Process{ 245 Type: constant.ProcessTypeWeb, 246 Command: *types.NewFilteredString("some-command-1"), 247 }, 248 Sidecars: []v7action.Sidecar{}, 249 }, 250 { 251 Process: v7action.Process{ 252 Type: "console", 253 Command: *types.NewFilteredString("some-command-2"), 254 }, 255 Sidecars: []v7action.Sidecar{}, 256 }, 257 { 258 Process: v7action.Process{ 259 Type: "random", 260 }, 261 Sidecars: []v7action.Sidecar{}, 262 }, 263 }, 264 }, 265 } 266 }) 267 268 When("displayStartCommand is true", func() { 269 BeforeEach(func() { 270 displayStartCommand = true 271 }) 272 273 It("displays the non-empty start command for each process", func() { 274 Expect(testUI.Out).To(Say(`type:\s+web`)) 275 Expect(testUI.Out).To(Say(`sidecars: `)) 276 Expect(testUI.Out).To(Say(`start command:\s+some-command-1`)) 277 278 Expect(testUI.Out).To(Say(`type:\s+console`)) 279 Expect(testUI.Out).To(Say(`sidecars: `)) 280 Expect(testUI.Out).To(Say(`start command:\s+some-command-2`)) 281 282 Expect(testUI.Out).To(Say(`type:\s+random`)) 283 Expect(testUI.Out).To(Say(`sidecars: `)) 284 Expect(testUI.Out).ToNot(Say("start command:")) 285 }) 286 }) 287 288 When("displayStartCommand is false", func() { 289 BeforeEach(func() { 290 displayStartCommand = false 291 }) 292 293 It("hides the start command", func() { 294 Expect(testUI.Out).ToNot(Say("start command:")) 295 }) 296 }) 297 }) 298 }) 299 300 When("the app has no instances", func() { 301 BeforeEach(func() { 302 summary = v7action.DetailedApplicationSummary{ 303 ApplicationSummary: v7action.ApplicationSummary{ 304 Application: resources.Application{ 305 GUID: "some-app-guid", 306 State: constant.ApplicationStarted, 307 }, 308 ProcessSummaries: v7action.ProcessSummaries{ 309 { 310 Process: v7action.Process{ 311 Type: constant.ProcessTypeWeb, 312 MemoryInMB: types.NullUint64{Value: 32, IsSet: true}, 313 DiskInMB: types.NullUint64{Value: 1024, IsSet: true}, 314 }, 315 Sidecars: []v7action.Sidecar{}, 316 }, 317 { 318 Process: v7action.Process{ 319 Type: "console", 320 MemoryInMB: types.NullUint64{Value: 16, IsSet: true}, 321 DiskInMB: types.NullUint64{Value: 512, IsSet: true}, 322 }, 323 Sidecars: []v7action.Sidecar{}, 324 }, 325 }, 326 }, 327 } 328 }) 329 330 It("lists information for each of the processes", func() { 331 Expect(testUI.Out).To(Say(`type:\s+web`)) 332 Expect(testUI.Out).To(Say(`sidecars: `)) 333 Expect(testUI.Out).To(Say(`instances:\s+0/0`)) 334 Expect(testUI.Out).To(Say(`memory usage:\s+32M`)) 335 Expect(testUI.Out).To(Say("There are no running instances of this process.")) 336 337 Expect(testUI.Out).To(Say(`type:\s+console`)) 338 Expect(testUI.Out).To(Say(`sidecars: `)) 339 Expect(testUI.Out).To(Say(`instances:\s+0/0`)) 340 Expect(testUI.Out).To(Say(`memory usage:\s+16M`)) 341 Expect(testUI.Out).To(Say("There are no running instances of this process.")) 342 }) 343 344 It("does not display the instance table", func() { 345 Expect(testUI.Out).NotTo(Say(`state\s+since\s+cpu\s+memory\s+disk\s+details`)) 346 }) 347 }) 348 349 When("the app has sidecars", func() { 350 BeforeEach(func() { 351 summary = v7action.DetailedApplicationSummary{ 352 ApplicationSummary: v7action.ApplicationSummary{ 353 Application: resources.Application{ 354 GUID: "some-app-guid", 355 State: constant.ApplicationStarted, 356 }, 357 ProcessSummaries: v7action.ProcessSummaries{ 358 { 359 Process: v7action.Process{ 360 Type: constant.ProcessTypeWeb, 361 MemoryInMB: types.NullUint64{Value: 32, IsSet: true}, 362 DiskInMB: types.NullUint64{Value: 1024, IsSet: true}, 363 }, 364 Sidecars: []v7action.Sidecar{ 365 {Name: "authenticator"}, 366 {Name: "clock"}, 367 }, 368 }, 369 }, 370 }, 371 } 372 }) 373 374 It("lists information for each of the processes", func() { 375 Expect(testUI.Out).To(Say(`type:\s+web`)) 376 Expect(testUI.Out).To(Say(`sidecars:\s+authenticator, clock`)) 377 Expect(testUI.Out).To(Say(`instances:\s+0/0`)) 378 Expect(testUI.Out).To(Say(`memory usage:\s+32M`)) 379 Expect(testUI.Out).To(Say("There are no running instances of this process.")) 380 }) 381 382 It("does not display the instance table", func() { 383 Expect(testUI.Out).NotTo(Say(`state\s+since\s+cpu\s+memory\s+disk\s+details`)) 384 }) 385 }) 386 387 When("the app is stopped", func() { 388 BeforeEach(func() { 389 summary = v7action.DetailedApplicationSummary{ 390 ApplicationSummary: v7action.ApplicationSummary{ 391 Application: resources.Application{ 392 GUID: "some-app-guid", 393 State: constant.ApplicationStopped, 394 }, 395 ProcessSummaries: v7action.ProcessSummaries{ 396 { 397 Process: v7action.Process{ 398 Type: constant.ProcessTypeWeb, 399 }, 400 Sidecars: []v7action.Sidecar{}, 401 }, 402 { 403 Process: v7action.Process{ 404 Type: "console", 405 }, 406 Sidecars: []v7action.Sidecar{}, 407 }, 408 }, 409 }, 410 } 411 }) 412 413 It("lists information for each of the processes", func() { 414 Expect(testUI.Out).To(Say(`type:\s+web`)) 415 Expect(testUI.Out).To(Say(`sidecars: `)) 416 Expect(testUI.Out).To(Say("There are no running instances of this process.")) 417 418 Expect(testUI.Out).To(Say(`type:\s+console`)) 419 Expect(testUI.Out).To(Say(`sidecars: `)) 420 Expect(testUI.Out).To(Say("There are no running instances of this process.")) 421 }) 422 423 It("does not display the instance table", func() { 424 Expect(testUI.Out).NotTo(Say(`state\s+since\s+cpu\s+memory\s+disk\s+details`)) 425 }) 426 }) 427 428 Describe("isolation segments", func() { 429 When("the isolation segment name is provided", func() { 430 var isolationSegmentName string 431 BeforeEach(func() { 432 isolationSegmentName = "potato beans" 433 summary.ProcessSummaries = v7action.ProcessSummaries{ 434 v7action.ProcessSummary{ 435 InstanceDetails: []v7action.ProcessInstance{ 436 {IsolationSegment: isolationSegmentName}, 437 }, 438 }, 439 } 440 }) 441 442 It("should output the isolation segment name", func() { 443 Expect(testUI.Out).To(Say(`isolation segment:\s+%s`, isolationSegmentName)) 444 }) 445 }) 446 447 When("the application summary has no isolation segment information", func() { 448 BeforeEach(func() { 449 summary = v7action.DetailedApplicationSummary{ 450 ApplicationSummary: v7action.ApplicationSummary{ 451 Application: resources.Application{ 452 GUID: "some-app-guid", 453 State: constant.ApplicationStopped, 454 }, 455 }, 456 } 457 }) 458 459 It("should not output isolation segment header", func() { 460 Expect(testUI.Out).ToNot(Say("isolation segment:")) 461 }) 462 }) 463 }) 464 465 Describe("last upload time", func() { 466 When("the application has a last uploaded time", func() { 467 var createdTime string 468 469 BeforeEach(func() { 470 createdTime = "2006-01-02T15:04:05-07:00" 471 summary.CurrentDroplet.CreatedAt = createdTime 472 }) 473 474 It("displays the uploaded time", func() { 475 t, err := time.Parse(time.RFC3339, createdTime) 476 Expect(err).To(Not(HaveOccurred())) 477 478 time := t.Local().Format("Mon 02 Jan 15:04:05 MST 2006") 479 Expect(testUI.Out).To(Say(`last uploaded:\s+%s`, time)) 480 }) 481 }) 482 483 When("the application does not have a last uploaded time", func() { 484 BeforeEach(func() { 485 summary.CurrentDroplet.CreatedAt = "" 486 }) 487 488 It("leaves last uploaded blank", func() { 489 Expect(testUI.Out).To(Say(`(?m)last uploaded:\s*\n`)) 490 }) 491 }) 492 }) 493 494 When("the application has routes", func() { 495 BeforeEach(func() { 496 summary.Routes = []resources.Route{ 497 {Host: "route1", URL: "route1.example.com"}, 498 {Host: "route2", URL: "route2.example.com"}, 499 } 500 }) 501 502 It("displays routes", func() { 503 Expect(testUI.Out).To(Say(`routes:\s+%s, %s`, "route1.example.com", "route2.example.com")) 504 }) 505 }) 506 507 When("the application has a stack", func() { 508 BeforeEach(func() { 509 summary.CurrentDroplet.Stack = "some-stack" 510 }) 511 512 It("displays stack", func() { 513 Expect(testUI.Out).To(Say(`stack:\s+some-stack`)) 514 }) 515 }) 516 517 When("the application is a docker app", func() { 518 BeforeEach(func() { 519 summary = v7action.DetailedApplicationSummary{ 520 ApplicationSummary: v7action.ApplicationSummary{ 521 Application: resources.Application{ 522 GUID: "some-guid", 523 Name: "some-app", 524 State: constant.ApplicationStarted, 525 LifecycleType: constant.AppLifecycleTypeDocker, 526 }, 527 }, 528 CurrentDroplet: resources.Droplet{ 529 Image: "docker/some-image", 530 }, 531 } 532 }) 533 534 It("displays the app information", func() { 535 Expect(testUI.Out).To(Say(`name:\s+some-app`)) 536 Expect(testUI.Out).To(Say(`requested state:\s+started`)) 537 Expect(testUI.Out).To(Say(`routes:\s+\n`)) 538 Expect(testUI.Out).To(Say(`stack:\s+\n`)) 539 Expect(testUI.Out).To(Say(`(?m)docker image:\s+docker/some-image$\n`)) 540 }) 541 542 It("does not display the buildpack info for docker apps", func() { 543 Expect(testUI.Out).ToNot(Say("buildpacks:")) 544 }) 545 }) 546 547 When("the application is a buildpack app", func() { 548 BeforeEach(func() { 549 summary = v7action.DetailedApplicationSummary{ 550 ApplicationSummary: v7action.ApplicationSummary{ 551 Application: resources.Application{ 552 LifecycleType: constant.AppLifecycleTypeBuildpack, 553 }, 554 }, 555 CurrentDroplet: resources.Droplet{ 556 Stack: "cflinuxfs2", 557 Buildpacks: []resources.DropletBuildpack{ 558 { 559 Name: "ruby_buildpack", 560 BuildpackName: "ruby_buildpack_name", 561 DetectOutput: "some-detect-output", 562 Version: "0.0.1", 563 }, 564 { 565 Name: "go_buildpack_without_detect_output", 566 BuildpackName: "go_buildpack_name", 567 DetectOutput: "", 568 Version: "0.0.2", 569 }, 570 { 571 Name: "go_buildpack_without_version", 572 BuildpackName: "go_buildpack_name", 573 DetectOutput: "", 574 Version: "", 575 }, 576 { 577 Name: "some-buildpack", 578 DetectOutput: "", 579 }, 580 }, 581 }, 582 } 583 }) 584 585 It("displays stack and buildpacks", func() { 586 Expect(testUI.Out).To(Say(`stack:\s+cflinuxfs2\n`)) 587 Expect(testUI.Out).To(Say(`buildpacks:\s+\n`)) 588 Expect(testUI.Out).To(Say(`name\s+version\s+detect output\s+buildpack name\n`)) 589 Expect(testUI.Out).To(Say(`ruby_buildpack\s+0.0.1\s+some-detect-output\s+ruby_buildpack_name\n`)) 590 Expect(testUI.Out).To(Say(`some-buildpack`)) 591 }) 592 }) 593 }) 594 })