You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

223 lines
6.9 KiB

  1. /*
  2. Copyright 2016 Google LLC
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package bigtable
  14. import (
  15. "errors"
  16. "flag"
  17. "fmt"
  18. "strings"
  19. "time"
  20. "cloud.google.com/go/bigtable/bttest"
  21. "golang.org/x/net/context"
  22. "google.golang.org/api/option"
  23. "google.golang.org/grpc"
  24. )
  25. var legacyUseProd string
  26. var integrationConfig IntegrationTestConfig
  27. func init() {
  28. c := &integrationConfig
  29. flag.BoolVar(&c.UseProd, "it.use-prod", false, "Use remote bigtable instead of local emulator")
  30. flag.StringVar(&c.AdminEndpoint, "it.admin-endpoint", "", "Admin api host and port")
  31. flag.StringVar(&c.DataEndpoint, "it.data-endpoint", "", "Data api host and port")
  32. flag.StringVar(&c.Project, "it.project", "", "Project to use for integration test")
  33. flag.StringVar(&c.Instance, "it.instance", "", "Bigtable instance to use")
  34. flag.StringVar(&c.Cluster, "it.cluster", "", "Bigtable cluster to use")
  35. flag.StringVar(&c.Table, "it.table", "", "Bigtable table to create")
  36. // Backwards compat
  37. flag.StringVar(&legacyUseProd, "use_prod", "", `DEPRECATED: if set to "proj,instance,table", run integration test against production`)
  38. }
  39. // IntegrationTestConfig contains parameters to pick and setup a IntegrationEnv for testing
  40. type IntegrationTestConfig struct {
  41. UseProd bool
  42. AdminEndpoint string
  43. DataEndpoint string
  44. Project string
  45. Instance string
  46. Cluster string
  47. Table string
  48. }
  49. // IntegrationEnv represents a testing environment.
  50. // The environment can be implemented using production or an emulator
  51. type IntegrationEnv interface {
  52. Config() IntegrationTestConfig
  53. NewAdminClient() (*AdminClient, error)
  54. // NewInstanceAdminClient will return nil if instance administration is unsupported in this environment
  55. NewInstanceAdminClient() (*InstanceAdminClient, error)
  56. NewClient() (*Client, error)
  57. Close()
  58. }
  59. // NewIntegrationEnv creates a new environment based on the command line args
  60. func NewIntegrationEnv() (IntegrationEnv, error) {
  61. c := integrationConfig
  62. if legacyUseProd != "" {
  63. fmt.Println("WARNING: using legacy commandline arg -use_prod, please switch to -it.*")
  64. parts := strings.SplitN(legacyUseProd, ",", 3)
  65. c.UseProd = true
  66. c.Project = parts[0]
  67. c.Instance = parts[1]
  68. c.Table = parts[2]
  69. }
  70. if integrationConfig.UseProd {
  71. return NewProdEnv(c)
  72. } else {
  73. return NewEmulatedEnv(c)
  74. }
  75. }
  76. // EmulatedEnv encapsulates the state of an emulator
  77. type EmulatedEnv struct {
  78. config IntegrationTestConfig
  79. server *bttest.Server
  80. }
  81. // NewEmulatedEnv builds and starts the emulator based environment
  82. func NewEmulatedEnv(config IntegrationTestConfig) (*EmulatedEnv, error) {
  83. srv, err := bttest.NewServer("localhost:0", grpc.MaxRecvMsgSize(200<<20), grpc.MaxSendMsgSize(100<<20))
  84. if err != nil {
  85. return nil, err
  86. }
  87. if config.Project == "" {
  88. config.Project = "project"
  89. }
  90. if config.Instance == "" {
  91. config.Instance = "instance"
  92. }
  93. if config.Table == "" {
  94. config.Table = "mytable"
  95. }
  96. config.AdminEndpoint = srv.Addr
  97. config.DataEndpoint = srv.Addr
  98. env := &EmulatedEnv{
  99. config: config,
  100. server: srv,
  101. }
  102. return env, nil
  103. }
  104. // Close stops & cleans up the emulator
  105. func (e *EmulatedEnv) Close() {
  106. e.server.Close()
  107. }
  108. // Config gets the config used to build this environment
  109. func (e *EmulatedEnv) Config() IntegrationTestConfig {
  110. return e.config
  111. }
  112. // NewAdminClient builds a new connected admin client for this environment
  113. func (e *EmulatedEnv) NewAdminClient() (*AdminClient, error) {
  114. timeout := 20 * time.Second
  115. ctx, _ := context.WithTimeout(context.Background(), timeout)
  116. conn, err := grpc.Dial(e.server.Addr, grpc.WithInsecure(), grpc.WithBlock())
  117. if err != nil {
  118. return nil, err
  119. }
  120. return NewAdminClient(ctx, e.config.Project, e.config.Instance, option.WithGRPCConn(conn))
  121. }
  122. // NewInstanceAdminClient returns nil for the emulated environment since the API is not implemented.
  123. func (e *EmulatedEnv) NewInstanceAdminClient() (*InstanceAdminClient, error) {
  124. return nil, nil
  125. }
  126. // NewClient builds a new connected data client for this environment
  127. func (e *EmulatedEnv) NewClient() (*Client, error) {
  128. timeout := 20 * time.Second
  129. ctx, _ := context.WithTimeout(context.Background(), timeout)
  130. conn, err := grpc.Dial(e.server.Addr, grpc.WithInsecure(), grpc.WithBlock(),
  131. grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(100<<20), grpc.MaxCallRecvMsgSize(100<<20)))
  132. if err != nil {
  133. return nil, err
  134. }
  135. return NewClient(ctx, e.config.Project, e.config.Instance, option.WithGRPCConn(conn))
  136. }
  137. // ProdEnv encapsulates the state necessary to connect to the external Bigtable service
  138. type ProdEnv struct {
  139. config IntegrationTestConfig
  140. }
  141. // NewProdEnv builds the environment representation
  142. func NewProdEnv(config IntegrationTestConfig) (*ProdEnv, error) {
  143. if config.Project == "" {
  144. return nil, errors.New("Project not set")
  145. }
  146. if config.Instance == "" {
  147. return nil, errors.New("Instance not set")
  148. }
  149. if config.Table == "" {
  150. return nil, errors.New("Table not set")
  151. }
  152. return &ProdEnv{config}, nil
  153. }
  154. // Close is a no-op for production environments
  155. func (e *ProdEnv) Close() {}
  156. // Config gets the config used to build this environment
  157. func (e *ProdEnv) Config() IntegrationTestConfig {
  158. return e.config
  159. }
  160. // NewAdminClient builds a new connected admin client for this environment
  161. func (e *ProdEnv) NewAdminClient() (*AdminClient, error) {
  162. timeout := 20 * time.Second
  163. ctx, _ := context.WithTimeout(context.Background(), timeout)
  164. var clientOpts []option.ClientOption
  165. if endpoint := e.config.AdminEndpoint; endpoint != "" {
  166. clientOpts = append(clientOpts, option.WithEndpoint(endpoint))
  167. }
  168. return NewAdminClient(ctx, e.config.Project, e.config.Instance, clientOpts...)
  169. }
  170. // NewInstanceAdminClient returns a new connected instance admin client for this environment
  171. func (e *ProdEnv) NewInstanceAdminClient() (*InstanceAdminClient, error) {
  172. timeout := 20 * time.Second
  173. ctx, _ := context.WithTimeout(context.Background(), timeout)
  174. var clientOpts []option.ClientOption
  175. if endpoint := e.config.AdminEndpoint; endpoint != "" {
  176. clientOpts = append(clientOpts, option.WithEndpoint(endpoint))
  177. }
  178. return NewInstanceAdminClient(ctx, e.config.Project, clientOpts...)
  179. }
  180. // NewClient builds a connected data client for this environment
  181. func (e *ProdEnv) NewClient() (*Client, error) {
  182. timeout := 20 * time.Second
  183. ctx, _ := context.WithTimeout(context.Background(), timeout)
  184. var clientOpts []option.ClientOption
  185. if endpoint := e.config.DataEndpoint; endpoint != "" {
  186. clientOpts = append(clientOpts, option.WithEndpoint(endpoint))
  187. }
  188. return NewClient(ctx, e.config.Project, e.config.Instance, clientOpts...)
  189. }