github.com/mook-as/cf-cli@v7.0.0-beta.28.0.20200120190804-b91c115fae48+incompatible/plugin/v7/rpc/cli_rpc_server_test.go (about) 1 // +build V7 2 3 package rpc_test 4 5 import ( 6 "errors" 7 "net" 8 "net/rpc" 9 "os" 10 "time" 11 12 "code.cloudfoundry.org/cli/actor/v7action" 13 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant" 14 "code.cloudfoundry.org/cli/cf/terminal" 15 "code.cloudfoundry.org/cli/command/commandfakes" 16 plugin "code.cloudfoundry.org/cli/plugin/v7" 17 plugin_models "code.cloudfoundry.org/cli/plugin/v7/models" 18 cmdRunner "code.cloudfoundry.org/cli/plugin/v7/rpc" 19 "code.cloudfoundry.org/cli/plugin/v7/rpc/rpcfakes" 20 "code.cloudfoundry.org/cli/types" 21 "code.cloudfoundry.org/cli/util/configv3" 22 . "github.com/onsi/ginkgo" 23 . "github.com/onsi/gomega" 24 ) 25 26 var _ = Describe("Server", func() { 27 28 var ( 29 err error 30 client *rpc.Client 31 rpcService *cmdRunner.CliRpcService 32 ) 33 34 AfterEach(func() { 35 if client != nil { 36 client.Close() 37 } 38 }) 39 40 BeforeEach(func() { 41 rpc.DefaultServer = rpc.NewServer() 42 }) 43 44 Describe(".NewRpcService", func() { 45 BeforeEach(func() { 46 rpcService, err = cmdRunner.NewRpcService(nil, nil, nil, rpc.DefaultServer, nil, nil) 47 Expect(err).ToNot(HaveOccurred()) 48 }) 49 50 It("returns an err of another Rpc process is already registered", func() { 51 _, err := cmdRunner.NewRpcService(nil, nil, nil, rpc.DefaultServer, nil, nil) 52 Expect(err).To(HaveOccurred()) 53 }) 54 }) 55 56 Describe(".Stop", func() { 57 BeforeEach(func() { 58 rpcService, err = cmdRunner.NewRpcService(nil, nil, nil, rpc.DefaultServer, nil, nil) 59 Expect(err).ToNot(HaveOccurred()) 60 61 err := rpcService.Start() 62 Expect(err).ToNot(HaveOccurred()) 63 64 pingCli(rpcService.Port()) 65 }) 66 67 It("shuts down the rpc server", func() { 68 rpcService.Stop() 69 70 //give time for server to stop 71 time.Sleep(50 * time.Millisecond) 72 73 client, err = rpc.Dial("tcp", "127.0.0.1:"+rpcService.Port()) 74 Expect(err).To(HaveOccurred()) 75 }) 76 }) 77 78 Describe(".Start", func() { 79 BeforeEach(func() { 80 rpcService, err = cmdRunner.NewRpcService(nil, nil, nil, rpc.DefaultServer, nil, nil) 81 Expect(err).ToNot(HaveOccurred()) 82 83 err := rpcService.Start() 84 Expect(err).ToNot(HaveOccurred()) 85 86 pingCli(rpcService.Port()) 87 }) 88 89 AfterEach(func() { 90 rpcService.Stop() 91 92 //give time for server to stop 93 time.Sleep(50 * time.Millisecond) 94 }) 95 96 It("Start an Rpc server for communication", func() { 97 client, err = rpc.Dial("tcp", "127.0.0.1:"+rpcService.Port()) 98 Expect(err).ToNot(HaveOccurred()) 99 }) 100 }) 101 102 Describe(".SetPluginMetadata", func() { 103 var ( 104 metadata *plugin.PluginMetadata 105 ) 106 107 BeforeEach(func() { 108 rpcService, err = cmdRunner.NewRpcService(nil, nil, nil, rpc.DefaultServer, nil, nil) 109 Expect(err).ToNot(HaveOccurred()) 110 111 err := rpcService.Start() 112 Expect(err).ToNot(HaveOccurred()) 113 114 pingCli(rpcService.Port()) 115 116 client, err = rpc.Dial("tcp", "127.0.0.1:"+rpcService.Port()) 117 Expect(err).ToNot(HaveOccurred()) 118 119 metadata = &plugin.PluginMetadata{ 120 Name: "foo", 121 Commands: []plugin.Command{ 122 {Name: "cmd_1", HelpText: "cm 1 help text"}, 123 {Name: "cmd_2", HelpText: "cmd 2 help text"}, 124 }, 125 } 126 }) 127 128 AfterEach(func() { 129 rpcService.Stop() 130 131 //give time for server to stop 132 time.Sleep(50 * time.Millisecond) 133 }) 134 135 It("set the rpc command's Return Data", func() { 136 var success bool 137 err = client.Call("CliRpcCmd.SetPluginMetadata", metadata, &success) 138 139 Expect(err).ToNot(HaveOccurred()) 140 Expect(success).To(BeTrue()) 141 Expect(rpcService.RpcCmd.PluginMetadata).To(Equal(metadata)) 142 }) 143 }) 144 145 Describe("disabling terminal output", func() { 146 var terminalOutputSwitch *rpcfakes.FakeTerminalOutputSwitch 147 148 BeforeEach(func() { 149 terminalOutputSwitch = new(rpcfakes.FakeTerminalOutputSwitch) 150 rpcService, err = cmdRunner.NewRpcService(nil, terminalOutputSwitch, nil, rpc.DefaultServer, nil, nil) 151 Expect(err).ToNot(HaveOccurred()) 152 153 err := rpcService.Start() 154 Expect(err).ToNot(HaveOccurred()) 155 156 pingCli(rpcService.Port()) 157 }) 158 159 It("should disable the terminal output switch", func() { 160 client, err = rpc.Dial("tcp", "127.0.0.1:"+rpcService.Port()) 161 Expect(err).ToNot(HaveOccurred()) 162 163 var success bool 164 err = client.Call("CliRpcCmd.DisableTerminalOutput", true, &success) 165 166 Expect(err).ToNot(HaveOccurred()) 167 Expect(success).To(BeTrue()) 168 Expect(terminalOutputSwitch.DisableTerminalOutputCallCount()).To(Equal(1)) 169 Expect(terminalOutputSwitch.DisableTerminalOutputArgsForCall(0)).To(Equal(true)) 170 }) 171 }) 172 173 Describe("Plugin API", func() { 174 var ( 175 fakePluginActor *rpcfakes.FakePluginActor 176 fakeConfig *commandfakes.FakeConfig 177 ) 178 179 BeforeEach(func() { 180 fakePluginActor = new(rpcfakes.FakePluginActor) 181 fakeConfig = new(commandfakes.FakeConfig) 182 outputCapture := terminal.NewTeePrinter(os.Stdout) 183 terminalOutputSwitch := terminal.NewTeePrinter(os.Stdout) 184 185 rpcService, err = cmdRunner.NewRpcService(outputCapture, terminalOutputSwitch, nil, rpc.DefaultServer, fakeConfig, fakePluginActor) 186 Expect(err).ToNot(HaveOccurred()) 187 188 err := rpcService.Start() 189 Expect(err).ToNot(HaveOccurred()) 190 191 pingCli(rpcService.Port()) 192 client, err = rpc.Dial("tcp", "127.0.0.1:"+rpcService.Port()) 193 Expect(err).ToNot(HaveOccurred()) 194 }) 195 196 AfterEach(func() { 197 rpcService.Stop() 198 199 //give time for server to stop 200 time.Sleep(50 * time.Millisecond) 201 }) 202 203 Describe("GetApp", func() { 204 var ( 205 summary v7action.DetailedApplicationSummary 206 ) 207 BeforeEach(func() { 208 summary = v7action.DetailedApplicationSummary{ 209 ApplicationSummary: v7action.ApplicationSummary{ 210 Application: v7action.Application{ 211 GUID: "some-app-guid", 212 Name: "some-app", 213 StackName: "some-stack", 214 State: constant.ApplicationStarted, 215 }, 216 ProcessSummaries: v7action.ProcessSummaries{ 217 { 218 Process: v7action.Process{ 219 Type: constant.ProcessTypeWeb, 220 Command: *types.NewFilteredString("some-command-1"), 221 MemoryInMB: types.NullUint64{IsSet: true, Value: 512}, 222 DiskInMB: types.NullUint64{IsSet: true, Value: 64}, 223 HealthCheckTimeout: 60, 224 Instances: types.NullInt{IsSet: true, Value: 5}, 225 }, 226 InstanceDetails: []v7action.ProcessInstance{ 227 {State: constant.ProcessInstanceRunning}, 228 {State: constant.ProcessInstanceRunning}, 229 {State: constant.ProcessInstanceCrashed}, 230 {State: constant.ProcessInstanceRunning}, 231 {State: constant.ProcessInstanceRunning}, 232 }, 233 }, 234 { 235 Process: v7action.Process{ 236 Type: "console", 237 Command: *types.NewFilteredString("some-command-2"), 238 MemoryInMB: types.NullUint64{IsSet: true, Value: 256}, 239 DiskInMB: types.NullUint64{IsSet: true, Value: 16}, 240 HealthCheckTimeout: 120, 241 Instances: types.NullInt{IsSet: true, Value: 1}, 242 }, 243 InstanceDetails: []v7action.ProcessInstance{ 244 {State: constant.ProcessInstanceRunning}, 245 }, 246 }, 247 }, 248 }, 249 CurrentDroplet: v7action.Droplet{ 250 Stack: "cflinuxfs2", 251 Buildpacks: []v7action.DropletBuildpack{ 252 { 253 Name: "ruby_buildpack", 254 DetectOutput: "some-detect-output", 255 }, 256 { 257 Name: "some-buildpack", 258 DetectOutput: "", 259 }, 260 }, 261 }, 262 } 263 fakePluginActor.GetDetailedAppSummaryReturns(summary, v7action.Warnings{"warning-1", "warning-2"}, nil) 264 265 fakeConfig.TargetedSpaceReturns(configv3.Space{ 266 Name: "some-space", 267 GUID: "some-space-guid", 268 }) 269 270 }) 271 272 It("retrieves the app summary", func() { 273 result := plugin_models.DetailedApplicationSummary{} 274 err := client.Call("CliRpcCmd.GetApp", "some-app", &result) 275 Expect(err).ToNot(HaveOccurred()) 276 277 Expect(fakePluginActor.GetDetailedAppSummaryCallCount()).To(Equal(1)) 278 appName, spaceGUID, withObfuscatedValues := fakePluginActor.GetDetailedAppSummaryArgsForCall(0) 279 Expect(appName).To(Equal("some-app")) 280 Expect(spaceGUID).To(Equal("some-space-guid")) 281 Expect(withObfuscatedValues).To(BeTrue()) 282 }) 283 284 It("populates the plugin model with the retrieved app", func() { 285 result := plugin_models.DetailedApplicationSummary{} 286 err := client.Call("CliRpcCmd.GetApp", "some-app", &result) 287 Expect(err).ToNot(HaveOccurred()) 288 289 //fmt.Fprintf(os.Stdout, "%+v", result) 290 Expect(result).To(BeEquivalentTo(summary)) 291 }) 292 293 Context("when retrieving the app fails", func() { 294 BeforeEach(func() { 295 fakePluginActor.GetDetailedAppSummaryReturns(v7action.DetailedApplicationSummary{}, nil, errors.New("some-error")) 296 }) 297 It("returns an error", func() { 298 result := plugin_models.DetailedApplicationSummary{} 299 err := client.Call("CliRpcCmd.GetApp", "some-app", &result) 300 Expect(err).To(MatchError("some-error")) 301 }) 302 }) 303 }) 304 305 Describe("GetOrg", func() { 306 var ( 307 labels map[string]types.NullString 308 metadata v7action.Metadata 309 org v7action.Organization 310 spaces []v7action.Space 311 domains []v7action.Domain 312 ) 313 314 BeforeEach(func() { 315 labels = map[string]types.NullString{ 316 "k1": types.NewNullString("v1"), 317 "k2": types.NewNullString("v2"), 318 } 319 320 metadata = v7action.Metadata{ 321 Labels: labels, 322 } 323 324 org = v7action.Organization{ 325 Name: "org-name", 326 GUID: "org-guid", 327 Metadata: &metadata, 328 } 329 330 spaces = []v7action.Space{ 331 v7action.Space{ 332 Name: "space-name-1", 333 GUID: "space-guid-1", 334 }, 335 v7action.Space{ 336 Name: "space-name-2", 337 GUID: "space-guid-2", 338 }, 339 } 340 341 domains = []v7action.Domain{ 342 v7action.Domain{ 343 Name: "yodie.com", 344 GUID: "yoodie.com-guid", 345 OrganizationGUID: org.GUID, 346 }, 347 } 348 349 fakePluginActor.GetOrganizationByNameReturns(org, nil, nil) 350 fakePluginActor.GetOrganizationSpacesReturns(spaces, nil, nil) 351 fakePluginActor.GetOrganizationDomainsReturns(domains, nil, nil) 352 }) 353 354 It("retrives the organization", func() { 355 result := plugin_models.OrgSummary{} 356 err := client.Call("CliRpcCmd.GetOrg", "org-name", &result) 357 Expect(err).ToNot(HaveOccurred()) 358 359 Expect(fakePluginActor.GetOrganizationByNameCallCount()).To(Equal(1)) 360 orgName := fakePluginActor.GetOrganizationByNameArgsForCall(0) 361 Expect(orgName).To(Equal(org.Name)) 362 }) 363 364 It("retrives the spaces for the organization", func() { 365 result := plugin_models.OrgSummary{} 366 err := client.Call("CliRpcCmd.GetOrg", "org-name", &result) 367 Expect(err).ToNot(HaveOccurred()) 368 369 Expect(fakePluginActor.GetOrganizationSpacesCallCount()).To(Equal(1)) 370 orgGUID := fakePluginActor.GetOrganizationSpacesArgsForCall(0) 371 Expect(orgGUID).To(Equal(org.GUID)) 372 }) 373 374 It("retrives the domains for the organization", func() { 375 result := plugin_models.OrgSummary{} 376 err := client.Call("CliRpcCmd.GetOrg", "org-name", &result) 377 Expect(err).ToNot(HaveOccurred()) 378 379 Expect(fakePluginActor.GetOrganizationDomainsCallCount()).To(Equal(1)) 380 orgGUID, labelSelector := fakePluginActor.GetOrganizationDomainsArgsForCall(0) 381 Expect(orgGUID).To(Equal(org.GUID)) 382 Expect(labelSelector).To(Equal("")) 383 }) 384 385 It("populates the plugin model with the retrieved org, space, and domain information", func() { 386 result := plugin_models.OrgSummary{} 387 err := client.Call("CliRpcCmd.GetOrg", "org-name", &result) 388 Expect(err).ToNot(HaveOccurred()) 389 390 Expect(result.Name).To(Equal(org.Name)) 391 Expect(result.GUID).To(Equal(org.GUID)) 392 393 Expect(len(result.Spaces)).To(Equal(2)) 394 Expect(result.Spaces[1].Name).To(Equal(spaces[1].Name)) 395 396 Expect(len(result.Domains)).To(Equal(1)) 397 Expect(result.Domains[0].Name).To(Equal(domains[0].Name)) 398 }) 399 400 It("populates the plugin model with Metadata", func() { 401 result := plugin_models.OrgSummary{} 402 err := client.Call("CliRpcCmd.GetOrg", "org-name", &result) 403 Expect(err).ToNot(HaveOccurred()) 404 405 Expect(result.Metadata).ToNot(BeNil()) 406 Expect(result.Metadata.Labels).To(BeEquivalentTo(labels)) 407 }) 408 409 Context("when retrieving the org fails", func() { 410 BeforeEach(func() { 411 fakePluginActor.GetOrganizationByNameReturns(v7action.Organization{}, nil, errors.New("org-error")) 412 }) 413 414 It("returns an error", func() { 415 result := plugin_models.OrgSummary{} 416 err := client.Call("CliRpcCmd.GetOrg", "some-org", &result) 417 Expect(err).To(MatchError("org-error")) 418 }) 419 }) 420 421 Context("when retrieving the space fails", func() { 422 BeforeEach(func() { 423 fakePluginActor.GetOrganizationSpacesReturns([]v7action.Space{}, nil, errors.New("space-error")) 424 }) 425 426 It("returns an error", func() { 427 result := plugin_models.OrgSummary{} 428 err := client.Call("CliRpcCmd.GetOrg", "some-org", &result) 429 Expect(err).To(MatchError("space-error")) 430 }) 431 }) 432 }) 433 434 Describe("GetCurrentSpace", func() { 435 BeforeEach(func() { 436 fakeConfig.TargetedSpaceReturns(configv3.Space{ 437 Name: "the-charlatans", 438 GUID: "united-travel-service", 439 }) 440 fakeConfig.TargetedOrganizationReturns(configv3.Organization{ 441 Name: "the-actress", 442 GUID: "family", 443 }) 444 expectedSpace := v7action.Space{ 445 GUID: "united-travel-service", 446 Name: "the-charlatans", 447 } 448 fakePluginActor.GetSpaceByNameAndOrganizationReturns(expectedSpace, v7action.Warnings{}, nil) 449 }) 450 451 It("populates the plugin Space object with the current space settings in config", func() { 452 var space plugin_models.Space 453 err = client.Call("CliRpcCmd.GetCurrentSpace", "", &space) 454 455 Expect(err).ToNot(HaveOccurred()) 456 457 result := plugin_models.DetailedApplicationSummary{} 458 err := client.Call("CliRpcCmd.GetApp", "some-app", &result) 459 Expect(err).ToNot(HaveOccurred()) 460 461 Expect(fakePluginActor.GetSpaceByNameAndOrganizationCallCount()).To(Equal(1)) 462 spaceName, orgGUID := fakePluginActor.GetSpaceByNameAndOrganizationArgsForCall(0) 463 Expect(spaceName).To(Equal("the-charlatans")) 464 Expect(orgGUID).To(Equal("family")) 465 466 Expect(space.Name).To(Equal("the-charlatans")) 467 Expect(space.GUID).To(Equal("united-travel-service")) 468 }) 469 470 Context("when retrieving the current space fails", func() { 471 BeforeEach(func() { 472 fakePluginActor.GetSpaceByNameAndOrganizationReturns(v7action.Space{}, nil, errors.New("some-error")) 473 }) 474 475 It("returns an error", func() { 476 result := plugin_models.DetailedApplicationSummary{} 477 err := client.Call("CliRpcCmd.GetCurrentSpace", "", &result) 478 Expect(err).To(MatchError("some-error")) 479 }) 480 }) 481 }) 482 483 Describe("Username", func() { 484 When("logged in", func() { 485 BeforeEach(func() { 486 fakeConfig.CurrentUserNameReturns("Yodie", nil) 487 }) 488 It("returns the logged in username", func() { 489 result := "" 490 err := client.Call("CliRpcCmd.Username", "", &result) 491 Expect(err).To(BeNil()) 492 Expect(result).To(Equal("Yodie")) 493 }) 494 }) 495 496 When("not logged in", func() { 497 BeforeEach(func() { 498 fakeConfig.CurrentUserNameReturns("", nil) 499 }) 500 It("returns the logged in username", func() { 501 result := "" 502 err := client.Call("CliRpcCmd.Username", "", &result) 503 Expect(result).To(Equal("")) 504 Expect(err).To(MatchError("not logged in")) 505 }) 506 }) 507 When("config errors", func() { 508 BeforeEach(func() { 509 fakeConfig.CurrentUserNameReturns("", errors.New("config failed..")) 510 }) 511 It("returns error", func() { 512 result := "" 513 err := client.Call("CliRpcCmd.Username", "", &result) 514 Expect(result).To(Equal("")) 515 Expect(err).To(MatchError("error processing config: config failed..")) 516 }) 517 }) 518 }) 519 Describe("AccessToken", func() { 520 521 BeforeEach(func() { 522 fakePluginActor.RefreshAccessTokenReturns("token example", nil) 523 }) 524 It("retrieves the access token", func() { 525 526 result := "" 527 err := client.Call("CliRpcCmd.AccessToken", "", &result) 528 Expect(err).ToNot(HaveOccurred()) 529 Expect(fakePluginActor.RefreshAccessTokenCallCount()).To(Equal(1)) 530 Expect(result).To(Equal("token example")) 531 532 }) 533 }) 534 535 Describe("IsSkipSSLValidation", func() { 536 When("skip ssl validation is false", func() { 537 BeforeEach(func() { 538 fakeConfig.SkipSSLValidationReturns(false) 539 }) 540 541 It("returns false", func() { 542 var result bool 543 err = client.Call("CliRpcCmd.IsSkipSSLValidation", "", &result) 544 545 Expect(err).ToNot(HaveOccurred()) 546 Expect(result).To(BeFalse()) 547 }) 548 }) 549 When("skip ssl validation is true", func() { 550 BeforeEach(func() { 551 fakeConfig.SkipSSLValidationReturns(true) 552 }) 553 554 It("returns false", func() { 555 var result bool 556 err = client.Call("CliRpcCmd.IsSkipSSLValidation", "", &result) 557 558 Expect(err).ToNot(HaveOccurred()) 559 Expect(result).To(BeTrue()) 560 }) 561 }) 562 }) 563 }) 564 565 Describe(".CallCoreCommand", func() { 566 567 Describe("CLI Config object methods", func() { 568 var ( 569 fakeConfig *commandfakes.FakeConfig 570 ) 571 572 BeforeEach(func() { 573 fakeConfig = new(commandfakes.FakeConfig) 574 }) 575 576 AfterEach(func() { 577 //give time for server to stop 578 time.Sleep(50 * time.Millisecond) 579 }) 580 581 Context(".ApiEndpoint", func() { 582 BeforeEach(func() { 583 rpcService, err = cmdRunner.NewRpcService(nil, nil, nil, rpc.DefaultServer, fakeConfig, nil) 584 err := rpcService.Start() 585 Expect(err).ToNot(HaveOccurred()) 586 587 pingCli(rpcService.Port()) 588 fakeConfig.TargetReturns("www.fake-domain.com") 589 }) 590 AfterEach(func() { 591 rpcService.Stop() 592 593 //give time for server to stop 594 time.Sleep(50 * time.Millisecond) 595 }) 596 597 It("returns the ApiEndpoint() setting in config", func() { 598 client, err = rpc.Dial("tcp", "127.0.0.1:"+rpcService.Port()) 599 Expect(err).ToNot(HaveOccurred()) 600 601 var result string 602 err = client.Call("CliRpcCmd.ApiEndpoint", "", &result) 603 Expect(err).ToNot(HaveOccurred()) 604 Expect(result).To(Equal("www.fake-domain.com")) 605 Expect(fakeConfig.TargetCallCount()).To(Equal(1)) 606 }) 607 }) 608 }) 609 }) 610 }) 611 612 func pingCli(port string) { 613 var connErr error 614 var conn net.Conn 615 for i := 0; i < 5; i++ { 616 conn, connErr = net.Dial("tcp", "127.0.0.1:"+port) 617 if connErr != nil { 618 time.Sleep(200 * time.Millisecond) 619 } else { 620 conn.Close() 621 break 622 } 623 } 624 Expect(connErr).ToNot(HaveOccurred()) 625 }