admin.go 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. /*
  2. Copyright 2015 Google Inc. All Rights Reserved.
  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. "fmt"
  16. "regexp"
  17. "strings"
  18. "golang.org/x/net/context"
  19. "google.golang.org/cloud"
  20. btcspb "google.golang.org/cloud/bigtable/internal/cluster_service_proto"
  21. bttspb "google.golang.org/cloud/bigtable/internal/table_service_proto"
  22. "google.golang.org/cloud/internal/transport"
  23. "google.golang.org/grpc"
  24. )
  25. const adminAddr = "bigtabletableadmin.googleapis.com:443"
  26. // AdminClient is a client type for performing admin operations within a specific cluster.
  27. type AdminClient struct {
  28. conn *grpc.ClientConn
  29. tClient bttspb.BigtableTableServiceClient
  30. project, zone, cluster string
  31. }
  32. // NewAdminClient creates a new AdminClient for a given project, zone and cluster.
  33. func NewAdminClient(ctx context.Context, project, zone, cluster string, opts ...cloud.ClientOption) (*AdminClient, error) {
  34. o := []cloud.ClientOption{
  35. cloud.WithEndpoint(adminAddr),
  36. cloud.WithScopes(AdminScope),
  37. cloud.WithUserAgent(clientUserAgent),
  38. }
  39. o = append(o, opts...)
  40. conn, err := transport.DialGRPC(ctx, o...)
  41. if err != nil {
  42. return nil, fmt.Errorf("dialing: %v", err)
  43. }
  44. return &AdminClient{
  45. conn: conn,
  46. tClient: bttspb.NewBigtableTableServiceClient(conn),
  47. project: project,
  48. zone: zone,
  49. cluster: cluster,
  50. }, nil
  51. }
  52. // Close closes the AdminClient.
  53. func (ac *AdminClient) Close() {
  54. ac.conn.Close()
  55. }
  56. func (ac *AdminClient) clusterPrefix() string {
  57. return fmt.Sprintf("projects/%s/zones/%s/clusters/%s", ac.project, ac.zone, ac.cluster)
  58. }
  59. // Tables returns a list of the tables in the cluster.
  60. func (ac *AdminClient) Tables(ctx context.Context) ([]string, error) {
  61. prefix := ac.clusterPrefix()
  62. req := &bttspb.ListTablesRequest{
  63. Name: prefix,
  64. }
  65. res, err := ac.tClient.ListTables(ctx, req)
  66. if err != nil {
  67. return nil, err
  68. }
  69. names := make([]string, 0, len(res.Tables))
  70. for _, tbl := range res.Tables {
  71. names = append(names, strings.TrimPrefix(tbl.Name, prefix+"/tables/"))
  72. }
  73. return names, nil
  74. }
  75. // CreateTable creates a new table in the cluster.
  76. // This method may return before the table's creation is complete.
  77. func (ac *AdminClient) CreateTable(ctx context.Context, table string) error {
  78. prefix := ac.clusterPrefix()
  79. req := &bttspb.CreateTableRequest{
  80. Name: prefix,
  81. TableId: table,
  82. }
  83. _, err := ac.tClient.CreateTable(ctx, req)
  84. if err != nil {
  85. return err
  86. }
  87. return nil
  88. }
  89. // CreateColumnFamily creates a new column family in a table.
  90. func (ac *AdminClient) CreateColumnFamily(ctx context.Context, table, family string) error {
  91. // TODO(dsymonds): Permit specifying gcexpr and any other family settings.
  92. prefix := ac.clusterPrefix()
  93. req := &bttspb.CreateColumnFamilyRequest{
  94. Name: prefix + "/tables/" + table,
  95. ColumnFamilyId: family,
  96. }
  97. _, err := ac.tClient.CreateColumnFamily(ctx, req)
  98. return err
  99. }
  100. // DeleteTable deletes a table and all of its data.
  101. func (ac *AdminClient) DeleteTable(ctx context.Context, table string) error {
  102. prefix := ac.clusterPrefix()
  103. req := &bttspb.DeleteTableRequest{
  104. Name: prefix + "/tables/" + table,
  105. }
  106. _, err := ac.tClient.DeleteTable(ctx, req)
  107. return err
  108. }
  109. // DeleteColumnFamily deletes a column family in a table and all of its data.
  110. func (ac *AdminClient) DeleteColumnFamily(ctx context.Context, table, family string) error {
  111. prefix := ac.clusterPrefix()
  112. req := &bttspb.DeleteColumnFamilyRequest{
  113. Name: prefix + "/tables/" + table + "/columnFamilies/" + family,
  114. }
  115. _, err := ac.tClient.DeleteColumnFamily(ctx, req)
  116. return err
  117. }
  118. // TableInfo represents information about a table.
  119. type TableInfo struct {
  120. Families []string
  121. }
  122. // TableInfo retrieves information about a table.
  123. func (ac *AdminClient) TableInfo(ctx context.Context, table string) (*TableInfo, error) {
  124. prefix := ac.clusterPrefix()
  125. req := &bttspb.GetTableRequest{
  126. Name: prefix + "/tables/" + table,
  127. }
  128. res, err := ac.tClient.GetTable(ctx, req)
  129. if err != nil {
  130. return nil, err
  131. }
  132. ti := &TableInfo{}
  133. for fam := range res.ColumnFamilies {
  134. ti.Families = append(ti.Families, fam)
  135. }
  136. return ti, nil
  137. }
  138. // SetGCPolicy specifies which cells in a column family should be garbage collected.
  139. // GC executes opportunistically in the background; table reads may return data
  140. // matching the GC policy.
  141. func (ac *AdminClient) SetGCPolicy(ctx context.Context, table, family string, policy GCPolicy) error {
  142. prefix := ac.clusterPrefix()
  143. tbl, err := ac.tClient.GetTable(ctx, &bttspb.GetTableRequest{
  144. Name: prefix + "/tables/" + table,
  145. })
  146. if err != nil {
  147. return err
  148. }
  149. fam, ok := tbl.ColumnFamilies[family]
  150. if !ok {
  151. return fmt.Errorf("unknown column family %q", family)
  152. }
  153. fam.GcRule = policy.proto()
  154. _, err = ac.tClient.UpdateColumnFamily(ctx, fam)
  155. return err
  156. }
  157. const clusterAdminAddr = "bigtableclusteradmin.googleapis.com:443"
  158. // ClusterAdminClient is a client type for performing admin operations on clusters.
  159. // These operations can be substantially more dangerous than those provided by AdminClient.
  160. type ClusterAdminClient struct {
  161. conn *grpc.ClientConn
  162. cClient btcspb.BigtableClusterServiceClient
  163. project string
  164. }
  165. // NewClusterAdminClient creates a new ClusterAdminClient for a given project.
  166. func NewClusterAdminClient(ctx context.Context, project string, opts ...cloud.ClientOption) (*ClusterAdminClient, error) {
  167. o := []cloud.ClientOption{
  168. cloud.WithEndpoint(clusterAdminAddr),
  169. cloud.WithScopes(ClusterAdminScope),
  170. cloud.WithUserAgent(clientUserAgent),
  171. }
  172. o = append(o, opts...)
  173. conn, err := transport.DialGRPC(ctx, o...)
  174. if err != nil {
  175. return nil, fmt.Errorf("dialing: %v", err)
  176. }
  177. return &ClusterAdminClient{
  178. conn: conn,
  179. cClient: btcspb.NewBigtableClusterServiceClient(conn),
  180. project: project,
  181. }, nil
  182. }
  183. // Close closes the ClusterAdminClient.
  184. func (cac *ClusterAdminClient) Close() {
  185. cac.conn.Close()
  186. }
  187. // ClusterInfo represents information about a cluster.
  188. type ClusterInfo struct {
  189. Name string // name of the cluster
  190. Zone string // GCP zone of the cluster (e.g. "us-central1-a")
  191. DisplayName string // display name for UIs
  192. ServeNodes int // number of allocated serve nodes
  193. }
  194. var clusterNameRegexp = regexp.MustCompile(`^projects/([^/]+)/zones/([^/]+)/clusters/([a-z][-a-z0-9]*)$`)
  195. // Clusters returns a list of clusters in the project.
  196. func (cac *ClusterAdminClient) Clusters(ctx context.Context) ([]*ClusterInfo, error) {
  197. req := &btcspb.ListClustersRequest{
  198. Name: "projects/" + cac.project,
  199. }
  200. res, err := cac.cClient.ListClusters(ctx, req)
  201. if err != nil {
  202. return nil, err
  203. }
  204. // TODO(dsymonds): Deal with failed_zones.
  205. var cis []*ClusterInfo
  206. for _, c := range res.Clusters {
  207. m := clusterNameRegexp.FindStringSubmatch(c.Name)
  208. if m == nil {
  209. return nil, fmt.Errorf("malformed cluster name %q", c.Name)
  210. }
  211. cis = append(cis, &ClusterInfo{
  212. Name: m[3],
  213. Zone: m[2],
  214. DisplayName: c.DisplayName,
  215. ServeNodes: int(c.ServeNodes),
  216. })
  217. }
  218. return cis, nil
  219. }
  220. /* TODO(dsymonds): Re-enable when there's a ClusterAdmin API.
  221. // SetClusterSize sets the number of server nodes for this cluster.
  222. func (ac *AdminClient) SetClusterSize(ctx context.Context, nodes int) error {
  223. req := &btcspb.GetClusterRequest{
  224. Name: ac.clusterPrefix(),
  225. }
  226. clu, err := ac.cClient.GetCluster(ctx, req)
  227. if err != nil {
  228. return err
  229. }
  230. clu.ServeNodes = int32(nodes)
  231. _, err = ac.cClient.UpdateCluster(ctx, clu)
  232. return err
  233. }
  234. */