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

provider/aws: Add support for EC2 Placement Group #3457

Merged
merged 3 commits into from
Oct 12, 2015
Merged
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
1 change: 1 addition & 0 deletions builtin/providers/aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ func Provider() terraform.ResourceProvider {
"aws_opsworks_mysql_layer": resourceAwsOpsworksMysqlLayer(),
"aws_opsworks_ganglia_layer": resourceAwsOpsworksGangliaLayer(),
"aws_opsworks_custom_layer": resourceAwsOpsworksCustomLayer(),
"aws_placement_group": resourceAwsPlacementGroup(),
"aws_proxy_protocol_policy": resourceAwsProxyProtocolPolicy(),
"aws_rds_cluster": resourceAwsRDSCluster(),
"aws_rds_cluster_instance": resourceAwsRDSClusterInstance(),
Expand Down
150 changes: 150 additions & 0 deletions builtin/providers/aws/resource_aws_placement_group.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package aws

import (
"fmt"
"log"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsPlacementGroup() *schema.Resource {
return &schema.Resource{
Create: resourceAwsPlacementGroupCreate,
Read: resourceAwsPlacementGroupRead,
Delete: resourceAwsPlacementGroupDelete,

Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"strategy": &schema.Schema{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should set a default value of Cluster here as it is the only supported value atm

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah but we need to have ForceNew enabled if there is no update :(

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not quite convinced it's a good idea to make it default, but I don't know Amazon's plans for the future.

Type: schema.TypeString,
Required: true,
ForceNew: true,
},
},
}
}

func resourceAwsPlacementGroupCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).ec2conn

name := d.Get("name").(string)
input := ec2.CreatePlacementGroupInput{
GroupName: aws.String(name),
Strategy: aws.String(d.Get("strategy").(string)),
}
log.Printf("[DEBUG] Creating EC2 Placement group: %s", input)
_, err := conn.CreatePlacementGroup(&input)
if err != nil {
return err
}

wait := resource.StateChangeConf{
Pending: []string{"pending"},
Target: "available",
Timeout: 5 * time.Minute,
MinTimeout: 1 * time.Second,
Refresh: func() (interface{}, string, error) {
out, err := conn.DescribePlacementGroups(&ec2.DescribePlacementGroupsInput{
GroupNames: []*string{aws.String(name)},
})

if err != nil {
return out, "", err
}

if len(out.PlacementGroups) == 0 {
return out, "", fmt.Errorf("Placement group not found (%q)", name)
}
pg := out.PlacementGroups[0]

return out, *pg.State, nil
},
}

_, err = wait.WaitForState()
if err != nil {
return err
}

log.Printf("[DEBUG] EC2 Placement group created: %q", name)

d.SetId(name)

return resourceAwsPlacementGroupRead(d, meta)
}

func resourceAwsPlacementGroupRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).ec2conn
input := ec2.DescribePlacementGroupsInput{
GroupNames: []*string{aws.String(d.Get("name").(string))},
}
out, err := conn.DescribePlacementGroups(&input)
if err != nil {
return err
}
pg := out.PlacementGroups[0]

log.Printf("[DEBUG] Received EC2 Placement Group: %s", pg)

d.Set("name", pg.GroupName)
d.Set("strategy", pg.Strategy)

return nil
}

func resourceAwsPlacementGroupDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).ec2conn

log.Printf("[DEBUG] Deleting EC2 Placement Group %q", d.Id())
_, err := conn.DeletePlacementGroup(&ec2.DeletePlacementGroupInput{
GroupName: aws.String(d.Id()),
})
if err != nil {
return err
}

wait := resource.StateChangeConf{
Pending: []string{"deleting"},
Target: "deleted",
Timeout: 5 * time.Minute,
MinTimeout: 1 * time.Second,
Refresh: func() (interface{}, string, error) {
out, err := conn.DescribePlacementGroups(&ec2.DescribePlacementGroupsInput{
GroupNames: []*string{aws.String(d.Id())},
})

if err != nil {
awsErr := err.(awserr.Error)
if awsErr.Code() == "InvalidPlacementGroup.Unknown" {
return out, "deleted", nil
}
return out, "", awsErr
}

if len(out.PlacementGroups) == 0 {
return out, "deleted", nil
}

pg := out.PlacementGroups[0]

return out, *pg.State, nil
},
}

