-
Notifications
You must be signed in to change notification settings - Fork 9.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
562 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package main | ||
|
||
import ( | ||
"github.com/hashicorp/terraform/builtin/providers/mysql" | ||
"github.com/hashicorp/terraform/plugin" | ||
) | ||
|
||
func main() { | ||
plugin.Serve(&plugin.ServeOpts{ | ||
ProviderFunc: mysql.Provider, | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
package main |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package mysql | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
mysqlc "github.com/ziutek/mymysql/thrsafe" | ||
|
||
"github.com/hashicorp/terraform/helper/schema" | ||
"github.com/hashicorp/terraform/terraform" | ||
) | ||
|
||
func Provider() terraform.ResourceProvider { | ||
return &schema.Provider{ | ||
Schema: map[string]*schema.Schema{ | ||
"endpoint": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
DefaultFunc: schema.EnvDefaultFunc("MYSQL_ENDPOINT", nil), | ||
}, | ||
|
||
"username": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
DefaultFunc: schema.EnvDefaultFunc("MYSQL_USERNAME", nil), | ||
}, | ||
|
||
"password": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Optional: true, | ||
DefaultFunc: schema.EnvDefaultFunc("MYSQL_PASSWORD", nil), | ||
}, | ||
}, | ||
|
||
ResourcesMap: map[string]*schema.Resource{ | ||
"mysql_database": resourceDatabase(), | ||
}, | ||
|
||
ConfigureFunc: providerConfigure, | ||
} | ||
} | ||
|
||
func providerConfigure(d *schema.ResourceData) (interface{}, error) { | ||
|
||
var username = d.Get("username").(string) | ||
var password = d.Get("password").(string) | ||
var endpoint = d.Get("endpoint").(string) | ||
|
||
proto := "tcp" | ||
if endpoint[0] == '/' { | ||
proto = "unix" | ||
} | ||
|
||
// mysqlc is the thread-safe implementation of mymysql, so we can | ||
// safely re-use the same connection between multiple parallel | ||
// operations. | ||
conn := mysqlc.New(proto, "", endpoint, username, password) | ||
|
||
err := conn.Connect() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return conn, nil | ||
} | ||
|
||
var identQuoteReplacer = strings.NewReplacer("`", "``") | ||
|
||
func quoteIdentifier(in string) string { | ||
return fmt.Sprintf("`%s`", identQuoteReplacer.Replace(in)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package mysql | ||
|
||
import ( | ||
"os" | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform/helper/schema" | ||
"github.com/hashicorp/terraform/terraform" | ||
) | ||
|
||
// To run these acceptance tests, you will need access to a MySQL server. | ||
// Amazon RDS is one way to get a MySQL server. If you use RDS, you can | ||
// use the root account credentials you specified when creating an RDS | ||
// instance to get the access necessary to run these tests. (the tests | ||
// assume full access to the server.) | ||
// | ||
// Set the MYSQL_ENDPOINT and MYSQL_USERNAME environment variables before | ||
// running the tests. If the given user has a password then you will also need | ||
// to set MYSQL_PASSWORD. | ||
// | ||
// The tests assume a reasonably-vanilla MySQL configuration. In particular, | ||
// they assume that the "utf8" character set is available and that | ||
// "utf8_bin" is a valid collation that isn't the default for that character | ||
// set. | ||
// | ||
// You can run the tests like this: | ||
// make testacc TEST=./builtin/providers/mysql | ||
|
||
var testAccProviders map[string]terraform.ResourceProvider | ||
var testAccProvider *schema.Provider | ||
|
||
func init() { | ||
testAccProvider = Provider().(*schema.Provider) | ||
testAccProviders = map[string]terraform.ResourceProvider{ | ||
"mysql": testAccProvider, | ||
} | ||
} | ||
|
||
func TestProvider(t *testing.T) { | ||
if err := Provider().(*schema.Provider).InternalValidate(); err != nil { | ||
t.Fatalf("err: %s", err) | ||
} | ||
} | ||
|
||
func TestProvider_impl(t *testing.T) { | ||
var _ terraform.ResourceProvider = Provider() | ||
} | ||
|
||
func testAccPreCheck(t *testing.T) { | ||
for _, name := range []string{"MYSQL_ENDPOINT", "MYSQL_USERNAME"} { | ||
if v := os.Getenv(name); v == "" { | ||
t.Fatal("MYSQL_ENDPOINT, MYSQL_USERNAME and optionally MYSQL_PASSWORD must be set for acceptance tests") | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
package mysql | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"strings" | ||
|
||
mysqlc "github.com/ziutek/mymysql/mysql" | ||
|
||
"github.com/hashicorp/terraform/helper/schema" | ||
) | ||
|
||
const defaultCharacterSetKeyword = "CHARACTER SET " | ||
const defaultCollateKeyword = "COLLATE " | ||
|
||
func resourceDatabase() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: CreateDatabase, | ||
Update: UpdateDatabase, | ||
Read: ReadDatabase, | ||
Delete: DeleteDatabase, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"name": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
|
||
"default_character_set": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Default: "utf8", | ||
}, | ||
|
||
"default_collation": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Default: "utf8_general_ci", | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func CreateDatabase(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(mysqlc.Conn) | ||
|
||
stmtSQL := databaseConfigSQL("CREATE", d) | ||
log.Println("Executing statement:", stmtSQL) | ||
|
||
_, _, err := conn.Query(stmtSQL) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
d.SetId(d.Get("name").(string)) | ||
|
||
return nil | ||
} | ||
|
||
func UpdateDatabase(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(mysqlc.Conn) | ||
|
||
stmtSQL := databaseConfigSQL("ALTER", d) | ||
log.Println("Executing statement:", stmtSQL) | ||
|
||
_, _, err := conn.Query(stmtSQL) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func ReadDatabase(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(mysqlc.Conn) | ||
|
||
// This is kinda flimsy-feeling, since it depends on the formatting | ||
// of the SHOW CREATE DATABASE output... but this data doesn't seem | ||
// to be available any other way, so hopefully MySQL keeps this | ||
// compatible in future releases. | ||
|
||
name := d.Id() | ||
stmtSQL := "SHOW CREATE DATABASE " + quoteIdentifier(name) | ||
|
||
log.Println("Executing query:", stmtSQL) | ||
rows, _, err := conn.Query(stmtSQL) | ||
if err != nil { | ||
if mysqlErr, ok := err.(*mysqlc.Error); ok { | ||
if mysqlErr.Code == mysqlc.ER_BAD_DB_ERROR { | ||
d.SetId("") | ||
return nil | ||
} | ||
} | ||
return err | ||
} | ||
|
||
row := rows[0] | ||
createSQL := string(row[1].([]byte)) | ||
|
||
defaultCharset := extractIdentAfter(createSQL, defaultCharacterSetKeyword) | ||
defaultCollation := extractIdentAfter(createSQL, defaultCollateKeyword) | ||
|
||
if defaultCollation == "" && defaultCharset != "" { | ||
// MySQL doesn't return the collation if it's the default one for | ||
// the charset, so if we don't have a collation we need to go | ||
// hunt for the default. | ||
stmtSQL := "SHOW COLLATION WHERE `Charset` = '%s' AND `Default` = 'Yes'" | ||
rows, _, err := conn.Query(stmtSQL, defaultCharset) | ||
if err != nil { | ||
return fmt.Errorf("Error getting default charset: %s", err) | ||
} | ||
if len(rows) == 0 { | ||
return fmt.Errorf("Charset %s has no default collation", defaultCharset) | ||
} | ||
row := rows[0] | ||
defaultCollation = string(row[0].([]byte)) | ||
} | ||
|
||
d.Set("default_character_set", defaultCharset) | ||
d.Set("default_collation", defaultCollation) | ||
|
||
return nil | ||
} | ||
|
||
func DeleteDatabase(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(mysqlc.Conn) | ||
|
||
name := d.Id() | ||
stmtSQL := "DROP DATABASE " + quoteIdentifier(name) | ||
log.Println("Executing statement:", stmtSQL) | ||
|
||
_, _, err := conn.Query(stmtSQL) | ||
if err == nil { | ||
d.SetId("") | ||
} | ||
return err | ||
} | ||
|
||
func databaseConfigSQL(verb string, d *schema.ResourceData) string { | ||
name := d.Get("name").(string) | ||
defaultCharset := d.Get("default_character_set").(string) | ||
defaultCollation := d.Get("default_collation").(string) | ||
|
||
var defaultCharsetClause string | ||
var defaultCollationClause string | ||
|
||
if defaultCharset != "" { | ||
defaultCharsetClause = defaultCharacterSetKeyword + quoteIdentifier(defaultCharset) | ||
} | ||
if defaultCollation != "" { | ||
defaultCollationClause = defaultCollateKeyword + quoteIdentifier(defaultCollation) | ||
} | ||
|
||
return fmt.Sprintf( | ||
"%s DATABASE %s %s %s", | ||
verb, | ||
quoteIdentifier(name), | ||
defaultCharsetClause, | ||
defaultCollationClause, | ||
) | ||
} | ||
|
||
func extractIdentAfter(sql string, keyword string) string { | ||
charsetIndex := strings.Index(sql, keyword) | ||
if charsetIndex != -1 { | ||
charsetIndex += len(keyword) | ||
remain := sql[charsetIndex:] | ||
spaceIndex := strings.IndexRune(remain, ' ') | ||
return remain[:spaceIndex] | ||
} | ||
|
||
return "" | ||
} |
Oops, something went wrong.