Skip to content
This repository has been archived by the owner on Nov 14, 2020. It is now read-only.

Allow to configure log_statement for a role. #132

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions postgresql/resource_postgresql_role.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const (
roleEncryptedPassAttr = "encrypted_password"
roleInheritAttr = "inherit"
roleLoginAttr = "login"
roleLogStatementAttr = "log_statement"
roleNameAttr = "name"
rolePasswordAttr = "password"
roleReplicationAttr = "replication"
Expand All @@ -39,6 +40,13 @@ const (
roleDepEncryptedAttr = "encrypted"
)

var allowedLogStatementOpts = []string{
"none",
"ddl",
"mod",
"all",
}

func resourcePostgreSQLRole() *schema.Resource {
return &schema.Resource{
Create: resourcePostgreSQLRoleCreate,
Expand Down Expand Up @@ -131,6 +139,13 @@ func resourcePostgreSQLRole() *schema.Resource {
Default: false,
Description: "Determine whether a role is allowed to log in",
},
roleLogStatementAttr: {
Type: schema.TypeString,
Optional: true,
Default: "none",
ValidateFunc: validation.StringInSlice(allowedLogStatementOpts, false),
Description: "Sets the log level for SQL statements",
},
roleReplicationAttr: {
Type: schema.TypeBool,
Optional: true,
Expand Down Expand Up @@ -295,6 +310,10 @@ func resourcePostgreSQLRoleCreate(d *schema.ResourceData, meta interface{}) erro
return err
}

if err = setRoleLogStatement(txn, d); err != nil {
return err
}

if err = txn.Commit(); err != nil {
return errwrap.Wrapf("could not commit transaction: {{err}}", err)
}
Expand Down Expand Up @@ -449,6 +468,7 @@ func resourcePostgreSQLRoleReadImpl(c *Client, d *schema.ResourceData) error {
d.Set(roleReplicationAttr, roleBypassRLS)
d.Set(roleRolesAttr, pgArrayToSet(roleRoles))
d.Set(roleSearchPathAttr, readSearchPath(roleConfig))
d.Set(roleLogStatementAttr, readLogStatement(roleConfig, d.Get(roleLogStatementAttr).(string)))

statementTimeout, err := readStatementTimeout(roleConfig)
if err != nil {
Expand Down Expand Up @@ -481,6 +501,19 @@ func readSearchPath(roleConfig pq.ByteaArray) []string {
return nil
}

// readLogStatement searches for a log_statement entry in the rolconfig array.
// In case no such value is present, it returns empty.
func readLogStatement(roleConfig pq.ByteaArray, defaultValue string) string {
for _, v := range roleConfig {
config := string(v)
if strings.HasPrefix(config, roleLogStatementAttr) {
var result = strings.TrimPrefix(config, roleLogStatementAttr+"=")
return result
}
}
return defaultValue
}

// readStatementTimeout searches for a statement_timeout entry in the rolconfig array.
// In case no such value is present, it returns nil.
func readStatementTimeout(roleConfig pq.ByteaArray) (int, error) {
Expand Down Expand Up @@ -600,6 +633,10 @@ func resourcePostgreSQLRoleUpdate(d *schema.ResourceData, meta interface{}) erro
return err
}

if err := setRoleLogStatement(txn, d); err != nil {
return err
}

if err := setRoleReplication(txn, d); err != nil {
return err
}
Expand Down Expand Up @@ -789,6 +826,22 @@ func setRoleLogin(txn *sql.Tx, d *schema.ResourceData) error {
return nil
}

func setRoleLogStatement(txn *sql.Tx, d *schema.ResourceData) error {
if !d.HasChange(roleLogStatementAttr) {
return nil
}

level := d.Get(roleLogStatementAttr).(string)

roleName := d.Get(roleNameAttr).(string)
sql := fmt.Sprintf("ALTER ROLE %s SET log_statement TO %s", pq.QuoteIdentifier(roleName), pq.QuoteIdentifier(level))
if _, err := txn.Exec(sql); err != nil {
return errwrap.Wrapf("Error updating role log_statement: {{err}}", err)
}

return nil
}

func setRoleReplication(txn *sql.Tx, d *schema.ResourceData) error {
if !d.HasChange(roleReplicationAttr) {
return nil
Expand Down
6 changes: 6 additions & 0 deletions postgresql/resource_postgresql_role_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ func TestAccPostgresqlRole_Basic(t *testing.T) {
resource.TestCheckResourceAttr("postgresql_role.role_with_defaults", "inherit", "false"),
resource.TestCheckResourceAttr("postgresql_role.role_with_defaults", "replication", "false"),
resource.TestCheckResourceAttr("postgresql_role.role_with_defaults", "bypass_row_level_security", "false"),
resource.TestCheckResourceAttr("postgresql_role.role_with_defaults", "log_statement", "none"),
resource.TestCheckResourceAttr("postgresql_role.role_with_defaults", "connection_limit", "-1"),
resource.TestCheckResourceAttr("postgresql_role.role_with_defaults", "encrypted_password", "true"),
resource.TestCheckResourceAttr("postgresql_role.role_with_defaults", "password", ""),
Expand Down Expand Up @@ -90,6 +91,7 @@ resource "postgresql_role" "update_role" {
roles = ["${postgresql_role.group_role.name}"]
search_path = ["mysearchpath"]
statement_timeout = 30000
log_statement = "all"
}
`
resource.Test(t, resource.TestCase{
Expand All @@ -112,6 +114,7 @@ resource "postgresql_role" "update_role" {
resource.TestCheckResourceAttr("postgresql_role.update_role", "roles.#", "0"),
resource.TestCheckResourceAttr("postgresql_role.update_role", "search_path.#", "0"),
resource.TestCheckResourceAttr("postgresql_role.update_role", "statement_timeout", "0"),
resource.TestCheckResourceAttr("postgresql_role.update_role", "log_statement", "none"),
testAccCheckRoleCanLogin(t, "update_role", "toto"),
),
},
Expand All @@ -134,6 +137,7 @@ resource "postgresql_role" "update_role" {
resource.TestCheckResourceAttr("postgresql_role.update_role", "search_path.#", "1"),
resource.TestCheckResourceAttr("postgresql_role.update_role", "search_path.0", "mysearchpath"),
resource.TestCheckResourceAttr("postgresql_role.update_role", "statement_timeout", "30000"),
resource.TestCheckResourceAttr("postgresql_role.update_role", "log_statement", "all"),
testAccCheckRoleCanLogin(t, "update_role2", "titi"),
),
},
Expand All @@ -150,6 +154,7 @@ resource "postgresql_role" "update_role" {
resource.TestCheckResourceAttr("postgresql_role.update_role", "roles.#", "0"),
resource.TestCheckResourceAttr("postgresql_role.update_role", "search_path.#", "0"),
resource.TestCheckResourceAttr("postgresql_role.update_role", "statement_timeout", "0"),
resource.TestCheckResourceAttr("postgresql_role.update_role", "log_statement", "none"),
testAccCheckRoleCanLogin(t, "update_role", "toto"),
),
},
Expand Down Expand Up @@ -322,6 +327,7 @@ resource "postgresql_role" "role_with_defaults" {
login = false
replication = false
bypass_row_level_security = false
log_statement = "none"
connection_limit = -1
encrypted_password = true
password = ""
Expand Down
2 changes: 2 additions & 0 deletions website/docs/r/postgresql_role.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ resource "postgresql_role" "my_replication_role" {

* `statement_timeout` - (Optional) Defines [`statement_timeout`](https://www.postgresql.org/docs/current/runtime-config-client.html#RUNTIME-CONFIG-CLIENT-STATEMENT) setting for this role which allows to abort any statement that takes more than the specified amount of time.

* `log_statement` - (Optional) Defines [`log_statement`](https://www.postgresql.org/docs/current/runtime-config-logging.html) setting for this role which controls the type of SQL statements that are logged. Default is `none`

## Import Example

`postgresql_role` supports importing resources. Supposing the following
Expand Down