Skip to content

Commit d0123ce

Browse files
committed
fix(postgresql_role): Postgresql 16 compatibility (with new roles management) (#407)
1 parent e3b3fe3 commit d0123ce

18 files changed

+673
-60
lines changed

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828
name: Set up Go
2929
uses: actions/setup-go@v2
3030
with:
31-
go-version: '1.20'
31+
go-version: '1.23'
3232
-
3333
name: Import GPG key
3434
id: import_gpg

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212

1313
strategy:
1414
matrix:
15-
pgversion: [15, 14, 13, 12, 11]
15+
pgversion: [16, 15, 14, 13, 12, 11]
1616

1717
env:
1818
PGVERSION: ${{ matrix.pgversion }}

.gitignore

Lines changed: 148 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,149 @@
1-
./*.tfstate
2-
.terraform/
3-
.terraform.lock.hcl*
4-
*.log
5-
.*.swp
6-
tests/docker-compose.*.yml
71
terraform-provider-postgresql
2+
3+
### JetBrains+all template
4+
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
5+
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
6+
7+
# User-specific stuff
8+
.idea/**/workspace.xml
9+
.idea/**/tasks.xml
10+
.idea/**/usage.statistics.xml
11+
.idea/**/dictionaries
12+
.idea/**/shelf
13+
14+
# AWS User-specific
15+
.idea/**/aws.xml
16+
17+
# Generated files
18+
.idea/**/contentModel.xml
19+
20+
# Sensitive or high-churn files
21+
.idea/**/dataSources/
22+
.idea/**/dataSources.ids
23+
.idea/**/dataSources.local.xml
24+
.idea/**/sqlDataSources.xml
25+
.idea/**/dynamic.xml
26+
.idea/**/uiDesigner.xml
27+
.idea/**/dbnavigator.xml
28+
29+
# Gradle
30+
.idea/**/gradle.xml
31+
.idea/**/libraries
32+
33+
# Gradle and Maven with auto-import
34+
# When using Gradle or Maven with auto-import, you should exclude module files,
35+
# since they will be recreated, and may cause churn. Uncomment if using
36+
# auto-import.
37+
# .idea/artifacts
38+
# .idea/compiler.xml
39+
# .idea/jarRepositories.xml
40+
# .idea/modules.xml
41+
# .idea/*.iml
42+
# .idea/modules
43+
# *.iml
44+
# *.ipr
45+
46+
# CMake
47+
cmake-build-*/
48+
49+
# Mongo Explorer plugin
50+
.idea/**/mongoSettings.xml
51+
52+
# File-based project format
53+
*.iws
54+
55+
# IntelliJ
56+
out/
57+
58+
# mpeltonen/sbt-idea plugin
59+
.idea_modules/
60+
61+
# JIRA plugin
62+
atlassian-ide-plugin.xml
63+
64+
# Cursive Clojure plugin
65+
.idea/replstate.xml
66+
67+
# SonarLint plugin
68+
.idea/sonarlint/
69+
70+
# Crashlytics plugin (for Android Studio and IntelliJ)
71+
com_crashlytics_export_strings.xml
72+
crashlytics.properties
73+
crashlytics-build.properties
74+
fabric.properties
75+
76+
# Editor-based Rest Client
77+
.idea/httpRequests
78+
79+
# Android studio 3.1+ serialized cache file
80+
.idea/caches/build_file_checksums.ser
81+
82+
83+
### Linux template
84+
*~
85+
86+
# temporary files which can be created if a process still has a handle open of a deleted file
87+
.fuse_hidden*
88+
89+
# KDE directory preferences
90+
.directory
91+
92+
# Linux trash folder which might appear on any partition or disk
93+
.Trash-*
94+
95+
# .nfs files are created when an open file is removed but is still being accessed
96+
.nfs*
97+
98+
### Go template
99+
# If you prefer the allow list template instead of the deny list, see community template:
100+
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
101+
#
102+
# Binaries for programs and plugins
103+
*.exe
104+
*.exe~
105+
*.dll
106+
*.so
107+
*.dylib
108+
109+
# Test binary, built with `go test -c`
110+
*.test
111+
112+
# Output of the go coverage tool, specifically when used with LiteIDE
113+
*.out
114+
115+
# Dependency directories (remove the comment below to include it)
116+
# vendor/
117+
118+
# Go workspace file
119+
go.work
120+
121+
### Windows template
122+
# Windows thumbnail cache files
123+
Thumbs.db
124+
Thumbs.db:encryptable
125+
ehthumbs.db
126+
ehthumbs_vista.db
127+
128+
# Dump file
129+
*.stackdump
130+
131+
# Folder config file
132+
[Dd]esktop.ini
133+
134+
# Recycle Bin used on file shares
135+
$RECYCLE.BIN/
136+
137+
# Windows Installer files
138+
*.cab
139+
*.msi
140+
*.msix
141+
*.msm
142+
*.msp
143+
144+
# Windows shortcuts
145+
*.lnk
146+
147+
.terraform
148+
.terraform.lock.hcl
149+
terraform.tfstate*

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Requirements
1212
------------
1313

1414
- [Terraform](https://www.terraform.io/downloads.html) 0.12.x
15-
- [Go](https://golang.org/doc/install) 1.16 (to build the provider plugin)
15+
- [Go](https://golang.org/doc/install) 1.21 (to build the provider plugin)
1616

1717
Building The Provider
1818
---------------------

examples/issues/407/dev.tfrc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# See https://www.terraform.io/cli/config/config-file#development-overrides-for-provider-developers
2+
# Use `go build -o ./examples/issues/407/postgresql/terraform-provider-postgresql` in the project root to build the provider.
3+
# Then run terraform in this example directory.
4+
5+
# terraform init
6+
# TF_CLI_CONFIG_FILE=dev.tfrc terraform apply
7+
8+
provider_installation {
9+
dev_overrides {
10+
"cyrilgdn/postgresql" = "./postgresql"
11+
}
12+
direct {}
13+
}

examples/issues/407/test.tf

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
terraform {
2+
required_version = ">= 1.0"
3+
4+
required_providers {
5+
postgresql = {
6+
source = "cyrilgdn/postgresql"
7+
version = "~>1"
8+
}
9+
}
10+
}
11+
12+
provider "postgresql" {
13+
superuser = false
14+
port = 25432
15+
username = "rds"
16+
password = "rds"
17+
sslmode = "disable"
18+
}
19+
20+
resource "postgresql_role" "test_role_with_createrole_self_grant" {
21+
name = "test_role_with_createrole_self_grant"
22+
parameters {
23+
createrole_self_grant = "set,inherit"
24+
}
25+
}

postgresql/config.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ const (
4444
featurePubWithoutTruncate
4545
featureFunction
4646
featureServer
47+
featureCreateRoleSelfGrant
4748
)
4849

4950
var (
@@ -115,6 +116,10 @@ var (
115116
featureServer: semver.MustParseRange(">=10.0.0"),
116117

117118
featureDatabaseOwnerRole: semver.MustParseRange(">=15.0.0"),
119+
120+
// New privileges rules in version 16
121+
// https://www.postgresql.org/docs/16/release-16.html#RELEASE-16-PRIVILEGES
122+
featureCreateRoleSelfGrant: semver.MustParseRange(">=16.0.0"),
118123
}
119124
)
120125

postgresql/provider.go

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ package postgresql
22

33
import (
44
"context"
5+
"database/sql"
56
"fmt"
7+
"github.com/hashicorp/terraform-plugin-log/tflog"
68
"os"
9+
"regexp"
710

811
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
912
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
10-
1113
"github.com/blang/semver"
1214
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
1315
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
@@ -363,6 +365,39 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) {
363365
}
364366
}
365367

368+
err := checkCreateRoleSelfGrant(config)
369+
if err != nil {
370+
return nil, err
371+
}
372+
366373
client := config.NewClient(d.Get("database").(string))
367374
return client, nil
368375
}
376+
377+
func checkCreateRoleSelfGrant(config Config) error {
378+
client := config.NewClient("postgres")
379+
connect, err := client.Connect()
380+
if err != nil {
381+
return err
382+
}
383+
384+
username := config.Username
385+
386+
if connect.featureSupported(featureCreateRoleSelfGrant) && !config.Superuser {
387+
var paramValue string
388+
query := `SELECT coalesce(replace(jsonb_path_query_first(to_jsonb(rolconfig),'$[*] ? (@ like_regex "^createrole_self_grant=")')::TEXT,'%[1]s=',''), '') FROM pg_catalog.pg_roles WHERE rolname = $1`
389+
err := connect.QueryRow(query, username).Scan(&paramValue)
390+
switch {
391+
case err == sql.ErrNoRows:
392+
// They don't have a parameter, just skip
393+
break
394+
case err != nil:
395+
tflog.Debug(context.Background(), fmt.Sprintf("User %s parameters cannot be checked: %v", username, err))
396+
}
397+
if !regexp.MustCompile(`^set\s*,\s*inherit$|^inherit\s*,\s*set$`).MatchString(paramValue) {
398+
msg := fmt.Sprintf("Provider user %[1]s has not configured property 'createrole_self_grant' properly. Creating new roles will not create non admin grants by default. You can set it by \"ALTER ROLE '%[1]s' SET createrole_self_grant 'set, inherit';\"", username)
399+
tflog.Warn(context.Background(), msg)
400+
}
401+
}
402+
return nil
403+
}

postgresql/provider_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,37 @@ func TestProvider_impl(t *testing.T) {
2929
var _ *schema.Provider = Provider()
3030
}
3131

32+
func TestAccProviderSetCreateRoleSelfGrant(t *testing.T) {
33+
skipIfNotAcc(t)
34+
35+
config := getTestConfig(t)
36+
client := config.NewClient("postgres")
37+
db, err := client.Connect()
38+
if err != nil {
39+
t.Fatal(err)
40+
}
41+
42+
if db.featureSupported(featureCreateRoleSelfGrant) {
43+
t.Skipf("Skip tests for unsuported feature %d in Postgres %s", featureCreateRoleSelfGrant, db.version)
44+
}
45+
46+
// Create NON superuser role
47+
if _, err = db.Exec("CREATE ROLE rds_srg LOGIN CREATEDB CREATEROLE PASSWORD 'rds_srg'"); err != nil {
48+
t.Fatalf("could not create role for test user paramaters: %v", err)
49+
}
50+
defer func() {
51+
_, _ = db.Exec("DROP ROLE rds_srg")
52+
}()
53+
54+
provider := Provider()
55+
provider.Configure(context.Background(), terraform.NewResourceConfigRaw(
56+
map[string]interface{}{
57+
"username": "rds_srg",
58+
"password": "rds_srg",
59+
},
60+
))
61+
}
62+
3263
func testAccPreCheck(t *testing.T) {
3364
var host string
3465
if host = os.Getenv("PGHOST"); host == "" {

postgresql/resource_postgresql_database_test.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,8 +220,17 @@ resource postgresql_database "test_db" {
220220
resource.TestCheckResourceAttr("postgresql_database.test_db", "name", "test_db"),
221221
resource.TestCheckResourceAttr("postgresql_database.test_db", "owner", "test_owner"),
222222

223-
// check if connected user does not have test_owner granted anymore.
224-
checkUserMembership(t, dsn, config.Username, "test_owner", false),
223+
func(state *terraform.State) error {
224+
connect, _ := config.NewClient("postgres").Connect()
225+
if connect.featureSupported(featureCreateRoleSelfGrant) {
226+
// in PG 16 all created roles have creator grant with admin option
227+
checkUserMembership(t, dsn, config.Username, "test_owner", true)
228+
} else {
229+
// check if connected user does not have test_owner granted anymore.
230+
checkUserMembership(t, dsn, config.Username, "test_owner", false)
231+
}
232+
return nil
233+
},
225234
),
226235
},
227236
},

0 commit comments

Comments
 (0)