Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master' into f-aws-security-gr…
Browse files Browse the repository at this point in the history
…oup-remove-default-egress

* upstream/master: (24 commits)
  helper/resource: fix accidentaly swallowing of acctest step errors
  Update CHANGELOG.md
  providers/aws: Implements DHCP Options Set support.
  update CHANGELOG
  update CHANGELOG
  Update CHANGELOG.md
  Update CHANGELOG.md
  Update CHANGELOG.md
  core: fix targeting with non-word chars
  update CHANGELOG
  update CHANGELOG
  docs: Fix styling in provider code block
  provider/openstack: enable_dhcp should be bool [GH-1741]
  config: add module raw configs to InterpolatedConfigs [GH-1448]
  terraform: EvalDeleteOutput and context test
  terraform: add output orphan transformer
  providers/aws: add source_security_group to elb
  core: graph command gets -verbose and -draw-cycles
  core: fix targeting in destroy w/ provisioners
  core: validate on verbose graph to detect some cycles earlier
  ...
  • Loading branch information
catsby committed May 1, 2015
2 parents 48fc0cd + 5d07394 commit 616bdc6
Show file tree
Hide file tree
Showing 49 changed files with 2,410 additions and 208 deletions.
20 changes: 16 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ FEATURES:
* **Environmental variables to set variables**: Environment variables can be
used to set variables. The environment variables must be in the format
`TF_VAR_name` and this will be checked last for a value.
* **New remote state backend: `s3`**: You can now store remote state in
an S3 bucket. [GH-1723]

IMPROVEMENTS:

Expand All @@ -17,13 +19,15 @@ IMPROVEMENTS:
* **New resource: `aws_customer_gateway`**
* **New resource: `aws_ebs_volume`**
* **New resource: `aws_lb_cookie_stickiness_policy`**
* **New resource: `aws_vpc_dhcp_options`**
* **New resource: `aws_vpc_dhcp_options_association`**
* **New resource: `google_dns_managed_zone`**
* **New resource: `google_dns_record_set`**
* **Migrate to upstream AWS SDK:** Migrate the AWS provider to
[awslabs/aws-sdk-go](https://github.com/awslabs/aws-sdk-go),
the offical `awslabs` library. Previously we had forked the library for
* **Migrate to upstream AWS SDK:** Migrate the AWS provider to
[awslabs/aws-sdk-go](https://github.com/awslabs/aws-sdk-go),
the offical `awslabs` library. Previously we had forked the library for
stability while `awslabs` refactored. Now that work has completed, and we've
migrated back to the upstream version.
migrated back to the upstream version.
* core: Improve error message on diff mismatch [GH-1501]
* provisioner/file: expand `~` in source path [GH-1569]
* provider/aws: Improved credential detection [GH-1470]
Expand All @@ -47,6 +51,7 @@ IMPROVEMENTS:
static default value [GH-1632]
* provider/aws: automatically set the private IP as the SSH address
if not specified and no public IP is available [GH-1623]
* provider/aws: `aws_elb` exports `source_security_group` field [GH-1708]
* provider/docker: `docker_container` can specify links [GH-1564]
* provider/google: `resource_compute_disk` supports snapshots [GH-1426]
* provider/google: `resource_compute_instance` supports specifying the
Expand All @@ -73,6 +78,10 @@ BUG FIXES:
if the value was computed [GH-1507]
* core: Fix issue where values in sets on resources couldn't contain
hyphens. [GH-1641]
* core: Outputs removed from the config are removed from the state [GH-1714]
* core: Validate against the worst-case graph during plan phase to catch cycles
that would previously only show up during apply [GH-1655]
* core: Referencing invalid module output in module validates [GH-1448]
* command: remote states with uppercase types work [GH-1356]
* provider/aws: launch configuration ID set after create success [GH-1518]
* provider/aws: Fixed an issue with creating ELBs without any tags [GH-1580]
Expand All @@ -84,6 +93,7 @@ BUG FIXES:
* provider/aws: ASG health check grace period can be updated in-place [GH-1682]
* provider/aws: ELB security groups can be updated in-place [GH-1662]
* provider/openstack: region config is not required [GH-1441]
* provider/openstack: `enable_dhcp` for networking subnet should be bool [GH-1741]
* provisioner/remote-exec: add random number to uploaded script path so
that parallel provisions work [GH-1588]

Expand Down Expand Up @@ -114,6 +124,8 @@ BUG FIXES:
systems so copying your ".terraform" folder works. [GH-1418]
* core: don't validate providers too early when nested in a module [GH-1380]
* core: fix race condition in `count.index` interpolation [GH-1454]
* core: properly initialize provisioners, fixing resource targeting
during destroy [GH-1544]
* command/push: don't ask for input if terraform.tfvars is present
* command/remote-config: remove spurrious error "nil" when initializing
remote state on a new configuration. [GH-1392]
Expand Down
2 changes: 2 additions & 0 deletions builtin/providers/aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ func Provider() terraform.ResourceProvider {
"aws_subnet": resourceAwsSubnet(),
"aws_vpc": resourceAwsVpc(),
"aws_vpc_peering_connection": resourceAwsVpcPeeringConnection(),
"aws_vpc_dhcp_options": resourceAwsVpcDhcpOptions(),
"aws_vpc_dhcp_options_association": resourceAwsVpcDhcpOptionsAssociation(),
"aws_vpn_gateway": resourceAwsVpnGateway(),
},

Expand Down
9 changes: 9 additions & 0 deletions builtin/providers/aws/resource_aws_elb.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ func resourceAwsElb() *schema.Resource {
},
},

"source_security_group": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
},

