-
Notifications
You must be signed in to change notification settings - Fork 9.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
16 changed files
with
940 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package main | ||
|
||
import ( | ||
"github.com/hashicorp/terraform/builtin/providers/postgresql" | ||
"github.com/hashicorp/terraform/plugin" | ||
) | ||
|
||
func main() { | ||
plugin.Serve(&plugin.ServeOpts{ | ||
ProviderFunc: postgresql.Provider, | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
package main |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package postgresql | ||
|
||
import ( | ||
"database/sql" | ||
"fmt" | ||
_ "github.com/lib/pq" //PostgreSQL db | ||
) | ||
|
||
// Config - provider config | ||
type Config struct { | ||
Host string | ||
Port int | ||
Username string | ||
Password string | ||
} | ||
|
||
// Client struct holding connection string | ||
type Client struct { | ||
username string | ||
connStr string | ||
} | ||
|
||
//NewClient returns new client config | ||
func (c *Config) NewClient() (*Client, error) { | ||
connStr := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=postgres", c.Host, c.Port, c.Username, c.Password) | ||
|
||
client := Client{ | ||
connStr: connStr, | ||
username: c.Username, | ||
} | ||
|
||
return &client, nil | ||
} | ||
|
||
//Connect will manually connect/diconnect to prevent a large number or db connections being made | ||
func (c *Client) Connect() (*sql.DB, error) { | ||
db, err := sql.Open("postgres", c.connStr) | ||
if err != nil { | ||
return nil, fmt.Errorf("Error connecting to postgresql server: %s", err) | ||
} | ||
|
||
return db, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package postgresql | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/hashicorp/terraform/helper/schema" | ||
"github.com/hashicorp/terraform/terraform" | ||
) | ||
|
||
// Provider returns a terraform.ResourceProvider. | ||
func Provider() terraform.ResourceProvider { | ||
return &schema.Provider{ | ||
Schema: map[string]*schema.Schema{ | ||
"host": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
DefaultFunc: schema.EnvDefaultFunc("POSTGRESQL_HOST", nil), | ||
Description: "The postgresql server address", | ||
}, | ||
"port": &schema.Schema{ | ||
Type: schema.TypeInt, | ||
Optional: true, | ||
Default: 5432, | ||
Description: "The postgresql server port", | ||
}, | ||
"username": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
DefaultFunc: schema.EnvDefaultFunc("POSTGRESQL_USERNAME", nil), | ||
Description: "Username for postgresql server connection", | ||
}, | ||
"password": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
DefaultFunc: schema.EnvDefaultFunc("POSTGRESQL_PASSWORD", nil), | ||
Description: "Password for postgresql server connection", | ||
}, | ||
}, | ||
|
||
ResourcesMap: map[string]*schema.Resource{ | ||
"postgresql_database": resourcePostgresqlDatabase(), | ||
"postgresql_role": resourcePostgresqlRole(), | ||
}, | ||
|
||
ConfigureFunc: providerConfigure, | ||
} | ||
} | ||
|
||
func providerConfigure(d *schema.ResourceData) (interface{}, error) { | ||
config := Config{ | ||
Host: d.Get("host").(string), | ||
Port: d.Get("port").(int), | ||
Username: d.Get("username").(string), | ||
Password: d.Get("password").(string), | ||
} | ||
|
||
client, err := config.NewClient() | ||
if err != nil { | ||
return nil, fmt.Errorf("Error initializing Postgresql client: %s", err) | ||
} | ||
|
||
return client, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package postgresql | ||
|
||
import ( | ||
"os" | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform/helper/schema" | ||
"github.com/hashicorp/terraform/terraform" | ||
) | ||
|
||
var testAccProviders map[string]terraform.ResourceProvider | ||
var testAccProvider *schema.Provider | ||
|
||
func init() { | ||
testAccProvider = Provider().(*schema.Provider) | ||
testAccProviders = map[string]terraform.ResourceProvider{ | ||
"postgresql": testAccProvider, | ||
} | ||
} | ||
|
||
func TestProvider(t *testing.T) { | ||
if err := Provider().(*schema.Provider).InternalValidate(); err != nil { | ||
t.Fatalf("err: %s", err) | ||
} | ||
} | ||
|
||
func TestProvider_impl(t *testing.T) { | ||
var _ terraform.ResourceProvider = Provider() | ||
} | ||
|
||
func testAccPreCheck(t *testing.T) { | ||
if v := os.Getenv("POSTGRESQL_HOST"); v == "" { | ||
t.Fatal("POSTGRESQL_HOST must be set for acceptance tests") | ||
} | ||
if v := os.Getenv("POSTGRESQL_USERNAME"); v == "" { | ||
t.Fatal("POSTGRESQL_USERNAME must be set for acceptance tests") | ||
} | ||
if v := os.Getenv("POSTGRESQL_PASSWORD"); v == "" { | ||
t.Fatal("POSTGRESQL_PASSWORD must be set for acceptance tests") | ||
} | ||
} |
160 changes: 160 additions & 0 deletions
160
builtin/providers/postgresql/resource_postgresql_database.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
package postgresql | ||
|
||
import ( | ||
"database/sql" | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/hashicorp/terraform/helper/schema" | ||
"github.com/lib/pq" | ||
) | ||
|
||
func resourcePostgresqlDatabase() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourcePostgresqlDatabaseCreate, | ||
Read: resourcePostgresqlDatabaseRead, | ||
Update: resourcePostgresqlDatabaseUpdate, | ||
Delete: resourcePostgresqlDatabaseDelete, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"name": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"owner": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Optional: true, | ||
ForceNew: false, | ||
Computed: true, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourcePostgresqlDatabaseCreate(d *schema.ResourceData, meta interface{}) error { | ||
client := meta.(*Client) | ||
conn, err := client.Connect() | ||
if err != nil { | ||
return err | ||
} | ||
defer conn.Close() | ||
|
||
dbName := d.Get("name").(string) | ||
dbOwner := d.Get("owner").(string) | ||
connUsername := client.username | ||
|
||
var dbOwnerCfg string | ||
if dbOwner != "" { | ||
dbOwnerCfg = fmt.Sprintf("WITH OWNER=%s", pq.QuoteIdentifier(dbOwner)) | ||
} else { | ||
dbOwnerCfg = "" | ||
} | ||
|
||
//needed in order to set the owner of the db if the connection user is not a superuser | ||
err = grantRoleMembership(conn, dbOwner, connUsername) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
query := fmt.Sprintf("CREATE DATABASE %s %s", pq.QuoteIdentifier(dbName), dbOwnerCfg) | ||
_, err = conn.Query(query) | ||
if err != nil { | ||
return fmt.Errorf("Error creating postgresql database %s: %s", dbName, err) | ||
} | ||
|
||
d.SetId(dbName) | ||
|
||
return nil | ||
} | ||
|
||
func resourcePostgresqlDatabaseDelete(d *schema.ResourceData, meta interface{}) error { | ||
client := meta.(*Client) | ||
conn, err := client.Connect() | ||
if err != nil { | ||
return err | ||
} | ||
defer conn.Close() | ||
|
||
dbName := d.Get("name").(string) | ||
connUsername := client.username | ||
dbOwner := d.Get("owner").(string) | ||
//needed in order to set the owner of the db if the connection user is not a superuser | ||
err = grantRoleMembership(conn, dbOwner, connUsername) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
query := fmt.Sprintf("DROP DATABASE %s", pq.QuoteIdentifier(dbName)) | ||
_, err = conn.Query(query) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
d.SetId("") | ||
|
||
return nil | ||
} | ||
|
||
func resourcePostgresqlDatabaseRead(d *schema.ResourceData, meta interface{}) error { | ||
client := meta.(*Client) | ||
conn, err := client.Connect() | ||
if err != nil { | ||
return err | ||
} | ||
defer conn.Close() | ||
|
||
dbName := d.Get("name").(string) | ||
|
||
var owner string | ||
err = conn.QueryRow("SELECT pg_catalog.pg_get_userbyid(d.datdba) from pg_database d WHERE datname=$1", dbName).Scan(&owner) | ||
switch { | ||
case err == sql.ErrNoRows: | ||
d.SetId("") | ||
return nil | ||
case err != nil: | ||
return fmt.Errorf("Error reading info about database: %s", err) | ||
default: | ||
d.Set("owner", owner) | ||
return nil | ||
} | ||
} | ||
|
||
func resourcePostgresqlDatabaseUpdate(d *schema.ResourceData, meta interface{}) error { | ||
client := meta.(*Client) | ||
conn, err := client.Connect() | ||
if err != nil { | ||
return err | ||
} | ||
defer conn.Close() | ||
|
||
dbName := d.Get("name").(string) | ||
|
||
if d.HasChange("owner") { | ||
owner := d.Get("owner").(string) | ||
if owner != "" { | ||
query := fmt.Sprintf("ALTER DATABASE %s OWNER TO %s", pq.QuoteIdentifier(dbName), pq.QuoteIdentifier(owner)) | ||
_, err := conn.Query(query) | ||
if err != nil { | ||
return fmt.Errorf("Error updating owner for database: %s", err) | ||
} | ||
} | ||
} | ||
|
||
return resourcePostgresqlDatabaseRead(d, meta) | ||
} | ||
|
||
func grantRoleMembership(conn *sql.DB, dbOwner string, connUsername string) error { | ||
if dbOwner != "" && dbOwner != connUsername { | ||
query := fmt.Sprintf("GRANT %s TO %s", pq.QuoteIdentifier(dbOwner), pq.QuoteIdentifier(connUsername)) | ||
_, err := conn.Query(query) | ||
if err != nil { | ||
//is already member or role | ||
if strings.Contains(err.Error(), "duplicate key value violates unique constraint") { | ||
return nil | ||
} | ||
return fmt.Errorf("Error granting membership: %s", err) | ||
} | ||
} | ||
return nil | ||
} |
Oops, something went wrong.