Skip to content

Commit 94b193a

Browse files
feat(bigtable): support row key schema for table. (#13614) (#23337)
[upstream:7fad263372825e11767174bd524ae055a663d45c] Signed-off-by: Modular Magician <[email protected]>
1 parent 971ad33 commit 94b193a

File tree

4 files changed

+213
-0
lines changed

4 files changed

+213
-0
lines changed

.changelog/13614.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:enhancement
2+
bigtable: add `row_key_schema` to `google_bigtable_table` resource
3+
```

google/services/bigtable/resource_bigtable_table.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,17 @@ func ResourceBigtableTable() *schema.Resource {
172172
},
173173
Description: `Defines an automated backup policy for a table, specified by Retention Period and Frequency. To _create_ a table with automated backup disabled, either omit the automated_backup_policy argument, or set both Retention Period and Frequency properties to "0". To disable automated backup on an _existing_ table that has automated backup enabled, set _both_ Retention Period and Frequency properties to "0". When updating an existing table, to modify the Retention Period or Frequency properties of the resource's automated backup policy, set the respective property to a non-zero value. If the automated_backup_policy argument is not provided in the configuration on update, the resource's automated backup policy will _not_ be modified.`,
174174
},
175+
"row_key_schema": {
176+
Type: schema.TypeString,
177+
Optional: true,
178+
DiffSuppressFunc: typeDiffFunc,
179+
Description: `Defines the row key schema of a table. To create or update a table with a row key schema, specify this argument.
180+
Note that in-place update is not supported, and any in-place modification to the schema will lead to failure.
181+
To update a schema, please clear it (by omitting the field), and update the resource again with a new schema.\n
182+
183+
The schema must be a valid JSON encoded string representing a Type's struct protobuf message. Note that for bytes sequence (like delimited_bytes.delimiter)
184+
the delimiter must be base64 encoded. For example, if you want to set a delimiter to a single byte character "#", it should be set to "Iw==", which is the base64 encoding of the byte sequence "#".`,
185+
},
175186
},
176187
UseJSONNumber: true,
177188
}
@@ -323,6 +334,15 @@ func resourceBigtableTableCreate(d *schema.ResourceData, meta interface{}) error
323334
}
324335
tblConf.ColumnFamilies = columnFamilies
325336

337+
// Set the row key schema if given
338+
if rks, ok := d.GetOk("row_key_schema"); ok {
339+
parsedSchema, err := getRowKeySchema(rks)
340+
if err != nil {
341+
return err
342+
}
343+
tblConf.RowKeySchema = parsedSchema
344+
}
345+
326346
// This method may return before the table's creation is complete - we may need to wait until
327347
// it exists in the future.
328348
// Set a longer timeout as creating table and adding column families can be pretty slow.
@@ -425,6 +445,17 @@ func resourceBigtableTableRead(d *schema.ResourceData, meta interface{}) error {
425445
}
426446
}
427447

448+
if table.RowKeySchema != nil {
449+
marshalledRowKey, err := bigtable.MarshalJSON(*table.RowKeySchema)
450+
if err != nil {
451+
return err
452+
}
453+
d.Set("row_key_schema", string(marshalledRowKey))
454+
} else {
455+
// String value is default to empty string, so need to set it to nil to specify that the row key schema is not set.
456+
d.Set("row_key_schema", nil)
457+
}
458+
428459
return nil
429460
}
430461

@@ -595,6 +626,23 @@ func resourceBigtableTableUpdate(d *schema.ResourceData, meta interface{}) error
595626
}
596627
}
597628

629+
if d.HasChange("row_key_schema") {
630+
changedRks := d.Get("row_key_schema").(string)
631+
if len(changedRks) == 0 {
632+
if err := c.UpdateTableRemoveRowKeySchema(ctxWithTimeout, name); err != nil {
633+
return fmt.Errorf("error removing row key schema on table %v: %v", name, err)
634+
}
635+
} else {
636+
rks, err := getRowKeySchema(changedRks)
637+
if err != nil {
638+
return fmt.Errorf("failed to parse row key schema string %v: %v", changedRks, err)
639+
}
640+
if err = c.UpdateTableWithRowKeySchema(ctxWithTimeout, name, *rks); err != nil {
641+
return fmt.Errorf("failed to update row key schema for table %v: %v", name, err)
642+
}
643+
}
644+
}
645+
598646
return resourceBigtableTableRead(d, meta)
599647
}
600648

@@ -705,3 +753,15 @@ func getType(input interface{}) (bigtable.Type, error) {
705753
}
706754
return output, nil
707755
}
756+
757+
func getRowKeySchema(input interface{}) (*bigtable.StructType, error) {
758+
rks, err := getType(input)
759+
if err != nil {
760+
return nil, err
761+
}
762+
structRks, ok := rks.(bigtable.StructType)
763+
if !ok {
764+
return nil, fmt.Errorf("only struct type is accepted as row key schema")
765+
}
766+
return &structRks, nil
767+
}

google/services/bigtable/resource_bigtable_table_meta.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ fields:
1313
- field: 'instance_name'
1414
- field: 'name'
1515
- field: 'project'
16+
- field: 'row_key_schema'
1617
- field: 'split_keys'

google/services/bigtable/resource_bigtable_table_test.go

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,97 @@ func TestAccBigtableTable_familyType(t *testing.T) {
163163
})
164164
}
165165

166+
func TestAccBigtableTable_testTableWithRowKeySchema(t *testing.T) {
167+
// bigtable instance does not use the shared HTTP client, this test creates an instance
168+
acctest.SkipIfVcr(t)
169+
t.Parallel()
170+
171+
instanceName := fmt.Sprintf("tf-test-%s", acctest.RandString(t, 10))
172+
tableName := fmt.Sprintf("tf-test-%s", acctest.RandString(t, 10))
173+
family := fmt.Sprintf("tf-test-%s", acctest.RandString(t, 10))
174+
175+
acctest.VcrTest(t, resource.TestCase{
176+
PreCheck: func() { acctest.AccTestPreCheck(t) },
177+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
178+
CheckDestroy: testAccCheckBigtableTableDestroyProducer(t),
179+
Steps: []resource.TestStep{
180+
{
181+
Config: testAccBigtableTable_rowKeySchema(instanceName, tableName, family, `{
182+
"structType": {
183+
"fields": [{
184+
"fieldName": "myfield",
185+
"type": {
186+
"stringType": { "encoding": { "utf8Bytes": { } } }
187+
}
188+
}],
189+
"encoding": { "orderedCodeBytes": { } }
190+
}
191+
}`),
192+
Check: resource.ComposeTestCheckFunc(
193+
testAccBigtableRowKeySchemaExists(t, "google_bigtable_table.table", true),
194+
),
195+
},
196+
{
197+
ResourceName: "google_bigtable_table.table",
198+
ImportState: true,
199+
ImportStateVerify: true,
200+
},
201+
{
202+
// In-place modification is not accepted
203+
Config: testAccBigtableTable_rowKeySchema(instanceName, tableName, family, `{
204+
"structType": {
205+
"fields": [{
206+
"fieldName": "newfieldname",
207+
"type": {
208+
"stringType": { "encoding": { "utf8Bytes": { } } }
209+
}
210+
}],
211+
"encoding": { "orderedCodeBytes": { } }
212+
}
213+
}`),
214+
ExpectError: regexp.MustCompile(".*Row key schema in-place modification is not allowed.*"),
215+
},
216+
{
217+
// Removing the schema is ok
218+
Config: testAccBigtableTable_family(instanceName, tableName, family),
219+
Check: resource.ComposeTestCheckFunc(
220+
testAccBigtableRowKeySchemaExists(t, "google_bigtable_table.table", false),
221+
),
222+
},
223+
{
224+
ResourceName: "google_bigtable_table.table",
225+
ImportState: true,
226+
ImportStateVerify: true,
227+
},
228+
// Set the schema to a new one is ok
229+
{
230+
Config: testAccBigtableTable_rowKeySchema(instanceName, tableName, family, `{
231+
"structType": {
232+
"fields": [
233+
{
234+
"fieldName": "mystringfield",
235+
"type": {
236+
"stringType": { "encoding": { "utf8Bytes": { } } }
237+
}
238+
},
239+
{
240+
"fieldName": "myintfield",
241+
"type": {
242+
"int64Type": { "encoding": { "bigEndianBytes": { } } }
243+
}
244+
}
245+
],
246+
"encoding": { "delimitedBytes": { "delimiter": "Iw==" } }
247+
}
248+
}`),
249+
Check: resource.ComposeTestCheckFunc(
250+
testAccBigtableRowKeySchemaExists(t, "google_bigtable_table.table", true),
251+
),
252+
},
253+
},
254+
})
255+
}
256+
166257
func TestAccBigtableTable_deletion_protection_protected(t *testing.T) {
167258
// bigtable instance does not use the shared HTTP client, this test creates an instance
168259
acctest.SkipIfVcr(t)
@@ -708,6 +799,35 @@ func testAccBigtableColumnFamilyExists(t *testing.T, table_name_space, family st
708799
}
709800
}
710801

802+
func testAccBigtableRowKeySchemaExists(t *testing.T, table_name_space string, expected_has_schema bool) resource.TestCheckFunc {
803+
ctx := context.Background()
804+
return func(s *terraform.State) error {
805+
rs, ok := s.RootModule().Resources[table_name_space]
806+
if !ok {
807+
return fmt.Errorf("Table not found during schema check: %v", table_name_space)
808+
}
809+
810+
config := acctest.GoogleProviderConfig(t)
811+
c, err := config.BigTableClientFactory(config.UserAgent).NewAdminClient(config.Project, rs.Primary.Attributes["instance_name"])
812+
if err != nil {
813+
return fmt.Errorf("Error starting admin client %s", err)
814+
}
815+
defer c.Close()
816+
817+
table, err := c.TableInfo(ctx, rs.Primary.Attributes["name"])
818+
if err != nil {
819+
return fmt.Errorf("Error retrieving table. Could not find %s in %s", rs.Primary.Attributes["name"], rs.Primary.Attributes["instance_name"])
820+
}
821+
822+
actual_has_schema := (table.RowKeySchema != nil)
823+
if actual_has_schema != expected_has_schema {
824+
return fmt.Errorf("expecting table to have row key schema to be %v, got %v", expected_has_schema, actual_has_schema)
825+
}
826+
827+
return nil
828+
}
829+
}
830+
711831
func testAccBigtableChangeStreamDisabled(t *testing.T) resource.TestCheckFunc {
712832
var ctx = context.Background()
713833
return func(s *terraform.State) error {
@@ -862,6 +982,35 @@ EOF
862982
`, instanceName, instanceName, tableName, family, familyType)
863983
}
864984

985+
func testAccBigtableTable_rowKeySchema(instanceName, tableName, family, rowKeySchema string) string {
986+
return fmt.Sprintf(`
987+
resource "google_bigtable_instance" "instance" {
988+
name = "%s"
989+
990+
cluster {
991+
cluster_id = "%s"
992+
zone = "us-central1-b"
993+
}
994+
995+
instance_type = "DEVELOPMENT"
996+
deletion_protection = false
997+
}
998+
999+
resource "google_bigtable_table" "table" {
1000+
name = "%s"
1001+
instance_name = google_bigtable_instance.instance.name
1002+
1003+
column_family {
1004+
family = "%s"
1005+
}
1006+
1007+
row_key_schema = <<EOF
1008+
%s
1009+
EOF
1010+
}
1011+
`, instanceName, instanceName, tableName, family, rowKeySchema)
1012+
}
1013+
8651014
func testAccBigtableTable_deletion_protection(instanceName, tableName, deletionProtection, family string) string {
8661015
return fmt.Sprintf(`
8671016
resource "google_bigtable_instance" "instance" {

0 commit comments

Comments
 (0)