From 20b3f8bf400666752cbc110c2274cb100a37f089 Mon Sep 17 00:00:00 2001 From: Bruno Ferreira Date: Fri, 12 Jan 2024 15:07:41 +0000 Subject: [PATCH] feat: add `log_statement` to role resource --- postgresql/resource_postgresql_role.go | 61 ++++++++++++++++++++ postgresql/resource_postgresql_role_test.go | 6 ++ website/docs/r/postgresql_role.html.markdown | 2 + 3 files changed, 69 insertions(+) diff --git a/postgresql/resource_postgresql_role.go b/postgresql/resource_postgresql_role.go index b7cb0fab..ab1840c3 100644 --- a/postgresql/resource_postgresql_role.go +++ b/postgresql/resource_postgresql_role.go @@ -34,6 +34,7 @@ const ( roleRolesAttr = "roles" roleSearchPathAttr = "search_path" roleStatementTimeoutAttr = "statement_timeout" + roleLogStatementAttr = "log_statement" roleAssumeRoleAttr = "assume_role" // Deprecated options @@ -168,6 +169,13 @@ func resourcePostgreSQLRole() *schema.Resource { Description: "Abort any statement that takes more than the specified number of milliseconds", ValidateFunc: validation.IntAtLeast(0), }, + roleLogStatementAttr: { + Type: schema.TypeString, + Optional: true, + Default: "none", + Description: "Sets the type of statements logged by this role", + ValidateFunc: validation.StringInSlice([]string{"none", "ddl", "mod", "all"}, false), + }, roleAssumeRoleAttr: { Type: schema.TypeString, Optional: true, @@ -307,6 +315,10 @@ func resourcePostgreSQLRoleCreate(db *DBConnection, d *schema.ResourceData) erro return err } + if err = setLogStatement(txn, d); err != nil { + return err + } + if err = setAssumeRole(txn, d); err != nil { return err } @@ -472,6 +484,13 @@ func resourcePostgreSQLRoleReadImpl(db *DBConnection, d *schema.ResourceData) er d.Set(roleIdleInTransactionSessionTimeoutAttr, idleInTransactionSessionTimeout) + logStatement, err := readLogStatement(roleConfig) + if err != nil { + return err + } + + d.Set(roleLogStatementAttr, logStatement) + d.SetId(roleName) password, err := readRolePassword(db, d, roleCanLogin) @@ -533,6 +552,19 @@ func readStatementTimeout(roleConfig pq.ByteaArray) (int, error) { return 0, nil } +// readLogStatement searches for a log_statement entry in the rolconfig array. +// In case no such value is present, it returns empty string. +func readLogStatement(roleConfig pq.ByteaArray) (string, error) { + for _, v := range roleConfig { + config := string(v) + if strings.HasPrefix(config, roleLogStatementAttr) { + var result = strings.Split(strings.TrimPrefix(config, roleLogStatementAttr+"="), ", ") + return result[0], nil + } + } + return "", nil +} + // readAssumeRole searches for a role entry in the rolconfig array. // In case no such value is present, it returns empty string. func readAssumeRole(roleConfig pq.ByteaArray) string { @@ -685,6 +717,10 @@ func resourcePostgreSQLRoleUpdate(db *DBConnection, d *schema.ResourceData) erro return err } + if err = setLogStatement(txn, d); err != nil { + return err + } + if err = setAssumeRole(txn, d); err != nil { return err } @@ -1038,6 +1074,31 @@ func setIdleInTransactionSessionTimeout(txn *sql.Tx, d *schema.ResourceData) err return nil } +func setLogStatement(txn *sql.Tx, d *schema.ResourceData) error { + if !d.HasChange(roleLogStatementAttr) { + return nil + } + + roleName := d.Get(roleNameAttr).(string) + logStatement := d.Get(roleLogStatementAttr).(string) + if logStatement != "" { + sql := fmt.Sprintf( + "ALTER ROLE %s SET log_statement TO %s", pq.QuoteIdentifier(roleName), logStatement, + ) + if _, err := txn.Exec(sql); err != nil { + return fmt.Errorf("could not set log_statement %s for %s: %w", logStatement, roleName, err) + } + } else { + sql := fmt.Sprintf( + "ALTER ROLE %s RESET log_statement", pq.QuoteIdentifier(roleName), + ) + if _, err := txn.Exec(sql); err != nil { + return fmt.Errorf("could not reset log_statement for %s: %w", roleName, err) + } + } + return nil +} + func setAssumeRole(txn *sql.Tx, d *schema.ResourceData) error { if !d.HasChange(roleAssumeRoleAttr) { return nil diff --git a/postgresql/resource_postgresql_role_test.go b/postgresql/resource_postgresql_role_test.go index ef502f00..8a2643bd 100644 --- a/postgresql/resource_postgresql_role_test.go +++ b/postgresql/resource_postgresql_role_test.go @@ -46,6 +46,7 @@ func TestAccPostgresqlRole_Basic(t *testing.T) { resource.TestCheckResourceAttr("postgresql_role.role_with_defaults", "skip_reassign_owned", "false"), resource.TestCheckResourceAttr("postgresql_role.role_with_defaults", "statement_timeout", "0"), resource.TestCheckResourceAttr("postgresql_role.role_with_defaults", "idle_in_transaction_session_timeout", "0"), + resource.TestCheckResourceAttr("postgresql_role.role_with_defaults", "log_statement", "none"), resource.TestCheckResourceAttr("postgresql_role.role_with_defaults", "assume_role", ""), resource.TestCheckResourceAttr("postgresql_role.role_with_create_database", "name", "role_with_create_database"), @@ -121,6 +122,7 @@ resource "postgresql_role" "update_role" { search_path = ["mysearchpath"] statement_timeout = 30000 idle_in_transaction_session_timeout = 60000 + log_statement = "none" assume_role = "${postgresql_role.group_role.name}" } ` @@ -145,6 +147,7 @@ resource "postgresql_role" "update_role" { resource.TestCheckResourceAttr("postgresql_role.update_role", "search_path.#", "0"), resource.TestCheckResourceAttr("postgresql_role.update_role", "statement_timeout", "0"), resource.TestCheckResourceAttr("postgresql_role.update_role", "idle_in_transaction_session_timeout", "0"), + resource.TestCheckResourceAttr("postgresql_role.role_with_defaults", "log_statement", "none"), resource.TestCheckResourceAttr("postgresql_role.update_role", "assume_role", ""), testAccCheckRoleCanLogin(t, "update_role", "toto"), ), @@ -166,6 +169,7 @@ resource "postgresql_role" "update_role" { 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", "idle_in_transaction_session_timeout", "60000"), + resource.TestCheckResourceAttr("postgresql_role.role_with_defaults", "log_statement", "none"), resource.TestCheckResourceAttr("postgresql_role.update_role", "assume_role", "group_role"), testAccCheckRoleCanLogin(t, "update_role2", "titi"), ), @@ -184,6 +188,7 @@ resource "postgresql_role" "update_role" { resource.TestCheckResourceAttr("postgresql_role.update_role", "search_path.#", "0"), resource.TestCheckResourceAttr("postgresql_role.update_role", "statement_timeout", "0"), resource.TestCheckResourceAttr("postgresql_role.update_role", "idle_in_transaction_session_timeout", "0"), + resource.TestCheckResourceAttr("postgresql_role.role_with_defaults", "log_statement", "none"), resource.TestCheckResourceAttr("postgresql_role.update_role", "assume_role", ""), testAccCheckRoleCanLogin(t, "update_role", "toto"), ), @@ -417,6 +422,7 @@ resource "postgresql_role" "role_with_defaults" { valid_until = "infinity" statement_timeout = 0 idle_in_transaction_session_timeout = 0 + log_statement = "none" assume_role = "" } diff --git a/website/docs/r/postgresql_role.html.markdown b/website/docs/r/postgresql_role.html.markdown index 54544893..f12b45eb 100644 --- a/website/docs/r/postgresql_role.html.markdown +++ b/website/docs/r/postgresql_role.html.markdown @@ -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#GUC-LOG-STATEMENT) setting for this role which allows to log each statement. + * `assume_role` - (Optional) Defines the role to switch to at login via [`SET ROLE`](https://www.postgresql.org/docs/current/sql-set-role.html). ## Import Example