Merge pull request #29157 from remilapeyre/unique-constraint
Add uniqueness constraint on workspaces name for the pg backend
This commit is contained in:
commit
431aa0280e
|
@ -111,7 +111,7 @@ func (b *Backend) configure(ctx context.Context) error {
|
||||||
|
|
||||||
query = `CREATE TABLE IF NOT EXISTS %s.%s (
|
query = `CREATE TABLE IF NOT EXISTS %s.%s (
|
||||||
id bigint NOT NULL DEFAULT nextval('public.global_states_id_seq') PRIMARY KEY,
|
id bigint NOT NULL DEFAULT nextval('public.global_states_id_seq') PRIMARY KEY,
|
||||||
name text,
|
name text UNIQUE,
|
||||||
data text
|
data text
|
||||||
)`
|
)`
|
||||||
if _, err := db.Exec(fmt.Sprintf(query, b.schemaName, statesTableName)); err != nil {
|
if _, err := db.Exec(fmt.Sprintf(query, b.schemaName, statesTableName)); err != nil {
|
||||||
|
|
|
@ -89,11 +89,13 @@ func TestBackendConfigSkipOptions(t *testing.T) {
|
||||||
SkipSchemaCreation bool
|
SkipSchemaCreation bool
|
||||||
SkipTableCreation bool
|
SkipTableCreation bool
|
||||||
SkipIndexCreation bool
|
SkipIndexCreation bool
|
||||||
|
TestIndexIsPresent bool
|
||||||
Setup func(t *testing.T, db *sql.DB, schemaName string)
|
Setup func(t *testing.T, db *sql.DB, schemaName string)
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
Name: "skip_schema_creation",
|
Name: "skip_schema_creation",
|
||||||
SkipSchemaCreation: true,
|
SkipSchemaCreation: true,
|
||||||
|
TestIndexIsPresent: true,
|
||||||
Setup: func(t *testing.T, db *sql.DB, schemaName string) {
|
Setup: func(t *testing.T, db *sql.DB, schemaName string) {
|
||||||
// create the schema as a prerequisites
|
// create the schema as a prerequisites
|
||||||
_, err := db.Query(fmt.Sprintf(`CREATE SCHEMA IF NOT EXISTS %s`, schemaName))
|
_, err := db.Query(fmt.Sprintf(`CREATE SCHEMA IF NOT EXISTS %s`, schemaName))
|
||||||
|
@ -103,8 +105,9 @@ func TestBackendConfigSkipOptions(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "skip_table_creation",
|
Name: "skip_table_creation",
|
||||||
SkipTableCreation: true,
|
SkipTableCreation: true,
|
||||||
|
TestIndexIsPresent: true,
|
||||||
Setup: func(t *testing.T, db *sql.DB, schemaName string) {
|
Setup: func(t *testing.T, db *sql.DB, schemaName string) {
|
||||||
// since the table needs to be already created the schema must be too
|
// since the table needs to be already created the schema must be too
|
||||||
_, err := db.Query(fmt.Sprintf(`CREATE SCHEMA %s`, schemaName))
|
_, err := db.Query(fmt.Sprintf(`CREATE SCHEMA %s`, schemaName))
|
||||||
|
@ -122,8 +125,9 @@ func TestBackendConfigSkipOptions(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "skip_index_creation",
|
Name: "skip_index_creation",
|
||||||
SkipIndexCreation: true,
|
SkipIndexCreation: true,
|
||||||
|
TestIndexIsPresent: true,
|
||||||
Setup: func(t *testing.T, db *sql.DB, schemaName string) {
|
Setup: func(t *testing.T, db *sql.DB, schemaName string) {
|
||||||
// Everything need to exists for the index to be created
|
// Everything need to exists for the index to be created
|
||||||
_, err := db.Query(fmt.Sprintf(`CREATE SCHEMA %s`, schemaName))
|
_, err := db.Query(fmt.Sprintf(`CREATE SCHEMA %s`, schemaName))
|
||||||
|
@ -144,6 +148,10 @@ func TestBackendConfigSkipOptions(t *testing.T) {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "missing_index",
|
||||||
|
SkipIndexCreation: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
|
@ -163,7 +171,9 @@ func TestBackendConfigSkipOptions(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
tc.Setup(t, db, schemaName)
|
if tc.Setup != nil {
|
||||||
|
tc.Setup(t, db, schemaName)
|
||||||
|
}
|
||||||
defer db.Query(fmt.Sprintf("DROP SCHEMA IF EXISTS %s CASCADE", schemaName))
|
defer db.Query(fmt.Sprintf("DROP SCHEMA IF EXISTS %s CASCADE", schemaName))
|
||||||
|
|
||||||
b := backend.TestBackendConfig(t, New(), config).(*Backend)
|
b := backend.TestBackendConfig(t, New(), config).(*Backend)
|
||||||
|
@ -179,14 +189,16 @@ func TestBackendConfigSkipOptions(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
// Make sure that the index exists
|
if tc.TestIndexIsPresent {
|
||||||
query := `select count(*) from pg_indexes where schemaname=$1 and tablename=$2 and indexname=$3;`
|
// Make sure that the index exists
|
||||||
var count int
|
query := `select count(*) from pg_indexes where schemaname=$1 and tablename=$2 and indexname=$3;`
|
||||||
if err := b.db.QueryRow(query, tc.Name, statesTableName, statesIndexName).Scan(&count); err != nil {
|
var count int
|
||||||
t.Fatal(err)
|
if err := b.db.QueryRow(query, tc.Name, statesTableName, statesIndexName).Scan(&count); err != nil {
|
||||||
}
|
t.Fatal(err)
|
||||||
if count != 1 {
|
}
|
||||||
t.Fatalf("The index has not been created (%d)", count)
|
if count != 1 {
|
||||||
|
t.Fatalf("The index has not been created (%d)", count)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = b.StateMgr(backend.DefaultStateName)
|
_, err = b.StateMgr(backend.DefaultStateName)
|
||||||
|
@ -202,6 +214,16 @@ func TestBackendConfigSkipOptions(t *testing.T) {
|
||||||
if c.Name != backend.DefaultStateName {
|
if c.Name != backend.DefaultStateName {
|
||||||
t.Fatal("RemoteClient name is not configured")
|
t.Fatal("RemoteClient name is not configured")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure that all workspace must have a unique name
|
||||||
|
_, err = db.Exec(fmt.Sprintf(`INSERT INTO %s.%s VALUES (100, 'unique_name_test', '')`, schemaName, statesTableName))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
_, err = db.Exec(fmt.Sprintf(`INSERT INTO %s.%s VALUES (101, 'unique_name_test', '')`, schemaName, statesTableName))
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Creating two workspaces with the same name did not raise an error")
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,9 +73,9 @@ 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.
|
* `skip_schema_creation` - If set to `true`, the Postgres schema must already exist. Terraform won't try to create the schema, this is useful when it has already been created by a database administrator.
|
||||||
* `skip_table_creation` - If set to `true`, the Postgres table must already exist. Terraform won't try to create the table. Useful when the Postgres user does not have "create table" permission on the database.
|
* `skip_table_creation` - If set to `true`, the Postgres table must already exist. Terraform won't try to create the table, this is useful when it has already been created by a database administrator.
|
||||||
* `skip_index_creation` - If set to `true`, the Postgres index must already exist. Terraform won't try to create the index. Useful when the Postgres user does not have "create index" permission on the database.
|
* `skip_index_creation` - If set to `true`, the Postgres index must already exist. Terraform won't try to create the index, this is useful when it has already been created by a database administrator.
|
||||||
|
|
||||||
## Technical Design
|
## Technical Design
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue