Skip to content
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
257 changes: 257 additions & 0 deletions postgresql/resource_postgresql_role.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ const (
roleCreateRoleAttr = "create_role"
roleEncryptedPassAttr = "encrypted_password"
roleIdleInTransactionSessionTimeoutAttr = "idle_in_transaction_session_timeout"
roleLogMinDurationStatementAttr = "log_min_duration_statement"
roleLogMinDurationSampleAttr = "log_min_duration_sample"
roleLogStatementSampleRateAttr = "log_statement_sample_rate"
roleLogTransactionSampleRateAttr = "log_transaction_sample_rate"
roleInheritAttr = "inherit"
roleLoginAttr = "login"
roleNameAttr = "name"
Expand Down Expand Up @@ -146,6 +150,30 @@ func resourcePostgreSQLRole() *schema.Resource {
Description: "Terminate any session with an open transaction that has been idle for longer than the specified duration in milliseconds",
ValidateFunc: validation.IntAtLeast(0),
},
roleLogMinDurationStatementAttr: {
Type: schema.TypeInt,
Optional: true,
Description: "Log a completed statement if it ran for at least the specified amount of time in milliseconds.",
ValidateFunc: validation.IntAtLeast(-1),
},
roleLogMinDurationSampleAttr: {
Type: schema.TypeInt,
Optional: true,
Description: "Allows sampling the duration of completed statements that ran for at least the specified amount of time. Sample rate is controlled by log_statement_sample_rate.",
ValidateFunc: validation.IntAtLeast(-1),
},
roleLogStatementSampleRateAttr: {
Type: schema.TypeFloat,
Optional: true,
Description: "Determines the fraction of statements with duration exceeding log_min_duration_sample that will be logged.",
ValidateFunc: validation.FloatBetween(0, 1),
},
roleLogTransactionSampleRateAttr: {
Type: schema.TypeFloat,
Optional: true,
Description: "Sets the fraction of transactions whose statements are all logged, in addition to statements logged for other reasons.",
ValidateFunc: validation.FloatBetween(0, 1),
},
roleInheritAttr: {
Type: schema.TypeBool,
Optional: true,
Expand Down Expand Up @@ -366,6 +394,22 @@ func resourcePostgreSQLRoleCreate(db *DBConnection, d *schema.ResourceData) erro
return err
}

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

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

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

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

if err = setAssumeRole(txn, d); err != nil {
return err
}
Expand Down Expand Up @@ -531,6 +575,34 @@ func resourcePostgreSQLRoleReadImpl(db *DBConnection, d *schema.ResourceData) er

d.Set(roleIdleInTransactionSessionTimeoutAttr, idleInTransactionSessionTimeout)

logMinDurationStatement, err := readLogMinDurationStatement(roleConfig)
if err != nil {
return err
}

d.Set(roleLogMinDurationStatementAttr, logMinDurationStatement)

logMinDurationSample, err := readLogMinDurationSample(roleConfig)
if err != nil {
return err
}

d.Set(roleLogMinDurationSampleAttr, logMinDurationSample)

logStatementSampleRate, err := readLogStatementSampleRate(roleConfig)
if err != nil {
return err
}

d.Set(roleLogStatementSampleRateAttr, logStatementSampleRate)

logTransactionSampleRate, err := readLogTransactionSampleRate(roleConfig)
if err != nil {
return err
}

d.Set(roleLogTransactionSampleRateAttr, logTransactionSampleRate)

d.SetId(roleName)

if _, ok := d.GetOk(rolePasswordAttr); ok {
Expand Down Expand Up @@ -576,6 +648,75 @@ func readIdleInTransactionSessionTimeout(roleConfig pq.ByteaArray) (int, error)
return 0, nil
}

// readLogMinDurationStatement searches for an log_min_duration_statement entry in the rolconfig array.
// In case no such value is present, it returns nil.
func readLogMinDurationStatement(roleConfig pq.ByteaArray) (int, error) {
for _, v := range roleConfig {
config := string(v)
if strings.HasPrefix(config, rolelogMinDurationStatementAttr) {
var result = strings.Split(strings.TrimPrefix(config, rolelogMinDurationStatementAttr+"="), ", ")
res, err := strconv.Atoi(result[0])
if err != nil {
return -1, fmt.Errorf("Error reading log_min_duration_statement: %w", err)
}
return res, nil
}
}
return 0, nil
}

// readLogMinDurationSample searches for an log_statement_sample_rate entry in the rolconfig array.
// In case no such value is present, it returns nil.
func readLogMinDurationSample(roleConfig pq.ByteaArray) (int, error) {
for _, v := range roleConfig {
config := string(v)
if strings.HasPrefix(config, rolelogMinDurationSampleAttr) {
var result = strings.Split(strings.TrimPrefix(config, rolelogMinDurationSampleAttr+"="), ", ")
res, err := strconv.Atoi(result[0])
if err != nil {
return -1, fmt.Errorf("Error reading log_min_duration_sample: %w", err)
}
return res, nil
}
}
return 0, nil
}

// readLogStatementSampleRate searches for an log_transaction_sample_rate entry in the rolconfig array.
// In case no such value is present, it returns nil.
func readLogStatementSampleRate(roleConfig pq.ByteaArray) (int, error) {
for _, v := range roleConfig {
config := string(v)
if strings.HasPrefix(config, rolelogStatementSampleRateAttr) {
var result = strings.Split(strings.TrimPrefix(config, rolelogStatementSampleRateAttr+"="), ", ")
res, err := strconv.Atoi(result[0])
if err != nil {
return -1, fmt.Errorf("Error reading log_statement_sample_rate", err)
}
return res, nil
}
}
return 0, nil
}


// readLogTransactionSampleRate searches for an log_transaction_sample_rate entry in the rolconfig array.
// In case no such value is present, it returns nil.
func readLogTransactionSampleRate(roleConfig pq.ByteaArray) (int, error) {
for _, v := range roleConfig {
config := string(v)
if strings.HasPrefix(config, rolelogTransactionSampleRateAttr) {
var result = strings.Split(strings.TrimPrefix(config, rolelogTransactionSampleRateAttr+"="), ", ")
res, err := strconv.Atoi(result[0])
if err != nil {
return -1, fmt.Errorf("Error reading log_transaction_sample_rate", err)
}
return res, nil
}
}
return 0, nil
}

// 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 @@ -745,6 +886,22 @@ func resourcePostgreSQLRoleUpdate(db *DBConnection, d *schema.ResourceData) erro
return err
}

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

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

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

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