_, err = wait.WaitForState()
if err != nil {
return err
}

d.SetId("")
return nil
}
98 changes: 98 additions & 0 deletions builtin/providers/aws/resource_aws_placement_group_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package aws

import (
"fmt"
"testing"

"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
)

func TestAccAWSPlacementGroup_basic(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSPlacementGroupDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSPlacementGroupConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSPlacementGroupExists("aws_placement_group.pg"),
),
},
},
})
}

func testAccCheckAWSPlacementGroupDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).ec2conn

for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_placement_group" {
continue
}
_, err := conn.DeletePlacementGroup(&ec2.DeletePlacementGroupInput{
GroupName: aws.String(rs.Primary.ID),
})
if err != nil {
return err
}
}
return nil
}

func testAccCheckAWSPlacementGroupExists(n string) 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 Placement Group ID is set")
}

conn := testAccProvider.Meta().(*AWSClient).ec2conn
_, err := conn.DescribePlacementGroups(&ec2.DescribePlacementGroupsInput{
GroupNames: []*string{aws.String(rs.Primary.ID)},
})

if err != nil {
return fmt.Errorf("Placement Group error: %v", err)
}
return nil
}
}

func testAccCheckAWSDestroyPlacementGroup(n string) 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 Placement Group ID is set")
}

conn := testAccProvider.Meta().(*AWSClient).ec2conn
_, err := conn.DeletePlacementGroup(&ec2.DeletePlacementGroupInput{
GroupName: aws.String(rs.Primary.ID),
})

if err != nil {
return fmt.Errorf("Error destroying Placement Group (%s): %s", rs.Primary.ID, err)
}
return nil
}
}

var testAccAWSPlacementGroupConfig = `
resource "aws_placement_group" "pg" {
name = "tf-test-pg"
strategy = "cluster"
}
`
34 changes: 34 additions & 0 deletions website/source/docs/providers/aws/r/placement_group.html.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
layout: "aws"
page_title: "AWS: aws_placement_group"
sidebar_current: "docs-aws-resource-placement-group"
description: |-
Provides an EC2 placement group.
---

# aws\_placement\_group

Provides an EC2 placement group. Read more about placement groups
in [AWS Docs](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/placement-groups.html).

## Example Usage

```
resource "aws_placement_group" "web" {
name = "hunky-dory-pg"
strategy = "cluster"
}
```

## Argument Reference

The following arguments are supported:

* `name` - (Required) The name of the placement group.
* `strategy` - (Required) The placement strategy. The only supported value is `cluster`

## Attributes Reference

The following attributes are exported:

* `id` - The name of the placement group.
6 changes: 5 additions & 1 deletion website/source/layouts/aws.erb
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
</li>


<li<%= sidebar_current(/^docs-aws-resource-(app|autoscaling|ebs|elb|eip|instance|launch|lb|proxy|spot|volume)/) %>>
<li<%= sidebar_current(/^docs-aws-resource-(app|autoscaling|ebs|elb|eip|instance|launch|lb|proxy|spot|volume|placement)/) %>>
<a href="#">EC2 Resources</a>
<ul class="nav nav-visible">

Expand Down Expand Up @@ -95,6 +95,10 @@
<a href="/docs/providers/aws/r/lb_cookie_stickiness_policy.html">aws_lb_cookie_stickiness_policy</a>
</li>

<li<%= sidebar_current("docs-aws-resource-placement-group") %>>
<a href="/docs/providers/aws/r/placement_group.html">aws_placement_group</a>
</li>

<li<%= sidebar_current("docs-aws-resource-proxy-protocol-policy") %>>
<a href="/docs/providers/aws/r/proxy_protocol_policy.html">aws_proxy_protocol_policy</a>
</li>
Expand Down