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",
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,10 +71,26 @@ func (b *Backend) configure(ctx context.Context) error {
// Prepare database schema, tables, & indexes.
var query string
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 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 (
id SERIAL PRIMARY KEY,
name TEXT,

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) {
testACC(t)
connStr := getDatabaseUrl()

View File

@ -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`, 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