testing.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. package plugin
  2. import (
  3. "bytes"
  4. "context"
  5. "io"
  6. "net"
  7. "net/rpc"
  8. "github.com/mitchellh/go-testing-interface"
  9. hclog "github.com/hashicorp/go-hclog"
  10. "github.com/hashicorp/go-plugin/internal/plugin"
  11. "google.golang.org/grpc"
  12. )
  13. // TestOptions allows specifying options that can affect the behavior of the
  14. // test functions
  15. type TestOptions struct {
  16. //ServerStdout causes the given value to be used in place of a blank buffer
  17. //for RPCServer's Stdout
  18. ServerStdout io.ReadCloser
  19. //ServerStderr causes the given value to be used in place of a blank buffer
  20. //for RPCServer's Stderr
  21. ServerStderr io.ReadCloser
  22. }
  23. // The testing file contains test helpers that you can use outside of
  24. // this package for making it easier to test plugins themselves.
  25. // TestConn is a helper function for returning a client and server
  26. // net.Conn connected to each other.
  27. func TestConn(t testing.T) (net.Conn, net.Conn) {
  28. // Listen to any local port. This listener will be closed
  29. // after a single connection is established.
  30. l, err := net.Listen("tcp", "127.0.0.1:0")
  31. if err != nil {
  32. t.Fatalf("err: %s", err)
  33. }
  34. // Start a goroutine to accept our client connection
  35. var serverConn net.Conn
  36. doneCh := make(chan struct{})
  37. go func() {
  38. defer close(doneCh)
  39. defer l.Close()
  40. var err error
  41. serverConn, err = l.Accept()
  42. if err != nil {
  43. t.Fatalf("err: %s", err)
  44. }
  45. }()
  46. // Connect to the server
  47. clientConn, err := net.Dial("tcp", l.Addr().String())
  48. if err != nil {
  49. t.Fatalf("err: %s", err)
  50. }
  51. // Wait for the server side to acknowledge it has connected
  52. <-doneCh
  53. return clientConn, serverConn
  54. }
  55. // TestRPCConn returns a rpc client and server connected to each other.
  56. func TestRPCConn(t testing.T) (*rpc.Client, *rpc.Server) {
  57. clientConn, serverConn := TestConn(t)
  58. server := rpc.NewServer()
  59. go server.ServeConn(serverConn)
  60. client := rpc.NewClient(clientConn)
  61. return client, server
  62. }
  63. // TestPluginRPCConn returns a plugin RPC client and server that are connected
  64. // together and configured.
  65. func TestPluginRPCConn(t testing.T, ps map[string]Plugin, opts *TestOptions) (*RPCClient, *RPCServer) {
  66. // Create two net.Conns we can use to shuttle our control connection
  67. clientConn, serverConn := TestConn(t)
  68. // Start up the server
  69. server := &RPCServer{Plugins: ps, Stdout: new(bytes.Buffer), Stderr: new(bytes.Buffer)}
  70. if opts != nil {
  71. if opts.ServerStdout != nil {
  72. server.Stdout = opts.ServerStdout
  73. }
  74. if opts.ServerStderr != nil {
  75. server.Stderr = opts.ServerStderr
  76. }
  77. }
  78. go server.ServeConn(serverConn)
  79. // Connect the client to the server
  80. client, err := NewRPCClient(clientConn, ps)
  81. if err != nil {
  82. t.Fatalf("err: %s", err)
  83. }
  84. return client, server
  85. }
  86. // TestGRPCConn returns a gRPC client conn and grpc server that are connected
  87. // together and configured. The register function is used to register services
  88. // prior to the Serve call. This is used to test gRPC connections.
  89. func TestGRPCConn(t testing.T, register func(*grpc.Server)) (*grpc.ClientConn, *grpc.Server) {
  90. // Create a listener
  91. l, err := net.Listen("tcp", "127.0.0.1:0")
  92. if err != nil {
  93. t.Fatalf("err: %s", err)
  94. }
  95. server := grpc.NewServer()
  96. register(server)
  97. go server.Serve(l)
  98. // Connect to the server
  99. conn, err := grpc.Dial(
  100. l.Addr().String(),
  101. grpc.WithBlock(),
  102. grpc.WithInsecure())
  103. if err != nil {
  104. t.Fatalf("err: %s", err)
  105. }
  106. // Connection successful, close the listener
  107. l.Close()
  108. return conn, server
  109. }
  110. // TestPluginGRPCConn returns a plugin gRPC client and server that are connected
  111. // together and configured. This is used to test gRPC connections.
  112. func TestPluginGRPCConn(t testing.T, ps map[string]Plugin) (*GRPCClient, *GRPCServer) {
  113. // Create a listener
  114. l, err := net.Listen("tcp", "127.0.0.1:0")
  115. if err != nil {
  116. t.Fatalf("err: %s", err)
  117. }
  118. // Start up the server
  119. server := &GRPCServer{
  120. Plugins: ps,
  121. DoneCh: make(chan struct{}),
  122. Server: DefaultGRPCServer,
  123. Stdout: new(bytes.Buffer),
  124. Stderr: new(bytes.Buffer),
  125. logger: hclog.Default(),
  126. }
  127. if err := server.Init(); err != nil {
  128. t.Fatalf("err: %s", err)
  129. }
  130. go server.Serve(l)
  131. // Connect to the server
  132. conn, err := grpc.Dial(
  133. l.Addr().String(),
  134. grpc.WithBlock(),
  135. grpc.WithInsecure())
  136. if err != nil {
  137. t.Fatalf("err: %s", err)
  138. }
  139. brokerGRPCClient := newGRPCBrokerClient(conn)
  140. broker := newGRPCBroker(brokerGRPCClient, nil)
  141. go broker.Run()
  142. go brokerGRPCClient.StartStream()
  143. // Create the client
  144. client := &GRPCClient{
  145. Conn: conn,
  146. Plugins: ps,
  147. broker: broker,
  148. doneCtx: context.Background(),
  149. controller: plugin.NewGRPCControllerClient(conn),
  150. }
  151. return client, server
  152. }