From d073a90cb197359f0946c687b2945d9f2d142fef Mon Sep 17 00:00:00 2001 From: Sam Dunne Date: Tue, 4 Oct 2016 11:05:51 +0100 Subject: [PATCH 1/4] Create provisioner for postgresql extensions This change adds support for installing postgresql extensions on a postgresql server. --- .../resource_postgresql_extension.go | 67 ++++++++++++ .../resource_postgresql_extension_test.go | 102 ++++++++++++++++++ .../r/postgresql_extension.html.markdown | 26 +++++ 3 files changed, 195 insertions(+) create mode 100644 builtin/providers/postgresql/resource_postgresql_extension.go create mode 100644 builtin/providers/postgresql/resource_postgresql_extension_test.go create mode 100644 website/source/docs/providers/postgresql/r/postgresql_extension.html.markdown diff --git a/builtin/providers/postgresql/resource_postgresql_extension.go b/builtin/providers/postgresql/resource_postgresql_extension.go new file mode 100644 index 000000000..eabf82d67 --- /dev/null +++ b/builtin/providers/postgresql/resource_postgresql_extension.go @@ -0,0 +1,67 @@ +package postgresql + +import ( + "database/sql" + "fmt" + + "github.com/hashicorp/errwrap" + "github.com/hashicorp/terraform/helper/schema" + "github.com/lib/pq" +) + +func resourcePostgreSQLExtension() *schema.Resource { + return &schema.Resource{ + Create: resourcePostgreSQLExtensionCreate, + Delete: resourcePostgreSQLExtensionDelete, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + } +} + +func resourcePostgreSQLExtensionCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*Client) + conn, err := client.Connect() + if err != nil { + return err + } + defer conn.Close() + + extensionName := d.Get("name").(string) + + query := fmt.Sprintf("CREATE EXTENSION %s", pq.QuoteIdentifier(extensionName)) + _, err = conn.Query(query) + if err != nil { + return errwrap.Wrapf("Error creating extension: {{err}}", err) + } + + d.SetId(extensionName) + + return resourcePostgreSQLRoleRead(d, meta) +} + +func resourcePostgreSQLExtensionDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*Client) + conn, err := client.Connect() + if err != nil { + return err + } + defer conn.Close() + + extensionName := d.Get("name").(string) + + query := fmt.Sprintf("DROP EXTENSION %s", pq.QuoteIdentifier(extensionName)) + _, err = conn.Query(query) + if err != nil { + return errwrap.Wrapf("Error deleting extension: {{err}}", err) + } + + d.SetId("") + + return nil +} diff --git a/builtin/providers/postgresql/resource_postgresql_extension_test.go b/builtin/providers/postgresql/resource_postgresql_extension_test.go new file mode 100644 index 000000000..3e20ea2d7 --- /dev/null +++ b/builtin/providers/postgresql/resource_postgresql_extension_test.go @@ -0,0 +1,102 @@ +package postgresql + +import ( + "database/sql" + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccPostgresqlExtension_Basic(t *testing.T) { + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckPostgresqlExtensionDestroy, + Steps: []resource.TestStep{ + { + Config: testAccPostgresqlExtensionConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckPostgresqlExtensionExists("postgresql_extension.myextension"), + resource.TestCheckResourceAttr( + "postgresql_role.myextension", "name", "pg_trgm"), + ), + }, + }, + }) +} + +func testAccCheckPostgresqlExtensionDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*Client) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "postgresql_extension" { + continue + } + + exists, err := checkExtensionExists(client, rs.Primary.ID) + + if err != nil { + return fmt.Errorf("Error checking extension %s", err) + } + + if exists { + return fmt.Errorf("Extension still exists after destroy") + } + } + + return nil +} + +func testAccCheckPostgresqlExtensionExists(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Resource not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + client := testAccProvider.Meta().(*Client) + exists, err := checkExtensionExists(client, rs.Primary.ID) + + if err != nil { + return fmt.Errorf("Error checking extension %s", err) + } + + if !exists { + return fmt.Errorf("Extension not found") + } + + return nil + } +} + +func checkExtensionExists(client *Client, extensionName string) (bool, error) { + conn, err := client.Connect() + if err != nil { + return false, err + } + defer conn.Close() + + var _rez int + err = conn.QueryRow("SELECT 1 from pg_extension d WHERE extname=$1", extensionName).Scan(&_rez) + switch { + case err == sql.ErrNoRows: + return false, nil + case err != nil: + return false, fmt.Errorf("Error reading info about extension: %s", err) + default: + return true, nil + } +} + +var testAccPostgresqlExtensionConfig = ` +resource "postgresql_extension" "myextension" { + name = "pg_trgm" +} +` diff --git a/website/source/docs/providers/postgresql/r/postgresql_extension.html.markdown b/website/source/docs/providers/postgresql/r/postgresql_extension.html.markdown new file mode 100644 index 000000000..ae8a9f26a --- /dev/null +++ b/website/source/docs/providers/postgresql/r/postgresql_extension.html.markdown @@ -0,0 +1,26 @@ +--- +layout: "postgresql" +page_title: "PostgreSQL: postgresql_extension" +sidebar_current: "docs-postgresql-resource-postgresql_extension" +description: |- + Creates and manages an extension on a PostgreSQL server. +--- + +# postgresql\_role + +The ``[pstgresql_extension]`` resource creates and manages an extension on a PostgreSQL +server. + + +## Usage + +``` +resource "postgresql_role" "my_extension" { + name = "pg_trgm" +} + +``` + +## Argument Reference + +* `name` - (Required) The name of the extension. \ No newline at end of file From 792a6c0232f04f39f3853f174840b4af5fe9d1a6 Mon Sep 17 00:00:00 2001 From: Sam Dunne Date: Wed, 5 Oct 2016 09:32:10 +0100 Subject: [PATCH 2/4] Remove unused dependency --- builtin/providers/postgresql/resource_postgresql_extension.go | 1 - 1 file changed, 1 deletion(-) diff --git a/builtin/providers/postgresql/resource_postgresql_extension.go b/builtin/providers/postgresql/resource_postgresql_extension.go index eabf82d67..eb374061f 100644 --- a/builtin/providers/postgresql/resource_postgresql_extension.go +++ b/builtin/providers/postgresql/resource_postgresql_extension.go @@ -1,7 +1,6 @@ package postgresql import ( - "database/sql" "fmt" "github.com/hashicorp/errwrap" From 5dbd9423ccf4e6a981c36afc682257c8887fbd47 Mon Sep 17 00:00:00 2001 From: Sam Dunne Date: Wed, 2 Nov 2016 14:36:39 +0000 Subject: [PATCH 3/4] Update based off review --- builtin/providers/postgresql/provider.go | 1 + .../resource_postgresql_extension.go | 28 ++++++++++++++++++- website/source/layouts/postgresql.erb | 3 ++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/builtin/providers/postgresql/provider.go b/builtin/providers/postgresql/provider.go index 4b73192ad..f720ea8e7 100644 --- a/builtin/providers/postgresql/provider.go +++ b/builtin/providers/postgresql/provider.go @@ -45,6 +45,7 @@ func Provider() terraform.ResourceProvider { ResourcesMap: map[string]*schema.Resource{ "postgresql_database": resourcePostgreSQLDatabase(), "postgresql_role": resourcePostgreSQLRole(), + "postgresql_extension": resourcePostgreSQLExtension(), }, ConfigureFunc: providerConfigure, diff --git a/builtin/providers/postgresql/resource_postgresql_extension.go b/builtin/providers/postgresql/resource_postgresql_extension.go index eb374061f..d4e990b56 100644 --- a/builtin/providers/postgresql/resource_postgresql_extension.go +++ b/builtin/providers/postgresql/resource_postgresql_extension.go @@ -1,6 +1,7 @@ package postgresql import ( + "database/sql" "fmt" "github.com/hashicorp/errwrap" @@ -11,6 +12,7 @@ import ( func resourcePostgreSQLExtension() *schema.Resource { return &schema.Resource{ Create: resourcePostgreSQLExtensionCreate, + Read: resourcePostgreSQLExtensionRead, Delete: resourcePostgreSQLExtensionDelete, Schema: map[string]*schema.Schema{ @@ -41,7 +43,31 @@ func resourcePostgreSQLExtensionCreate(d *schema.ResourceData, meta interface{}) d.SetId(extensionName) - return resourcePostgreSQLRoleRead(d, meta) + return resourcePostgreSQLExtensionRead(d, meta) +} + +func resourcePostgreSQLExtensionRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*Client) + conn, err := client.Connect() + if err != nil { + return err + } + defer conn.Close() + + extensionName := d.Get("name").(string) + + var hasExtension bool + err = conn.QueryRow("SELECT 1 from pg_extension d WHERE extname=$1", extensionName).Scan(&hasExtension) + switch { + case err == sql.ErrNoRows: + d.SetId("") + return nil + case err != nil: + return errwrap.Wrapf("Error reading extension: {{err}}", err) + default: + d.Set("extension", hasExtension) + return nil + } } func resourcePostgreSQLExtensionDelete(d *schema.ResourceData, meta interface{}) error { diff --git a/website/source/layouts/postgresql.erb b/website/source/layouts/postgresql.erb index 7375784ce..50aef15a6 100644 --- a/website/source/layouts/postgresql.erb +++ b/website/source/layouts/postgresql.erb @@ -19,6 +19,9 @@ > postgresql_role + > + postgresql_extensions + From cb40a3ef147250343f64f6f1d2ab2e060b0591c8 Mon Sep 17 00:00:00 2001 From: Sam Dunne Date: Wed, 2 Nov 2016 15:30:21 +0000 Subject: [PATCH 4/4] Format code --- builtin/providers/postgresql/provider.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/builtin/providers/postgresql/provider.go b/builtin/providers/postgresql/provider.go index f720ea8e7..bec0b46d0 100644 --- a/builtin/providers/postgresql/provider.go +++ b/builtin/providers/postgresql/provider.go @@ -43,9 +43,9 @@ func Provider() terraform.ResourceProvider { }, ResourcesMap: map[string]*schema.Resource{ - "postgresql_database": resourcePostgreSQLDatabase(), - "postgresql_role": resourcePostgreSQLRole(), - "postgresql_extension": resourcePostgreSQLExtension(), + "postgresql_database": resourcePostgreSQLDatabase(), + "postgresql_role": resourcePostgreSQLRole(), + "postgresql_extension": resourcePostgreSQLExtension(), }, ConfigureFunc: providerConfigure,