From 4ba89a1a8fdda1d288fb5b0aa711ca6fecc86dcf Mon Sep 17 00:00:00 2001 From: Adrian Chelaru Date: Tue, 27 Oct 2015 12:04:19 +0200 Subject: [PATCH 1/8] postgresql provider initial commit --- builtin/bins/provider-postgresql/main.go | 12 ++ builtin/bins/provider-postgresql/main_test.go | 1 + builtin/providers/postgresql/config.go | 25 ++++ builtin/providers/postgresql/provider.go | 55 +++++++++ builtin/providers/postgresql/provider_test.go | 41 +++++++ .../postgresql/resource_postgresql_db.go | 27 +++++ .../resource_postgresql_db_funcs.go | 76 ++++++++++++ .../postgresql/resource_postgresql_db_test.go | 109 ++++++++++++++++++ .../postgresql/resource_postgresql_role.go | 28 +++++ .../resource_postgresql_role_funcs.go | 84 ++++++++++++++ .../resource_postgresql_role_test.go | 104 +++++++++++++++++ 11 files changed, 562 insertions(+) create mode 100644 builtin/bins/provider-postgresql/main.go create mode 100644 builtin/bins/provider-postgresql/main_test.go create mode 100644 builtin/providers/postgresql/config.go create mode 100644 builtin/providers/postgresql/provider.go create mode 100644 builtin/providers/postgresql/provider_test.go create mode 100644 builtin/providers/postgresql/resource_postgresql_db.go create mode 100644 builtin/providers/postgresql/resource_postgresql_db_funcs.go create mode 100644 builtin/providers/postgresql/resource_postgresql_db_test.go create mode 100644 builtin/providers/postgresql/resource_postgresql_role.go create mode 100644 builtin/providers/postgresql/resource_postgresql_role_funcs.go create mode 100644 builtin/providers/postgresql/resource_postgresql_role_test.go diff --git a/builtin/bins/provider-postgresql/main.go b/builtin/bins/provider-postgresql/main.go new file mode 100644 index 000000000000..860ae37f487a --- /dev/null +++ b/builtin/bins/provider-postgresql/main.go @@ -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, + }) +} diff --git a/builtin/bins/provider-postgresql/main_test.go b/builtin/bins/provider-postgresql/main_test.go new file mode 100644 index 000000000000..06ab7d0f9a35 --- /dev/null +++ b/builtin/bins/provider-postgresql/main_test.go @@ -0,0 +1 @@ +package main diff --git a/builtin/providers/postgresql/config.go b/builtin/providers/postgresql/config.go new file mode 100644 index 000000000000..7883892a2e7b --- /dev/null +++ b/builtin/providers/postgresql/config.go @@ -0,0 +1,25 @@ +package postgresql + +import ( + "database/sql" + "fmt" + _ "github.com/lib/pq" +) + +type Config struct { + Host string + Username string + Password string +} + +// NewClient() return new db conn +func (c *Config) NewClient() (*sql.DB, error) { + connStr := fmt.Sprintf("host=%s user=%s password=%s", c.Host, c.Username, c.Password) + + db, err := sql.Open("postgres", connStr) + if err != nil { + return nil, fmt.Errorf("Error connecting to postgresql server: %s", err) + } + + return db, nil +} diff --git a/builtin/providers/postgresql/provider.go b/builtin/providers/postgresql/provider.go new file mode 100644 index 000000000000..ba2a7c7f6e54 --- /dev/null +++ b/builtin/providers/postgresql/provider.go @@ -0,0 +1,55 @@ +package postgresql + +import ( + "fmt" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/terraform" +) + +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", + }, + "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_db": resourcePostgresqlDb(), + "postgresql_role": resourcePostgresqlRole(), + }, + + ConfigureFunc: providerConfigure, + } +} + +func providerConfigure(d *schema.ResourceData) (interface{}, error) { + config := Config{ + Host: d.Get("host").(string), + 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 +} diff --git a/builtin/providers/postgresql/provider_test.go b/builtin/providers/postgresql/provider_test.go new file mode 100644 index 000000000000..401c130cece1 --- /dev/null +++ b/builtin/providers/postgresql/provider_test.go @@ -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") + } +} diff --git a/builtin/providers/postgresql/resource_postgresql_db.go b/builtin/providers/postgresql/resource_postgresql_db.go new file mode 100644 index 000000000000..b041d873c6ed --- /dev/null +++ b/builtin/providers/postgresql/resource_postgresql_db.go @@ -0,0 +1,27 @@ +package postgresql + +import ( + "github.com/hashicorp/terraform/helper/schema" +) + +func resourcePostgresqlDb() *schema.Resource { + return &schema.Resource{ + Create: resourcePostgresqlDbCreate, + Read: resourcePostgresqlDbRead, + Update: resourcePostgresqlDbUpdate, + Delete: resourcePostgresqlDbDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "owner": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: false, + }, + }, + } +} diff --git a/builtin/providers/postgresql/resource_postgresql_db_funcs.go b/builtin/providers/postgresql/resource_postgresql_db_funcs.go new file mode 100644 index 000000000000..e5672d458e77 --- /dev/null +++ b/builtin/providers/postgresql/resource_postgresql_db_funcs.go @@ -0,0 +1,76 @@ +package postgresql + +import ( + "fmt" + "database/sql" + + "github.com/lib/pq" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourcePostgresqlDbCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*sql.DB) + dbName := d.Get("name").(string) + dbOwner := d.Get("owner").(string) + + query := fmt.Sprintf("CREATE DATABASE %s WITH OWNER=%s", pq.QuoteIdentifier(dbName), pq.QuoteIdentifier(dbOwner)) + _, err := conn.Query(query) + if err != nil { + return fmt.Errorf("Error creating postgresql database: %s", err) + } + + d.SetId(dbName) + + return nil +} + +func resourcePostgresqlDbDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*sql.DB) + dbName := d.Get("name").(string) + + query := fmt.Sprintf("DROP DATABASE %s", pq.QuoteIdentifier(dbName)) + _, err := conn.Query(query) + if err != nil { + return err + } + + d.SetId("") + + return nil +} + +func resourcePostgresqlDbRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*sql.DB) + 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 + } + + return nil +} + +func resourcePostgresqlDbUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*sql.DB) + dbName := d.Get("name").(string) + + if d.HasChange("owner") { + owner := d.Get("owner").(string) + 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 resourcePostgresqlDbRead(d, meta) +} diff --git a/builtin/providers/postgresql/resource_postgresql_db_test.go b/builtin/providers/postgresql/resource_postgresql_db_test.go new file mode 100644 index 000000000000..fae3fce8ddff --- /dev/null +++ b/builtin/providers/postgresql/resource_postgresql_db_test.go @@ -0,0 +1,109 @@ +package postgresql + +import ( + "fmt" + "testing" + "database/sql" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccPostgresqlDb_Basic(t *testing.T) { + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckPostgresqlDbDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccPostgresqlDbConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckPostgresqlDbExists("postgresql_db.mydb", "myrole"), + resource.TestCheckResourceAttr( + "postgresql_db.mydb", "name", "mydb"), + resource.TestCheckResourceAttr( + "postgresql_db.mydb", "owner", "myrole"), + ), + }, + }, + }) +} + +func testAccCheckPostgresqlDbDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*sql.DB) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "postgresql_db" { + continue + } + + exists, err := checkDbExists(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 testAccCheckPostgresqlDbExists(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().(*sql.DB) + exists, err := checkDbExists(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 checkDbExists(conn *sql.DB, dbName string) (bool, error) { + 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 testAccPostgresqlDbConfig = ` +resource "postgresql_role" "myrole" { + name = "myrole" + login = true +} + +resource "postgresql_db" "mydb" { + name = "mydb" + owner = "${postgresql_role.myrole.name}" +} +` \ No newline at end of file diff --git a/builtin/providers/postgresql/resource_postgresql_role.go b/builtin/providers/postgresql/resource_postgresql_role.go new file mode 100644 index 000000000000..f7fb0e891330 --- /dev/null +++ b/builtin/providers/postgresql/resource_postgresql_role.go @@ -0,0 +1,28 @@ +package postgresql + +import ( + "github.com/hashicorp/terraform/helper/schema" +) + +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, + }, + }, + } +} diff --git a/builtin/providers/postgresql/resource_postgresql_role_funcs.go b/builtin/providers/postgresql/resource_postgresql_role_funcs.go new file mode 100644 index 000000000000..79007e785f3e --- /dev/null +++ b/builtin/providers/postgresql/resource_postgresql_role_funcs.go @@ -0,0 +1,84 @@ +package postgresql + +import ( + "fmt" + "database/sql" + + "github.com/lib/pq" + "github.com/hashicorp/terraform/helper/schema" +) + + +func resourcePostgresqlRoleCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*sql.DB) + roleName := d.Get("name").(string) + loginAttr := getLoginStr(d.Get("login").(bool)) + + query := fmt.Sprintf("CREATE ROLE %s %s", pq.QuoteIdentifier(roleName), pq.QuoteIdentifier(loginAttr)) + _, 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 { + conn := meta.(*sql.DB) + 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 { + conn := meta.(*sql.DB) + 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 + } + + return nil +} + +func resourcePostgresqlRoleUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*sql.DB) + 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) + } + } + + return resourcePostgresqlRoleRead(d, meta) +} + +func getLoginStr(canLogin bool) string { + if canLogin { + return "login" + } + return "nologin" +} diff --git a/builtin/providers/postgresql/resource_postgresql_role_test.go b/builtin/providers/postgresql/resource_postgresql_role_test.go new file mode 100644 index 000000000000..8bfa73b01857 --- /dev/null +++ b/builtin/providers/postgresql/resource_postgresql_role_test.go @@ -0,0 +1,104 @@ +package postgresql + +import ( + "fmt" + "testing" + "database/sql" + + "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().(*sql.DB) + + 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().(*sql.DB) + 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(conn *sql.DB, roleName string) (bool, error) { + 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 +} +` \ No newline at end of file From 06084cd1815919123711008881c7bdebf04c0524 Mon Sep 17 00:00:00 2001 From: Adrian Chelaru Date: Mon, 9 Nov 2015 13:15:33 +0200 Subject: [PATCH 2/8] postgresql provider improvements + documentation --- builtin/providers/postgresql/config.go | 3 +- builtin/providers/postgresql/provider.go | 11 +- builtin/providers/postgresql/provider_test.go | 42 ++--- ..._db.go => resource_postgresql_database.go} | 12 +- ... => resource_postgresql_database_funcs.go} | 34 ++-- .../resource_postgresql_database_test.go | 109 +++++++++++++ .../postgresql/resource_postgresql_db_test.go | 109 ------------- .../resource_postgresql_role_funcs.go | 7 +- .../resource_postgresql_role_test.go | 150 +++++++++--------- website/source/assets/stylesheets/_docs.scss | 1 + .../providers/postgresql/index.html.markdown | 63 ++++++++ .../r/postgresql_database.html.markdown | 30 ++++ .../r/postgresql_role.html.markdown | 31 ++++ website/source/layouts/docs.erb | 4 + website/source/layouts/postgresql.erb | 29 ++++ 15 files changed, 402 insertions(+), 233 deletions(-) rename builtin/providers/postgresql/{resource_postgresql_db.go => resource_postgresql_database.go} (58%) rename builtin/providers/postgresql/{resource_postgresql_db_funcs.go => resource_postgresql_database_funcs.go} (55%) create mode 100644 builtin/providers/postgresql/resource_postgresql_database_test.go delete mode 100644 builtin/providers/postgresql/resource_postgresql_db_test.go create mode 100644 website/source/docs/providers/postgresql/index.html.markdown create mode 100644 website/source/docs/providers/postgresql/r/postgresql_database.html.markdown create mode 100644 website/source/docs/providers/postgresql/r/postgresql_role.html.markdown create mode 100644 website/source/layouts/postgresql.erb diff --git a/builtin/providers/postgresql/config.go b/builtin/providers/postgresql/config.go index 7883892a2e7b..f48b112f5dca 100644 --- a/builtin/providers/postgresql/config.go +++ b/builtin/providers/postgresql/config.go @@ -8,13 +8,14 @@ import ( type Config struct { Host string + Port int Username string Password string } // NewClient() return new db conn func (c *Config) NewClient() (*sql.DB, error) { - connStr := fmt.Sprintf("host=%s user=%s password=%s", c.Host, c.Username, c.Password) + connStr := fmt.Sprintf("host=%s port=%d user=%s password=%s", c.Host, c.Port, c.Username, c.Password) db, err := sql.Open("postgres", connStr) if err != nil { diff --git a/builtin/providers/postgresql/provider.go b/builtin/providers/postgresql/provider.go index ba2a7c7f6e54..81a14186aed2 100644 --- a/builtin/providers/postgresql/provider.go +++ b/builtin/providers/postgresql/provider.go @@ -16,6 +16,12 @@ func Provider() terraform.ResourceProvider { 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, @@ -31,8 +37,8 @@ func Provider() terraform.ResourceProvider { }, ResourcesMap: map[string]*schema.Resource{ - "postgresql_db": resourcePostgresqlDb(), - "postgresql_role": resourcePostgresqlRole(), + "postgresql_database": resourcePostgresqlDatabase(), + "postgresql_role": resourcePostgresqlRole(), }, ConfigureFunc: providerConfigure, @@ -42,6 +48,7 @@ func Provider() terraform.ResourceProvider { 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), } diff --git a/builtin/providers/postgresql/provider_test.go b/builtin/providers/postgresql/provider_test.go index 401c130cece1..19c65cb38b93 100644 --- a/builtin/providers/postgresql/provider_test.go +++ b/builtin/providers/postgresql/provider_test.go @@ -1,41 +1,41 @@ package postgresql import ( - "os" - "testing" + "os" + "testing" - "github.com/hashicorp/terraform/helper/schema" - "github.com/hashicorp/terraform/terraform" + "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, - } + 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) - } + if err := Provider().(*schema.Provider).InternalValidate(); err != nil { + t.Fatalf("err: %s", err) + } } func TestProvider_impl(t *testing.T) { - var _ terraform.ResourceProvider = Provider() + 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") - } + 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") + } } diff --git a/builtin/providers/postgresql/resource_postgresql_db.go b/builtin/providers/postgresql/resource_postgresql_database.go similarity index 58% rename from builtin/providers/postgresql/resource_postgresql_db.go rename to builtin/providers/postgresql/resource_postgresql_database.go index b041d873c6ed..136321c2c138 100644 --- a/builtin/providers/postgresql/resource_postgresql_db.go +++ b/builtin/providers/postgresql/resource_postgresql_database.go @@ -4,12 +4,12 @@ import ( "github.com/hashicorp/terraform/helper/schema" ) -func resourcePostgresqlDb() *schema.Resource { +func resourcePostgresqlDatabase() *schema.Resource { return &schema.Resource{ - Create: resourcePostgresqlDbCreate, - Read: resourcePostgresqlDbRead, - Update: resourcePostgresqlDbUpdate, - Delete: resourcePostgresqlDbDelete, + Create: resourcePostgresqlDatabaseCreate, + Read: resourcePostgresqlDatabaseRead, + Update: resourcePostgresqlDatabaseUpdate, + Delete: resourcePostgresqlDatabaseDelete, Schema: map[string]*schema.Schema{ "name": &schema.Schema{ @@ -19,7 +19,7 @@ func resourcePostgresqlDb() *schema.Resource { }, "owner": &schema.Schema{ Type: schema.TypeString, - Required: true, + Optional: true, ForceNew: false, }, }, diff --git a/builtin/providers/postgresql/resource_postgresql_db_funcs.go b/builtin/providers/postgresql/resource_postgresql_database_funcs.go similarity index 55% rename from builtin/providers/postgresql/resource_postgresql_db_funcs.go rename to builtin/providers/postgresql/resource_postgresql_database_funcs.go index e5672d458e77..237cb8f213b0 100644 --- a/builtin/providers/postgresql/resource_postgresql_db_funcs.go +++ b/builtin/providers/postgresql/resource_postgresql_database_funcs.go @@ -1,19 +1,25 @@ package postgresql import ( - "fmt" "database/sql" + "fmt" - "github.com/lib/pq" "github.com/hashicorp/terraform/helper/schema" + "github.com/lib/pq" ) -func resourcePostgresqlDbCreate(d *schema.ResourceData, meta interface{}) error { +func resourcePostgresqlDatabaseCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*sql.DB) dbName := d.Get("name").(string) dbOwner := d.Get("owner").(string) + var dbOwnerCfg string + if dbOwner != "" { + dbOwnerCfg = fmt.Sprintf("WITH OWNER=%s", pq.QuoteIdentifier(dbOwner)) + } else { + dbOwnerCfg = "" + } - query := fmt.Sprintf("CREATE DATABASE %s WITH OWNER=%s", pq.QuoteIdentifier(dbName), pq.QuoteIdentifier(dbOwner)) + 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", err) @@ -24,7 +30,7 @@ func resourcePostgresqlDbCreate(d *schema.ResourceData, meta interface{}) error return nil } -func resourcePostgresqlDbDelete(d *schema.ResourceData, meta interface{}) error { +func resourcePostgresqlDatabaseDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*sql.DB) dbName := d.Get("name").(string) @@ -39,7 +45,7 @@ func resourcePostgresqlDbDelete(d *schema.ResourceData, meta interface{}) error return nil } -func resourcePostgresqlDbRead(d *schema.ResourceData, meta interface{}) error { +func resourcePostgresqlDatabaseRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*sql.DB) dbName := d.Get("name").(string) @@ -55,22 +61,22 @@ func resourcePostgresqlDbRead(d *schema.ResourceData, meta interface{}) error { d.Set("owner", owner) return nil } - - return nil } -func resourcePostgresqlDbUpdate(d *schema.ResourceData, meta interface{}) error { +func resourcePostgresqlDatabaseUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*sql.DB) dbName := d.Get("name").(string) if d.HasChange("owner") { owner := d.Get("owner").(string) - 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) + 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 resourcePostgresqlDbRead(d, meta) + return resourcePostgresqlDatabaseRead(d, meta) } diff --git a/builtin/providers/postgresql/resource_postgresql_database_test.go b/builtin/providers/postgresql/resource_postgresql_database_test.go new file mode 100644 index 000000000000..b4994f727f0e --- /dev/null +++ b/builtin/providers/postgresql/resource_postgresql_database_test.go @@ -0,0 +1,109 @@ +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 testAccCheckPostgresqlDatabaseDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*sql.DB) + + 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().(*sql.DB) + 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(conn *sql.DB, dbName string) (bool, error) { + 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}" +} +` diff --git a/builtin/providers/postgresql/resource_postgresql_db_test.go b/builtin/providers/postgresql/resource_postgresql_db_test.go deleted file mode 100644 index fae3fce8ddff..000000000000 --- a/builtin/providers/postgresql/resource_postgresql_db_test.go +++ /dev/null @@ -1,109 +0,0 @@ -package postgresql - -import ( - "fmt" - "testing" - "database/sql" - - "github.com/hashicorp/terraform/helper/resource" - "github.com/hashicorp/terraform/terraform" -) - -func TestAccPostgresqlDb_Basic(t *testing.T) { - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckPostgresqlDbDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: testAccPostgresqlDbConfig, - Check: resource.ComposeTestCheckFunc( - testAccCheckPostgresqlDbExists("postgresql_db.mydb", "myrole"), - resource.TestCheckResourceAttr( - "postgresql_db.mydb", "name", "mydb"), - resource.TestCheckResourceAttr( - "postgresql_db.mydb", "owner", "myrole"), - ), - }, - }, - }) -} - -func testAccCheckPostgresqlDbDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*sql.DB) - - for _, rs := range s.RootModule().Resources { - if rs.Type != "postgresql_db" { - continue - } - - exists, err := checkDbExists(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 testAccCheckPostgresqlDbExists(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().(*sql.DB) - exists, err := checkDbExists(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 checkDbExists(conn *sql.DB, dbName string) (bool, error) { - 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 testAccPostgresqlDbConfig = ` -resource "postgresql_role" "myrole" { - name = "myrole" - login = true -} - -resource "postgresql_db" "mydb" { - name = "mydb" - owner = "${postgresql_role.myrole.name}" -} -` \ No newline at end of file diff --git a/builtin/providers/postgresql/resource_postgresql_role_funcs.go b/builtin/providers/postgresql/resource_postgresql_role_funcs.go index 79007e785f3e..64b965ec9d25 100644 --- a/builtin/providers/postgresql/resource_postgresql_role_funcs.go +++ b/builtin/providers/postgresql/resource_postgresql_role_funcs.go @@ -1,14 +1,13 @@ package postgresql import ( - "fmt" "database/sql" + "fmt" - "github.com/lib/pq" "github.com/hashicorp/terraform/helper/schema" + "github.com/lib/pq" ) - func resourcePostgresqlRoleCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*sql.DB) roleName := d.Get("name").(string) @@ -56,8 +55,6 @@ func resourcePostgresqlRoleRead(d *schema.ResourceData, meta interface{}) error d.Set("login", canLogin) return nil } - - return nil } func resourcePostgresqlRoleUpdate(d *schema.ResourceData, meta interface{}) error { diff --git a/builtin/providers/postgresql/resource_postgresql_role_test.go b/builtin/providers/postgresql/resource_postgresql_role_test.go index 8bfa73b01857..51ee6182ba8c 100644 --- a/builtin/providers/postgresql/resource_postgresql_role_test.go +++ b/builtin/providers/postgresql/resource_postgresql_role_test.go @@ -1,99 +1,99 @@ package postgresql import ( - "fmt" - "testing" - "database/sql" + "database/sql" + "fmt" + "testing" - "github.com/hashicorp/terraform/helper/resource" - "github.com/hashicorp/terraform/terraform" + "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"), - ), - }, - }, - }) + 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().(*sql.DB) + client := testAccProvider.Meta().(*sql.DB) - for _, rs := range s.RootModule().Resources { - if rs.Type != "postgresql_role" { - continue - } + for _, rs := range s.RootModule().Resources { + if rs.Type != "postgresql_role" { + continue + } - exists, err := checkRoleExists(client, rs.Primary.ID) + exists, err := checkRoleExists(client, rs.Primary.ID) - if err != nil { - return fmt.Errorf("Error checking role %s", err) - } + if err != nil { + return fmt.Errorf("Error checking role %s", err) + } - if exists { - return fmt.Errorf("Role still exists after destroy") - } - } + if exists { + return fmt.Errorf("Role still exists after destroy") + } + } - return nil + 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().(*sql.DB) - 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 - } + 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().(*sql.DB) + 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(conn *sql.DB, roleName string) (bool, error) { - 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 _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 = ` @@ -101,4 +101,4 @@ resource "postgresql_role" "myrole2" { name = "myrole2" login = true } -` \ No newline at end of file +` diff --git a/website/source/assets/stylesheets/_docs.scss b/website/source/assets/stylesheets/_docs.scss index 0defd251a51e..8f6bd6f0e0e2 100755 --- a/website/source/assets/stylesheets/_docs.scss +++ b/website/source/assets/stylesheets/_docs.scss @@ -21,6 +21,7 @@ body.layout-heroku, body.layout-mailgun, body.layout-openstack, body.layout-packet, +body.layout-postgresql, body.layout-rundeck, body.layout-template, body.layout-vsphere, diff --git a/website/source/docs/providers/postgresql/index.html.markdown b/website/source/docs/providers/postgresql/index.html.markdown new file mode 100644 index 000000000000..f560f1f68ebf --- /dev/null +++ b/website/source/docs/providers/postgresql/index.html.markdown @@ -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. \ No newline at end of file diff --git a/website/source/docs/providers/postgresql/r/postgresql_database.html.markdown b/website/source/docs/providers/postgresql/r/postgresql_database.html.markdown new file mode 100644 index 000000000000..1badeacc6c25 --- /dev/null +++ b/website/source/docs/providers/postgresql/r/postgresql_database.html.markdown @@ -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\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. diff --git a/website/source/docs/providers/postgresql/r/postgresql_role.html.markdown b/website/source/docs/providers/postgresql/r/postgresql_role.html.markdown new file mode 100644 index 000000000000..69abaa3b5adf --- /dev/null +++ b/website/source/docs/providers/postgresql/r/postgresql_role.html.markdown @@ -0,0 +1,31 @@ +--- +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\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 +} + +``` + +## 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. Coresponds to the LOGIN/NOLOGIN +clauses in 'CREATE ROLE'. Default value is false. \ No newline at end of file diff --git a/website/source/layouts/docs.erb b/website/source/layouts/docs.erb index 937c120de4b6..1de1cb691cb5 100644 --- a/website/source/layouts/docs.erb +++ b/website/source/layouts/docs.erb @@ -181,6 +181,10 @@ Packet + > + Postgresql + + > Rundeck diff --git a/website/source/layouts/postgresql.erb b/website/source/layouts/postgresql.erb new file mode 100644 index 000000000000..276675985a08 --- /dev/null +++ b/website/source/layouts/postgresql.erb @@ -0,0 +1,29 @@ +<% wrap_layout :inner do %> + <% content_for :sidebar do %> + + <% end %> + + <%= yield %> + <% end %> \ No newline at end of file From 87f5ffda06f87fbdb6a090ffa7a38a38be290b60 Mon Sep 17 00:00:00 2001 From: Adrian Chelaru Date: Mon, 9 Nov 2015 13:48:29 +0200 Subject: [PATCH 3/8] fixed capitalization issue in postgresql provider docs --- .../source/docs/providers/postgresql/index.html.markdown | 8 ++++---- .../postgresql/r/postgresql_database.html.markdown | 8 ++++---- .../providers/postgresql/r/postgresql_role.html.markdown | 8 ++++---- website/source/layouts/docs.erb | 2 +- website/source/layouts/postgresql.erb | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/website/source/docs/providers/postgresql/index.html.markdown b/website/source/docs/providers/postgresql/index.html.markdown index f560f1f68ebf..36761b626a36 100644 --- a/website/source/docs/providers/postgresql/index.html.markdown +++ b/website/source/docs/providers/postgresql/index.html.markdown @@ -1,14 +1,14 @@ --- layout: "postgresql" -page_title: "Provider: Postgresql" +page_title: "Provider: PostgreSQL" sidebar_current: "docs-postgresql-index" description: |- - A provider for Postgresql Server. + A provider for PostgreSQL Server. --- -# Postgresql Provider +# PostgreSQL Provider -The Postgresql provider gives the ability to deploy and configure resources in a Postgresql server. +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. diff --git a/website/source/docs/providers/postgresql/r/postgresql_database.html.markdown b/website/source/docs/providers/postgresql/r/postgresql_database.html.markdown index 1badeacc6c25..8d17ff7abe57 100644 --- a/website/source/docs/providers/postgresql/r/postgresql_database.html.markdown +++ b/website/source/docs/providers/postgresql/r/postgresql_database.html.markdown @@ -1,14 +1,14 @@ --- layout: "postgresql" -page_title: "Postgresql: postgresql_database" +page_title: "PostgreSQL: postgresql_database" sidebar_current: "docs-postgresql-resource-postgresql_database" description: |- - Creates and manages a database on a Postgresql server. + Creates and manages a database on a PostgreSQL server. --- # postgresql\postgresql_database -The ``postgresql_database`` resource creates and manages a database on a Postgresql +The ``postgresql_database`` resource creates and manages a database on a PostgreSQL server. @@ -24,7 +24,7 @@ resource "postgresql_database" "my_db" { ## 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 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. diff --git a/website/source/docs/providers/postgresql/r/postgresql_role.html.markdown b/website/source/docs/providers/postgresql/r/postgresql_role.html.markdown index 69abaa3b5adf..e2801aabc5e6 100644 --- a/website/source/docs/providers/postgresql/r/postgresql_role.html.markdown +++ b/website/source/docs/providers/postgresql/r/postgresql_role.html.markdown @@ -1,14 +1,14 @@ --- layout: "postgresql" -page_title: "Postgresql: postgresql_role" +page_title: "PostgreSQL: postgresql_role" sidebar_current: "docs-postgresql-resource-postgresql_role" description: |- - Creates and manages a database on a Postgresql server. + Creates and manages a database on a PostgreSQL server. --- # postgresql\postgresql_role -The ``postgresql_role`` resource creates and manages a role on a Postgresql +The ``postgresql_role`` resource creates and manages a role on a PostgreSQL server. @@ -24,7 +24,7 @@ resource "postgresql_role" "my_role" { ## Argument Reference -* `name` - (Required) The name of the role. Must be unique on the Postgresql server instance +* `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. Coresponds to the LOGIN/NOLOGIN diff --git a/website/source/layouts/docs.erb b/website/source/layouts/docs.erb index 1de1cb691cb5..c47ec0c9a7a1 100644 --- a/website/source/layouts/docs.erb +++ b/website/source/layouts/docs.erb @@ -182,7 +182,7 @@ > - Postgresql + PostgreSQL > diff --git a/website/source/layouts/postgresql.erb b/website/source/layouts/postgresql.erb index 276675985a08..7375784ce64e 100644 --- a/website/source/layouts/postgresql.erb +++ b/website/source/layouts/postgresql.erb @@ -7,7 +7,7 @@ > - Postgresql Provider + PostgreSQL Provider > From 638f1320d7665d7d2c67b01d8af1ebe5f7dca8be Mon Sep 17 00:00:00 2001 From: Adrian Chelaru Date: Tue, 10 Nov 2015 11:43:33 +0200 Subject: [PATCH 4/8] set owner to computed + extended + small update on doc --- .../resource_postgresql_database.go | 1 + .../resource_postgresql_database_test.go | 24 +++++++++++++++++++ .../r/postgresql_database.html.markdown | 2 +- .../r/postgresql_role.html.markdown | 2 +- 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/builtin/providers/postgresql/resource_postgresql_database.go b/builtin/providers/postgresql/resource_postgresql_database.go index 136321c2c138..8d1e2c4b6be9 100644 --- a/builtin/providers/postgresql/resource_postgresql_database.go +++ b/builtin/providers/postgresql/resource_postgresql_database.go @@ -21,6 +21,7 @@ func resourcePostgresqlDatabase() *schema.Resource { Type: schema.TypeString, Optional: true, ForceNew: false, + Computed: true, }, }, } diff --git a/builtin/providers/postgresql/resource_postgresql_database_test.go b/builtin/providers/postgresql/resource_postgresql_database_test.go index b4994f727f0e..a2eb4fad01fe 100644 --- a/builtin/providers/postgresql/resource_postgresql_database_test.go +++ b/builtin/providers/postgresql/resource_postgresql_database_test.go @@ -30,6 +30,25 @@ 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{ + 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().(*sql.DB) @@ -106,4 +125,9 @@ resource "postgresql_database" "mydb" { name = "mydb" owner = "${postgresql_role.myrole.name}" } + +resource "postgresql_database" "mydb_default_owner" { + name = "mydb_default_owner" +} + ` diff --git a/website/source/docs/providers/postgresql/r/postgresql_database.html.markdown b/website/source/docs/providers/postgresql/r/postgresql_database.html.markdown index 8d17ff7abe57..0c23a7d129ee 100644 --- a/website/source/docs/providers/postgresql/r/postgresql_database.html.markdown +++ b/website/source/docs/providers/postgresql/r/postgresql_database.html.markdown @@ -6,7 +6,7 @@ description: |- Creates and manages a database on a PostgreSQL server. --- -# postgresql\postgresql_database +# postgresql\_database The ``postgresql_database`` resource creates and manages a database on a PostgreSQL server. diff --git a/website/source/docs/providers/postgresql/r/postgresql_role.html.markdown b/website/source/docs/providers/postgresql/r/postgresql_role.html.markdown index e2801aabc5e6..f9143a1368b1 100644 --- a/website/source/docs/providers/postgresql/r/postgresql_role.html.markdown +++ b/website/source/docs/providers/postgresql/r/postgresql_role.html.markdown @@ -6,7 +6,7 @@ description: |- Creates and manages a database on a PostgreSQL server. --- -# postgresql\postgresql_role +# postgresql\_role The ``postgresql_role`` resource creates and manages a role on a PostgreSQL server. From e35caae4c7600ec6aff20e762aa90f307adc9fb1 Mon Sep 17 00:00:00 2001 From: Adrian Chelaru Date: Wed, 11 Nov 2015 14:34:25 +0200 Subject: [PATCH 5/8] added password support to role creation --- .../postgresql/resource_postgresql_role.go | 11 ++++++ .../resource_postgresql_role_funcs.go | 34 ++++++++++++++++++- .../resource_postgresql_role_test.go | 22 ++++++++++++ .../r/postgresql_role.html.markdown | 8 ++++- 4 files changed, 73 insertions(+), 2 deletions(-) diff --git a/builtin/providers/postgresql/resource_postgresql_role.go b/builtin/providers/postgresql/resource_postgresql_role.go index f7fb0e891330..7f19bdf28150 100644 --- a/builtin/providers/postgresql/resource_postgresql_role.go +++ b/builtin/providers/postgresql/resource_postgresql_role.go @@ -23,6 +23,17 @@ func resourcePostgresqlRole() *schema.Resource { 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, + }, }, } } diff --git a/builtin/providers/postgresql/resource_postgresql_role_funcs.go b/builtin/providers/postgresql/resource_postgresql_role_funcs.go index 64b965ec9d25..90952ad35080 100644 --- a/builtin/providers/postgresql/resource_postgresql_role_funcs.go +++ b/builtin/providers/postgresql/resource_postgresql_role_funcs.go @@ -12,8 +12,11 @@ func resourcePostgresqlRoleCreate(d *schema.ResourceData, meta interface{}) erro conn := meta.(*sql.DB) roleName := d.Get("name").(string) loginAttr := getLoginStr(d.Get("login").(bool)) + password := d.Get("password").(string) - query := fmt.Sprintf("CREATE ROLE %s %s", pq.QuoteIdentifier(roleName), pq.QuoteIdentifier(loginAttr)) + 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) @@ -70,6 +73,28 @@ func resourcePostgresqlRoleUpdate(d *schema.ResourceData, meta interface{}) erro } } + 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) + } + } + + 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) + } + } + return resourcePostgresqlRoleRead(d, meta) } @@ -79,3 +104,10 @@ func getLoginStr(canLogin bool) string { } return "nologin" } + +func getEncryptedStr(isEncrypted bool) string { + if isEncrypted { + return "encrypted" + } + return "unencrypted" +} diff --git a/builtin/providers/postgresql/resource_postgresql_role_test.go b/builtin/providers/postgresql/resource_postgresql_role_test.go index 51ee6182ba8c..fdcb0fe2b6ce 100644 --- a/builtin/providers/postgresql/resource_postgresql_role_test.go +++ b/builtin/providers/postgresql/resource_postgresql_role_test.go @@ -101,4 +101,26 @@ 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" +} ` diff --git a/website/source/docs/providers/postgresql/r/postgresql_role.html.markdown b/website/source/docs/providers/postgresql/r/postgresql_role.html.markdown index f9143a1368b1..a6afdf9c8277 100644 --- a/website/source/docs/providers/postgresql/r/postgresql_role.html.markdown +++ b/website/source/docs/providers/postgresql/r/postgresql_role.html.markdown @@ -18,6 +18,8 @@ server. resource "postgresql_role" "my_role" { name = "my_role" login = true + password = "mypass" + encrypted = true } ``` @@ -28,4 +30,8 @@ resource "postgresql_role" "my_role" { 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. Coresponds to the LOGIN/NOLOGIN -clauses in 'CREATE ROLE'. Default value is false. \ No newline at end of file +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. \ No newline at end of file From bd51cbdd9e3efc133ba056d5fd58d5149998f78d Mon Sep 17 00:00:00 2001 From: Adrian Chelaru Date: Mon, 16 Nov 2015 14:54:55 +0200 Subject: [PATCH 6/8] using partial state for postgresql_role --- .../postgresql/resource_postgresql_role_funcs.go | 10 +++++++++- .../postgresql/r/postgresql_role.html.markdown | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/builtin/providers/postgresql/resource_postgresql_role_funcs.go b/builtin/providers/postgresql/resource_postgresql_role_funcs.go index 90952ad35080..cf48b2180066 100644 --- a/builtin/providers/postgresql/resource_postgresql_role_funcs.go +++ b/builtin/providers/postgresql/resource_postgresql_role_funcs.go @@ -62,6 +62,8 @@ func resourcePostgresqlRoleRead(d *schema.ResourceData, meta interface{}) error func resourcePostgresqlRoleUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*sql.DB) + d.Partial(true) + roleName := d.Get("name").(string) if d.HasChange("login") { @@ -71,10 +73,11 @@ func resourcePostgresqlRoleUpdate(d *schema.ResourceData, meta interface{}) erro 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)) @@ -83,6 +86,8 @@ func resourcePostgresqlRoleUpdate(d *schema.ResourceData, meta interface{}) erro if err != nil { return fmt.Errorf("Error updating password attribute for role: %s", err) } + + d.SetPartial("password") } if d.HasChange("encrypted") { @@ -93,8 +98,11 @@ func resourcePostgresqlRoleUpdate(d *schema.ResourceData, meta interface{}) erro if err != nil { return fmt.Errorf("Error updating encrypted attribute for role: %s", err) } + + d.SetPartial("encrypted") } + d.Partial(false) return resourcePostgresqlRoleRead(d, meta) } diff --git a/website/source/docs/providers/postgresql/r/postgresql_role.html.markdown b/website/source/docs/providers/postgresql/r/postgresql_role.html.markdown index a6afdf9c8277..a5d5c17d87b9 100644 --- a/website/source/docs/providers/postgresql/r/postgresql_role.html.markdown +++ b/website/source/docs/providers/postgresql/r/postgresql_role.html.markdown @@ -29,7 +29,7 @@ resource "postgresql_role" "my_role" { * `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. Coresponds to the LOGIN/NOLOGIN +* `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. From e448a564b4b640d06d348ca484be75eaa6e03815 Mon Sep 17 00:00:00 2001 From: Adrian Chelaru Date: Mon, 23 Nov 2015 14:29:47 +0200 Subject: [PATCH 7/8] opening/closing connections manually to avoid to many open connections --- builtin/providers/postgresql/config.go | 27 +++++++++--- builtin/providers/postgresql/provider.go | 1 + .../resource_postgresql_database_funcs.go | 43 +++++++++++++++---- .../resource_postgresql_database_test.go | 19 ++++++-- .../resource_postgresql_role_funcs.go | 38 +++++++++++++--- .../resource_postgresql_role_test.go | 14 ++++-- 6 files changed, 113 insertions(+), 29 deletions(-) diff --git a/builtin/providers/postgresql/config.go b/builtin/providers/postgresql/config.go index f48b112f5dca..454c016141bd 100644 --- a/builtin/providers/postgresql/config.go +++ b/builtin/providers/postgresql/config.go @@ -3,9 +3,10 @@ package postgresql import ( "database/sql" "fmt" - _ "github.com/lib/pq" + _ "github.com/lib/pq" //PostgreSQL db ) +// Config - provider config type Config struct { Host string Port int @@ -13,11 +14,27 @@ type Config struct { Password string } -// NewClient() return new db conn -func (c *Config) NewClient() (*sql.DB, error) { - connStr := fmt.Sprintf("host=%s port=%d user=%s password=%s", c.Host, c.Port, c.Username, c.Password) +// 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 +} - db, err := sql.Open("postgres", connStr) +//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) } diff --git a/builtin/providers/postgresql/provider.go b/builtin/providers/postgresql/provider.go index 81a14186aed2..c048ec3ece76 100644 --- a/builtin/providers/postgresql/provider.go +++ b/builtin/providers/postgresql/provider.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/terraform" ) +// Provider returns a terraform.ResourceProvider. func Provider() terraform.ResourceProvider { return &schema.Provider{ Schema: map[string]*schema.Schema{ diff --git a/builtin/providers/postgresql/resource_postgresql_database_funcs.go b/builtin/providers/postgresql/resource_postgresql_database_funcs.go index 237cb8f213b0..c2ad9270d12b 100644 --- a/builtin/providers/postgresql/resource_postgresql_database_funcs.go +++ b/builtin/providers/postgresql/resource_postgresql_database_funcs.go @@ -3,15 +3,21 @@ package postgresql import ( "database/sql" "fmt" - "github.com/hashicorp/terraform/helper/schema" "github.com/lib/pq" ) func resourcePostgresqlDatabaseCreate(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*sql.DB) + 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) + var dbOwnerCfg string if dbOwner != "" { dbOwnerCfg = fmt.Sprintf("WITH OWNER=%s", pq.QuoteIdentifier(dbOwner)) @@ -20,9 +26,9 @@ func resourcePostgresqlDatabaseCreate(d *schema.ResourceData, meta interface{}) } query := fmt.Sprintf("CREATE DATABASE %s %s", pq.QuoteIdentifier(dbName), dbOwnerCfg) - _, err := conn.Query(query) + _, err = conn.Query(query) if err != nil { - return fmt.Errorf("Error creating postgresql database: %s", err) + return fmt.Errorf("Error creating postgresql database %s: %s", dbName, err) } d.SetId(dbName) @@ -31,11 +37,17 @@ func resourcePostgresqlDatabaseCreate(d *schema.ResourceData, meta interface{}) } func resourcePostgresqlDatabaseDelete(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*sql.DB) + client := meta.(*Client) + conn, err := client.Connect() + if err != nil { + return err + } + defer conn.Close() + dbName := d.Get("name").(string) query := fmt.Sprintf("DROP DATABASE %s", pq.QuoteIdentifier(dbName)) - _, err := conn.Query(query) + _, err = conn.Query(query) if err != nil { return err } @@ -46,11 +58,17 @@ func resourcePostgresqlDatabaseDelete(d *schema.ResourceData, meta interface{}) } func resourcePostgresqlDatabaseRead(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*sql.DB) + 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) + 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("") @@ -64,7 +82,13 @@ func resourcePostgresqlDatabaseRead(d *schema.ResourceData, meta interface{}) er } func resourcePostgresqlDatabaseUpdate(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*sql.DB) + client := meta.(*Client) + conn, err := client.Connect() + if err != nil { + return err + } + defer conn.Close() + dbName := d.Get("name").(string) if d.HasChange("owner") { @@ -80,3 +104,4 @@ func resourcePostgresqlDatabaseUpdate(d *schema.ResourceData, meta interface{}) return resourcePostgresqlDatabaseRead(d, meta) } + diff --git a/builtin/providers/postgresql/resource_postgresql_database_test.go b/builtin/providers/postgresql/resource_postgresql_database_test.go index a2eb4fad01fe..35d2b271c948 100644 --- a/builtin/providers/postgresql/resource_postgresql_database_test.go +++ b/builtin/providers/postgresql/resource_postgresql_database_test.go @@ -50,7 +50,7 @@ func TestAccPostgresqlDatabase_DefaultOwner(t *testing.T) { } func testAccCheckPostgresqlDatabaseDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*sql.DB) + client := testAccProvider.Meta().(*Client) for _, rs := range s.RootModule().Resources { if rs.Type != "postgresql_database" { @@ -87,7 +87,7 @@ func testAccCheckPostgresqlDatabaseExists(n string, owner string) resource.TestC return fmt.Errorf("Wrong owner for db expected %s got %s", owner, actualOwner) } - client := testAccProvider.Meta().(*sql.DB) + client := testAccProvider.Meta().(*Client) exists, err := checkDatabaseExists(client, rs.Primary.ID) if err != nil { @@ -102,9 +102,15 @@ func testAccCheckPostgresqlDatabaseExists(n string, owner string) resource.TestC } } -func checkDatabaseExists(conn *sql.DB, dbName string) (bool, error) { +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) + err = conn.QueryRow("SELECT 1 from pg_database d WHERE datname=$1", dbName).Scan(&_rez) switch { case err == sql.ErrNoRows: return false, nil @@ -126,6 +132,11 @@ resource "postgresql_database" "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" } diff --git a/builtin/providers/postgresql/resource_postgresql_role_funcs.go b/builtin/providers/postgresql/resource_postgresql_role_funcs.go index cf48b2180066..aeafcf0b8ea7 100644 --- a/builtin/providers/postgresql/resource_postgresql_role_funcs.go +++ b/builtin/providers/postgresql/resource_postgresql_role_funcs.go @@ -9,7 +9,13 @@ import ( ) func resourcePostgresqlRoleCreate(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*sql.DB) + 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) @@ -17,7 +23,7 @@ func resourcePostgresqlRoleCreate(d *schema.ResourceData, meta interface{}) erro 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) + _, err = conn.Query(query) if err != nil { return fmt.Errorf("Error creating role: %s", err) } @@ -28,11 +34,17 @@ func resourcePostgresqlRoleCreate(d *schema.ResourceData, meta interface{}) erro } func resourcePostgresqlRoleDelete(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*sql.DB) + 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) + _, err = conn.Query(query) if err != nil { return err } @@ -43,11 +55,17 @@ func resourcePostgresqlRoleDelete(d *schema.ResourceData, meta interface{}) erro } func resourcePostgresqlRoleRead(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*sql.DB) + 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) + err = conn.QueryRow("select rolcanlogin from pg_roles where rolname=$1", roleName).Scan(&canLogin) switch { case err == sql.ErrNoRows: d.SetId("") @@ -61,7 +79,13 @@ func resourcePostgresqlRoleRead(d *schema.ResourceData, meta interface{}) error } func resourcePostgresqlRoleUpdate(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*sql.DB) + client := meta.(*Client) + conn, err := client.Connect() + if err != nil { + return err + } + defer conn.Close() + d.Partial(true) roleName := d.Get("name").(string) diff --git a/builtin/providers/postgresql/resource_postgresql_role_test.go b/builtin/providers/postgresql/resource_postgresql_role_test.go index fdcb0fe2b6ce..0839b2ef6c46 100644 --- a/builtin/providers/postgresql/resource_postgresql_role_test.go +++ b/builtin/providers/postgresql/resource_postgresql_role_test.go @@ -31,7 +31,7 @@ func TestAccPostgresqlRole_Basic(t *testing.T) { } func testAccCheckPostgresqlRoleDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*sql.DB) + client := testAccProvider.Meta().(*Client) for _, rs := range s.RootModule().Resources { if rs.Type != "postgresql_role" { @@ -68,7 +68,7 @@ func testAccCheckPostgresqlRoleExists(n string, canLogin string) resource.TestCh return fmt.Errorf("Wrong value for login expected %s got %s", canLogin, actualCanLogin) } - client := testAccProvider.Meta().(*sql.DB) + client := testAccProvider.Meta().(*Client) exists, err := checkRoleExists(client, rs.Primary.ID) if err != nil { @@ -83,9 +83,15 @@ func testAccCheckPostgresqlRoleExists(n string, canLogin string) resource.TestCh } } -func checkRoleExists(conn *sql.DB, roleName string) (bool, error) { +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) + err = conn.QueryRow("SELECT 1 from pg_roles d WHERE rolname=$1", roleName).Scan(&_rez) switch { case err == sql.ErrNoRows: return false, nil From 8ccf7fdc67dc6c354786069fe8e0c41fce0e35bd Mon Sep 17 00:00:00 2001 From: Adrian Chelaru Date: Mon, 23 Nov 2015 14:32:05 +0200 Subject: [PATCH 8/8] fix for running with non superuser --- .../resource_postgresql_database_funcs.go | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/builtin/providers/postgresql/resource_postgresql_database_funcs.go b/builtin/providers/postgresql/resource_postgresql_database_funcs.go index c2ad9270d12b..cc80597f52d0 100644 --- a/builtin/providers/postgresql/resource_postgresql_database_funcs.go +++ b/builtin/providers/postgresql/resource_postgresql_database_funcs.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/hashicorp/terraform/helper/schema" "github.com/lib/pq" + "strings" ) func resourcePostgresqlDatabaseCreate(d *schema.ResourceData, meta interface{}) error { @@ -17,6 +18,7 @@ func resourcePostgresqlDatabaseCreate(d *schema.ResourceData, meta interface{}) dbName := d.Get("name").(string) dbOwner := d.Get("owner").(string) + connUsername := client.username var dbOwnerCfg string if dbOwner != "" { @@ -25,6 +27,12 @@ func resourcePostgresqlDatabaseCreate(d *schema.ResourceData, meta interface{}) 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 { @@ -45,6 +53,13 @@ func resourcePostgresqlDatabaseDelete(d *schema.ResourceData, meta interface{}) 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) @@ -105,3 +120,17 @@ func resourcePostgresqlDatabaseUpdate(d *schema.ResourceData, meta interface{}) 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 +}