remote-state/pg: add option to skip schema creation (#21607)

* 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 <yann@2kmail.net>
This commit is contained in:
yanndegat 2019-08-27 17:14:32 +02:00 committed by Kristin Laemmert
parent c93b0199f3
commit be5280e4e1
3 changed files with 72 additions and 4 deletions

View File

@ -31,6 +31,13 @@ func New() backend.Backend {
Description: "Name of the automatically managed Postgres schema to store state", Description: "Name of the automatically managed Postgres schema to store state",
Default: "terraform_remote_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 Postgres schema",
Default: false,
},
}, },
} }
@ -64,9 +71,25 @@ func (b *Backend) configure(ctx context.Context) error {
// Prepare database schema, tables, & indexes. // Prepare database schema, tables, & indexes.
var query string var query string
query = `CREATE SCHEMA IF NOT EXISTS %s`
if _, err := db.Exec(fmt.Sprintf(query, b.schemaName)); err != nil { if !data.Get("skip_schema_creation").(bool) {
return err // 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 be avoided if ever
// a user hasn't been granted the `CREATE SCHEMA` privilege
if count < 1 {
// tries to create the schema
query = `CREATE SCHEMA IF NOT EXISTS %s`
if _, err := db.Exec(fmt.Sprintf(query, b.schemaName)); err != nil {
return err
}
}
} }
query = `CREATE TABLE IF NOT EXISTS %s.%s ( query = `CREATE TABLE IF NOT EXISTS %s.%s (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,

View File

@ -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) { func TestBackendStates(t *testing.T) {
testACC(t) testACC(t)
connStr := getDatabaseUrl() connStr := getDatabaseUrl()

View File

@ -71,7 +71,8 @@ The following configuration options or environment variables are supported:
* `conn_str` - (Required) Postgres connection string; a `postgres://` URL * `conn_str` - (Required) Postgres connection string; a `postgres://` URL
* `schema_name` - Name of the automatically-managed Postgres schema, default `terraform_remote_state`. * `schema_name` - Name of the automatically-managed Postgres schema, default `terraform_remote_state`.
* `skip_schema_creation` - If set to `true`, the Postgres schema must already exist. Terraform won't try to create the schema. Useful when the Postgres user does not have "create schema" permission on the database.
## Technical Design ## Technical Design
Postgres version 9.5 or newer is required to support advisory locks and the "ON CONFLICT" upsert syntax. Postgres version 9.5 or newer is required to support advisory locks and the "ON CONFLICT" upsert syntax.