Skip to content

Commit c1c8d2a

Browse files
otherchenAndrew Chenzph
authored
Support placement_policy resource and database configuration (#7)
Supports creating a placement policy and adding it to a database: ``` resource "mysql_ti_placement_policy" "test" { name = "%s" primary_region = "%s" regions = %s constraints = %s } resource "mysql_database" "test" { name = "%s" default_character_set = "%s" default_collation = "%s" placement_policy = mysql_ti_placement_policy.test.name } ``` --------- Co-authored-by: Andrew Chen <andrewchen@plaid.com> Co-authored-by: Zander Hill <zander@xargs.io>
1 parent 29408c9 commit c1c8d2a

File tree

5 files changed

+474
-14
lines changed

5 files changed

+474
-14
lines changed

mysql/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ func Provider() *schema.Provider {
256256
"mysql_ti_config": resourceTiConfigVariable(),
257257
"mysql_ti_resource_group": resourceTiResourceGroup(),
258258
"mysql_ti_resource_group_user_assignment": resourceTiResourceGroupUserAssignment(),
259+
"mysql_ti_placement_policy": resourceTiPlacementPolicy(),
259260
"mysql_rds_config": resourceRDSConfig(),
260261
"mysql_default_roles": resourceDefaultRoles(),
261262
},

mysql/resource_database.go

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"errors"
77
"fmt"
88
"log"
9+
"regexp"
910
"strings"
1011

1112
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
@@ -15,8 +16,12 @@ import (
1516

1617
const defaultCharacterSetKeyword = "CHARACTER SET "
1718
const defaultCollateKeyword = "COLLATE "
19+
const placementPolicyKeyword = "PLACEMENT POLICY="
20+
const placementPolicyDefault = "default"
1821
const unknownDatabaseErrCode = 1049
1922

23+
var placementPolicyRegex = regexp.MustCompile(fmt.Sprintf("%s`([a-zA-Z0-9_-]+)`", placementPolicyKeyword))
24+
2025
func resourceDatabase() *schema.Resource {
2126
return &schema.Resource{
2227
CreateContext: CreateDatabase,
@@ -44,6 +49,12 @@ func resourceDatabase() *schema.Resource {
4449
Optional: true,
4550
Default: "utf8mb4_general_ci",
4651
},
52+
53+
"placement_policy": {
54+
Type: schema.TypeString,
55+
Optional: true,
56+
Default: "",
57+
},
4758
},
4859
}
4960
}
@@ -54,7 +65,10 @@ func CreateDatabase(ctx context.Context, d *schema.ResourceData, meta interface{
5465
return diag.FromErr(err)
5566
}
5667

57-
stmtSQL := databaseConfigSQL("CREATE", d)
68+
stmtSQL, err := databaseConfigSQL("CREATE", d, db)
69+
if err != nil {
70+
return diag.Errorf("failed constructing create SQL statement: %v", err)
71+
}
5872
log.Println("[DEBUG] Executing statement:", stmtSQL)
5973

6074
_, err = db.ExecContext(ctx, stmtSQL)
@@ -73,7 +87,10 @@ func UpdateDatabase(ctx context.Context, d *schema.ResourceData, meta interface{
7387
return diag.FromErr(err)
7488
}
7589

76-
stmtSQL := databaseConfigSQL("ALTER", d)
90+
stmtSQL, err := databaseConfigSQL("ALTER", d, db)
91+
if err != nil {
92+
return diag.Errorf("failed constructing update SQL statement: %v", err)
93+
}
7794
log.Println("[DEBUG] Executing statement:", stmtSQL)
7895

7996
_, err = db.ExecContext(ctx, stmtSQL)
@@ -111,6 +128,12 @@ func ReadDatabase(ctx context.Context, d *schema.ResourceData, meta interface{})
111128

112129
defaultCharset := extractIdentAfter(createSQL, defaultCharacterSetKeyword)
113130
defaultCollation := extractIdentAfter(createSQL, defaultCollateKeyword)
131+
placementPolicyMatches := placementPolicyRegex.FindStringSubmatch(createSQL)
132+
133+
placementPolicy := ""
134+
if len(placementPolicyMatches) >= 2 {
135+
placementPolicy = placementPolicyMatches[1]
136+
}
114137

115138
if defaultCollation == "" && defaultCharset != "" {
116139
// MySQL doesn't return the collation if it's the default one for
@@ -145,6 +168,7 @@ func ReadDatabase(ctx context.Context, d *schema.ResourceData, meta interface{})
145168
d.Set("name", name)
146169
d.Set("default_character_set", defaultCharset)
147170
d.Set("default_collation", defaultCollation)
171+
d.Set("placement_policy", placementPolicy)
148172

149173
return nil
150174
}
@@ -168,13 +192,15 @@ func DeleteDatabase(ctx context.Context, d *schema.ResourceData, meta interface{
168192
return nil
169193
}
170194

171-
func databaseConfigSQL(verb string, d *schema.ResourceData) string {
195+
func databaseConfigSQL(verb string, d *schema.ResourceData, db *sql.DB) (string, error) {
172196
name := d.Get("name").(string)
173197
defaultCharset := d.Get("default_character_set").(string)
174198
defaultCollation := d.Get("default_collation").(string)
199+
placementPolicy := d.Get("placement_policy").(string)
175200

176201
var defaultCharsetClause string
177202
var defaultCollationClause string
203+
var placementPolicyClause string
178204

179205
if defaultCharset != "" {
180206
defaultCharsetClause = defaultCharacterSetKeyword + quoteIdentifier(defaultCharset)
@@ -183,13 +209,29 @@ func databaseConfigSQL(verb string, d *schema.ResourceData) string {
183209
defaultCollationClause = defaultCollateKeyword + quoteIdentifier(defaultCollation)
184210
}
185211

212+
isTiDB, _, _, err := serverTiDB(db)
213+
if err != nil {
214+
return "", err
215+
}
216+
217+
if isTiDB {
218+
if placementPolicy != "" {
219+
placementPolicyClause = placementPolicyKeyword + quoteIdentifier(placementPolicy)
220+
} else {
221+
placementPolicyClause = placementPolicyKeyword + quoteIdentifier(placementPolicyDefault)
222+
}
223+
} else if placementPolicy != "" {
224+
return "", fmt.Errorf("placement_policy is only supported for TiDB")
225+
}
226+
186227
return fmt.Sprintf(
187-
"%s DATABASE %s %s %s",
228+
"%s DATABASE %s %s %s %s",
188229
verb,
189230
quoteIdentifier(name),
190231
defaultCharsetClause,
191232
defaultCollationClause,
192-
)
233+
placementPolicyClause,
234+
), nil
193235
}
194236

195237
func extractIdentAfter(sql string, keyword string) string {

mysql/resource_database_test.go

Lines changed: 67 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@ func TestAccDatabase_collationChange(t *testing.T) {
5151
CheckDestroy: testAccDatabaseCheckDestroy(dbName),
5252
Steps: []resource.TestStep{
5353
{
54-
Config: testAccDatabaseConfigFull(dbName, charset1, collation1),
54+
Config: testAccDatabaseConfigFull(dbName, charset1, collation1, ""),
5555
Check: resource.ComposeTestCheckFunc(
56-
testAccDatabaseCheckFull("mysql_database.test", dbName, charset1, collation1),
56+
testAccDatabaseCheckFull("mysql_database.test", dbName, charset1, collation1, ""),
5757
),
5858
},
5959
{
@@ -70,20 +70,58 @@ func TestAccDatabase_collationChange(t *testing.T) {
7070

7171
db.Exec(fmt.Sprintf("ALTER DATABASE %s CHARACTER SET %s COLLATE %s", dbName, charset2, collation2))
7272
},
73-
Config: testAccDatabaseConfigFull(dbName, charset1, collation1),
73+
Config: testAccDatabaseConfigFull(dbName, charset1, collation1, ""),
7474
Check: resource.ComposeTestCheckFunc(
75-
testAccDatabaseCheckFull(resourceName, dbName, charset1, collation1),
75+
testAccDatabaseCheckFull(resourceName, dbName, charset1, collation1, ""),
76+
),
77+
},
78+
},
79+
})
80+
}
81+
82+
func TestAccDatabase_placementPolicyChange(t *testing.T) {
83+
dbName := "terraform_acceptance_test"
84+
85+
charset1 := "latin1"
86+
collation1 := "latin1_bin"
87+
placementPolicy1 := "test_policy"
88+
placementPolicy2 := "test_policy_v2"
89+
placementPolicyResourceName := "mysql_ti_placement_policy.test.name"
90+
91+
resource.Test(t, resource.TestCase{
92+
PreCheck: func() {
93+
testAccPreCheckSkipNotTiDB(t)
94+
},
95+
ProviderFactories: testAccProviderFactories,
96+
CheckDestroy: testAccDatabaseCheckDestroy(dbName),
97+
Steps: []resource.TestStep{
98+
{
99+
Config: testAccDatabaseAndPlacementPolicy(dbName, charset1, collation1, placementPolicy1, placementPolicyResourceName),
100+
Check: resource.ComposeTestCheckFunc(
101+
testAccDatabaseCheckFull("mysql_database.test", dbName, charset1, collation1, placementPolicy1),
102+
),
103+
},
104+
{
105+
Config: testAccDatabaseAndPlacementPolicy(dbName, charset1, collation1, placementPolicy1, ""),
106+
Check: resource.ComposeTestCheckFunc(
107+
testAccDatabaseCheckFull("mysql_database.test", dbName, charset1, collation1, ""),
108+
),
109+
},
110+
{
111+
Config: testAccDatabaseAndPlacementPolicy(dbName, charset1, collation1, placementPolicy2, placementPolicyResourceName),
112+
Check: resource.ComposeTestCheckFunc(
113+
testAccDatabaseCheckFull("mysql_database.test", dbName, charset1, collation1, placementPolicy2),
76114
),
77115
},
78116
},
79117
})
80118
}
81119

82120
func testAccDatabaseCheckBasic(rn string, name string) resource.TestCheckFunc {
83-
return testAccDatabaseCheckFull(rn, name, "utf8mb4", "utf8mb4_bin")
121+
return testAccDatabaseCheckFull(rn, name, "utf8mb4", "utf8mb4_bin", "")
84122
}
85123

86-
func testAccDatabaseCheckFull(rn string, name string, charset string, collation string) resource.TestCheckFunc {
124+
func testAccDatabaseCheckFull(rn string, name string, charset string, collation string, placementPolicy string) resource.TestCheckFunc {
87125
return func(s *terraform.State) error {
88126
rs, ok := s.RootModule().Resources[rn]
89127
if !ok {
@@ -123,6 +161,12 @@ func testAccDatabaseCheckFull(rn string, name string, charset string, collation
123161
}
124162
}
125163

164+
if !strings.Contains(createSQL, fmt.Sprintf("PLACEMENT POLICY=`%s`", placementPolicy)) && placementPolicy != "" {
165+
return fmt.Errorf("placement policy expected %s", placementPolicy)
166+
} else if strings.Contains(createSQL, "PLACEMENT POLICY=") && placementPolicy == "" {
167+
return fmt.Errorf("placement policy expected to be empty")
168+
}
169+
126170
return nil
127171
}
128172
}
@@ -150,14 +194,28 @@ func testAccDatabaseCheckDestroy(name string) resource.TestCheckFunc {
150194
}
151195

152196
func testAccDatabaseConfigBasic(name string) string {
153-
return testAccDatabaseConfigFull(name, "utf8mb4", "utf8mb4_bin")
197+
return testAccDatabaseConfigFull(name, "utf8mb4", "utf8mb4_bin", "")
154198
}
155199

156-
func testAccDatabaseConfigFull(name string, charset string, collation string) string {
200+
func testAccDatabaseConfigFull(name string, charset string, collation string, placementPolicy string) string {
201+
placementPolicyConfig := ""
202+
if placementPolicy != "" {
203+
placementPolicyConfig = fmt.Sprintf(`placement_policy = %s`, placementPolicy)
204+
}
205+
157206
return fmt.Sprintf(`
158207
resource "mysql_database" "test" {
159208
name = "%s"
160209
default_character_set = "%s"
161210
default_collation = "%s"
162-
}`, name, charset, collation)
211+
%s
212+
}`, name, charset, collation, placementPolicyConfig)
213+
}
214+
215+
func testAccDatabaseAndPlacementPolicy(name string, charset string, collation string, placementPolicy string, databasePlacementPolicy string) string {
216+
return fmt.Sprintf(
217+
"%s\n%s",
218+
testAccPlacementPolicyConfigBasic(placementPolicy),
219+
testAccDatabaseConfigFull(name, charset, collation, databasePlacementPolicy),
220+
)
163221
}

0 commit comments

Comments
 (0)