Automatically perform a `REASSIGN OWNED` and `DROP OWNED BY` when

removing a PostgreSQL role.

Add manual overrides if this isn't the desired behavior, but it should
universally be the desired outcome except when a ROLE name is reused
across multiple databases in the same PostgreSQL cluster, in which case
the `skip_drop_role` is necessary for all but the last PostgreSQL
provider.
This commit is contained in:
Sean Chittenden 2016-12-22 00:36:21 -08:00
parent 2e3c843db3
commit 897609878f
No known key found for this signature in database
GPG Key ID: 4EBC9DC16C2E5E16
3 changed files with 92 additions and 16 deletions

View File

@ -23,6 +23,8 @@ const (
roleNameAttr = "name"
rolePasswordAttr = "password"
roleReplicationAttr = "replication"
roleSkipDropRoleAttr = "skip_drop_role"
roleSkipReassignOwnedAttr = "skip_reassign_owned"
roleSuperuserAttr = "superuser"
roleValidUntilAttr = "valid_until"
@ -120,6 +122,18 @@ func resourcePostgreSQLRole() *schema.Resource {
Default: false,
Description: "Determine whether a role bypasses every row-level security (RLS) policy",
},
roleSkipDropRoleAttr: {
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: "Skip actually running the DROP ROLE command when removing a ROLE from PostgreSQL",
},
roleSkipReassignOwnedAttr: {
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: "Skip actually running the REASSIGN OWNED command when removing a role from PostgreSQL",
},
},
}
}
@ -240,12 +254,36 @@ func resourcePostgreSQLRoleDelete(d *schema.ResourceData, meta interface{}) erro
}
defer conn.Close()
txn, err := conn.Begin()
if err != nil {
return err
}
defer txn.Rollback()
roleName := d.Get(roleNameAttr).(string)
query := fmt.Sprintf("DROP ROLE %s", pq.QuoteIdentifier(roleName))
queries := make([]string, 0, 3)
if !d.Get(roleSkipReassignOwnedAttr).(bool) {
queries = append(queries, fmt.Sprintf("REASSIGN OWNED BY %s TO CURRENT_USER", pq.QuoteIdentifier(roleName)))
queries = append(queries, fmt.Sprintf("DROP OWNED BY %s", pq.QuoteIdentifier(roleName)))
}
if !d.Get(roleSkipDropRoleAttr).(bool) {
queries = append(queries, fmt.Sprintf("DROP ROLE %s", pq.QuoteIdentifier(roleName)))
}
if len(queries) > 0 {
for _, query := range queries {
_, err = conn.Query(query)
if err != nil {
return errwrap.Wrapf("Error deleting role: {{err}}", err)
}
}
if err := txn.Commit(); err != nil {
return errwrap.Wrapf("Error committing schema: {{err}}", err)
}
}
d.SetId("")
@ -282,6 +320,8 @@ func resourcePostgreSQLRoleRead(d *schema.ResourceData, meta interface{}) error
d.Set(roleInheritAttr, roleInherit)
d.Set(roleLoginAttr, roleCanLogin)
d.Set(roleReplicationAttr, roleReplication)
d.Set(roleSkipDropRoleAttr, d.Get(roleSkipDropRoleAttr).(bool))
d.Set(roleSkipReassignOwnedAttr, d.Get(roleSkipReassignOwnedAttr).(bool))
d.Set(roleSuperuserAttr, roleSuperuser)
d.Set(roleValidUntilAttr, roleValidUntil)
d.SetId(roleName)

View File

@ -23,6 +23,10 @@ func TestAccPostgresqlRole_Basic(t *testing.T) {
"postgresql_role.myrole2", "name", "myrole2"),
resource.TestCheckResourceAttr(
"postgresql_role.myrole2", "login", "true"),
resource.TestCheckResourceAttr(
"postgresql_role.myrole2", "skip_drop_role", "false"),
resource.TestCheckResourceAttr(
"postgresql_role.myrole2", "skip_reassign_owned", "false"),
resource.TestCheckResourceAttr(
"postgresql_role.role_with_defaults", "name", "testing_role_with_defaults"),
@ -46,6 +50,10 @@ func TestAccPostgresqlRole_Basic(t *testing.T) {
"postgresql_role.role_with_defaults", "password", ""),
resource.TestCheckResourceAttr(
"postgresql_role.role_with_defaults", "valid_until", "infinity"),
resource.TestCheckResourceAttr(
"postgresql_role.role_with_defaults", "skip_drop_role", "false"),
resource.TestCheckResourceAttr(
"postgresql_role.role_with_defaults", "skip_reassign_owned", "false"),
),
},
},
@ -164,6 +172,8 @@ resource "postgresql_role" "role_with_defaults" {
connection_limit = -1
encrypted_password = true
password = ""
skip_drop_role = false
skip_reassign_owned = false
valid_until = "infinity"
}
`

View File

@ -11,6 +11,15 @@ description: |-
The ``postgresql_role`` resource creates and manages a role on a PostgreSQL
server.
When a ``postgresql_role`` resource is removed, the PostgreSQL ROLE will
automatically run a [`REASSIGN
OWNED`](https://www.postgresql.org/docs/current/static/sql-reassign-owned.html)
and [`DROP
OWNED`](https://www.postgresql.org/docs/current/static/sql-drop-owned.html) to
the `CURRENT_USER` (normally the connected user for the provider). If the
specified PostgreSQL ROLE owns objects in multiple PostgreSQL databases in the
same PostgreSQL Cluster, one PostgreSQL provider per database must be created
and all but the final ``postgresql_role`` must specify a `skip_drop_role`.
## Usage
@ -82,6 +91,23 @@ resource "postgresql_role" "my_replication_role" {
datetime. If omitted or the magic value `NULL` is used, `valid_until` will be
set to `infinity`. Default is `NULL`, therefore `infinity`.
* `skip_drop_role` - (Optional) When a PostgreSQL ROLE exists in multiple
databases and the ROLE is dropped, the
[cleanup of ownership of objects](https://www.postgresql.org/docs/current/static/role-removal.html)
in each of the respective databases must occur before the ROLE can be dropped
from the catalog. Set this option to true when there are multiple databases
in a PostgreSQL cluster using the same PostgreSQL ROLE for object ownership.
This is the third and final step taken when removing a ROLE from a database.
* `skip_reassign_owned` - (Optional) When a PostgreSQL ROLE exists in multiple
databases and the ROLE is dropped, a
[`REASSIGN OWNED`](https://www.postgresql.org/docs/current/static/sql-reassign-owned.html) in
must be executed on each of the respective databases before the `DROP ROLE`
can be executed to dropped the ROLE from the catalog. This is the first and
second steps taken when removing a ROLE from a database (the second step being
an implicit
[`DROP OWNED`](https://www.postgresql.org/docs/current/static/sql-drop-owned.html)).
## Import Example
`postgresql_role` supports importing resources. Supposing the following