"subnets": &schema.Schema{
Type: schema.TypeSet,
Elem: &schema.Schema{Type: schema.TypeString},
Expand Down Expand Up @@ -280,6 +286,9 @@ func resourceAwsElbRead(d *schema.ResourceData, meta interface{}) error {
d.Set("instances", flattenInstances(lb.Instances))
d.Set("listener", flattenListeners(lb.ListenerDescriptions))
d.Set("security_groups", lb.SecurityGroups)
if lb.SourceSecurityGroup != nil {
d.Set("source_security_group", lb.SourceSecurityGroup.GroupName)
}
d.Set("subnets", lb.Subnets)
d.Set("idle_timeout", lbAttrs.ConnectionSettings.IdleTimeout)
d.Set("connection_draining", lbAttrs.ConnectionDraining.Enabled)
Expand Down
6 changes: 6 additions & 0 deletions builtin/providers/aws/resource_aws_vpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ func resourceAwsVpc() *schema.Resource {
Computed: true,
},

"dhcp_options_id": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},

"default_security_group_id": &schema.Schema{
Type: schema.TypeString,
Computed: true,
Expand Down Expand Up @@ -126,6 +131,7 @@ func resourceAwsVpcRead(d *schema.ResourceData, meta interface{}) error {
vpc := vpcRaw.(*ec2.VPC)
vpcid := d.Id()
d.Set("cidr_block", vpc.CIDRBlock)
d.Set("dhcp_options_id", vpc.DHCPOptionsID)

// Tags
d.Set("tags", tagsToMapSDK(vpc.Tags))
Expand Down
280 changes: 280 additions & 0 deletions builtin/providers/aws/resource_aws_vpc_dhcp_options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,280 @@
package aws

import (
"fmt"
"log"
"strings"
"time"

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

func resourceAwsVpcDhcpOptions() *schema.Resource {
return &schema.Resource{
Create: resourceAwsVpcDhcpOptionsCreate,
Read: resourceAwsVpcDhcpOptionsRead,
Update: resourceAwsVpcDhcpOptionsUpdate,
Delete: resourceAwsVpcDhcpOptionsDelete,

Schema: map[string]*schema.Schema{
"domain_name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},

"domain_name_servers": &schema.Schema{
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
},

"ntp_servers": &schema.Schema{
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
},

"netbios_node_type": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},

"netbios_name_servers": &schema.Schema{
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
},

"tags": &schema.Schema{
Type: schema.TypeMap,
Optional: true,
},
},
}
}

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

setDHCPOption := func(key string) *ec2.NewDHCPConfiguration {
log.Printf("[DEBUG] Setting DHCP option %s...", key)
tfKey := strings.Replace(key, "-", "_", -1)

value, ok := d.GetOk(tfKey)
if !ok {
return nil
}

if v, ok := value.(string); ok {
return &ec2.NewDHCPConfiguration{
Key: aws.String(key),
Values: []*string{
aws.String(v),
},
}
}

if v, ok := value.([]interface{}); ok {
var s []*string
for _, attr := range v {
s = append(s, aws.String(attr.(string)))
}

return &ec2.NewDHCPConfiguration{
Key: aws.String(key),
Values: s,
}
}

return nil
}

createOpts := &ec2.CreateDHCPOptionsInput{
DHCPConfigurations: []*ec2.NewDHCPConfiguration{
setDHCPOption("domain-name"),
setDHCPOption("domain-name-servers"),
setDHCPOption("ntp-servers"),
setDHCPOption("netbios-node-type"),
setDHCPOption("netbios-name-servers"),
},
}

resp, err := conn.CreateDHCPOptions(createOpts)
if err != nil {
return fmt.Errorf("Error creating DHCP Options Set: %s", err)
}

dos := resp.DHCPOptions
d.SetId(*dos.DHCPOptionsID)
log.Printf("[INFO] DHCP Options Set ID: %s", d.Id())

// Wait for the DHCP Options to become available
log.Printf("[DEBUG] Waiting for DHCP Options (%s) to become available", d.Id())
stateConf := &resource.StateChangeConf{
Pending: []string{"pending"},
Target: "",
Refresh: DHCPOptionsStateRefreshFunc(conn, d.Id()),
Timeout: 1 * time.Minute,
}
if _, err := stateConf.WaitForState(); err != nil {
return fmt.Errorf(
"Error waiting for DHCP Options (%s) to become available: %s",
d.Id(), err)
}

return resourceAwsVpcDhcpOptionsUpdate(d, meta)
}

func resourceAwsVpcDhcpOptionsRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).ec2conn
req := &ec2.DescribeDHCPOptionsInput{
DHCPOptionsIDs: []*string{
aws.String(d.Id()),
},
}

resp, err := conn.DescribeDHCPOptions(req)
if err != nil {
return fmt.Errorf("Error retrieving DHCP Options: %s", err)
}

if len(resp.DHCPOptions) == 0 {
return nil
}

opts := resp.DHCPOptions[0]
d.Set("tags", tagsToMapSDK(opts.Tags))

for _, cfg := range opts.DHCPConfigurations {
tfKey := strings.Replace(*cfg.Key, "-", "_", -1)

if _, ok := d.Get(tfKey).(string); ok {
d.Set(tfKey, cfg.Values[0].Value)
} else {
values := make([]string, 0, len(cfg.Values))
for _, v := range cfg.Values {
values = append(values, *v.Value)
}

d.Set(tfKey, values)
}
}

return nil
}

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

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