if err = setAssumeRole(txn, d); err != nil {
return err
}
Expand Down Expand Up @@ -1119,6 +1276,106 @@ func setIdleInTransactionSessionTimeout(txn *sql.Tx, d *schema.ResourceData) err
return nil
}

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

roleName := d.Get(roleNameAttr).(string)
logMinDurationStatement := d.Get(roleLogMinDurationStatementAttr).(int)
if logMinDurationStatement != 0 {
sql := fmt.Sprintf(
"ALTER ROLE %s SET log_min_duration_statement TO %d", pq.QuoteIdentifier(roleName), logMinDurationStatement,
)
if _, err := txn.Exec(sql); err != nil {
return fmt.Errorf("could not set log_min_duration_statement %d for %s: %w", logMinDurationStatement, roleName, err)
}
} else {
sql := fmt.Sprintf(
"ALTER ROLE %s RESET log_min_duration_statement", pq.QuoteIdentifier(roleName),
)
if _, err := txn.Exec(sql); err != nil {
return fmt.Errorf("could not reset log_min_duration_statement for %s: %w", roleName, err)
}
}
return nil
}

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

roleName := d.Get(roleNameAttr).(string)
logMinDurationSample := d.Get(roleLogMinDurationSampleAttr).(int)
if logMinDurationSample != 0 {
sql := fmt.Sprintf(
"ALTER ROLE %s SET log_min_duration_sample TO %d", pq.QuoteIdentifier(roleName), logMinDurationSample,
)
if _, err := txn.Exec(sql); err != nil {
return fmt.Errorf("could not set log_min_duration_sample %d for %s: %w", logMinDurationSample, roleName, err)
}
} else {
sql := fmt.Sprintf(
"ALTER ROLE %s RESET log_min_duration_sample", pq.QuoteIdentifier(roleName),
)
if _, err := txn.Exec(sql); err != nil {
return fmt.Errorf("could not reset log_min_duration_sample for %s: %w", roleName, err)
}
}
return nil
}

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

roleName := d.Get(roleNameAttr).(string)
logStatementSampleRate := d.Get(roleLogStatementSampleRateAttr).(int)
if logStatementSampleRate != 0 {
sql := fmt.Sprintf(
"ALTER ROLE %s SET log_statement_sample_rate TO %d", pq.QuoteIdentifier(roleName), logStatementSampleRate,
)
if _, err := txn.Exec(sql); err != nil {
return fmt.Errorf("could not set log_statement_sample_rate %d for %s: %w", logStatementSampleRate, roleName, err)
}
} else {
sql := fmt.Sprintf(
"ALTER ROLE %s RESET log_statement_sample_rate", pq.QuoteIdentifier(roleName),
)
if _, err := txn.Exec(sql); err != nil {
return fmt.Errorf("could not reset log_statement_sample_rate for %s: %w", roleName, err)
}
}
return nil
}

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

