Skip to content

Commit 65eb45c

Browse files
Add new resource to support ALTER ROLE
1 parent 4c52d3a commit 65eb45c

5 files changed

+403
-0
lines changed

postgresql/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ func Provider() *schema.Provider {
166166
"postgresql_schema": resourcePostgreSQLSchema(),
167167
"postgresql_role": resourcePostgreSQLRole(),
168168
"postgresql_function": resourcePostgreSQLFunction(),
169+
"postgresql_alter_role": resourcePostgreSQLAlterRole(),
169170
},
170171

171172
ConfigureFunc: providerConfigure,
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
package postgresql
2+
3+
import (
4+
"database/sql"
5+
"fmt"
6+
"log"
7+
"strings"
8+
9+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
10+
"github.com/lib/pq"
11+
)
12+
13+
const (
14+
getAlterRoleQuery = `
15+
SELECT
16+
rolname as role,
17+
rolconfig as role_parameters
18+
FROM
19+
pg_catalog.pg_roles
20+
WHERE
21+
rolname = $1
22+
`
23+
)
24+
25+
func resourcePostgreSQLAlterRole() *schema.Resource {
26+
return &schema.Resource{
27+
Create: PGResourceFunc(resourcePostgreSQLAlterRoleCreate),
28+
Read: PGResourceFunc(resourcePostgreSQLAlterRoleRead),
29+
Delete: PGResourceFunc(resourcePostgreSQLAlterRoleDelete),
30+
31+
Schema: map[string]*schema.Schema{
32+
"role_name": {
33+
Type: schema.TypeString,
34+
Required: true,
35+
ForceNew: true,
36+
Description: "The name of the role to alter the attributes of",
37+
},
38+
"parameter_key": {
39+
Type: schema.TypeString,
40+
Required: true,
41+
ForceNew: true,
42+
Description: "The name of the parameter to alter on the role",
43+
},
44+
"parameter_value": {
45+
Type: schema.TypeString,
46+
Required: true,
47+
ForceNew: true,
48+
Description: "The value of the parameter which is being set",
49+
},
50+
},
51+
}
52+
}
53+
54+
func resourcePostgreSQLAlterRoleRead(db *DBConnection, d *schema.ResourceData) error {
55+
if !db.featureSupported(featurePrivileges) {
56+
return fmt.Errorf(
57+
"postgresql_alter_role resource is not supported for this Postgres version (%s)",
58+
db.version,
59+
)
60+
}
61+
62+
return readAlterRole(db, d)
63+
}
64+
65+
func resourcePostgreSQLAlterRoleCreate(db *DBConnection, d *schema.ResourceData) error {
66+
if !db.featureSupported(featurePrivileges) {
67+
return fmt.Errorf(
68+
"postgresql_alter_role resource is not supported for this Postgres version (%s)",
69+
db.version,
70+
)
71+
}
72+
73+
txn, err := startTransaction(db.client, "")
74+
if err != nil {
75+
return err
76+
}
77+
defer deferredRollback(txn)
78+
79+
// Reset the role alterations before altering them again.
80+
if err = resetAlterRole(txn, d); err != nil {
81+
return err
82+
}
83+
84+
if err = alterRole(txn, d); err != nil {
85+
return err
86+
}
87+
88+
if err = txn.Commit(); err != nil {
89+
return fmt.Errorf("could not commit transaction: %w", err)
90+
}
91+
92+
d.SetId(generateAlterRoleID(d))
93+
94+
return readAlterRole(db, d)
95+
}
96+
97+
func resourcePostgreSQLAlterRoleDelete(db *DBConnection, d *schema.ResourceData) error {
98+
if !db.featureSupported(featurePrivileges) {
99+
return fmt.Errorf(
100+
"postgresql_alter_role resource is not supported for this Postgres version (%s)",
101+
db.version,
102+
)
103+
}
104+
105+
txn, err := startTransaction(db.client, "")
106+
if err != nil {
107+
return err
108+
}
109+
defer deferredRollback(txn)
110+
111+
if err = resetAlterRole(txn, d); err != nil {
112+
return err
113+
}
114+
115+
if err = txn.Commit(); err != nil {
116+
return fmt.Errorf("could not commit transaction: %w", err)
117+
}
118+
119+
return nil
120+
}
121+
122+
func readAlterRole(db QueryAble, d *schema.ResourceData) error {
123+
var (
124+
roleName string
125+
roleParameters pq.ByteaArray
126+
)
127+
128+
alterRoleID := d.Id()
129+
alterParameterKey := d.Get("parameter_key")
130+
131+
values := []interface{}{
132+
&roleName,
133+
&roleParameters,
134+
}
135+
136+
err := db.QueryRow(getAlterRoleQuery, d.Get("role_name")).Scan(values...)
137+
switch {
138+
case err == sql.ErrNoRows:
139+
log.Printf("[WARN] PostgreSQL alter role (%q) not found", alterRoleID)
140+
d.SetId("")
141+
return nil
142+
case err != nil:
143+
return fmt.Errorf("error reading alter role: %w", err)
144+
}
145+
146+
d.Set("parameter_key", alterParameterKey)
147+
d.Set("parameter_value", "")
148+
d.Set("role_name", roleName)
149+
d.SetId(generateAlterRoleID(d))
150+
151+
for _, v := range roleParameters {
152+
parameter := string(v)
153+
parameterKey := strings.Split(parameter, "=")[0]
154+
parameterValue := strings.Split(parameter, "=")[1]
155+
if parameterKey == alterParameterKey {
156+
d.Set("parameter_key", parameterKey)
157+
d.Set("parameter_value", parameterValue)
158+
}
159+
}
160+
161+
return nil
162+
}
163+
164+
func createAlterRoleQuery(d *schema.ResourceData) string {
165+
alterRole, _ := d.Get("role_name").(string)
166+
alterParameterKey, _ := d.Get("parameter_key").(string)
167+
alterParameterValue, _ := d.Get("parameter_value").(string)
168+
169+
query := fmt.Sprintf(
170+
"ALTER ROLE %s SET %s TO %s",
171+
pq.QuoteIdentifier(alterRole),
172+
pq.QuoteIdentifier(alterParameterKey),
173+
pq.QuoteIdentifier(alterParameterValue),
174+
)
175+
176+
return query
177+
}
178+
179+
func createResetAlterRoleQuery(d *schema.ResourceData) string {
180+
alterRole, _ := d.Get("role_name").(string)
181+
alterParameterKey, _ := d.Get("parameter_key").(string)
182+
183+
return fmt.Sprintf(
184+
"ALTER ROLE %s RESET %s",
185+
pq.QuoteIdentifier(alterRole),
186+
pq.QuoteIdentifier(alterParameterKey),
187+
)
188+
}
189+
190+
func alterRole(txn *sql.Tx, d *schema.ResourceData) error {
191+
query := createAlterRoleQuery(d)
192+
log.Println(query)
193+
if _, err := txn.Exec(query); err != nil {
194+
return fmt.Errorf("could not execute alter query: (%s): %w", query, err)
195+
}
196+
return nil
197+
}
198+
199+
func resetAlterRole(txn *sql.Tx, d *schema.ResourceData) error {
200+
query := createResetAlterRoleQuery(d)
201+
fmt.Println(query)
202+
if _, err := txn.Exec(query); err != nil {
203+
return fmt.Errorf("could not execute alter reset query (%s): %w", query, err)
204+
}
205+
return nil
206+
}
207+
208+
func generateAlterRoleID(d *schema.ResourceData) string {
209+
return strings.Join([]string{d.Get("role_name").(string), d.Get("parameter_key").(string), d.Get("parameter_value").(string)}, "_")
210+
}
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
package postgresql
2+
3+
import (
4+
"database/sql"
5+
"fmt"
6+
"testing"
7+
8+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
9+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
10+
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
11+
"github.com/lib/pq"
12+
)
13+
14+
func TestCreateAlterRoleQuery(t *testing.T) {
15+
var roleName = "foo"
16+
var parameterKey = "log_statement"
17+
var parameterValue = "ALL"
18+
19+
cases := []struct {
20+
resource map[string]interface{}
21+
expected string
22+
}{
23+
{
24+
resource: map[string]interface{}{
25+
"role_name": roleName,
26+
"parameter_key": parameterKey,
27+
"parameter_value": parameterValue,
28+
},
29+
expected: fmt.Sprintf("ALTER ROLE %s SET %s TO %s",
30+
pq.QuoteIdentifier(roleName),
31+
pq.QuoteIdentifier(parameterKey),
32+
pq.QuoteIdentifier(parameterValue)),
33+
},
34+
}
35+
36+
for _, c := range cases {
37+
out := createAlterRoleQuery(schema.TestResourceDataRaw(t, resourcePostgreSQLAlterRole().Schema, c.resource))
38+
if out != c.expected {
39+
t.Fatalf("Error matching output and expected: %#v vs %#v", out, c.expected)
40+
}
41+
}
42+
}
43+
44+
func TestResetRoleQuery(t *testing.T) {
45+
var roleName = "foo"
46+
var parameterKey = "log_statement"
47+
48+
expected := fmt.Sprintf("ALTER ROLE %s RESET %s", pq.QuoteIdentifier(roleName), pq.QuoteIdentifier(parameterKey))
49+
50+
cases := []struct {
51+
resource map[string]interface{}
52+
}{
53+
{
54+
resource: map[string]interface{}{
55+
"role_name": roleName,
56+
"parameter_key": parameterKey,
57+
},
58+
},
59+
}
60+
61+
for _, c := range cases {
62+
out := createResetAlterRoleQuery(schema.TestResourceDataRaw(t, resourcePostgreSQLAlterRole().Schema, c.resource))
63+
if out != expected {
64+
t.Fatalf("Error matching output and expected: %#v vs %#v", out, expected)
65+
}
66+
}
67+
}
68+
69+
func TestAccPostgresqlAlterRole(t *testing.T) {
70+
skipIfNotAcc(t)
71+
72+
config := getTestConfig(t)
73+
dsn := config.connStr("postgres")
74+
75+
dbSuffix, teardown := setupTestDatabase(t, false, true)
76+
defer teardown()
77+
78+
_, roleName := getTestDBNames(dbSuffix)
79+
80+
parameterKey := "log_statement"
81+
parameterValue := "ALL"
82+
83+
testAccPostgresqlAlterRoleResources := fmt.Sprintf(`
84+
resource "postgresql_alter_role" "alter_role" {
85+
role_name = "%s"
86+
parameter_key = "%s"
87+
parameter_value = "%s"
88+
}
89+
`, roleName, parameterKey, parameterValue)
90+
91+
resource.Test(t, resource.TestCase{
92+
PreCheck: func() {
93+
testAccPreCheck(t)
94+
testCheckCompatibleVersion(t, featurePrivileges)
95+
},
96+
Providers: testAccProviders,
97+
Steps: []resource.TestStep{
98+
{
99+
Config: testAccPostgresqlAlterRoleResources,
100+
Check: resource.ComposeTestCheckFunc(
101+
resource.TestCheckResourceAttr(
102+
"postgresql_alter_role.alter_role", "role_name", roleName),
103+
resource.TestCheckResourceAttr(
104+
"postgresql_alter_role.alter_role", "parameter_key", parameterKey),
105+
resource.TestCheckResourceAttr(
106+
"postgresql_alter_role.alter_role", "parameter_value", parameterValue),
107+
checkAlterRole(t, dsn, roleName, parameterKey, parameterValue),
108+
),
109+
},
110+
},
111+
})
112+
}
113+
114+
func checkAlterRole(t *testing.T, dsn, role string, parameterKey string, parameterValue string) resource.TestCheckFunc {
115+
return func(s *terraform.State) error {
116+
db, err := sql.Open("postgres", dsn)
117+
if err != nil {
118+
t.Fatalf("could to create connection pool: %v", err)
119+
}
120+
defer db.Close()
121+
122+
roleParameter := fmt.Sprintf("%s=%s", parameterKey, parameterValue)
123+
var _rez int
124+
err = db.QueryRow(`
125+
SELECT 1
126+
FROM pg_catalog.pg_roles
127+
WHERE rolname = $1
128+
AND $2=ANY(rolconfig)
129+
`, role, roleParameter).Scan(&_rez)
130+
131+
switch {
132+
case err == sql.ErrNoRows:
133+
return fmt.Errorf(
134+
"Role %s does not have the following attribute assigned %s",
135+
role, roleParameter,
136+
)
137+
138+
case err != nil:
139+
t.Fatalf("could not check role attributes: %v", err)
140+
}
141+
142+
return nil
143+
}
144+
}

0 commit comments

Comments
 (0)