Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added log drain url argument for space #388

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/resources/space.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Provides a Heroku Private Space resource for running apps in isolated, highly av

## Example Usage

A Heroku "team" was originally called an "organization", and that is still
A Heroku "team" was originally called an "organization", and that is still
the identifier used in this resource.

```hcl-terraform
Expand Down Expand Up @@ -58,6 +58,7 @@ The following attributes are exported:
* `cidr` - The space's CIDR.
* `data_cidr` - The space's Data CIDR.
* `outbound_ips` - The space's stable outbound [NAT IPs](https://devcenter.heroku.com/articles/platform-api-reference#space-network-address-translation).
* `log_drain_url` - URL to which all apps will drain logs. Only settable during space creation and enables direct logging. Must use HTTPS.

## Import

Expand Down
40 changes: 36 additions & 4 deletions heroku/resource_heroku_space.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@ type spaceWithNAT struct {

func resourceHerokuSpace() *schema.Resource {
return &schema.Resource{
Create: resourceHerokuSpaceCreate,
Read: resourceHerokuSpaceRead,
Update: resourceHerokuSpaceUpdate,
Delete: resourceHerokuSpaceDelete,
Create: resourceHerokuSpaceCreate,
Read: resourceHerokuSpaceRead,
Update: resourceHerokuSpaceUpdate,
Delete: resourceHerokuSpaceDelete,
CustomizeDiff: resourceHerokuSpaceCustomizeDiff,

Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
Expand Down Expand Up @@ -73,6 +74,11 @@ func resourceHerokuSpace() *schema.Resource {
Default: false,
ForceNew: true,
},

"log_drain_url": {
Type: schema.TypeString,
Optional: true,
},
},
}
}
Expand Down Expand Up @@ -107,6 +113,11 @@ func resourceHerokuSpaceCreate(d *schema.ResourceData, meta interface{}) error {
opts.DataCIDR = &vs
}

if v, ok := d.GetOk("log_drain_url"); ok {
vs := v.(string)
opts.LogDrainURL = &vs
}

space, err := client.SpaceCreate(context.TODO(), opts)
if err != nil {
return err
Expand Down Expand Up @@ -186,6 +197,27 @@ func resourceHerokuSpaceDelete(d *schema.ResourceData, meta interface{}) error {
return nil
}

func resourceHerokuSpaceCustomizeDiff(ctx context.Context, diff *schema.ResourceDiff, v interface{}) error {
// Since `log_drain_url` is only settable during space creation but it can be updated if it was set
// at time of creating the space, checking here if it was previously set then update that otherwise
// force a new space creation
existing, new := diff.GetChange("log_drain_url")
log.Printf("[DEBUG] Diffing log_drain_url: existing '%s', new '%s'", existing, new)
if existing == new {
return nil
}

if existing.(string) != "" && new.(string) != "" {
return nil
}

if err := diff.ForceNew("log_drain_url"); err != nil {
return fmt.Errorf("error forcing new space resource: %s", err)
}

return nil
}

// SpaceStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch
// a Space.
func SpaceStateRefreshFunc(client *heroku.Service, id string) resource.StateRefreshFunc {
Expand Down
105 changes: 104 additions & 1 deletion heroku/resource_heroku_space_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func TestAccHerokuSpace(t *testing.T) {
resource.TestCheckResourceAttrSet("heroku_space.foobar", "outbound_ips.#"),
resource.TestCheckResourceAttr("heroku_space.foobar", "cidr", "10.0.0.0/16"),
resource.TestCheckResourceAttrSet("heroku_space.foobar", "data_cidr"),
resource.TestCheckResourceAttrSet("heroku_space.foobar", "log_drain_url"),
),
},
// append space test Steps, sharing the space, instead of recreating for each test
Expand All @@ -47,6 +48,82 @@ func TestAccHerokuSpace(t *testing.T) {
})
}

func TestAccHerokuSpaceLogDrain(t *testing.T) {
var space spaceWithNAT
spaceName := fmt.Sprintf("tftest1-%s", acctest.RandString(10))
org := testAccConfig.GetAnyOrganizationOrSkip(t)

spaceConfigWithLogDrain1 := testAccCheckHerokuShieldSpaceConfig_withLogDrain(spaceName, org, "10.0.0.0/16", "https://drain1.example.com")
spaceConfigWithLogDrain2 := testAccCheckHerokuShieldSpaceConfig_withLogDrain(spaceName, org, "10.0.0.0/16", "https://drain2.example.com")
spaceConfigWithoutLogDrain := testAccCheckHerokuShieldSpaceConfig_withoutLogDrain(spaceName, org, "10.0.0.0/16")

var space1Id string
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccCheckHerokuSpaceDestroy,
Steps: []resource.TestStep{
{
ResourceName: "heroku_space.foobar",
Config: spaceConfigWithLogDrain1,
Check: resource.ComposeTestCheckFunc(
testAccCheckHerokuSpaceExists("heroku_space.foobar", &space),
resource.TestCheckResourceAttrSet("heroku_space.foobar", "outbound_ips.#"),
resource.TestCheckResourceAttr("heroku_space.foobar", "cidr", "10.0.0.0/16"),
resource.TestCheckResourceAttrSet("heroku_space.foobar", "data_cidr"),
resource.TestCheckResourceAttr("heroku_space.foobar", "log_drain_url", "https://drain1.example.com"),
func(s *terraform.State) error {
space1Id = space.ID
return nil
},
),
},
{
ResourceName: "heroku_space.foobar",
Config: spaceConfigWithLogDrain2,
Check: resource.ComposeTestCheckFunc(
testAccCheckHerokuSpaceExists("heroku_space.foobar", &space),
resource.TestCheckResourceAttrSet("heroku_space.foobar", "outbound_ips.#"),
resource.TestCheckResourceAttr("heroku_space.foobar", "cidr", "10.0.0.0/16"),
resource.TestCheckResourceAttrSet("heroku_space.foobar", "data_cidr"),
resource.TestCheckResourceAttr("heroku_space.foobar", "log_drain_url", "https://drain2.example.com"),
func(s *terraform.State) error {
if space1Id == "" {
return fmt.Errorf("Space ID not set for space 1")
}
if space.ID != space1Id {
return fmt.Errorf("Space ID changed: %s -> %s", space1Id, space.ID)
}
return nil
},
),
},
{
ResourceName: "heroku_space.foobar",
Config: spaceConfigWithoutLogDrain,
Check: resource.ComposeTestCheckFunc(
testAccCheckHerokuSpaceExists("heroku_space.foobar", &space),
resource.TestCheckResourceAttrSet("heroku_space.foobar", "outbound_ips.#"),
resource.TestCheckResourceAttr("heroku_space.foobar", "cidr", "10.0.0.0/16"),
resource.TestCheckResourceAttrSet("heroku_space.foobar", "data_cidr"),
resource.TestCheckNoResourceAttr("heroku_space.foobar", "log_drain_url"),
func(s *terraform.State) error {
if space1Id == "" {
return fmt.Errorf("Space ID not set for space 1")
}
if space.ID == space1Id {
return fmt.Errorf("expected space id to change, but got same value: %s", space.ID)
}
return nil
},
),
},
},
})
}

// Permanently skipping Space_Shield test, as this is little more than an attribute test that takes at least 8-minutes to run.
// It's really just testing Shield space provisioning, which this Terraform provider is not responsible for validating.
//
Expand All @@ -60,7 +137,33 @@ resource "heroku_space" "foobar" {
name = "%s"
organization = "%s"
region = "virginia"
cidr = "%s"
cidr = "%s"
shield = true
}
`, spaceName, orgName, cidr)
}

func testAccCheckHerokuShieldSpaceConfig_withLogDrain(spaceName, orgName, cidr, logDrainURL string) string {
return fmt.Sprintf(`
resource "heroku_space" "foobar" {
name = "%s"
organization = "%s"
region = "virginia"
cidr = "%s"
shield = true
log_drain_url = "%s"
}
`, spaceName, orgName, cidr, logDrainURL)
}

func testAccCheckHerokuShieldSpaceConfig_withoutLogDrain(spaceName, orgName, cidr string) string {
return fmt.Sprintf(`
resource "heroku_space" "foobar" {
name = "%s"
organization = "%s"
region = "virginia"
cidr = "%s"
shield = true
}
`, spaceName, orgName, cidr)
}
Expand Down
Loading