roleName := d.Get(roleNameAttr).(string)
logTransactionSampleRate := d.Get(roleLogTransactionSampleRateAttr).(int)
if logTransactionSampleRate != 0 {
sql := fmt.Sprintf(
"ALTER ROLE %s SET log_transaction_sample_rate TO %d", pq.QuoteIdentifier(roleName), logTransactionSampleRate,
)
if _, err := txn.Exec(sql); err != nil {
return fmt.Errorf("could not set log_transaction_sample_rate %d for %s: %w", logTransactionSampleRate, roleName, err)
}
} else {
sql := fmt.Sprintf(
"ALTER ROLE %s RESET log_transaction_sample_rate", pq.QuoteIdentifier(roleName),
)
if _, err := txn.Exec(sql); err != nil {
return fmt.Errorf("could not reset log_transaction_sample_rate for %s: %w", roleName, err)
}
}
return nil
}

func setAssumeRole(txn *sql.Tx, d *schema.ResourceData) error {
if !d.HasChange(roleAssumeRoleAttr) {
return nil
Expand Down
24 changes: 24 additions & 0 deletions postgresql/resource_postgresql_role_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ 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_min_duration_statement", "-1"),
resource.TestCheckResourceAttr("postgresql_role.role_with_defaults", "log_min_duration_sample", "-1"),
resource.TestCheckResourceAttr("postgresql_role.role_with_defaults", "log_statement_sample_rate", "1.0"),
resource.TestCheckResourceAttr("postgresql_role.role_with_defaults", "log_transaction_sample_rate", "0.0"),
resource.TestCheckResourceAttr("postgresql_role.role_with_defaults", "assume_role", ""),

resource.TestCheckResourceAttr("postgresql_role.role_with_create_database", "name", "role_with_create_database"),
Expand Down Expand Up @@ -122,6 +126,10 @@ resource "postgresql_role" "update_role" {
search_path = ["mysearchpath"]
statement_timeout = 30000
idle_in_transaction_session_timeout = 60000
log_min_duration_statement = 30000
log_min_duration_sample = 1000
log_statement_sample_rate = 0.01
log_transaction_sample_rate = 0.001
assume_role = "${postgresql_role.group_role.name}"
}
`
Expand All @@ -146,6 +154,10 @@ 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.update_role", "log_min_duration_statement", "-1"),
resource.TestCheckResourceAttr("postgresql_role.update_role", "log_min_duration_sample", "-1"),
resource.TestCheckResourceAttr("postgresql_role.update_role", "log_statement_sample_rate", "1.0"),
resource.TestCheckResourceAttr("postgresql_role.update_role", "log_transaction_sample_rate", "0.0"),
resource.TestCheckResourceAttr("postgresql_role.update_role", "assume_role", ""),
testAccCheckRoleCanLogin(t, "update_role", "toto"),
),
Expand All @@ -167,6 +179,10 @@ 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.update_role", "log_min_duration_statement", "30000"),
resource.TestCheckResourceAttr("postgresql_role.update_role", "log_min_duration_sample", "1000"),
resource.TestCheckResourceAttr("postgresql_role.update_role", "log_statement_sample_rate", "0.01"),
resource.TestCheckResourceAttr("postgresql_role.update_role", "log_transaction_sample_rate", "0.001"),
resource.TestCheckResourceAttr("postgresql_role.update_role", "assume_role", "group_role"),
testAccCheckRoleCanLogin(t, "update_role2", "titi"),
),
Expand All @@ -185,6 +201,10 @@ 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.update_role", "log_min_duration_statement", "-1"),
resource.TestCheckResourceAttr("postgresql_role.update_role", "log_min_duration_sample", "-1"),
resource.TestCheckResourceAttr("postgresql_role.update_role", "log_statement_sample_rate", "1.0"),
resource.TestCheckResourceAttr("postgresql_role.update_role", "log_transaction_sample_rate", "0.0"),
resource.TestCheckResourceAttr("postgresql_role.update_role", "assume_role", ""),
testAccCheckRoleCanLogin(t, "update_role", "toto"),
),
Expand Down Expand Up @@ -431,6 +451,10 @@ resource "postgresql_role" "role_with_defaults" {
valid_until = "infinity"
statement_timeout = 0
idle_in_transaction_session_timeout = 0
log_min_duration_statement = -1
log_min_duration_sample = -1
log_statement_sample_rate = 1.0
log_transaction_sample_rate = 0.0
assume_role = ""
}

Expand Down