Skip to content

Commit 9ab60ec

Browse files
committed
Add an Exists function for each resource type. Also add a provider RWMutex.
Some of the checks didn't support concurrent updates. This should improve the reliability of the provider.
1 parent 15e5831 commit 9ab60ec

6 files changed

Lines changed: 189 additions & 14 deletions

File tree

builtin/providers/postgresql/GNUmakefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# env TESTARGS='-test.parallel=1 -run TestAccPostgresqlSchema_AddPolicy' TF_LOG=warn make test
1+
# env TESTARGS='-run TestAccPostgresqlSchema_AddPolicy' TF_LOG=warn make test
22
#
33
# NOTE: As of PostgreSQL 9.6.1 the -test.parallel=1 is required when
44
# performing `DROP ROLE`-related actions. This behavior and requirement

builtin/providers/postgresql/config.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"database/sql"
66
"fmt"
77
"log"
8+
"sync"
89
"unicode"
910

1011
_ "github.com/lib/pq" //PostgreSQL db
@@ -27,6 +28,13 @@ type Config struct {
2728
type Client struct {
2829
username string
2930
connStr string
31+
32+
// PostgreSQL lock on pg_catalog. Many of the operations that Terraform
33+
// performs are not permitted to be concurrent. Unlike traditional
34+
// PostgreSQL tables that use MVCC, many of the PostgreSQL system
35+
// catalogs look like tables, but are not in-fact able to be
36+
// concurrently updated.
37+
catalogLock sync.RWMutex
3038
}
3139

3240
// NewClient returns new client config

builtin/providers/postgresql/resource_postgresql_database.go

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ func resourcePostgreSQLDatabase() *schema.Resource {
3232
Read: resourcePostgreSQLDatabaseRead,
3333
Update: resourcePostgreSQLDatabaseUpdate,
3434
Delete: resourcePostgreSQLDatabaseDelete,
35+
Exists: resourcePostgreSQLDatabaseExists,
3536
Importer: &schema.ResourceImporter{
3637
State: schema.ImportStatePassthrough,
3738
},
@@ -107,6 +108,10 @@ func resourcePostgreSQLDatabase() *schema.Resource {
107108

108109
func resourcePostgreSQLDatabaseCreate(d *schema.ResourceData, meta interface{}) error {
109110
c := meta.(*Client)
111+
112+
c.catalogLock.Lock()
113+
defer c.catalogLock.Unlock()
114+
110115
conn, err := c.Connect()
111116
if err != nil {
112117
return errwrap.Wrapf("Error connecting to PostgreSQL: {{err}}", err)
@@ -184,11 +189,14 @@ func resourcePostgreSQLDatabaseCreate(d *schema.ResourceData, meta interface{})
184189

185190
d.SetId(dbName)
186191

187-
return resourcePostgreSQLDatabaseRead(d, meta)
192+
return resourcePostgreSQLDatabaseReadImpl(d, meta)
188193
}
189194

190195
func resourcePostgreSQLDatabaseDelete(d *schema.ResourceData, meta interface{}) error {
191196
c := meta.(*Client)
197+
c.catalogLock.Lock()
198+
defer c.catalogLock.Unlock()
199+
192200
conn, err := c.Connect()
193201
if err != nil {
194202
return errwrap.Wrapf("Error connecting to PostgreSQL: {{err}}", err)
@@ -220,7 +228,38 @@ func resourcePostgreSQLDatabaseDelete(d *schema.ResourceData, meta interface{})
220228
return nil
221229
}
222230

231+
func resourcePostgreSQLDatabaseExists(d *schema.ResourceData, meta interface{}) (bool, error) {
232+
c := meta.(*Client)
233+
c.catalogLock.RLock()
234+
defer c.catalogLock.RUnlock()
235+
236+
conn, err := c.Connect()
237+
if err != nil {
238+
return false, err
239+
}
240+
defer conn.Close()
241+
242+
var dbName string
243+
err = conn.QueryRow("SELECT d.datname from pg_database d WHERE datname=$1", d.Id()).Scan(&dbName)
244+
switch {
245+
case err == sql.ErrNoRows:
246+
return false, nil
247+
case err != nil:
248+
return false, err
249+
}
250+
251+
return true, nil
252+
}
253+
223254
func resourcePostgreSQLDatabaseRead(d *schema.ResourceData, meta interface{}) error {
255+
c := meta.(*Client)
256+
c.catalogLock.RLock()
257+
defer c.catalogLock.RUnlock()
258+
259+
return resourcePostgreSQLDatabaseReadImpl(d, meta)
260+
}
261+
262+
func resourcePostgreSQLDatabaseReadImpl(d *schema.ResourceData, meta interface{}) error {
224263
c := meta.(*Client)
225264
conn, err := c.Connect()
226265
if err != nil {
@@ -276,6 +315,9 @@ func resourcePostgreSQLDatabaseRead(d *schema.ResourceData, meta interface{}) er
276315

277316
func resourcePostgreSQLDatabaseUpdate(d *schema.ResourceData, meta interface{}) error {
278317
c := meta.(*Client)
318+
c.catalogLock.Lock()
319+
defer c.catalogLock.Unlock()
320+
279321
conn, err := c.Connect()
280322
if err != nil {
281323
return err
@@ -308,7 +350,7 @@ func resourcePostgreSQLDatabaseUpdate(d *schema.ResourceData, meta interface{})
308350

309351
// Empty values: ALTER DATABASE name RESET configuration_parameter;
310352

311-
return resourcePostgreSQLDatabaseRead(d, meta)
353+
return resourcePostgreSQLDatabaseReadImpl(d, meta)
312354
}
313355

314356
func setDBName(conn *sql.DB, d *schema.ResourceData) error {

builtin/providers/postgresql/resource_postgresql_extension.go

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ func resourcePostgreSQLExtension() *schema.Resource {
2424
Read: resourcePostgreSQLExtensionRead,
2525
Update: resourcePostgreSQLExtensionUpdate,
2626
Delete: resourcePostgreSQLExtensionDelete,
27+
Exists: resourcePostgreSQLExtensionExists,
2728
Importer: &schema.ResourceImporter{
2829
State: schema.ImportStatePassthrough,
2930
},
@@ -52,6 +53,9 @@ func resourcePostgreSQLExtension() *schema.Resource {
5253

5354
func resourcePostgreSQLExtensionCreate(d *schema.ResourceData, meta interface{}) error {
5455
c := meta.(*Client)
56+
c.catalogLock.Lock()
57+
defer c.catalogLock.Unlock()
58+
5559
conn, err := c.Connect()
5660
if err != nil {
5761
return err
@@ -79,11 +83,43 @@ func resourcePostgreSQLExtensionCreate(d *schema.ResourceData, meta interface{})
7983

8084
d.SetId(extName)
8185

82-
return resourcePostgreSQLExtensionRead(d, meta)
86+
return resourcePostgreSQLExtensionReadImpl(d, meta)
87+
}
88+
89+
func resourcePostgreSQLExtensionExists(d *schema.ResourceData, meta interface{}) (bool, error) {
90+
c := meta.(*Client)
91+
c.catalogLock.Lock()
92+
defer c.catalogLock.Unlock()
93+
94+
conn, err := c.Connect()
95+
if err != nil {
96+
return false, err
97+
}
98+
defer conn.Close()
99+
100+
var extName string
101+
err = conn.QueryRow("SELECT extname FROM pg_catalog.pg_extension WHERE extname = $1", d.Id()).Scan(&extName)
102+
switch {
103+
case err == sql.ErrNoRows:
104+
return false, nil
105+
case err != nil:
106+
return false, err
107+
}
108+
109+
return true, nil
83110
}
84111

85112
func resourcePostgreSQLExtensionRead(d *schema.ResourceData, meta interface{}) error {
86113
c := meta.(*Client)
114+
c.catalogLock.RLock()
115+
defer c.catalogLock.RUnlock()
116+
117+
return resourcePostgreSQLExtensionReadImpl(d, meta)
118+
}
119+
120+
func resourcePostgreSQLExtensionReadImpl(d *schema.ResourceData, meta interface{}) error {
121+
c := meta.(*Client)
122+
87123
conn, err := c.Connect()
88124
if err != nil {
89125
return err
@@ -111,6 +147,9 @@ func resourcePostgreSQLExtensionRead(d *schema.ResourceData, meta interface{}) e
111147

112148
func resourcePostgreSQLExtensionDelete(d *schema.ResourceData, meta interface{}) error {
113149
c := meta.(*Client)
150+
c.catalogLock.Lock()
151+
defer c.catalogLock.Unlock()
152+
114153
conn, err := c.Connect()
115154
if err != nil {
116155
return err
@@ -132,6 +171,9 @@ func resourcePostgreSQLExtensionDelete(d *schema.ResourceData, meta interface{})
132171

133172
func resourcePostgreSQLExtensionUpdate(d *schema.ResourceData, meta interface{}) error {
134173
c := meta.(*Client)
174+
c.catalogLock.Lock()
175+
defer c.catalogLock.Unlock()
176+
135177
conn, err := c.Connect()
136178
if err != nil {
137179
return err
@@ -148,7 +190,7 @@ func resourcePostgreSQLExtensionUpdate(d *schema.ResourceData, meta interface{})
148190
return err
149191
}
150192

151-
return resourcePostgreSQLExtensionRead(d, meta)
193+
return resourcePostgreSQLExtensionReadImpl(d, meta)
152194
}
153195

154196
func setExtSchema(conn *sql.DB, d *schema.ResourceData) error {

builtin/providers/postgresql/resource_postgresql_role.go

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ func resourcePostgreSQLRole() *schema.Resource {
3838
Read: resourcePostgreSQLRoleRead,
3939
Update: resourcePostgreSQLRoleUpdate,
4040
Delete: resourcePostgreSQLRoleDelete,
41+
Exists: resourcePostgreSQLRoleExists,
4142
Importer: &schema.ResourceImporter{
4243
State: schema.ImportStatePassthrough,
4344
},
@@ -140,6 +141,9 @@ func resourcePostgreSQLRole() *schema.Resource {
140141

141142
func resourcePostgreSQLRoleCreate(d *schema.ResourceData, meta interface{}) error {
142143
c := meta.(*Client)
144+
c.catalogLock.Lock()
145+
defer c.catalogLock.Unlock()
146+
143147
conn, err := c.Connect()
144148
if err != nil {
145149
return errwrap.Wrapf("Error connecting to PostgreSQL: {{err}}", err)
@@ -243,12 +247,15 @@ func resourcePostgreSQLRoleCreate(d *schema.ResourceData, meta interface{}) erro
243247

244248
d.SetId(roleName)
245249

246-
return resourcePostgreSQLRoleRead(d, meta)
250+
return resourcePostgreSQLRoleReadImpl(d, meta)
247251
}
248252

249253
func resourcePostgreSQLRoleDelete(d *schema.ResourceData, meta interface{}) error {
250-
client := meta.(*Client)
251-
conn, err := client.Connect()
254+
c := meta.(*Client)
255+
c.catalogLock.Lock()
256+
defer c.catalogLock.Unlock()
257+
258+
conn, err := c.Connect()
252259
if err != nil {
253260
return err
254261
}
@@ -290,7 +297,38 @@ func resourcePostgreSQLRoleDelete(d *schema.ResourceData, meta interface{}) erro
290297
return nil
291298
}
292299

300+
func resourcePostgreSQLRoleExists(d *schema.ResourceData, meta interface{}) (bool, error) {
301+
c := meta.(*Client)
302+
c.catalogLock.RLock()
303+
defer c.catalogLock.RUnlock()
304+
305+
conn, err := c.Connect()
306+
if err != nil {
307+
return false, err
308+
}
309+
defer conn.Close()
310+
311+
var roleName string
312+
err = conn.QueryRow("SELECT rolname FROM pg_catalog.pg_roles WHERE rolname=$1", d.Id()).Scan(&roleName)
313+
switch {
314+
case err == sql.ErrNoRows:
315+
return false, nil
316+
case err != nil:
317+
return false, err
318+
}
319+
320+
return true, nil
321+
}
322+
293323
func resourcePostgreSQLRoleRead(d *schema.ResourceData, meta interface{}) error {
324+
c := meta.(*Client)
325+
c.catalogLock.RLock()
326+
defer c.catalogLock.RUnlock()
327+
328+
return resourcePostgreSQLRoleReadImpl(d, meta)
329+
}
330+
331+
func resourcePostgreSQLRoleReadImpl(d *schema.ResourceData, meta interface{}) error {
294332
c := meta.(*Client)
295333
conn, err := c.Connect()
296334
if err != nil {
@@ -347,6 +385,9 @@ func resourcePostgreSQLRoleRead(d *schema.ResourceData, meta interface{}) error
347385

348386
func resourcePostgreSQLRoleUpdate(d *schema.ResourceData, meta interface{}) error {
349387
c := meta.(*Client)
388+
c.catalogLock.Lock()
389+
defer c.catalogLock.Unlock()
390+
350391
conn, err := c.Connect()
351392
if err != nil {
352393
return err
@@ -393,7 +434,7 @@ func resourcePostgreSQLRoleUpdate(d *schema.ResourceData, meta interface{}) erro
393434
return err
394435
}
395436

396-
return resourcePostgreSQLRoleRead(d, meta)
437+
return resourcePostgreSQLRoleReadImpl(d, meta)
397438
}
398439

399440
func setRoleName(conn *sql.DB, d *schema.ResourceData) error {

0 commit comments

Comments
 (0)