`postgresql_database` resource provider should now be feature complete.
* Add support to import databases. See docs. * Add support for renaming databases * Add support for all known PostgreSQL database attributes, including: * "allow_connections" * "lc_ctype" * "lc_collate" * "connection_limit" * "encoding" * "is_template" * "owner" * "tablespace_name" * "template"
This commit is contained in:
parent
59f4ad6fd1
commit
5280c37bea
|
@ -2,7 +2,9 @@ package postgresql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/hashicorp/errwrap"
|
"github.com/hashicorp/errwrap"
|
||||||
|
@ -10,71 +12,89 @@ import (
|
||||||
"github.com/lib/pq"
|
"github.com/lib/pq"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
dbAllowConnsAttr = "allow_connections"
|
||||||
|
dbCTypeAttr = "lc_ctype"
|
||||||
|
dbCollationAttr = "lc_collate"
|
||||||
|
dbConnLimitAttr = "connection_limit"
|
||||||
|
dbEncodingAttr = "encoding"
|
||||||
|
dbIsTemplateAttr = "is_template"
|
||||||
|
dbNameAttr = "name"
|
||||||
|
dbOwnerAttr = "owner"
|
||||||
|
dbTablespaceAttr = "tablespace_name"
|
||||||
|
dbTemplateAttr = "template"
|
||||||
|
)
|
||||||
|
|
||||||
func resourcePostgreSQLDatabase() *schema.Resource {
|
func resourcePostgreSQLDatabase() *schema.Resource {
|
||||||
return &schema.Resource{
|
return &schema.Resource{
|
||||||
Create: resourcePostgreSQLDatabaseCreate,
|
Create: resourcePostgreSQLDatabaseCreate,
|
||||||
Read: resourcePostgreSQLDatabaseRead,
|
Read: resourcePostgreSQLDatabaseRead,
|
||||||
Update: resourcePostgreSQLDatabaseUpdate,
|
Update: resourcePostgreSQLDatabaseUpdate,
|
||||||
Delete: resourcePostgreSQLDatabaseDelete,
|
Delete: resourcePostgreSQLDatabaseDelete,
|
||||||
|
Importer: &schema.ResourceImporter{
|
||||||
|
State: schema.ImportStatePassthrough,
|
||||||
|
},
|
||||||
|
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
"name": {
|
dbNameAttr: {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
Description: "The PostgreSQL database name to connect to",
|
Description: "The PostgreSQL database name to connect to",
|
||||||
},
|
},
|
||||||
"owner": {
|
dbOwnerAttr: {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Computed: true,
|
Computed: true,
|
||||||
Description: "The role name of the user who will own the new database",
|
Description: "The role name of the user who will own the new database",
|
||||||
},
|
},
|
||||||
"template": {
|
dbTemplateAttr: {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
Computed: true,
|
||||||
Description: "The name of the template from which to create the new database",
|
Description: "The name of the template from which to create the new database",
|
||||||
},
|
},
|
||||||
"encoding": {
|
dbEncodingAttr: {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Computed: true,
|
Computed: true,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
Description: "Character set encoding to use in the new database",
|
Description: "Character set encoding to use in the new database",
|
||||||
},
|
},
|
||||||
"lc_collate": {
|
dbCollationAttr: {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Computed: true,
|
Computed: true,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
Description: "Collation order (LC_COLLATE) to use in the new database",
|
Description: "Collation order (LC_COLLATE) to use in the new database",
|
||||||
},
|
},
|
||||||
"lc_ctype": {
|
dbCTypeAttr: {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Computed: true,
|
Computed: true,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
Description: "Character classification (LC_CTYPE) to use in the new database",
|
Description: "Character classification (LC_CTYPE) to use in the new database",
|
||||||
},
|
},
|
||||||
"tablespace_name": {
|
dbTablespaceAttr: {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Computed: true,
|
Computed: true,
|
||||||
Description: "The name of the tablespace that will be associated with the new database",
|
Description: "The name of the tablespace that will be associated with the new database",
|
||||||
},
|
},
|
||||||
"connection_limit": {
|
dbConnLimitAttr: {
|
||||||
Type: schema.TypeInt,
|
Type: schema.TypeInt,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Computed: true,
|
Computed: true,
|
||||||
Description: "How many concurrent connections can be made to this database",
|
Description: "How many concurrent connections can be made to this database",
|
||||||
ValidateFunc: validateConnLimit,
|
ValidateFunc: validateConnLimit,
|
||||||
},
|
},
|
||||||
"allow_connections": {
|
dbAllowConnsAttr: {
|
||||||
Type: schema.TypeBool,
|
Type: schema.TypeBool,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Computed: true,
|
Computed: true,
|
||||||
Description: "If false then no one can connect to this database",
|
Description: "If false then no one can connect to this database",
|
||||||
},
|
},
|
||||||
"is_template": {
|
dbIsTemplateAttr: {
|
||||||
Type: schema.TypeBool,
|
Type: schema.TypeBool,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Computed: true,
|
Computed: true,
|
||||||
|
@ -93,46 +113,101 @@ func validateConnLimit(v interface{}, key string) (warnings []string, errors []e
|
||||||
}
|
}
|
||||||
|
|
||||||
func resourcePostgreSQLDatabaseCreate(d *schema.ResourceData, meta interface{}) error {
|
func resourcePostgreSQLDatabaseCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
client := meta.(*Client)
|
c := meta.(*Client)
|
||||||
conn, err := client.Connect()
|
conn, err := c.Connect()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errwrap.Wrapf("Error connecting to PostgreSQL: {{err}}", err)
|
return errwrap.Wrapf("Error connecting to PostgreSQL: {{err}}", err)
|
||||||
}
|
}
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
connUsername := client.username
|
|
||||||
|
|
||||||
const numOptions = 9
|
|
||||||
createOpts := make([]string, 0, numOptions)
|
|
||||||
|
|
||||||
stringOpts := []struct {
|
stringOpts := []struct {
|
||||||
hclKey string
|
hclKey string
|
||||||
sqlKey string
|
sqlKey string
|
||||||
}{
|
}{
|
||||||
{"owner", "OWNER"},
|
{dbOwnerAttr, "OWNER"},
|
||||||
{"template", "TEMPLATE"},
|
{dbTemplateAttr, "TEMPLATE"},
|
||||||
{"encoding", "ENCODING"},
|
{dbEncodingAttr, "ENCODING"},
|
||||||
{"lc_collate", "LC_COLLATE"},
|
{dbCollationAttr, "LC_COLLATE"},
|
||||||
{"lc_ctype", "LC_CTYPE"},
|
{dbCTypeAttr, "LC_CTYPE"},
|
||||||
{"tablespace_name", "TABLESPACE"},
|
{dbTablespaceAttr, "TABLESPACE"},
|
||||||
}
|
}
|
||||||
|
intOpts := []struct {
|
||||||
|
hclKey string
|
||||||
|
sqlKey string
|
||||||
|
}{
|
||||||
|
{dbConnLimitAttr, "CONNECTION LIMIT"},
|
||||||
|
}
|
||||||
|
boolOpts := []struct {
|
||||||
|
hclKey string
|
||||||
|
sqlKey string
|
||||||
|
}{
|
||||||
|
{dbAllowConnsAttr, "ALLOW_CONNECTIONS"},
|
||||||
|
{dbIsTemplateAttr, "IS_TEMPLATE"},
|
||||||
|
}
|
||||||
|
|
||||||
|
createOpts := make([]string, 0, len(stringOpts)+len(intOpts)+len(boolOpts))
|
||||||
|
|
||||||
for _, opt := range stringOpts {
|
for _, opt := range stringOpts {
|
||||||
v, ok := d.GetOk(opt.hclKey)
|
v, ok := d.GetOk(opt.hclKey)
|
||||||
var val string
|
var val string
|
||||||
if !ok {
|
if !ok {
|
||||||
// Set the owner to the connection username
|
switch {
|
||||||
if opt.hclKey == "owner" && v.(string) == "" {
|
case opt.hclKey == dbOwnerAttr && v.(string) == "":
|
||||||
val = connUsername
|
// No owner specified in the config, default to using
|
||||||
} else {
|
// the connecting username.
|
||||||
|
val = c.username
|
||||||
|
case strings.ToUpper(v.(string)) == "DEFAULT" &&
|
||||||
|
(opt.hclKey == dbTemplateAttr ||
|
||||||
|
opt.hclKey == dbEncodingAttr ||
|
||||||
|
opt.hclKey == dbCollationAttr ||
|
||||||
|
opt.hclKey == dbCTypeAttr):
|
||||||
|
|
||||||
|
// Use the defaults from the template database
|
||||||
|
// as opposed to best practices.
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val = v.(string)
|
val = v.(string)
|
||||||
|
|
||||||
// Set the owner to the connection username
|
switch {
|
||||||
if opt.hclKey == "owner" && val == "" {
|
case opt.hclKey == dbOwnerAttr && (val == "" || strings.ToUpper(val) == "DEFAULT"):
|
||||||
val = connUsername
|
// Owner was blank/DEFAULT, default to using the connecting username.
|
||||||
|
val = c.username
|
||||||
|
d.Set(dbOwnerAttr, val)
|
||||||
|
case opt.hclKey == dbTablespaceAttr && (val == "" || strings.ToUpper(val) == "DEFAULT"):
|
||||||
|
val = "pg_default"
|
||||||
|
d.Set(dbTablespaceAttr, val)
|
||||||
|
case opt.hclKey == dbTemplateAttr:
|
||||||
|
if val == "" {
|
||||||
|
val = "template0"
|
||||||
|
d.Set(dbTemplateAttr, val)
|
||||||
|
} else if strings.ToUpper(val) == "DEFAULT" {
|
||||||
|
val = ""
|
||||||
|
}
|
||||||
|
case opt.hclKey == dbEncodingAttr:
|
||||||
|
if val == "" {
|
||||||
|
val = "UTF8"
|
||||||
|
d.Set(dbEncodingAttr, val)
|
||||||
|
} else if strings.ToUpper(val) == "DEFAULT" {
|
||||||
|
val = ""
|
||||||
|
}
|
||||||
|
case opt.hclKey == dbCollationAttr:
|
||||||
|
if val == "" {
|
||||||
|
val = "C"
|
||||||
|
d.Set(dbCollationAttr, val)
|
||||||
|
} else if strings.ToUpper(val) == "DEFAULT" {
|
||||||
|
val = ""
|
||||||
|
}
|
||||||
|
case opt.hclKey == dbCTypeAttr:
|
||||||
|
if val == "" {
|
||||||
|
val = "C"
|
||||||
|
d.Set(dbCTypeAttr, val)
|
||||||
|
} else if strings.ToUpper(val) == "DEFAULT" {
|
||||||
|
val = ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if val != "" {
|
if val != "" {
|
||||||
|
@ -140,12 +215,6 @@ func resourcePostgreSQLDatabaseCreate(d *schema.ResourceData, meta interface{})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
intOpts := []struct {
|
|
||||||
hclKey string
|
|
||||||
sqlKey string
|
|
||||||
}{
|
|
||||||
{"connection_limit", "CONNECTION LIMIT"},
|
|
||||||
}
|
|
||||||
for _, opt := range intOpts {
|
for _, opt := range intOpts {
|
||||||
v, ok := d.GetOk(opt.hclKey)
|
v, ok := d.GetOk(opt.hclKey)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -156,13 +225,6 @@ func resourcePostgreSQLDatabaseCreate(d *schema.ResourceData, meta interface{})
|
||||||
createOpts = append(createOpts, fmt.Sprintf("%s=%d", opt.sqlKey, val))
|
createOpts = append(createOpts, fmt.Sprintf("%s=%d", opt.sqlKey, val))
|
||||||
}
|
}
|
||||||
|
|
||||||
boolOpts := []struct {
|
|
||||||
hclKey string
|
|
||||||
sqlKey string
|
|
||||||
}{
|
|
||||||
{"allow_connections", "ALLOW_CONNECTIONS"},
|
|
||||||
{"is_template", "IS_TEMPLATE"},
|
|
||||||
}
|
|
||||||
for _, opt := range boolOpts {
|
for _, opt := range boolOpts {
|
||||||
v, ok := d.GetOk(opt.hclKey)
|
v, ok := d.GetOk(opt.hclKey)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -176,7 +238,7 @@ func resourcePostgreSQLDatabaseCreate(d *schema.ResourceData, meta interface{})
|
||||||
createOpts = append(createOpts, fmt.Sprintf("%s=%s", opt.sqlKey, valStr))
|
createOpts = append(createOpts, fmt.Sprintf("%s=%s", opt.sqlKey, valStr))
|
||||||
}
|
}
|
||||||
|
|
||||||
dbName := d.Get("name").(string)
|
dbName := d.Get(dbNameAttr).(string)
|
||||||
createStr := strings.Join(createOpts, " ")
|
createStr := strings.Join(createOpts, " ")
|
||||||
if len(createOpts) > 0 {
|
if len(createOpts) > 0 {
|
||||||
createStr = " WITH " + createStr
|
createStr = " WITH " + createStr
|
||||||
|
@ -193,22 +255,14 @@ func resourcePostgreSQLDatabaseCreate(d *schema.ResourceData, meta interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func resourcePostgreSQLDatabaseDelete(d *schema.ResourceData, meta interface{}) error {
|
func resourcePostgreSQLDatabaseDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
client := meta.(*Client)
|
c := meta.(*Client)
|
||||||
conn, err := client.Connect()
|
conn, err := c.Connect()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errwrap.Wrapf("Error connecting to PostgreSQL: {{err}}", err)
|
return errwrap.Wrapf("Error connecting to PostgreSQL: {{err}}", err)
|
||||||
}
|
}
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
dbName := d.Get("name").(string)
|
dbName := d.Get(dbNameAttr).(string)
|
||||||
connUsername := client.username
|
|
||||||
dbOwner := d.Get("owner").(string)
|
|
||||||
//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
|
|
||||||
}
|
|
||||||
|
|
||||||
query := fmt.Sprintf("DROP DATABASE %s", pq.QuoteIdentifier(dbName))
|
query := fmt.Sprintf("DROP DATABASE %s", pq.QuoteIdentifier(dbName))
|
||||||
_, err = conn.Query(query)
|
_, err = conn.Query(query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -221,50 +275,94 @@ func resourcePostgreSQLDatabaseDelete(d *schema.ResourceData, meta interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func resourcePostgreSQLDatabaseRead(d *schema.ResourceData, meta interface{}) error {
|
func resourcePostgreSQLDatabaseRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
client := meta.(*Client)
|
c := meta.(*Client)
|
||||||
conn, err := client.Connect()
|
conn, err := c.Connect()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
dbName := d.Get("name").(string)
|
dbId := d.Id()
|
||||||
|
var dbName, ownerName string
|
||||||
var owner string
|
err = conn.QueryRow("SELECT d.datname, pg_catalog.pg_get_userbyid(d.datdba) from pg_database d WHERE datname=$1", dbId).Scan(&dbName, &ownerName)
|
||||||
err = conn.QueryRow("SELECT pg_catalog.pg_get_userbyid(d.datdba) from pg_database d WHERE datname=$1", dbName).Scan(&owner)
|
|
||||||
switch {
|
switch {
|
||||||
case err == sql.ErrNoRows:
|
case err == sql.ErrNoRows:
|
||||||
|
log.Printf("[WARN] PostgreSQL database (%s) not found", d.Id())
|
||||||
|
d.SetId("")
|
||||||
|
return nil
|
||||||
|
case err != nil:
|
||||||
|
return errwrap.Wrapf("Error reading database: {{err}}", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var dbEncoding, dbCollation, dbCType, dbTablespaceName string
|
||||||
|
var dbConnLimit int
|
||||||
|
var dbAllowConns, dbIsTemplate bool
|
||||||
|
err = conn.QueryRow(`SELECT pg_catalog.pg_encoding_to_char(d.encoding), d.datcollate, d.datctype, ts.spcname, d.datconnlimit, d.datallowconn, d.datistemplate FROM pg_catalog.pg_database AS d, pg_catalog.pg_tablespace AS ts WHERE d.datname = $1 AND d.dattablespace = ts.oid`, dbId).
|
||||||
|
Scan(
|
||||||
|
&dbEncoding, &dbCollation, &dbCType, &dbTablespaceName,
|
||||||
|
&dbConnLimit, &dbAllowConns, &dbIsTemplate,
|
||||||
|
)
|
||||||
|
switch {
|
||||||
|
case err == sql.ErrNoRows:
|
||||||
|
log.Printf("[WARN] PostgreSQL database (%s) not found", d.Id())
|
||||||
d.SetId("")
|
d.SetId("")
|
||||||
return nil
|
return nil
|
||||||
case err != nil:
|
case err != nil:
|
||||||
return errwrap.Wrapf("Error reading database: {{err}}", err)
|
return errwrap.Wrapf("Error reading database: {{err}}", err)
|
||||||
default:
|
default:
|
||||||
d.Set("owner", owner)
|
d.Set(dbNameAttr, dbName)
|
||||||
|
d.Set(dbOwnerAttr, ownerName)
|
||||||
|
d.Set(dbEncodingAttr, dbEncoding)
|
||||||
|
d.Set(dbCollationAttr, dbCollation)
|
||||||
|
d.Set(dbCTypeAttr, dbCType)
|
||||||
|
d.Set(dbTablespaceAttr, dbTablespaceName)
|
||||||
|
d.Set(dbConnLimitAttr, dbConnLimit)
|
||||||
|
d.Set(dbAllowConnsAttr, dbAllowConns)
|
||||||
|
d.Set(dbIsTemplateAttr, dbIsTemplate)
|
||||||
|
dbTemplate := d.Get(dbTemplateAttr).(string)
|
||||||
|
if dbTemplate == "" {
|
||||||
|
dbTemplate = "template0"
|
||||||
|
}
|
||||||
|
d.Set(dbTemplateAttr, dbTemplate)
|
||||||
|
d.SetId(dbName)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func resourcePostgreSQLDatabaseUpdate(d *schema.ResourceData, meta interface{}) error {
|
func resourcePostgreSQLDatabaseUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
client := meta.(*Client)
|
c := meta.(*Client)
|
||||||
conn, err := client.Connect()
|
conn, err := c.Connect()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
dbName := d.Get("name").(string)
|
if err := setDBName(conn, d); err != nil {
|
||||||
|
return err
|
||||||
if d.HasChange("owner") {
|
|
||||||
owner := d.Get("owner").(string)
|
|
||||||
if owner != "" {
|
|
||||||
query := fmt.Sprintf("ALTER DATABASE %s OWNER TO %s", pq.QuoteIdentifier(dbName), pq.QuoteIdentifier(owner))
|
|
||||||
_, err := conn.Query(query)
|
|
||||||
if err != nil {
|
|
||||||
return errwrap.Wrapf("Error updating owner: {{err}}", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := setDBOwner(conn, d); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := setDBTablespace(conn, d); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := setDBConnLimit(conn, d); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := setDBAllowConns(conn, d); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := setDBIsTemplate(conn, d); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty values: ALTER DATABASE name RESET configuration_parameter;
|
||||||
|
|
||||||
return resourcePostgreSQLDatabaseRead(d, meta)
|
return resourcePostgreSQLDatabaseRead(d, meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,7 +371,7 @@ func grantRoleMembership(conn *sql.DB, dbOwner string, connUsername string) erro
|
||||||
query := fmt.Sprintf("GRANT %s TO %s", pq.QuoteIdentifier(dbOwner), pq.QuoteIdentifier(connUsername))
|
query := fmt.Sprintf("GRANT %s TO %s", pq.QuoteIdentifier(dbOwner), pq.QuoteIdentifier(connUsername))
|
||||||
_, err := conn.Query(query)
|
_, err := conn.Query(query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
//is already member or role
|
// is already member or role
|
||||||
if strings.Contains(err.Error(), "duplicate key value violates unique constraint") {
|
if strings.Contains(err.Error(), "duplicate key value violates unique constraint") {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -282,3 +380,109 @@ func grantRoleMembership(conn *sql.DB, dbOwner string, connUsername string) erro
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setDBName(conn *sql.DB, d *schema.ResourceData) error {
|
||||||
|
if !d.HasChange(dbNameAttr) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
oraw, nraw := d.GetChange(dbNameAttr)
|
||||||
|
o := oraw.(string)
|
||||||
|
n := nraw.(string)
|
||||||
|
if n == "" {
|
||||||
|
return errors.New("Error setting database name to an empty string")
|
||||||
|
}
|
||||||
|
|
||||||
|
query := fmt.Sprintf("ALTER DATABASE %s RENAME TO %s", pq.QuoteIdentifier(o), pq.QuoteIdentifier(n))
|
||||||
|
if _, err := conn.Query(query); err != nil {
|
||||||
|
return errwrap.Wrapf("Error updating database name: {{err}}", err)
|
||||||
|
}
|
||||||
|
d.SetId(n)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setDBOwner(conn *sql.DB, d *schema.ResourceData) error {
|
||||||
|
if !d.HasChange(dbOwnerAttr) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
owner := d.Get(dbOwnerAttr).(string)
|
||||||
|
if owner == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
dbName := d.Get(dbNameAttr).(string)
|
||||||
|
query := fmt.Sprintf("ALTER DATABASE %s OWNER TO %s", pq.QuoteIdentifier(dbName), pq.QuoteIdentifier(owner))
|
||||||
|
if _, err := conn.Query(query); err != nil {
|
||||||
|
return errwrap.Wrapf("Error updating database OWNER: {{err}}", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setDBTablespace(conn *sql.DB, d *schema.ResourceData) error {
|
||||||
|
if !d.HasChange(dbTablespaceAttr) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tbspName := d.Get(dbTablespaceAttr).(string)
|
||||||
|
dbName := d.Get(dbNameAttr).(string)
|
||||||
|
var query string
|
||||||
|
if tbspName == "" || strings.ToUpper(tbspName) == "DEFAULT" {
|
||||||
|
query = fmt.Sprintf("ALTER DATABASE %s RESET TABLESPACE", pq.QuoteIdentifier(dbName))
|
||||||
|
} else {
|
||||||
|
query = fmt.Sprintf("ALTER DATABASE %s SET TABLESPACE %s", pq.QuoteIdentifier(dbName), pq.QuoteIdentifier(tbspName))
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := conn.Query(query); err != nil {
|
||||||
|
return errwrap.Wrapf("Error updating database TABLESPACE: {{err}}", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setDBConnLimit(conn *sql.DB, d *schema.ResourceData) error {
|
||||||
|
if !d.HasChange(dbConnLimitAttr) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
connLimit := d.Get(dbConnLimitAttr).(int)
|
||||||
|
dbName := d.Get(dbNameAttr).(string)
|
||||||
|
query := fmt.Sprintf("ALTER DATABASE %s CONNECTION LIMIT = %d", pq.QuoteIdentifier(dbName), connLimit)
|
||||||
|
if _, err := conn.Query(query); err != nil {
|
||||||
|
return errwrap.Wrapf("Error updating database CONNECTION LIMIT: {{err}}", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setDBAllowConns(conn *sql.DB, d *schema.ResourceData) error {
|
||||||
|
if !d.HasChange(dbAllowConnsAttr) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
allowConns := d.Get(dbAllowConnsAttr).(bool)
|
||||||
|
dbName := d.Get(dbNameAttr).(string)
|
||||||
|
query := fmt.Sprintf("ALTER DATABASE %s ALLOW_CONNECTIONS %t", pq.QuoteIdentifier(dbName), allowConns)
|
||||||
|
if _, err := conn.Query(query); err != nil {
|
||||||
|
return errwrap.Wrapf("Error updating database ALLOW_CONNECTIONS: {{err}}", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setDBIsTemplate(conn *sql.DB, d *schema.ResourceData) error {
|
||||||
|
if !d.HasChange(dbIsTemplateAttr) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
isTemplate := d.Get(dbIsTemplateAttr).(bool)
|
||||||
|
dbName := d.Get(dbNameAttr).(string)
|
||||||
|
query := fmt.Sprintf("ALTER DATABASE %s IS_TEMPLATE %t", pq.QuoteIdentifier(dbName), isTemplate)
|
||||||
|
if _, err := conn.Query(query); err != nil {
|
||||||
|
return errwrap.Wrapf("Error updating database IS_TEMPLATE: {{err}}", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -25,6 +25,26 @@ func TestAccPostgresqlDatabase_Basic(t *testing.T) {
|
||||||
"postgresql_database.mydb", "name", "mydb"),
|
"postgresql_database.mydb", "name", "mydb"),
|
||||||
resource.TestCheckResourceAttr(
|
resource.TestCheckResourceAttr(
|
||||||
"postgresql_database.mydb", "owner", "myrole"),
|
"postgresql_database.mydb", "owner", "myrole"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"postgresql_database.all_opts", "owner", "myrole"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"postgresql_database.all_opts", "name", "all_opts_name"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"postgresql_database.all_opts", "template", "template0"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"postgresql_database.all_opts", "encoding", "UTF8"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"postgresql_database.all_opts", "lc_collate", "C"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"postgresql_database.all_opts", "lc_ctype", "C"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"postgresql_database.all_opts", "tablespace_name", "pg_default"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"postgresql_database.all_opts", "connection_limit", "-1"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"postgresql_database.all_opts", "allow_connections", "false"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"postgresql_database.all_opts", "is_template", "false"),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -134,11 +154,11 @@ resource "postgresql_database" "mydb2" {
|
||||||
owner = "${postgresql_role.myrole.name}"
|
owner = "${postgresql_role.myrole.name}"
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "postgresql_database" "mydb3" {
|
resource "postgresql_database" "all_opts" {
|
||||||
name = "mydb3"
|
name = "all_opts_name"
|
||||||
owner = "${postgresql_role.myrole.name}"
|
owner = "${postgresql_role.myrole.name}"
|
||||||
template = "template1"
|
template = "template0"
|
||||||
encoding = "SQL_ASCII"
|
encoding = "UTF8"
|
||||||
lc_collate = "C"
|
lc_collate = "C"
|
||||||
lc_ctype = "C"
|
lc_ctype = "C"
|
||||||
tablespace_name = "pg_default"
|
tablespace_name = "pg_default"
|
||||||
|
|
|
@ -159,6 +159,9 @@ To make a resource importable, please see the
|
||||||
* openstack_networking_secgroup_v2
|
* openstack_networking_secgroup_v2
|
||||||
* openstack_networking_subnet_v2
|
* openstack_networking_subnet_v2
|
||||||
|
|
||||||
|
### PostgreSQL
|
||||||
|
|
||||||
|
* postgresql_database
|
||||||
|
|
||||||
### Triton
|
### Triton
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,8 @@ description: |-
|
||||||
|
|
||||||
# postgresql\_database
|
# postgresql\_database
|
||||||
|
|
||||||
The ``postgresql_database`` resource creates and manages a database on a PostgreSQL
|
The ``postgresql_database`` resource creates and manages a database instance on
|
||||||
server.
|
a PostgreSQL server.
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
@ -18,13 +18,90 @@ server.
|
||||||
resource "postgresql_database" "my_db" {
|
resource "postgresql_database" "my_db" {
|
||||||
name = "my_db"
|
name = "my_db"
|
||||||
owner = "my_role"
|
owner = "my_role"
|
||||||
|
template = "template0"
|
||||||
|
collation = "C"
|
||||||
|
connection_limit = -1
|
||||||
|
allow_connections = true
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Argument Reference
|
## Argument Reference
|
||||||
|
|
||||||
* `name` - (Required) The name of the database. Must be unique on the PostgreSQL server instance
|
* `name` - (Required) The name of the database. Must be unique on the PostgreSQL
|
||||||
where it is configured.
|
server instance where it is configured.
|
||||||
|
|
||||||
* `owner` - (Optional) The owner role of the database. If not specified the default is the user executing the command. To create a database owned by another role, you must be a direct or indirect member of that role, or be a superuser.
|
* `owner` - (Optional) The role name of the user who will own the database, or
|
||||||
|
`DEFAULT` to use the default (namely, the user executing the command). To
|
||||||
|
create a database owned by another role or to change the owner of an existing
|
||||||
|
database, you must be a direct or indirect member of the specified role, or
|
||||||
|
the username in the provider is a superuser.
|
||||||
|
|
||||||
|
* `tablespace_name` - (Optional) The name of the tablespace that will be
|
||||||
|
associated with the database, or `DEFAULT` to use the template database's
|
||||||
|
tablespace. This tablespace will be the default tablespace used for objects
|
||||||
|
created in this database.
|
||||||
|
|
||||||
|
* `connection_limit` - (Optional) How many concurrent connections can be made to
|
||||||
|
this database. `-1` (the default) means no limit.
|
||||||
|
|
||||||
|
* `allow_connections` - (Optional) If `false` then no one can connect to this
|
||||||
|
database. The default is `true`, allowing connections (except as restricted by
|
||||||
|
other mechanisms, such as `GRANT` or `REVOKE CONNECT`).
|
||||||
|
|
||||||
|
* `is_template` - (Optional) If `true`, then this database can be cloned by any
|
||||||
|
user with `CREATEDB` privileges; if `false` (the default), then only
|
||||||
|
superusers or the owner of the database can clone it.
|
||||||
|
|
||||||
|
* `template` - (Optional) The name of the template database from which to create
|
||||||
|
the database, or `DEFAULT` to use the default template (`template0`). NOTE:
|
||||||
|
the default in Terraform is `template0`, not `template1`. Changing this value
|
||||||
|
will force the creation of a new resource as this value can only be changed
|
||||||
|
when a database is created.
|
||||||
|
|
||||||
|
* `encoding` - (Optional) Character set encoding to use in the database.
|
||||||
|
Specify a string constant (e.g. `UTF8` or `SQL_ASCII`), or an integer encoding
|
||||||
|
number. If unset or set to an empty string the default encoding is set to
|
||||||
|
`UTF8`. If set to `DEFAULT` Terraform will use the same encoding as the
|
||||||
|
template database. Changing this value will force the creation of a new
|
||||||
|
resource as this value can only be changed when a database is created.
|
||||||
|
|
||||||
|
* `lc_collate` - (Optional) Collation order (`LC_COLLATE`) to use in the
|
||||||
|
database. This affects the sort order applied to strings, e.g. in queries
|
||||||
|
with `ORDER BY`, as well as the order used in indexes on text columns. If
|
||||||
|
unset or set to an empty string the default collation is set to `C`. If set
|
||||||
|
to `DEFAULT` Terraform will use the same collation order as the specified
|
||||||
|
`template` database. Changing this value will force the creation of a new
|
||||||
|
resource as this value can only be changed when a database is created.
|
||||||
|
|
||||||
|
* `lc_ctype` - (Optional) Character classification (`LC_CTYPE`) to use in the
|
||||||
|
database. This affects the categorization of characters, e.g. lower, upper and
|
||||||
|
digit. If unset or set to an empty string the default character classification
|
||||||
|
is set to `C`. If set to `DEFAULT` Terraform will use the character
|
||||||
|
classification of the specified `template` database. Changing this value will
|
||||||
|
force the creation of a new resource as this value can only be changed when a
|
||||||
|
database is created.
|
||||||
|
|
||||||
|
## Import Example
|
||||||
|
|
||||||
|
`postgresql_database` supports importing resources. Supposing the following
|
||||||
|
Terraform:
|
||||||
|
|
||||||
|
```
|
||||||
|
provider "postgresql" {
|
||||||
|
alias = "admindb"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "postgresql_database" "db1" {
|
||||||
|
provider = "postgresql.admindb"
|
||||||
|
|
||||||
|
name = "db1"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
It is possible to import a `postgresql_database` resource with the following
|
||||||
|
command:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ terraform import postgresql_database.testdb1 testdb1
|
||||||
|
```
|
||||||
|
|
Loading…
Reference in New Issue