Skip to content

Commit

Permalink
remote-state/pg: skip schema creation instruction if exists
Browse files Browse the repository at this point in the history
* add `skip_schema_creation` option
* add sanity check to avoid situations where postgres users
  hasn't been granted the "CREATE SCHEMA" right

closes #21604

Signed-off-by: yann degat <[email protected]>
  • Loading branch information
yanndegat committed Jun 5, 2019
1 parent 2f893f2 commit a37949d
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 3 deletions.
29 changes: 26 additions & 3 deletions backend/remote-state/pg/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ func New() backend.Backend {
Description: "Name of the automatically managed Postgres schema to store state",
Default: "terraform_remote_state",
},

"skip_schema_creation": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Description: "If set to `true`, terraform won't try to create the schema",
Default: false,
},
},
}

Expand Down Expand Up @@ -64,9 +71,25 @@ func (b *Backend) configure(ctx context.Context) error {

// Prepare database schema, tables, & indexes.
var query string
query = `CREATE SCHEMA IF NOT EXISTS %s`
if _, err := db.Exec(fmt.Sprintf(query, b.schemaName)); err != nil {
return err

if !data.Get("skip_schema_creation").(bool) {
// list all schemas to see if it exists
var count int
query = `select count(1) from information_schema.schemata where lower(schema_name) = lower('%s')`
if err := db.QueryRow(fmt.Sprintf(query, b.schemaName)).Scan(&count); err != nil {
return err
}

// skip schema creation if schema already exists
// `CREATE SCHEMA IF NOT EXISTS` is to avoid if ever
// a user hasn't been granted the `CREATE SCHEMA` right
if count < 1 {
// tries to create the schema
query = `CREATE SCHEMA %s`
if _, err := db.Exec(fmt.Sprintf(query, b.schemaName)); err != nil {
return err
}
}
}
query = `CREATE TABLE IF NOT EXISTS %s.%s (
id SERIAL PRIMARY KEY,
Expand Down
44 changes: 44 additions & 0 deletions backend/remote-state/pg/backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,50 @@ func TestBackendConfig(t *testing.T) {
}
}

func TestBackendConfigSkipSchema(t *testing.T) {
testACC(t)
connStr := getDatabaseUrl()
schemaName := fmt.Sprintf("terraform_%s", t.Name())
db, err := sql.Open("postgres", connStr)
if err != nil {
t.Fatal(err)
}

// create the schema as a prerequisites
db.Query(fmt.Sprintf("CREATE SCHEMA IF NOT EXISTS %s", schemaName))
defer db.Query(fmt.Sprintf("DROP SCHEMA IF EXISTS %s CASCADE", schemaName))

config := backend.TestWrapConfig(map[string]interface{}{
"conn_str": connStr,
"schema_name": schemaName,
"skip_schema_creation": true,
})
b := backend.TestBackendConfig(t, New(), config).(*Backend)

if b == nil {
t.Fatal("Backend could not be configured")
}

_, err = b.db.Query(fmt.Sprintf("SELECT name, data FROM %s.%s LIMIT 1", schemaName, statesTableName))
if err != nil {
t.Fatal(err)
}

_, err = b.StateMgr(backend.DefaultStateName)
if err != nil {
t.Fatal(err)
}

s, err := b.StateMgr(backend.DefaultStateName)
if err != nil {
t.Fatal(err)
}
c := s.(*remote.State).Client.(*RemoteClient)
if c.Name != backend.DefaultStateName {
t.Fatal("RemoteClient name is not configured")
}
}

func TestBackendStates(t *testing.T) {
testACC(t)
connStr := getDatabaseUrl()
Expand Down
1 change: 1 addition & 0 deletions website/docs/backends/types/pg.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ The following configuration options or environment variables are supported:

* `conn_str` - (Required) Postgres connection string; a `postgres://` URL
* `schema_name` - Name of the automatically-managed Postgres schema, default `terraform_remote_state`.
* `skip_schema_creation` - If set to `true`, terraform won't try to create the schema.

## Technical Design

Expand Down

0 comments on commit a37949d

Please sign in to comment.