From 43257492b930013906675a0b81491b20cbb49f90 Mon Sep 17 00:00:00 2001 From: Bernerd Schaefer Date: Mon, 24 Apr 2017 17:08:13 -0700 Subject: [PATCH 1/6] Add first failing test for heroku_space --- builtin/providers/heroku/provider.go | 5 +- .../providers/heroku/resource_heroku_space.go | 31 ++++++ .../heroku/resource_heroku_space_test.go | 95 +++++++++++++++++++ 3 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 builtin/providers/heroku/resource_heroku_space.go create mode 100644 builtin/providers/heroku/resource_heroku_space_test.go diff --git a/builtin/providers/heroku/provider.go b/builtin/providers/heroku/provider.go index b6ea2bc98..6a8c9b986 100644 --- a/builtin/providers/heroku/provider.go +++ b/builtin/providers/heroku/provider.go @@ -25,11 +25,12 @@ func Provider() terraform.ResourceProvider { }, ResourcesMap: map[string]*schema.Resource{ - "heroku_app": resourceHerokuApp(), "heroku_addon": resourceHerokuAddon(), + "heroku_app": resourceHerokuApp(), + "heroku_cert": resourceHerokuCert(), "heroku_domain": resourceHerokuDomain(), "heroku_drain": resourceHerokuDrain(), - "heroku_cert": resourceHerokuCert(), + "heroku_space": resourceHerokuSpace(), }, ConfigureFunc: providerConfigure, diff --git a/builtin/providers/heroku/resource_heroku_space.go b/builtin/providers/heroku/resource_heroku_space.go new file mode 100644 index 000000000..d11a01194 --- /dev/null +++ b/builtin/providers/heroku/resource_heroku_space.go @@ -0,0 +1,31 @@ +package heroku + +import "github.com/hashicorp/terraform/helper/schema" + +func resourceHerokuSpace() *schema.Resource { + return &schema.Resource{ + Create: func(_ *schema.ResourceData, _ interface{}) error { return nil }, + Read: func(_ *schema.ResourceData, _ interface{}) error { return nil }, + Update: func(_ *schema.ResourceData, _ interface{}) error { return nil }, + Delete: func(_ *schema.ResourceData, _ interface{}) error { return nil }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + + "organization": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "region": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + }, + } +} diff --git a/builtin/providers/heroku/resource_heroku_space_test.go b/builtin/providers/heroku/resource_heroku_space_test.go new file mode 100644 index 000000000..d9112f321 --- /dev/null +++ b/builtin/providers/heroku/resource_heroku_space_test.go @@ -0,0 +1,95 @@ +package heroku + +import ( + "context" + "fmt" + "os" + "testing" + + heroku "github.com/cyberdelia/heroku-go/v3" + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccHerokuSpace_Basic(t *testing.T) { + var space heroku.SpaceInfoResult + spaceName := fmt.Sprintf("tftest-%s", acctest.RandString(10)) + org := os.Getenv("HEROKU_ORGANIZATION") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + if org == "" { + t.Skip("HEROKU_ORGANIZATION is not set; skipping test.") + } + }, + Providers: testAccProviders, + CheckDestroy: testAccCheckHerokuSpaceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckHerokuSpaceConfig_basic(spaceName, org), + Check: resource.ComposeTestCheckFunc( + testAccCheckHerokuSpaceExists("heroku_space.foobar", &space), + ), + }, + }, + }) +} + +func testAccCheckHerokuSpaceConfig_basic(spaceName, orgName string) string { + return fmt.Sprintf(` +resource "heroku_space" "foobar" { + name = "%s" + organization = "%s" + region = "virginia" +} +`, spaceName, orgName) +} + +func testAccCheckHerokuSpaceExists(n string, space *heroku.SpaceInfoResult) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No space name set") + } + + client := testAccProvider.Meta().(*heroku.Service) + + foundSpace, err := client.SpaceInfo(context.TODO(), rs.Primary.ID) + if err != nil { + return err + } + + if foundSpace.Name != rs.Primary.ID { + return fmt.Errorf("Space not found") + } + + *space = *foundSpace + + return nil + } +} + +func testAccCheckHerokuSpaceDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*heroku.Service) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "heroku_space" { + continue + } + + _, err := client.SpaceInfo(context.TODO(), rs.Primary.ID) + + if err == nil { + return fmt.Errorf("Space still exists") + } + } + + return nil +} From dbf82e651ed780f9fd792faa50bc63bc22dafe9a Mon Sep 17 00:00:00 2001 From: Bernerd Schaefer Date: Mon, 24 Apr 2017 17:34:34 -0700 Subject: [PATCH 2/6] Implementation --- .../providers/heroku/resource_heroku_space.go | 90 +++++++++++++++++-- .../heroku/resource_heroku_space_test.go | 21 ++++- 2 files changed, 105 insertions(+), 6 deletions(-) diff --git a/builtin/providers/heroku/resource_heroku_space.go b/builtin/providers/heroku/resource_heroku_space.go index d11a01194..82e21e56d 100644 --- a/builtin/providers/heroku/resource_heroku_space.go +++ b/builtin/providers/heroku/resource_heroku_space.go @@ -1,13 +1,19 @@ package heroku -import "github.com/hashicorp/terraform/helper/schema" +import ( + "context" + "log" + + heroku "github.com/cyberdelia/heroku-go/v3" + "github.com/hashicorp/terraform/helper/schema" +) func resourceHerokuSpace() *schema.Resource { return &schema.Resource{ - Create: func(_ *schema.ResourceData, _ interface{}) error { return nil }, - Read: func(_ *schema.ResourceData, _ interface{}) error { return nil }, - Update: func(_ *schema.ResourceData, _ interface{}) error { return nil }, - Delete: func(_ *schema.ResourceData, _ interface{}) error { return nil }, + Create: resourceHerokuSpaceCreate, + Read: resourceHerokuSpaceRead, + Update: resourceHerokuSpaceUpdate, + Delete: resourceHerokuSpaceDelete, Schema: map[string]*schema.Schema{ "name": { @@ -29,3 +35,77 @@ func resourceHerokuSpace() *schema.Resource { }, } } + +func resourceHerokuSpaceCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*heroku.Service) + + opts := heroku.SpaceCreateOpts{} + opts.Name = d.Get("name").(string) + opts.Organization = d.Get("organization").(string) + + if v, ok := d.GetOk("region"); ok { + vs := v.(string) + opts.Region = &vs + } + + space, err := client.SpaceCreate(context.TODO(), opts) + if err != nil { + return err + } + + d.SetId(space.ID) + log.Printf("[INFO] Space ID: %s", d.Id()) + + setSpaceAttributes(d, (*heroku.Space)(space)) + return nil +} + +func resourceHerokuSpaceRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*heroku.Service) + + space, err := client.SpaceInfo(context.TODO(), d.Id()) + if err != nil { + return err + } + + setSpaceAttributes(d, (*heroku.Space)(space)) + return nil +} + +func resourceHerokuSpaceUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*heroku.Service) + + if !d.HasChange("name") { + return nil + } + + name := d.Get("name").(string) + opts := heroku.SpaceUpdateOpts{Name: &name} + + space, err := client.SpaceUpdate(context.TODO(), d.Id(), opts) + if err != nil { + return err + } + + setSpaceAttributes(d, (*heroku.Space)(space)) + return nil +} + +func setSpaceAttributes(d *schema.ResourceData, space *heroku.Space) { + d.Set("name", space.Name) + d.Set("organization", space.Organization.Name) + d.Set("region", space.Region.Name) +} + +func resourceHerokuSpaceDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*heroku.Service) + + log.Printf("[INFO] Deleting space: %s", d.Id()) + _, err := client.SpaceDelete(context.TODO(), d.Id()) + if err != nil { + return err + } + + d.SetId("") + return nil +} diff --git a/builtin/providers/heroku/resource_heroku_space_test.go b/builtin/providers/heroku/resource_heroku_space_test.go index d9112f321..72a2ed0c5 100644 --- a/builtin/providers/heroku/resource_heroku_space_test.go +++ b/builtin/providers/heroku/resource_heroku_space_test.go @@ -15,6 +15,7 @@ import ( func TestAccHerokuSpace_Basic(t *testing.T) { var space heroku.SpaceInfoResult spaceName := fmt.Sprintf("tftest-%s", acctest.RandString(10)) + spaceName2 := fmt.Sprintf("tftest-%s", acctest.RandString(10)) org := os.Getenv("HEROKU_ORGANIZATION") resource.Test(t, resource.TestCase{ @@ -31,6 +32,14 @@ func TestAccHerokuSpace_Basic(t *testing.T) { Config: testAccCheckHerokuSpaceConfig_basic(spaceName, org), Check: resource.ComposeTestCheckFunc( testAccCheckHerokuSpaceExists("heroku_space.foobar", &space), + testAccCheckHerokuSpaceAttributes(&space, spaceName), + ), + }, + { + Config: testAccCheckHerokuSpaceConfig_basic(spaceName2, org), + Check: resource.ComposeTestCheckFunc( + testAccCheckHerokuSpaceExists("heroku_space.foobar", &space), + testAccCheckHerokuSpaceAttributes(&space, spaceName2), ), }, }, @@ -66,7 +75,7 @@ func testAccCheckHerokuSpaceExists(n string, space *heroku.SpaceInfoResult) reso return err } - if foundSpace.Name != rs.Primary.ID { + if foundSpace.ID != rs.Primary.ID { return fmt.Errorf("Space not found") } @@ -76,6 +85,16 @@ func testAccCheckHerokuSpaceExists(n string, space *heroku.SpaceInfoResult) reso } } +func testAccCheckHerokuSpaceAttributes(space *heroku.SpaceInfoResult, spaceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + if space.Name != spaceName { + return fmt.Errorf("Bad name: %s", space.Name) + } + + return nil + } +} + func testAccCheckHerokuSpaceDestroy(s *terraform.State) error { client := testAccProvider.Meta().(*heroku.Service) From de54855f6312f6372acae33f337d9b403d945410 Mon Sep 17 00:00:00 2001 From: Bernerd Schaefer Date: Mon, 24 Apr 2017 17:42:54 -0700 Subject: [PATCH 3/6] Website --- .../providers/heroku/r/space.html.markdown | 48 +++++++++++++++++++ website/source/layouts/heroku.erb | 4 ++ 2 files changed, 52 insertions(+) create mode 100644 website/source/docs/providers/heroku/r/space.html.markdown diff --git a/website/source/docs/providers/heroku/r/space.html.markdown b/website/source/docs/providers/heroku/r/space.html.markdown new file mode 100644 index 000000000..6464d9252 --- /dev/null +++ b/website/source/docs/providers/heroku/r/space.html.markdown @@ -0,0 +1,48 @@ +--- +layout: "heroku" +page_title: "Heroku: heroku_space" +sidebar_current: "docs-heroku-resource-space" +description: |- + Provides a Heroku Space resource for running apps in isolated, highly available, secure app execution environments. +--- + +# heroku\_space + +Provides a Heroku Space resource for running apps in isolated, highly available, secure app execution environments. + +## Example Usage + +```hcl +# Create a new Heroku space +resource "heroku_space" "default" { + name = "test-space" + organization = "my-company" + region = "virginia" +} + +# Create a new Heroku app in test-space +resource "heroku_app" "default" { + name = "test-app" + space = "${heroku_space.default.name}" + organization = { + name = "my-company" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the space. +* `organization` - (Required) The name of the organization which will own the space. +* `region` - (Optional) The region that the space should be created in. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the space. +* `name` - The space's name. +* `plan` - The space's organization. +* `region` - The space's region. diff --git a/website/source/layouts/heroku.erb b/website/source/layouts/heroku.erb index 99704acf5..0957af9a0 100644 --- a/website/source/layouts/heroku.erb +++ b/website/source/layouts/heroku.erb @@ -32,6 +32,10 @@ > heroku_drain + + > + heroku_space + From 81e8db56f0ec8f6e39ecefd066d3c0c6d81f0139 Mon Sep 17 00:00:00 2001 From: Bernerd Schaefer Date: Mon, 24 Apr 2017 17:50:52 -0700 Subject: [PATCH 4/6] Fix typo in docs --- website/source/docs/providers/heroku/r/space.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/providers/heroku/r/space.html.markdown b/website/source/docs/providers/heroku/r/space.html.markdown index 6464d9252..ae573029b 100644 --- a/website/source/docs/providers/heroku/r/space.html.markdown +++ b/website/source/docs/providers/heroku/r/space.html.markdown @@ -44,5 +44,5 @@ The following attributes are exported: * `id` - The ID of the space. * `name` - The space's name. -* `plan` - The space's organization. +* `organization` - The space's organization. * `region` - The space's region. From 26d6a562f15a18dada492ae13dc7cab58c6212fb Mon Sep 17 00:00:00 2001 From: Bernerd Schaefer Date: Tue, 25 Apr 2017 15:27:01 -0700 Subject: [PATCH 5/6] Use // for inline comments --- website/source/docs/providers/heroku/r/space.html.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/source/docs/providers/heroku/r/space.html.markdown b/website/source/docs/providers/heroku/r/space.html.markdown index ae573029b..089fe2159 100644 --- a/website/source/docs/providers/heroku/r/space.html.markdown +++ b/website/source/docs/providers/heroku/r/space.html.markdown @@ -13,14 +13,14 @@ Provides a Heroku Space resource for running apps in isolated, highly available, ## Example Usage ```hcl -# Create a new Heroku space +// Create a new Heroku space resource "heroku_space" "default" { name = "test-space" organization = "my-company" region = "virginia" } -# Create a new Heroku app in test-space +// Create a new Heroku app in test-space resource "heroku_app" "default" { name = "test-app" space = "${heroku_space.default.name}" From e7c904a87d9a4da6f97e30afb64b458c7fddcf3a Mon Sep 17 00:00:00 2001 From: Bernerd Schaefer Date: Tue, 25 Apr 2017 15:32:27 -0700 Subject: [PATCH 6/6] Add note about type conversion --- builtin/providers/heroku/resource_heroku_space.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/builtin/providers/heroku/resource_heroku_space.go b/builtin/providers/heroku/resource_heroku_space.go index 82e21e56d..3e90fffbb 100644 --- a/builtin/providers/heroku/resource_heroku_space.go +++ b/builtin/providers/heroku/resource_heroku_space.go @@ -56,6 +56,8 @@ func resourceHerokuSpaceCreate(d *schema.ResourceData, meta interface{}) error { d.SetId(space.ID) log.Printf("[INFO] Space ID: %s", d.Id()) + // The type conversion here can be dropped when the vendored version of + // heroku-go is updated. setSpaceAttributes(d, (*heroku.Space)(space)) return nil } @@ -68,6 +70,8 @@ func resourceHerokuSpaceRead(d *schema.ResourceData, meta interface{}) error { return err } + // The type conversion here can be dropped when the vendored version of + // heroku-go is updated. setSpaceAttributes(d, (*heroku.Space)(space)) return nil } @@ -87,6 +91,8 @@ func resourceHerokuSpaceUpdate(d *schema.ResourceData, meta interface{}) error { return err } + // The type conversion here can be dropped when the vendored version of + // heroku-go is updated. setSpaceAttributes(d, (*heroku.Space)(space)) return nil }