Merge #3653: PostgreSQL provider
This commit is contained in:
commit
29d42662fe
|
@ -2,6 +2,7 @@
|
|||
|
||||
FEATURES:
|
||||
* **New provider: `vcd` - VMware vCloud Director** [GH-3785]
|
||||
* **New provider: `postgresql` - Create PostgreSQL databases and roles** [GH-3653]
|
||||
* **New resource: `google_pubsub_topic`** [GH-3671]
|
||||
* **New resource: `google_pubsub_subscription`** [GH-3671]
|
||||
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/builtin/providers/postgresql"
|
||||
"github.com/hashicorp/terraform/plugin"
|
||||
)
|
||||
|
||||
func main() {
|
||||
plugin.Serve(&plugin.ServeOpts{
|
||||
ProviderFunc: postgresql.Provider,
|
||||
})
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
package main
|
|
@ -0,0 +1,43 @@
|
|||
package postgresql
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
_ "github.com/lib/pq" //PostgreSQL db
|
||||
)
|
||||
|
||||
// Config - provider config
|
||||
type Config struct {
|
||||
Host string
|
||||
Port int
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
// Client struct holding connection string
|
||||
type Client struct {
|
||||
username string
|
||||
connStr string
|
||||
}
|
||||
|
||||
//NewClient returns new client config
|
||||
func (c *Config) NewClient() (*Client, error) {
|
||||
connStr := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=postgres", c.Host, c.Port, c.Username, c.Password)
|
||||
|
||||
client := Client{
|
||||
connStr: connStr,
|
||||
username: c.Username,
|
||||
}
|
||||
|
||||
return &client, nil
|
||||
}
|
||||
|
||||
//Connect will manually connect/diconnect to prevent a large number or db connections being made
|
||||
func (c *Client) Connect() (*sql.DB, error) {
|
||||
db, err := sql.Open("postgres", c.connStr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error connecting to postgresql server: %s", err)
|
||||
}
|
||||
|
||||
return db, nil
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
package postgresql
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
// Provider returns a terraform.ResourceProvider.
|
||||
func Provider() terraform.ResourceProvider {
|
||||
return &schema.Provider{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"host": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
DefaultFunc: schema.EnvDefaultFunc("POSTGRESQL_HOST", nil),
|
||||
Description: "The postgresql server address",
|
||||
},
|
||||
"port": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
Default: 5432,
|
||||
Description: "The postgresql server port",
|
||||
},
|
||||
"username": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
DefaultFunc: schema.EnvDefaultFunc("POSTGRESQL_USERNAME", nil),
|
||||
Description: "Username for postgresql server connection",
|
||||
},
|
||||
"password": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
DefaultFunc: schema.EnvDefaultFunc("POSTGRESQL_PASSWORD", nil),
|
||||
Description: "Password for postgresql server connection",
|
||||
},
|
||||
},
|
||||
|
||||
ResourcesMap: map[string]*schema.Resource{
|
||||
"postgresql_database": resourcePostgresqlDatabase(),
|
||||
"postgresql_role": resourcePostgresqlRole(),
|
||||
},
|
||||
|
||||
ConfigureFunc: providerConfigure,
|
||||
}
|
||||
}
|
||||
|
||||
func providerConfigure(d *schema.ResourceData) (interface{}, error) {
|
||||
config := Config{
|
||||
Host: d.Get("host").(string),
|
||||
Port: d.Get("port").(int),
|
||||
Username: d.Get("username").(string),
|
||||
Password: d.Get("password").(string),
|
||||
}
|
||||
|
||||
client, err := config.NewClient()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error initializing Postgresql client: %s", err)
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package postgresql
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
var testAccProviders map[string]terraform.ResourceProvider
|
||||
var testAccProvider *schema.Provider
|
||||
|
||||
func init() {
|
||||
testAccProvider = Provider().(*schema.Provider)
|
||||
testAccProviders = map[string]terraform.ResourceProvider{
|
||||
"postgresql": 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) {
|
||||
if v := os.Getenv("POSTGRESQL_HOST"); v == "" {
|
||||
t.Fatal("POSTGRESQL_HOST must be set for acceptance tests")
|
||||
}
|
||||
if v := os.Getenv("POSTGRESQL_USERNAME"); v == "" {
|
||||
t.Fatal("POSTGRESQL_USERNAME must be set for acceptance tests")
|
||||
}
|
||||
if v := os.Getenv("POSTGRESQL_PASSWORD"); v == "" {
|
||||
t.Fatal("POSTGRESQL_PASSWORD must be set for acceptance tests")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
package postgresql
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/lib/pq"
|
||||
)
|
||||
|
||||
func resourcePostgresqlDatabase() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Create: resourcePostgresqlDatabaseCreate,
|
||||
Read: resourcePostgresqlDatabaseRead,
|
||||
Update: resourcePostgresqlDatabaseUpdate,
|
||||
Delete: resourcePostgresqlDatabaseDelete,
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"name": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"owner": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ForceNew: false,
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func resourcePostgresqlDatabaseCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*Client)
|
||||
conn, err := client.Connect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
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 = ""
|
||||
}
|
||||
|
||||
//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("CREATE DATABASE %s %s", pq.QuoteIdentifier(dbName), dbOwnerCfg)
|
||||
_, err = conn.Query(query)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating postgresql database %s: %s", dbName, err)
|
||||
}
|
||||
|
||||
d.SetId(dbName)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourcePostgresqlDatabaseDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*Client)
|
||||
conn, err := client.Connect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
dbName := d.Get("name").(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))
|
||||
_, err = conn.Query(query)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.SetId("")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourcePostgresqlDatabaseRead(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*Client)
|
||||
conn, err := client.Connect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
dbName := d.Get("name").(string)
|
||||
|
||||
var owner string
|
||||
err = conn.QueryRow("SELECT pg_catalog.pg_get_userbyid(d.datdba) from pg_database d WHERE datname=$1", dbName).Scan(&owner)
|
||||
switch {
|
||||
case err == sql.ErrNoRows:
|
||||
d.SetId("")
|
||||
return nil
|
||||
case err != nil:
|
||||
return fmt.Errorf("Error reading info about database: %s", err)
|
||||
default:
|
||||
d.Set("owner", owner)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func resourcePostgresqlDatabaseUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*Client)
|
||||
conn, err := client.Connect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
dbName := d.Get("name").(string)
|
||||
|
||||
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 fmt.Errorf("Error updating owner for database: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return resourcePostgresqlDatabaseRead(d, meta)
|
||||
}
|
||||
|
||||
func grantRoleMembership(conn *sql.DB, dbOwner string, connUsername string) error {
|
||||
if dbOwner != "" && dbOwner != connUsername {
|
||||
query := fmt.Sprintf("GRANT %s TO %s", pq.QuoteIdentifier(dbOwner), pq.QuoteIdentifier(connUsername))
|
||||
_, err := conn.Query(query)
|
||||
if err != nil {
|
||||
//is already member or role
|
||||
if strings.Contains(err.Error(), "duplicate key value violates unique constraint") {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Error granting membership: %s", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
package postgresql
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"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{
|
||||
resource.TestStep{
|
||||
Config: testAccPostgresqlDatabaseConfig,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckPostgresqlDatabaseExists("postgresql_database.mydb", "myrole"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"postgresql_database.mydb", "name", "mydb"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"postgresql_database.mydb", "owner", "myrole"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccPostgresqlDatabase_DefaultOwner(t *testing.T) {
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckPostgresqlDatabaseDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccPostgresqlDatabaseConfig,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckPostgresqlDatabaseExists("postgresql_database.mydb_default_owner", ""),
|
||||
resource.TestCheckResourceAttr(
|
||||
"postgresql_database.mydb_default_owner", "name", "mydb_default_owner"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccCheckPostgresqlDatabaseDestroy(s *terraform.State) error {
|
||||
client := testAccProvider.Meta().(*Client)
|
||||
|
||||
for _, rs := range s.RootModule().Resources {
|
||||
if rs.Type != "postgresql_database" {
|
||||
continue
|
||||
}
|
||||
|
||||
exists, err := checkDatabaseExists(client, rs.Primary.ID)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error checking db %s", err)
|
||||
}
|
||||
|
||||
if exists {
|
||||
return fmt.Errorf("Db still exists after destroy")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func testAccCheckPostgresqlDatabaseExists(n string, owner string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
return fmt.Errorf("Resource not found: %s", n)
|
||||
}
|
||||
|
||||
if rs.Primary.ID == "" {
|
||||
return fmt.Errorf("No ID is set")
|
||||
}
|
||||
|
||||
actualOwner := rs.Primary.Attributes["owner"]
|
||||
if actualOwner != owner {
|
||||
return fmt.Errorf("Wrong owner for db expected %s got %s", owner, actualOwner)
|
||||
}
|
||||
|
||||
client := testAccProvider.Meta().(*Client)
|
||||
exists, err := checkDatabaseExists(client, rs.Primary.ID)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error checking db %s", err)
|
||||
}
|
||||
|
||||
if !exists {
|
||||
return fmt.Errorf("Db not found")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func checkDatabaseExists(client *Client, dbName string) (bool, error) {
|
||||
conn, err := client.Connect()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
var _rez int
|
||||
err = conn.QueryRow("SELECT 1 from pg_database d WHERE datname=$1", dbName).Scan(&_rez)
|
||||
switch {
|
||||
case err == sql.ErrNoRows:
|
||||
return false, nil
|
||||
case err != nil:
|
||||
return false, fmt.Errorf("Error reading info about database: %s", err)
|
||||
default:
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
var testAccPostgresqlDatabaseConfig = `
|
||||
resource "postgresql_role" "myrole" {
|
||||
name = "myrole"
|
||||
login = true
|
||||
}
|
||||
|
||||
resource "postgresql_database" "mydb" {
|
||||
name = "mydb"
|
||||
owner = "${postgresql_role.myrole.name}"
|
||||
}
|
||||
|
||||
resource "postgresql_database" "mydb2" {
|
||||
name = "mydb2"
|
||||
owner = "${postgresql_role.myrole.name}"
|
||||
}
|
||||
|
||||
resource "postgresql_database" "mydb_default_owner" {
|
||||
name = "mydb_default_owner"
|
||||
}
|
||||
|
||||
`
|
|
@ -0,0 +1,179 @@
|
|||
package postgresql
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/lib/pq"
|
||||
)
|
||||
|
||||
func resourcePostgresqlRole() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Create: resourcePostgresqlRoleCreate,
|
||||
Read: resourcePostgresqlRoleRead,
|
||||
Update: resourcePostgresqlRoleUpdate,
|
||||
Delete: resourcePostgresqlRoleDelete,
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"name": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"login": &schema.Schema{
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
ForceNew: false,
|
||||
Default: false,
|
||||
},
|
||||
"password": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ForceNew: false,
|
||||
},
|
||||
"encrypted": &schema.Schema{
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
ForceNew: false,
|
||||
Default: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func resourcePostgresqlRoleCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*Client)
|
||||
conn, err := client.Connect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
roleName := d.Get("name").(string)
|
||||
loginAttr := getLoginStr(d.Get("login").(bool))
|
||||
password := d.Get("password").(string)
|
||||
|
||||
encryptedCfg := getEncryptedStr(d.Get("encrypted").(bool))
|
||||
|
||||
query := fmt.Sprintf("CREATE ROLE %s %s %s PASSWORD '%s'", pq.QuoteIdentifier(roleName), loginAttr, encryptedCfg, password)
|
||||
_, err = conn.Query(query)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating role: %s", err)
|
||||
}
|
||||
|
||||
d.SetId(roleName)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourcePostgresqlRoleDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*Client)
|
||||
conn, err := client.Connect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
roleName := d.Get("name").(string)
|
||||
|
||||
query := fmt.Sprintf("DROP ROLE %s", pq.QuoteIdentifier(roleName))
|
||||
_, err = conn.Query(query)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.SetId("")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourcePostgresqlRoleRead(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*Client)
|
||||
conn, err := client.Connect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
roleName := d.Get("name").(string)
|
||||
|
||||
var canLogin bool
|
||||
err = conn.QueryRow("select rolcanlogin from pg_roles where rolname=$1", roleName).Scan(&canLogin)
|
||||
switch {
|
||||
case err == sql.ErrNoRows:
|
||||
d.SetId("")
|
||||
return nil
|
||||
case err != nil:
|
||||
return fmt.Errorf("Error reading info about role: %s", err)
|
||||
default:
|
||||
d.Set("login", canLogin)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func resourcePostgresqlRoleUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*Client)
|
||||
conn, err := client.Connect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
d.Partial(true)
|
||||
|
||||
roleName := d.Get("name").(string)
|
||||
|
||||
if d.HasChange("login") {
|
||||
loginAttr := getLoginStr(d.Get("login").(bool))
|
||||
query := fmt.Sprintf("ALTER ROLE %s %s", pq.QuoteIdentifier(roleName), pq.QuoteIdentifier(loginAttr))
|
||||
_, err := conn.Query(query)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error updating login attribute for role: %s", err)
|
||||
}
|
||||
|
||||
d.SetPartial("login")
|
||||
}
|
||||
|
||||
password := d.Get("password").(string)
|
||||
if d.HasChange("password") {
|
||||
encryptedCfg := getEncryptedStr(d.Get("encrypted").(bool))
|
||||
|
||||
query := fmt.Sprintf("ALTER ROLE %s %s PASSWORD '%s'", pq.QuoteIdentifier(roleName), encryptedCfg, password)
|
||||
_, err := conn.Query(query)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error updating password attribute for role: %s", err)
|
||||
}
|
||||
|
||||
d.SetPartial("password")
|
||||
}
|
||||
|
||||
if d.HasChange("encrypted") {
|
||||
encryptedCfg := getEncryptedStr(d.Get("encrypted").(bool))
|
||||
|
||||
query := fmt.Sprintf("ALTER ROLE %s %s PASSWORD '%s'", pq.QuoteIdentifier(roleName), encryptedCfg, password)
|
||||
_, err := conn.Query(query)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error updating encrypted attribute for role: %s", err)
|
||||
}
|
||||
|
||||
d.SetPartial("encrypted")
|
||||
}
|
||||
|
||||
d.Partial(false)
|
||||
return resourcePostgresqlRoleRead(d, meta)
|
||||
}
|
||||
|
||||
func getLoginStr(canLogin bool) string {
|
||||
if canLogin {
|
||||
return "login"
|
||||
}
|
||||
return "nologin"
|
||||
}
|
||||
|
||||
func getEncryptedStr(isEncrypted bool) string {
|
||||
if isEncrypted {
|
||||
return "encrypted"
|
||||
}
|
||||
return "unencrypted"
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
package postgresql
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestAccPostgresqlRole_Basic(t *testing.T) {
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckPostgresqlRoleDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccPostgresqlRoleConfig,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckPostgresqlRoleExists("postgresql_role.myrole2", "true"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"postgresql_role.myrole2", "name", "myrole2"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"postgresql_role.myrole2", "login", "true"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccCheckPostgresqlRoleDestroy(s *terraform.State) error {
|
||||
client := testAccProvider.Meta().(*Client)
|
||||
|
||||
for _, rs := range s.RootModule().Resources {
|
||||
if rs.Type != "postgresql_role" {
|
||||
continue
|
||||
}
|
||||
|
||||
exists, err := checkRoleExists(client, rs.Primary.ID)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error checking role %s", err)
|
||||
}
|
||||
|
||||
if exists {
|
||||
return fmt.Errorf("Role still exists after destroy")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func testAccCheckPostgresqlRoleExists(n string, canLogin string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
return fmt.Errorf("Resource not found: %s", n)
|
||||
}
|
||||
|
||||
if rs.Primary.ID == "" {
|
||||
return fmt.Errorf("No ID is set")
|
||||
}
|
||||
|
||||
actualCanLogin := rs.Primary.Attributes["login"]
|
||||
if actualCanLogin != canLogin {
|
||||
return fmt.Errorf("Wrong value for login expected %s got %s", canLogin, actualCanLogin)
|
||||
}
|
||||
|
||||
client := testAccProvider.Meta().(*Client)
|
||||
exists, err := checkRoleExists(client, rs.Primary.ID)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error checking role %s", err)
|
||||
}
|
||||
|
||||
if !exists {
|
||||
return fmt.Errorf("Role not found")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func checkRoleExists(client *Client, roleName string) (bool, error) {
|
||||
conn, err := client.Connect()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
var _rez int
|
||||
err = conn.QueryRow("SELECT 1 from pg_roles d WHERE rolname=$1", roleName).Scan(&_rez)
|
||||
switch {
|
||||
case err == sql.ErrNoRows:
|
||||
return false, nil
|
||||
case err != nil:
|
||||
return false, fmt.Errorf("Error reading info about role: %s", err)
|
||||
default:
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
var testAccPostgresqlRoleConfig = `
|
||||
resource "postgresql_role" "myrole2" {
|
||||
name = "myrole2"
|
||||
login = true
|
||||
}
|
||||
|
||||
resource "postgresql_role" "role_with_pwd" {
|
||||
name = "role_with_pwd"
|
||||
login = true
|
||||
password = "mypass"
|
||||
}
|
||||
|
||||
resource "postgresql_role" "role_with_pwd_encr" {
|
||||
name = "role_with_pwd_encr"
|
||||
login = true
|
||||
password = "mypass"
|
||||
encrypted = true
|
||||
}
|
||||
|
||||
resource "postgresql_role" "role_with_pwd_no_login" {
|
||||
name = "role_with_pwd_no_login"
|
||||
password = "mypass"
|
||||
}
|
||||
|
||||
resource "postgresql_role" "role_simple" {
|
||||
name = "role_simple"
|
||||
}
|
||||
`
|
|
@ -21,6 +21,7 @@ body.layout-heroku,
|
|||
body.layout-mailgun,
|
||||
body.layout-openstack,
|
||||
body.layout-packet,
|
||||
body.layout-postgresql,
|
||||
body.layout-rundeck,
|
||||
body.layout-statuscake,
|
||||
body.layout-template,
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
---
|
||||
layout: "postgresql"
|
||||
page_title: "Provider: PostgreSQL"
|
||||
sidebar_current: "docs-postgresql-index"
|
||||
description: |-
|
||||
A provider for PostgreSQL Server.
|
||||
---
|
||||
|
||||
# PostgreSQL Provider
|
||||
|
||||
The PostgreSQL provider gives the ability to deploy and configure resources in a PostgreSQL server.
|
||||
|
||||
Use the navigation to the left to read about the available resources.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
provider "postgresql" {
|
||||
host = "postgres_server_ip"
|
||||
port = 5432
|
||||
username = "postgres_user"
|
||||
password = "postgres_password"
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Configuring multiple servers can be done by specifying the alias option.
|
||||
|
||||
```
|
||||
provider "postgresql" {
|
||||
alias = "pg1"
|
||||
host = "postgres_server_ip1"
|
||||
username = "postgres_user1"
|
||||
password = "postgres_password1"
|
||||
}
|
||||
|
||||
provider "postgresql" {
|
||||
alias = "pg2"
|
||||
host = "postgres_server_ip2"
|
||||
username = "postgres_user2"
|
||||
password = "postgres_password2"
|
||||
}
|
||||
|
||||
resource "postgresql_database" "my_db1" {
|
||||
provider = "postgresql.pg1"
|
||||
name = "my_db1"
|
||||
}
|
||||
resource "postgresql_database" "my_db2" {
|
||||
provider = "postgresql.pg2"
|
||||
name = "my_db2"
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
|
||||
## Argument Reference
|
||||
|
||||
The following arguments are supported:
|
||||
|
||||
* `host` - (Required) The address for the postgresql server connection.
|
||||
* `port` - (Optional) The port for the postgresql server connection. (Default 5432)
|
||||
* `username` - (Required) Username for the server connection.
|
||||
* `password` - (Optional) Password for the server connection.
|
|
@ -0,0 +1,30 @@
|
|||
---
|
||||
layout: "postgresql"
|
||||
page_title: "PostgreSQL: postgresql_database"
|
||||
sidebar_current: "docs-postgresql-resource-postgresql_database"
|
||||
description: |-
|
||||
Creates and manages a database on a PostgreSQL server.
|
||||
---
|
||||
|
||||
# postgresql\_database
|
||||
|
||||
The ``postgresql_database`` resource creates and manages a database on a PostgreSQL
|
||||
server.
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
resource "postgresql_database" "my_db" {
|
||||
name = "my_db"
|
||||
owner = "my_role
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Argument Reference
|
||||
|
||||
* `name` - (Required) The name of the database. Must be unique on the PostgreSQL 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.
|
|
@ -0,0 +1,37 @@
|
|||
---
|
||||
layout: "postgresql"
|
||||
page_title: "PostgreSQL: postgresql_role"
|
||||
sidebar_current: "docs-postgresql-resource-postgresql_role"
|
||||
description: |-
|
||||
Creates and manages a database on a PostgreSQL server.
|
||||
---
|
||||
|
||||
# postgresql\_role
|
||||
|
||||
The ``postgresql_role`` resource creates and manages a role on a PostgreSQL
|
||||
server.
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
resource "postgresql_role" "my_role" {
|
||||
name = "my_role"
|
||||
login = true
|
||||
password = "mypass"
|
||||
encrypted = true
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Argument Reference
|
||||
|
||||
* `name` - (Required) The name of the role. Must be unique on the PostgreSQL server instance
|
||||
where it is configured.
|
||||
|
||||
* `login` - (Optional) Configures whether a role is allowed to log in; that is, whether the role can be given as the initial session authorization name during client connection. Corresponds to the LOGIN/NOLOGIN
|
||||
clauses in 'CREATE ROLE'. Default value is false.
|
||||
|
||||
* `password` - (Optional) Sets the role's password. (A password is only of use for roles having the LOGIN attribute, but you can nonetheless define one for roles without it.) If you do not plan to use password authentication you can omit this option. If no password is specified, the password will be set to null and password authentication will always fail for that user.
|
||||
|
||||
* `encrypted` - (Optional) Corresponds to ENCRYPTED, UNENCRYPTED in PostgreSQL. This controls whether the password is stored encrypted in the system catalogs. Default is false.
|
|
@ -181,6 +181,10 @@
|
|||
<a href="/docs/providers/packet/index.html">Packet</a>
|
||||
</li>
|
||||
|
||||
<li<%= sidebar_current("docs-providers-postgresql") %>>
|
||||
<a href="/docs/providers/postgresql/index.html">PostgreSQL</a>
|
||||
</li>
|
||||
|
||||
<li<%= sidebar_current("docs-providers-rundeck") %>>
|
||||
<a href="/docs/providers/rundeck/index.html">Rundeck</a>
|
||||
</li>
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
<% wrap_layout :inner do %>
|
||||
<% content_for :sidebar do %>
|
||||
<div class="docs-sidebar hidden-print affix-top" role="complementary">
|
||||
<ul class="nav docs-sidenav">
|
||||
<li<%= sidebar_current("docs-home") %>>
|
||||
<a href="/docs/providers/index.html">« Documentation Home</a>
|
||||
</li>
|
||||
|
||||
<li<%= sidebar_current("docs-postgresql-index") %>>
|
||||
<a href="/docs/providers/postgresql/index.html">PostgreSQL Provider</a>
|
||||
</li>
|
||||
|
||||
<li<%= sidebar_current(/^docs-postgresql-resource/) %>>
|
||||
<a href="#">Resources</a>
|
||||
<ul class="nav nav-visible">
|
||||
<li<%= sidebar_current("docs-postgresql-resource-postgresql_database") %>>
|
||||
<a href="/docs/providers/postgresql/r/postgresql_database.html">postgresql_database</a>
|
||||
</li>
|
||||
<li<%= sidebar_current("docs-postgresql-resource-postgresql_role") %>>
|
||||
<a href="/docs/providers/postgresql/r/postgresql_role.html">postgresql_role</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= yield %>
|
||||
<% end %>
|
Loading…
Reference in New Issue