From f3add9e7efbb3066a743d88a86cf8c8f61cc4c0a Mon Sep 17 00:00:00 2001 From: Sean Chittenden Date: Mon, 5 Sep 2016 18:40:35 -0700 Subject: [PATCH] Flesh out the CREATE DATABASE for PostgreSQL. --- .../resource_postgresql_database.go | 135 ++++++++++++++++-- .../resource_postgresql_database_test.go | 22 ++- 2 files changed, 139 insertions(+), 18 deletions(-) diff --git a/builtin/providers/postgresql/resource_postgresql_database.go b/builtin/providers/postgresql/resource_postgresql_database.go index 0cffcaa98..352493691 100644 --- a/builtin/providers/postgresql/resource_postgresql_database.go +++ b/builtin/providers/postgresql/resource_postgresql_database.go @@ -21,13 +21,61 @@ func resourcePostgreSQLDatabase() *schema.Resource { "name": { Type: schema.TypeString, Required: true, - ForceNew: true, }, "owner": { Type: schema.TypeString, Optional: true, Computed: true, }, + "template": { + Type: schema.TypeString, + Optional: true, + Description: "The name of the template from which to create the new database.", + }, + "encoding": { + Type: schema.TypeString, + Optional: true, + Description: "Character set encoding to use in the new database.", + }, + "lc_collate": { + Type: schema.TypeString, + Optional: true, + Description: "Collation order (LC_COLLATE) to use in the new database.", + }, + "lc_ctype": { + Type: schema.TypeString, + Optional: true, + Description: "Character classification (LC_CTYPE) to use in the new database.", + }, + "tablespace_name": { + Type: schema.TypeString, + Optional: true, + Description: "The name of the tablespace that will be associated with the new database.", + }, + "connection_limit": { + Type: schema.TypeInt, + Optional: true, + Description: "How many concurrent connections can be made to this database.", + ValidateFunc: func(v interface{}, key string) (warnings []string, errors []error) { + value := v.(int) + if value < -1 { + errors = append(errors, fmt.Errorf("%d can not be less than -1", key)) + } + return + }, + }, + "allow_connections": { + Type: schema.TypeBool, + Optional: true, + Default: true, + Description: "If false then no one can connect to this database.", + }, + "is_template": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "If true, then this database can be cloned by any user with CREATEDB privileges.", + }, }, } } @@ -40,24 +88,85 @@ func resourcePostgreSQLDatabaseCreate(d *schema.ResourceData, meta interface{}) } defer conn.Close() - dbName := d.Get("name").(string) - dbOwner := d.Get("owner").(string) connUsername := client.username - var dbOwnerCfg string - if dbOwner != "" { - dbOwnerCfg = fmt.Sprintf("WITH OWNER=%s", pq.QuoteIdentifier(dbOwner)) - } else { - dbOwnerCfg = "" + const numOptions = 9 + createOpts := make([]string, 0, numOptions) + + stringOpts := []struct { + hclKey string + sqlKey string + }{ + {"owner", "OWNER"}, + {"template", "TEMPLATE"}, + {"encoding", "ENCODING"}, + {"lc_collate", "LC_COLLATE"}, + {"lc_ctype", "LC_CTYPE"}, + {"tablespace_name", "TABLESPACE"}, + } + for _, opt := range stringOpts { + v, ok := d.GetOk(opt.hclKey) + var val string + if !ok { + // Set the owner to the connection username + if opt.hclKey == "owner" && v.(string) == "" { + val = connUsername + } else { + continue + } + } + + val = v.(string) + + // Set the owner to the connection username + if opt.hclKey == "owner" && val == "" { + val = connUsername + } + + if val != "" { + createOpts = append(createOpts, fmt.Sprintf("%s=%s", opt.sqlKey, pq.QuoteIdentifier(val))) + } } - //needed in order to set the owner of the db if the connection user is not a superuser - err = grantRoleMembership(conn, dbOwner, connUsername) - if err != nil { - return err + intOpts := []struct { + hclKey string + sqlKey string + }{ + {"connection_limit", "CONNECTION LIMIT"}, + } + for _, opt := range intOpts { + v, ok := d.GetOk(opt.hclKey) + if !ok { + continue + } + + val := v.(int) + createOpts = append(createOpts, fmt.Sprintf("%s=%d", opt.sqlKey, val)) } - query := fmt.Sprintf("CREATE DATABASE %s %s", pq.QuoteIdentifier(dbName), dbOwnerCfg) + boolOpts := []struct { + hclKey string + sqlKey string + }{ + {"allow_connections", "ALLOW_CONNECTIONS"}, + {"is_template", "IS_TEMPLATE"}, + } + for _, opt := range boolOpts { + v, ok := d.GetOk(opt.hclKey) + if !ok { + continue + } + + val := v.(bool) + createOpts = append(createOpts, fmt.Sprintf("%s=%t", opt.sqlKey, val)) + } + + dbName := d.Get("name").(string) + createStr := strings.Join(createOpts, " ") + if len(createOpts) > 0 { + createStr = " WITH " + createStr + } + query := fmt.Sprintf("CREATE DATABASE %s%s", pq.QuoteIdentifier(dbName), createStr) _, err = conn.Query(query) if err != nil { return errwrap.Wrapf(fmt.Sprintf("Error creating database %s: {{err}}", dbName), err) diff --git a/builtin/providers/postgresql/resource_postgresql_database_test.go b/builtin/providers/postgresql/resource_postgresql_database_test.go index 4fb20e918..a6a0dc4ef 100644 --- a/builtin/providers/postgresql/resource_postgresql_database_test.go +++ b/builtin/providers/postgresql/resource_postgresql_database_test.go @@ -6,19 +6,19 @@ import ( "testing" "errors" + "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" ) func TestAccPostgresqlDatabase_Basic(t *testing.T) { - resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckPostgresqlDatabaseDestroy, Steps: []resource.TestStep{ { - Config: testAccPostgresqlDatabaseConfig, + Config: testAccPostgreSQLDatabaseConfig, Check: resource.ComposeTestCheckFunc( testAccCheckPostgresqlDatabaseExists("postgresql_database.mydb"), resource.TestCheckResourceAttr( @@ -32,14 +32,13 @@ func TestAccPostgresqlDatabase_Basic(t *testing.T) { } func TestAccPostgresqlDatabase_DefaultOwner(t *testing.T) { - resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckPostgresqlDatabaseDestroy, Steps: []resource.TestStep{ { - Config: testAccPostgresqlDatabaseConfig, + Config: testAccPostgreSQLDatabaseConfig, Check: resource.ComposeTestCheckFunc( testAccCheckPostgresqlDatabaseExists("postgresql_database.mydb_default_owner"), resource.TestCheckResourceAttr( @@ -119,7 +118,7 @@ func checkDatabaseExists(client *Client, dbName string) (bool, error) { } } -var testAccPostgresqlDatabaseConfig = ` +var testAccPostgreSQLDatabaseConfig = ` resource "postgresql_role" "myrole" { name = "myrole" login = true @@ -135,6 +134,19 @@ resource "postgresql_database" "mydb2" { owner = "${postgresql_role.myrole.name}" } +resource "postgresql_database" "mydb3" { + name = "mydb3" + owner = "${postgresql_role.myrole.name}" + template = "template1" + encoding = "SQL_ASCII" + lc_collate = "C" + lc_ctype = "C" + tablespace_name = "pg_default" + connection_limit = -1 + allow_connections = false + is_template = false +} + resource "postgresql_database" "mydb_default_owner" { name = "mydb_default_owner" }