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

Setting a map of default parameters for Role. Draft, WIP #155

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
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
151 changes: 151 additions & 0 deletions postgresql/resource_postgresql_role.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const (
roleLoginAttr = "login"
roleNameAttr = "name"
rolePasswordAttr = "password"
roleParametersAttr = "parameters"
roleReplicationAttr = "replication"
roleSkipDropRoleAttr = "skip_drop_role"
roleSkipReassignOwnedAttr = "skip_reassign_owned"
Expand All @@ -38,6 +39,8 @@ const (
roleDepEncryptedAttr = "encrypted"
)

var roleStandaloneParameters = [...]string{roleSearchPathAttr, roleStatementTimeoutAttr}

func resourcePostgreSQLRole() *schema.Resource {
return &schema.Resource{
Create: resourcePostgreSQLRoleCreate,
Expand Down Expand Up @@ -160,6 +163,13 @@ func resourcePostgreSQLRole() *schema.Resource {
Description: "Abort any statement that takes more than the specified number of milliseconds",
ValidateFunc: validation.IntAtLeast(0),
},
roleParametersAttr: {
Type: schema.TypeMap,
Optional: true,
Description: "Specifies default parameters which will be set for the role",
Elem: &schema.Schema{Type: schema.TypeString},
ValidateFunc: validateRoleParameters,
},
},
}
}
Expand Down Expand Up @@ -294,6 +304,10 @@ func resourcePostgreSQLRoleCreate(d *schema.ResourceData, meta interface{}) erro
return err
}

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

if err = txn.Commit(); err != nil {
return fmt.Errorf("could not commit transaction: %w", err)
}
Expand Down Expand Up @@ -458,6 +472,11 @@ func resourcePostgreSQLRoleReadImpl(c *Client, d *schema.ResourceData) error {

d.SetId(roleName)

err = getRoleParameters(c.DB(), d)
if err != nil {
return err
}

password, err := readRolePassword(c, d, roleCanLogin)
if err != nil {
return err
Expand Down Expand Up @@ -628,6 +647,10 @@ func resourcePostgreSQLRoleUpdate(d *schema.ResourceData, meta interface{}) erro
return err
}

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

if err = txn.Commit(); err != nil {
return fmt.Errorf("could not commit transaction: %w", err)
}
Expand Down Expand Up @@ -900,6 +923,134 @@ func grantRoles(txn *sql.Tx, d *schema.ResourceData) error {
return nil
}

func isStandaloneRoleParameter(paramName string) bool {
for _, standaloneParameter := range roleStandaloneParameters {
if strings.ToLower(paramName) == strings.ToLower(standaloneParameter) {
return true
}
}
return false
}

func validateRoleParameters(value interface{}, key string) (ws []string, es []error) {
paramValues := value.(map[string]interface{})

for p, v := range paramValues {
if len(v.(string)) == 0 {
es = append(es, fmt.Errorf("Parameter %s has no value", p))
}

if isStandaloneRoleParameter(p) {
es = append(es, fmt.Errorf("Parameter %s should be set by dedicated Role resource property", p))
}
}
return
}

func setRoleParameters(txn *sql.Tx, d *schema.ResourceData) error {
roleName := d.Get(roleNameAttr).(string)
roleParameters := d.Get(roleParametersAttr).(map[string]interface{})

for param, value := range roleParameters {
// We completely ignore parameters set by other properties
if isStandaloneRoleParameter(param) {
continue
}

query := fmt.Sprintf(
"ALTER ROLE %s SET %s TO %s", pq.QuoteIdentifier(roleName), param, pq.QuoteLiteral(value.(string)),
)

fmt.Printf("Setting: %s\n", query)

if _, err := txn.Exec(query); err != nil {
return fmt.Errorf("Could not set parameter %s for %s: %w", param, roleName, err)
}
}

return nil
}

func getRoleParameters(db *sql.DB, d *schema.ResourceData) error {
var params map[string]string
var err error

if params, err = getRoleParametersImpl(db, d); err != nil {
return err
}

return d.Set(roleParametersAttr, params)
}

func alterRoleParameters(txn *sql.Tx, d *schema.ResourceData) error {
roleName := d.Get(roleNameAttr).(string)
roleParameters := d.Get(roleParametersAttr).(map[string]interface{})

var existingParams map[string]string
var err error

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

// Unset parameters that currently exist, but are not supposed to be set
for param := range existingParams {
// We completely ignore parameters set by other properties
if isStandaloneRoleParameter(param) {
continue
}

if _, ok := roleParameters[param]; !ok {
sql := fmt.Sprintf(
"ALTER ROLE %s RESET %s", pq.QuoteIdentifier(roleName), param,
)

fmt.Printf("Altering: %s\n", sql)

if _, err := txn.Exec(sql); err != nil {
return fmt.Errorf("Could not reset %s parameter for %s: %w", param, roleName, err)
}
}
}

// Now let's set actual parameters
return setRoleParameters(txn, d)
}

func getRoleParametersImpl(db QueryAble, d *schema.ResourceData) (map[string]string, error) {
roleName := d.Get(roleNameAttr).(string)

query := "SELECT option_name, option_value FROM (SELECT (pg_options_to_table(r.rolconfig)).* FROM pg_roles r where rolname = $1) t"
rows, err := db.Query(query, roleName)

if err != nil {
return nil, fmt.Errorf("Unable to read role paramaters for %s: %w", roleName, err)
}
defer rows.Close()

dbParams := make(map[string]string)

for rows.Next() {
var (
paramName string
paramValue string
)
if err := rows.Scan(&paramName, &paramValue); err != nil {
return nil, fmt.Errorf("Unable to read role paramaters for %s: %w", roleName, err)
}

// We completely ignore parameters set by other properties
if isStandaloneRoleParameter(paramName) {
fmt.Printf("Ignored: %s, %s\n", paramName, paramValue)
continue
}

fmt.Printf("Getting: %s, %s\n", paramName, paramValue)
dbParams[paramName] = paramValue
}
return dbParams, nil
}

func alterSearchPath(txn *sql.Tx, d *schema.ResourceData) error {
role := d.Get(roleNameAttr).(string)
searchPathInterface := d.Get(roleSearchPathAttr).([]interface{})
Expand Down
14 changes: 14 additions & 0 deletions postgresql/resource_postgresql_role_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ resource "postgresql_role" "update_role" {
login = true
password = "toto"
valid_until = "2099-05-04 12:00:00+00"
parameters = {
application_name = "First_app"
}
}
`

Expand All @@ -90,6 +93,11 @@ resource "postgresql_role" "update_role" {
roles = ["${postgresql_role.group_role.name}"]
search_path = ["mysearchpath"]
statement_timeout = 30000
parameters = {
application_name = "Final"
log_statement = "all"
role = "${postgresql_role.group_role.name}"
}
}
`
resource.Test(t, resource.TestCase{
Expand Down Expand Up @@ -342,6 +350,12 @@ resource "postgresql_role" "sub_role" {
"${postgresql_role.myrole2.id}",
"${postgresql_role.role_simple.id}",
]
parameters = {
application_name = "aaa"
role = "${postgresql_role.myrole2.id}"
//setting this will raise validation error
//search_path = "aaa"
}
}

resource "postgresql_role" "role_with_search_path" {
Expand Down