return resource.Retry(3*time.Minute, func() error {
log.Printf("[INFO] Deleting DHCP Options ID %s...", d.Id())
_, err := conn.DeleteDHCPOptions(&ec2.DeleteDHCPOptionsInput{
DHCPOptionsID: aws.String(d.Id()),
})

if err == nil {
return nil
}

log.Printf("[WARN] %s", err)

ec2err, ok := err.(aws.APIError)
if !ok {
return err
}

switch ec2err.Code {
case "InvalidDhcpOptionsID.NotFound":
return nil
case "DependencyViolation":
// If it is a dependency violation, we want to disassociate
// all VPCs using the given DHCP Options ID, and retry deleting.
vpcs, err2 := findVPCsByDHCPOptionsID(conn, d.Id())
if err2 != nil {
log.Printf("[ERROR] %s", err2)
return err2
}

for _, vpc := range vpcs {
log.Printf("[INFO] Disassociating DHCP Options Set %s from VPC %s...", d.Id(), *vpc.VPCID)
if _, err := conn.AssociateDHCPOptions(&ec2.AssociateDHCPOptionsInput{
DHCPOptionsID: aws.String("default"),
VPCID: vpc.VPCID,
}); err != nil {
return err
}
}
return err //retry
default:
// Any other error, we want to quit the retry loop immediately
return resource.RetryError{Err: err}
}

return nil
})
}

func findVPCsByDHCPOptionsID(conn *ec2.EC2, id string) ([]*ec2.VPC, error) {
req := &ec2.DescribeVPCsInput{
Filters: []*ec2.Filter{
&ec2.Filter{
Name: aws.String("dhcp-options-id"),
Values: []*string{
aws.String(id),
},
},
},
}

resp, err := conn.DescribeVPCs(req)
if err != nil {
if ec2err, ok := err.(aws.APIError); ok && ec2err.Code == "InvalidVpcID.NotFound" {
return nil, nil
}
return nil, err
}

return resp.VPCs, nil
}

func DHCPOptionsStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
DescribeDhcpOpts := &ec2.DescribeDHCPOptionsInput{
DHCPOptionsIDs: []*string{
aws.String(id),
},
}

resp, err := conn.DescribeDHCPOptions(DescribeDhcpOpts)
if err != nil {
if ec2err, ok := err.(aws.APIError); ok && ec2err.Code == "InvalidDhcpOptionsID.NotFound" {
resp = nil
} else {
log.Printf("Error on DHCPOptionsStateRefresh: %s", err)
return nil, "", err
}
}

if resp == nil {
// Sometimes AWS just has consistency issues and doesn't see
// our instance yet. Return an empty state.
return nil, "", nil
}

dos := resp.DHCPOptions[0]
return dos, "", nil
}
}
Loading

0 comments on commit 616bdc6

Please sign in to comment.