From eccd5ad308c8cde1b2a494393669704e6f3b94a1 Mon Sep 17 00:00:00 2001 From: Sander van Harmelen Date: Fri, 21 Nov 2014 17:58:34 +0100 Subject: [PATCH] Refactored about 90% MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Still need to update 2 resources and check the acceptance tests, but overall we’re nearly there :smiley: --- builtin/providers/aws/config.go | 69 ++- builtin/providers/aws/provider.go | 74 ++-- builtin/providers/aws/provider_test.go | 32 ++ .../aws/resource_aws_autoscaling_group.go | 63 ++- .../resource_aws_autoscaling_group_test.go | 4 +- .../providers/aws/resource_aws_db_instance.go | 416 +++++++++--------- .../aws/resource_aws_db_instance_test.go | 6 +- .../aws/resource_aws_db_parameter_group.go | 140 +++--- .../resource_aws_db_parameter_group_test.go | 4 +- .../aws/resource_aws_db_security_group.go | 235 +++++----- .../resource_aws_db_security_group_test.go | 6 +- .../aws/resource_aws_db_subnet_group.go | 36 +- .../aws/resource_aws_db_subnet_group_test.go | 4 +- builtin/providers/aws/resource_aws_eip.go | 105 +++-- .../providers/aws/resource_aws_eip_test.go | 4 +- builtin/providers/aws/resource_aws_elb.go | 140 +++--- .../providers/aws/resource_aws_elb_test.go | 4 +- .../providers/aws/resource_aws_instance.go | 134 +++--- .../aws/resource_aws_instance_test.go | 4 +- .../aws/resource_aws_internet_gateway.go | 205 ++++----- .../aws/resource_aws_internet_gateway_test.go | 4 +- .../aws/resource_aws_launch_configuration.go | 43 +- .../resource_aws_launch_configuration_test.go | 4 +- .../aws/resource_aws_route53_record.go | 222 +++++----- .../aws/resource_aws_route53_record_test.go | 4 +- .../aws/resource_aws_route53_zone.go | 120 +++-- .../aws/resource_aws_route53_zone_test.go | 4 +- .../providers/aws/resource_aws_route_table.go | 346 ++++++--------- .../resource_aws_route_table_association.go | 173 ++++---- ...source_aws_route_table_association_test.go | 4 +- .../aws/resource_aws_route_table_test.go | 4 +- .../providers/aws/resource_aws_s3_bucket.go | 100 ++--- .../aws/resource_aws_s3_bucket_test.go | 4 +- .../aws/resource_aws_security_group.go | 266 ++++++----- .../aws/resource_aws_security_group_test.go | 4 +- builtin/providers/aws/resource_aws_subnet.go | 12 +- .../providers/aws/resource_aws_subnet_test.go | 4 +- builtin/providers/aws/resource_aws_vpc.go | 132 +++--- .../providers/aws/resource_aws_vpc_test.go | 4 +- builtin/providers/aws/resource_provider.go | 135 ------ .../providers/aws/resource_provider_test.go | 95 ---- builtin/providers/aws/resources.go | 98 ----- 42 files changed, 1501 insertions(+), 1966 deletions(-) delete mode 100644 builtin/providers/aws/resource_provider.go delete mode 100644 builtin/providers/aws/resource_provider_test.go delete mode 100644 builtin/providers/aws/resources.go diff --git a/builtin/providers/aws/config.go b/builtin/providers/aws/config.go index 8479cb9ae..c1be060bb 100644 --- a/builtin/providers/aws/config.go +++ b/builtin/providers/aws/config.go @@ -2,17 +2,74 @@ package aws import ( "fmt" - "os" + "log" "strings" "unicode" + "github.com/hashicorp/terraform/helper/multierror" + "github.com/mitchellh/goamz/autoscaling" "github.com/mitchellh/goamz/aws" + "github.com/mitchellh/goamz/ec2" + "github.com/mitchellh/goamz/elb" + "github.com/mitchellh/goamz/rds" + "github.com/mitchellh/goamz/route53" + "github.com/mitchellh/goamz/s3" ) type Config struct { - AccessKey string `mapstructure:"access_key"` - SecretKey string `mapstructure:"secret_key"` - Region string `mapstructure:"region"` + AccessKey string + SecretKey string + Region string +} + +type AWSClient struct { + ec2conn *ec2.EC2 + elbconn *elb.ELB + autoscalingconn *autoscaling.AutoScaling + s3conn *s3.S3 + rdsconn *rds.Rds + route53 *route53.Route53 +} + +// Client configures and returns a fully initailized AWSClient +func (c *Config) Client() (interface{}, error) { + var client AWSClient + + // Get the auth and region. This can fail if keys/regions were not + // specified and we're attempting to use the environment. + var errs []error + log.Println("[INFO] Building AWS auth structure") + auth, err := c.AWSAuth() + if err != nil { + errs = append(errs, err) + } + + log.Println("[INFO] Building AWS region structure") + region, err := c.AWSRegion() + if err != nil { + errs = append(errs, err) + } + + if len(errs) == 0 { + log.Println("[INFO] Initializing EC2 connection") + client.ec2conn = ec2.New(auth, region) + log.Println("[INFO] Initializing ELB connection") + client.elbconn = elb.New(auth, region) + log.Println("[INFO] Initializing AutoScaling connection") + client.autoscalingconn = autoscaling.New(auth, region) + log.Println("[INFO] Initializing S3 connection") + client.s3conn = s3.New(auth, region) + log.Println("[INFO] Initializing RDS connection") + client.rdsconn = rds.New(auth, region) + log.Println("[INFO] Initializing Route53 connection") + client.route53 = route53.New(auth, region) + } + + if len(errs) > 0 { + return nil, &multierror.Error{Errors: errs} + } + + return &client, nil } // AWSAuth returns a valid aws.Auth object for access to AWS services, or @@ -56,10 +113,6 @@ func (c *Config) AWSRegion() (aws.Region, error) { } } - if v := os.Getenv("AWS_REGION"); v != "" { - return aws.Regions[v], nil - } - md, err := aws.GetMetaData("placement/availability-zone") if err != nil { return aws.Region{}, err diff --git a/builtin/providers/aws/provider.go b/builtin/providers/aws/provider.go index 9eed4ee86..6c4a2e250 100644 --- a/builtin/providers/aws/provider.go +++ b/builtin/providers/aws/provider.go @@ -18,14 +18,6 @@ func Provider() *schema.Provider { return &schema.Provider{ Schema: map[string]*schema.Schema{ - "region": &schema.Schema{ - Type: schema.TypeString, - Required: true, - DefaultFunc: envDefaultFunc("AWS_REGION"), - Description: descriptions["region"], - InputDefault: "us-east-1", - }, - "access_key": &schema.Schema{ Type: schema.TypeString, Required: true, @@ -39,30 +31,38 @@ func Provider() *schema.Provider { DefaultFunc: envDefaultFunc("AWS_SECRET_KEY"), Description: descriptions["secret_key"], }, + + "region": &schema.Schema{ + Type: schema.TypeString, + Required: true, + DefaultFunc: envDefaultFunc("AWS_REGION"), + Description: descriptions["region"], + InputDefault: "us-east-1", + }, }, ResourcesMap: map[string]*schema.Resource{ - "aws_autoscaling_group": resourceAwsAutoscalingGroup(), - "aws_eip": resourceAwsEip(), - "aws_elb": resourceAwsElb(), - "aws_instance": resourceAwsInstance(), - "aws_launch_configuration": resourceAwsLaunchConfiguration(), - "aws_security_group": resourceAwsSecurityGroup(), - "aws_db_subnet_group": resourceAwsDbSubnetGroup(), - "aws_vpc": resourceAwsVpc(), - "aws_db_parameter_group": resourceAwsDbParameterGroup(), - "aws_subnet": resourceAwsSubnet(), + "aws_autoscaling_group": resourceAwsAutoscalingGroup(), + //"aws_db_instance": resourceAwsDbInstance(), + "aws_db_parameter_group": resourceAwsDbParameterGroup(), + //"aws_db_security_group": resourceAwsDbSecurityGroup(), + "aws_db_subnet_group": resourceAwsDbSubnetGroup(), + "aws_eip": resourceAwsEip(), + "aws_elb": resourceAwsElb(), + "aws_instance": resourceAwsInstance(), + "aws_internet_gateway": resourceAwsInternetGateway(), + "aws_launch_configuration": resourceAwsLaunchConfiguration(), + "aws_route53_record": resourceAwsRoute53Record(), + "aws_route53_zone": resourceAwsRoute53Zone(), + "aws_route_table": resourceAwsRouteTable(), + "aws_route_table_association": resourceAwsRouteTableAssociation(), + "aws_s3_bucket": resourceAwsS3Bucket(), + "aws_security_group": resourceAwsSecurityGroup(), + "aws_subnet": resourceAwsSubnet(), + "aws_vpc": resourceAwsVpc(), }, - } -} -func envDefaultFunc(k string) schema.SchemaDefaultFunc { - return func() (interface{}, error) { - if v := os.Getenv(k); v != "" { - return v, nil - } - - return nil, nil + ConfigureFunc: providerConfigure, } } @@ -80,3 +80,23 @@ func init() { "from the 'Security & Credentials' section of the AWS console.", } } + +func envDefaultFunc(k string) schema.SchemaDefaultFunc { + return func() (interface{}, error) { + if v := os.Getenv(k); v != "" { + return v, nil + } + + return nil, nil + } +} + +func providerConfigure(d *schema.ResourceData) (interface{}, error) { + config := Config{ + AccessKey: d.Get("access_key").(string), + SecretKey: d.Get("secret_key").(string), + Region: d.Get("region").(string), + } + + return config.Client() +} diff --git a/builtin/providers/aws/provider_test.go b/builtin/providers/aws/provider_test.go index 480c88bf8..bfa150296 100644 --- a/builtin/providers/aws/provider_test.go +++ b/builtin/providers/aws/provider_test.go @@ -1,11 +1,43 @@ package aws import ( + "log" + "os" "testing" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/terraform" ) +var testAccProviders map[string]terraform.ResourceProvider +var testAccProvider *schema.Provider + +func init() { + testAccProvider = Provider() + testAccProviders = map[string]terraform.ResourceProvider{ + "aws": testAccProvider, + } +} + func TestProvider(t *testing.T) { if err := Provider().InternalValidate(); err != nil { t.Fatalf("err: %s", err) } } + +func TestProvider_impl(t *testing.T) { + var _ terraform.ResourceProvider = Provider() +} + +func testAccPreCheck(t *testing.T) { + if v := os.Getenv("AWS_ACCESS_KEY"); v == "" { + t.Fatal("AWS_ACCESS_KEY must be set for acceptance tests") + } + if v := os.Getenv("AWS_SECRET_KEY"); v == "" { + t.Fatal("AWS_SECRET_KEY must be set for acceptance tests") + } + if v := os.Getenv("AWS_REGION"); v == "" { + log.Println("[INFO] Test: Using us-west-2 as test region") + os.Setenv("AWS_REGION", "us-west-2") + } +} diff --git a/builtin/providers/aws/resource_aws_autoscaling_group.go b/builtin/providers/aws/resource_aws_autoscaling_group.go index 54c744989..184ed455c 100644 --- a/builtin/providers/aws/resource_aws_autoscaling_group.go +++ b/builtin/providers/aws/resource_aws_autoscaling_group.go @@ -110,8 +110,7 @@ func resourceAwsAutoscalingGroup() *schema.Resource { } func resourceAwsAutoscalingGroupCreate(d *schema.ResourceData, meta interface{}) error { - p := meta.(*ResourceProvider) - autoscalingconn := p.autoscalingconn + autoscalingconn := meta.(*AWSClient).autoscalingconn var autoScalingGroupOpts autoscaling.CreateAutoScalingGroup autoScalingGroupOpts.Name = d.Get("name").(string) @@ -161,9 +160,32 @@ func resourceAwsAutoscalingGroupCreate(d *schema.ResourceData, meta interface{}) return resourceAwsAutoscalingGroupRead(d, meta) } +func resourceAwsAutoscalingGroupRead(d *schema.ResourceData, meta interface{}) error { + g, err := getAwsAutoscalingGroup(d, meta) + if err != nil { + return err + } + if g == nil { + return nil + } + + d.Set("availability_zones", g.AvailabilityZones) + d.Set("default_cooldown", g.DefaultCooldown) + d.Set("desired_capacity", g.DesiredCapacity) + d.Set("health_check_grace_period", g.HealthCheckGracePeriod) + d.Set("health_check_type", g.HealthCheckType) + d.Set("launch_configuration", g.LaunchConfigurationName) + d.Set("load_balancers", g.LoadBalancerNames) + d.Set("min_size", g.MinSize) + d.Set("max_size", g.MaxSize) + d.Set("name", g.Name) + d.Set("vpc_zone_identifier", g.VPCZoneIdentifier) + + return nil +} + func resourceAwsAutoscalingGroupUpdate(d *schema.ResourceData, meta interface{}) error { - p := meta.(*ResourceProvider) - autoscalingconn := p.autoscalingconn + autoscalingconn := meta.(*AWSClient).autoscalingconn opts := autoscaling.UpdateAutoScalingGroup{ Name: d.Id(), @@ -195,8 +217,7 @@ func resourceAwsAutoscalingGroupUpdate(d *schema.ResourceData, meta interface{}) } func resourceAwsAutoscalingGroupDelete(d *schema.ResourceData, meta interface{}) error { - p := meta.(*ResourceProvider) - autoscalingconn := p.autoscalingconn + autoscalingconn := meta.(*AWSClient).autoscalingconn // Read the autoscaling group first. If it doesn't exist, we're done. // We need the group in order to check if there are instances attached. @@ -238,35 +259,10 @@ func resourceAwsAutoscalingGroupDelete(d *schema.ResourceData, meta interface{}) return nil } -func resourceAwsAutoscalingGroupRead(d *schema.ResourceData, meta interface{}) error { - g, err := getAwsAutoscalingGroup(d, meta) - if err != nil { - return err - } - if g == nil { - return nil - } - - d.Set("availability_zones", g.AvailabilityZones) - d.Set("default_cooldown", g.DefaultCooldown) - d.Set("desired_capacity", g.DesiredCapacity) - d.Set("health_check_grace_period", g.HealthCheckGracePeriod) - d.Set("health_check_type", g.HealthCheckType) - d.Set("launch_configuration", g.LaunchConfigurationName) - d.Set("load_balancers", g.LoadBalancerNames) - d.Set("min_size", g.MinSize) - d.Set("max_size", g.MaxSize) - d.Set("name", g.Name) - d.Set("vpc_zone_identifier", g.VPCZoneIdentifier) - - return nil -} - func getAwsAutoscalingGroup( d *schema.ResourceData, meta interface{}) (*autoscaling.AutoScalingGroup, error) { - p := meta.(*ResourceProvider) - autoscalingconn := p.autoscalingconn + autoscalingconn := meta.(*AWSClient).autoscalingconn describeOpts := autoscaling.DescribeAutoScalingGroups{ Names: []string{d.Id()}, @@ -298,8 +294,7 @@ func getAwsAutoscalingGroup( } func resourceAwsAutoscalingGroupDrain(d *schema.ResourceData, meta interface{}) error { - p := meta.(*ResourceProvider) - autoscalingconn := p.autoscalingconn + autoscalingconn := meta.(*AWSClient).autoscalingconn // First, set the capacity to zero so the group will drain log.Printf("[DEBUG] Reducing autoscaling group capacity to zero") diff --git a/builtin/providers/aws/resource_aws_autoscaling_group_test.go b/builtin/providers/aws/resource_aws_autoscaling_group_test.go index 79ce2f6b7..b35de9320 100644 --- a/builtin/providers/aws/resource_aws_autoscaling_group_test.go +++ b/builtin/providers/aws/resource_aws_autoscaling_group_test.go @@ -72,7 +72,7 @@ func TestAccAWSAutoScalingGroupWithLoadBalancer(t *testing.T) { }) } func testAccCheckAWSAutoScalingGroupDestroy(s *terraform.State) error { - conn := testAccProvider.autoscalingconn + conn := testAccProvider.Meta().(*AWSClient).autoscalingconn for _, rs := range s.RootModule().Resources { if rs.Type != "aws_autoscaling_group" { @@ -164,7 +164,7 @@ func testAccCheckAWSAutoScalingGroupExists(n string, group *autoscaling.AutoScal return fmt.Errorf("No AutoScaling Group ID is set") } - conn := testAccProvider.autoscalingconn + conn := testAccProvider.Meta().(*AWSClient).autoscalingconn describeOpts := autoscaling.DescribeAutoScalingGroups{ Names: []string{rs.Primary.ID}, diff --git a/builtin/providers/aws/resource_aws_db_instance.go b/builtin/providers/aws/resource_aws_db_instance.go index e2c1e60e6..c63809a99 100644 --- a/builtin/providers/aws/resource_aws_db_instance.go +++ b/builtin/providers/aws/resource_aws_db_instance.go @@ -1,5 +1,6 @@ package aws +/* import ( "fmt" "log" @@ -8,111 +9,187 @@ import ( "time" "github.com/hashicorp/terraform/flatmap" - "github.com/hashicorp/terraform/helper/config" - "github.com/hashicorp/terraform/helper/diff" + "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/terraform" "github.com/mitchellh/goamz/rds" ) -func resource_aws_db_instance_create( - s *terraform.InstanceState, - d *terraform.InstanceDiff, - meta interface{}) (*terraform.InstanceState, error) { - p := meta.(*ResourceProvider) - conn := p.rdsconn +func resourceAwsDbInstance() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsDbInstanceCreate, + Read: resourceAwsDbInstanceRead, + Delete: resourceAwsDbInstanceDelete, - // Merge the diff into the state so that we have all the attributes - // properly. - rs := s.MergeDiff(d) + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, - var err error - var attr string + "username": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "password": &schema.Schema{ + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + + "engine": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "engine_version": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "allocated_storage": &schema.Schema{ + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + + "identifier": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "instance_class": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + // tot hier + "health_check_type": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + + "availability_zones": &schema.Schema{ + Type: schema.TypeSet, + Required: true, + ForceNew: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: func(v interface{}) int { + return hashcode.String(v.(string)) + }, + }, + + "load_balancers": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + ForceNew: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: func(v interface{}) int { + return hashcode.String(v.(string)) + }, + }, + + "vpc_zone_identifier": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Computed: true, + ForceNew: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: func(v interface{}) int { + return hashcode.String(v.(string)) + }, + }, + }, + } +} + +func resourceAwsDbInstanceCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).rdsconn opts := rds.CreateDBInstance{} - if attr = rs.Attributes["allocated_storage"]; attr != "" { - opts.AllocatedStorage, err = strconv.Atoi(attr) - opts.SetAllocatedStorage = true + opts.AllocatedStorage = d.Get("allocated_storage").(int) + opts.SetAllocatedStorage = true + + if attr, ok := d.GetOk("instance_class"); ok { + opts.DBInstanceClass = attr.(string) } - if attr = rs.Attributes["backup_retention_period"]; attr != "" { - opts.BackupRetentionPeriod, err = strconv.Atoi(attr) + if attr, ok := d.GetOk("backup_retention_period"); ok { + opts.BackupRetentionPeriod = attr.(int) opts.SetBackupRetentionPeriod = true } - if attr = rs.Attributes["iops"]; attr != "" { - opts.Iops, err = strconv.Atoi(attr) + if attr, ok := d.GetOk("iops"); ok { + opts.Iops = attr.(int) opts.SetIops = true } - if attr = rs.Attributes["port"]; attr != "" { - opts.Port, err = strconv.Atoi(attr) + if attr, ok := d.GetOk("port"); ok { + opts.Port = attr.(int) opts.SetPort = true } - if attr = rs.Attributes["availability_zone"]; attr != "" { - opts.AvailabilityZone = attr + if attr, ok := d.GetOk("availability_zone"); ok { + opts.AvailabilityZone = attr.(string) } - if attr = rs.Attributes["instance_class"]; attr != "" { - opts.DBInstanceClass = attr + if attr, ok := d.GetOk("maintenance_window"); ok { + opts.PreferredMaintenanceWindow = attr.(string) } - if attr = rs.Attributes["maintenance_window"]; attr != "" { - opts.PreferredMaintenanceWindow = attr + if attr, ok := d.GetOk("backup_window"); ok { + opts.PreferredBackupWindow = attr.(string) } - if attr = rs.Attributes["backup_window"]; attr != "" { - opts.PreferredBackupWindow = attr + if attr, ok := d.GetOk("multi_az"); ok { + opts.MultiAZ = attr.(bool) } - if attr = rs.Attributes["multi_az"]; attr == "true" { - opts.MultiAZ = true + if attr, ok := d.GetOk("publicly_accessible"); ok { + opts.PubliclyAccessible = attr.(bool) } - if attr = rs.Attributes["publicly_accessible"]; attr == "true" { - opts.PubliclyAccessible = true + if attr, ok := d.GetOk("db_subnet_group_name"); ok { + opts.DBSubnetGroupName = attr.(string) } - if attr = rs.Attributes["db_subnet_group_name"]; attr != "" { - opts.DBSubnetGroupName = attr + if attr, ok := d.GetOk("parameter_group_name"); ok { + opts.DBParameterGroupName = attr.(string) } - if attr = rs.Attributes["parameter_group_name"]; attr != "" { - opts.DBParameterGroupName = attr - } - - if err != nil { - return nil, fmt.Errorf("Error parsing configuration: %s", err) + if d.Get("vpc_security_group_ids.#").(int) > 0 { + opts.VpcSecurityGroupIds = d.Get("vpc_security_group_ids").([]string) } - if _, ok := rs.Attributes["vpc_security_group_ids.#"]; ok { - opts.VpcSecurityGroupIds = expandStringList(flatmap.Expand( - rs.Attributes, "vpc_security_group_ids").([]interface{})) + if d.Get("security_group_names.#").(int) > 0 { + opts.DBSecurityGroupNames = d.Get("security_group_names").([]string) } - if _, ok := rs.Attributes["security_group_names.#"]; ok { - opts.DBSecurityGroupNames = expandStringList(flatmap.Expand( - rs.Attributes, "security_group_names").([]interface{})) - } - - opts.DBInstanceIdentifier = rs.Attributes["identifier"] - opts.DBName = rs.Attributes["name"] - opts.MasterUsername = rs.Attributes["username"] - opts.MasterUserPassword = rs.Attributes["password"] - opts.EngineVersion = rs.Attributes["engine_version"] - opts.Engine = rs.Attributes["engine"] + opts.DBInstanceIdentifier = d.Get("identifier").(string) + opts.DBName = d.Get("name").(string) + opts.MasterUsername = d.Get("username").(string) + opts.MasterUserPassword = d.Get("password").(string) + opts.EngineVersion = d.Get("engine_version").(string) + opts.Engine = d.Get("engine").(string) log.Printf("[DEBUG] DB Instance create configuration: %#v", opts) - _, err = conn.CreateDBInstance(&opts) + _, err := conn.CreateDBInstance(&opts) if err != nil { - return nil, fmt.Errorf("Error creating DB Instance: %s", err) + return fmt.Errorf("Error creating DB Instance: %s", err) } - rs.ID = rs.Attributes["identifier"] + d.SetId(d.Get("identifier").(string)) - log.Printf("[INFO] DB Instance ID: %s", rs.ID) + log.Printf("[INFO] DB Instance ID: %s", d.Id()) log.Println( "[INFO] Waiting for DB Instance to be available") @@ -120,7 +197,7 @@ func resource_aws_db_instance_create( stateConf := &resource.StateChangeConf{ Pending: []string{"creating", "backing-up", "modifying"}, Target: "available", - Refresh: DBInstanceStateRefreshFunc(rs.ID, conn), + Refresh: resourceAwsDbInstanceStateRefreshFunc(d.Id(), conn), Timeout: 20 * time.Minute, MinTimeout: 10 * time.Second, Delay: 30 * time.Second, // Wait 30 secs before starting @@ -129,135 +206,14 @@ func resource_aws_db_instance_create( // Wait, catching any errors _, err = stateConf.WaitForState() if err != nil { - return rs, err - } - - v, err := resource_aws_db_instance_retrieve(rs.ID, conn) - if err != nil { - return rs, err - } - - return resource_aws_db_instance_update_state(rs, v) -} - -func resource_aws_db_instance_update( - s *terraform.InstanceState, - d *terraform.InstanceDiff, - meta interface{}) (*terraform.InstanceState, error) { - panic("Cannot update DB") -} - -func resource_aws_db_instance_destroy( - s *terraform.InstanceState, - meta interface{}) error { - p := meta.(*ResourceProvider) - conn := p.rdsconn - - log.Printf("[DEBUG] DB Instance destroy: %v", s.ID) - - opts := rds.DeleteDBInstance{DBInstanceIdentifier: s.ID} - - if s.Attributes["skip_final_snapshot"] == "true" { - opts.SkipFinalSnapshot = true - } else { - opts.FinalDBSnapshotIdentifier = s.Attributes["final_snapshot_identifier"] - } - - log.Printf("[DEBUG] DB Instance destroy configuration: %v", opts) - if _, err := conn.DeleteDBInstance(&opts); err != nil { return err } - log.Println( - "[INFO] Waiting for DB Instance to be destroyed") - stateConf := &resource.StateChangeConf{ - Pending: []string{"creating", "backing-up", - "modifying", "deleting", "available"}, - Target: "", - Refresh: DBInstanceStateRefreshFunc(s.ID, conn), - Timeout: 20 * time.Minute, - MinTimeout: 10 * time.Second, - Delay: 30 * time.Second, // Wait 30 secs before starting - } - if _, err := stateConf.WaitForState(); err != nil { - return err - } - - return nil + return resourceAwsDbInstanceRead(d, meta) } -func resource_aws_db_instance_refresh( - s *terraform.InstanceState, - meta interface{}) (*terraform.InstanceState, error) { - p := meta.(*ResourceProvider) - conn := p.rdsconn - - v, err := resource_aws_db_instance_retrieve(s.ID, conn) - - if err != nil { - return s, err - } - if v == nil { - s.ID = "" - return s, nil - } - - return resource_aws_db_instance_update_state(s, v) -} - -func resource_aws_db_instance_diff( - s *terraform.InstanceState, - c *terraform.ResourceConfig, - meta interface{}) (*terraform.InstanceDiff, error) { - - b := &diff.ResourceBuilder{ - Attrs: map[string]diff.AttrType{ - "allocated_storage": diff.AttrTypeCreate, - "availability_zone": diff.AttrTypeCreate, - "backup_retention_period": diff.AttrTypeCreate, - "backup_window": diff.AttrTypeCreate, - "engine": diff.AttrTypeCreate, - "engine_version": diff.AttrTypeCreate, - "identifier": diff.AttrTypeCreate, - "instance_class": diff.AttrTypeCreate, - "iops": diff.AttrTypeCreate, - "maintenance_window": diff.AttrTypeCreate, - "multi_az": diff.AttrTypeCreate, - "name": diff.AttrTypeCreate, - "password": diff.AttrTypeCreate, - "port": diff.AttrTypeCreate, - "publicly_accessible": diff.AttrTypeCreate, - "username": diff.AttrTypeCreate, - "vpc_security_group_ids": diff.AttrTypeCreate, - "security_group_names": diff.AttrTypeCreate, - "db_subnet_group_name": diff.AttrTypeCreate, - "parameter_group_name": diff.AttrTypeCreate, - "skip_final_snapshot": diff.AttrTypeUpdate, - "final_snapshot_identifier": diff.AttrTypeUpdate, - }, - - ComputedAttrs: []string{ - "address", - "availability_zone", - "backup_retention_period", - "backup_window", - "engine_version", - "maintenance_window", - "endpoint", - "status", - "multi_az", - "port", - "address", - "password", - }, - } - - return b.Diff(s, c) -} - -func resource_aws_db_instance_update_state( - s *terraform.InstanceState, - v *rds.DBInstance) (*terraform.InstanceState, error) { +func resourceAwsDbInstanceRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).rdsconn s.Attributes["address"] = v.Address s.Attributes["allocated_storage"] = strconv.Itoa(v.AllocatedStorage) @@ -293,6 +249,61 @@ func resource_aws_db_instance_update_state( return s, nil } +func resourceAwsDbInstanceDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).rdsconn + + log.Printf("[DEBUG] DB Instance destroy: %v", d.Id()) + + opts := rds.DeleteDBInstance{DBInstanceIdentifier: d.Id()} + + if d.Get("skip_final_snapshot").(string) == "true" { + opts.SkipFinalSnapshot = true + } else { + opts.FinalDBSnapshotIdentifier = s.Attributes["final_snapshot_identifier"] + } + + log.Printf("[DEBUG] DB Instance destroy configuration: %v", opts) + if _, err := conn.DeleteDBInstance(&opts); err != nil { + return err + } + + log.Println( + "[INFO] Waiting for DB Instance to be destroyed") + stateConf := &resource.StateChangeConf{ + Pending: []string{"creating", "backing-up", + "modifying", "deleting", "available"}, + Target: "", + Refresh: resourceAwsDbInstanceStateRefreshFunc(s.ID, conn), + Timeout: 20 * time.Minute, + MinTimeout: 10 * time.Second, + Delay: 30 * time.Second, // Wait 30 secs before starting + } + if _, err := stateConf.WaitForState(); err != nil { + return err + } + + return nil +} + +func resource_aws_db_instance_refresh( + s *terraform.InstanceState, + meta interface{}) (*terraform.InstanceState, error) { + p := meta.(*ResourceProvider) + conn := p.rdsconn + + v, err := resource_aws_db_instance_retrieve(s.ID, conn) + + if err != nil { + return s, err + } + if v == nil { + s.ID = "" + return s, nil + } + + return resource_aws_db_instance_update_state(s, v) +} + func resource_aws_db_instance_retrieve(id string, conn *rds.Rds) (*rds.DBInstance, error) { opts := rds.DescribeDBInstances{ DBInstanceIdentifier: id, @@ -321,39 +332,7 @@ func resource_aws_db_instance_retrieve(id string, conn *rds.Rds) (*rds.DBInstanc return &v, nil } -func resource_aws_db_instance_validation() *config.Validator { - return &config.Validator{ - Required: []string{ - "allocated_storage", - "engine", - "engine_version", - "identifier", - "instance_class", - "name", - "password", - "username", - }, - Optional: []string{ - "availability_zone", - "backup_retention_period", - "backup_window", - "iops", - "maintenance_window", - "multi_az", - "port", - "publicly_accessible", - "vpc_security_group_ids.*", - "skip_final_snapshot", - "security_group_names.*", - "db_subnet_group_name", - "parameter_group_name", - "skip_final_snapshot", - "final_snapshot_identifier", - }, - } -} - -func DBInstanceStateRefreshFunc(id string, conn *rds.Rds) resource.StateRefreshFunc { +func resourceAwsDbInstanceStateRefreshFunc(id string, conn *rds.Rds) resource.StateRefreshFunc { return func() (interface{}, string, error) { v, err := resource_aws_db_instance_retrieve(id, conn) @@ -369,3 +348,4 @@ func DBInstanceStateRefreshFunc(id string, conn *rds.Rds) resource.StateRefreshF return v, v.DBInstanceStatus, nil } } +*/ diff --git a/builtin/providers/aws/resource_aws_db_instance_test.go b/builtin/providers/aws/resource_aws_db_instance_test.go index ba6eebd66..91266094d 100644 --- a/builtin/providers/aws/resource_aws_db_instance_test.go +++ b/builtin/providers/aws/resource_aws_db_instance_test.go @@ -1,5 +1,6 @@ package aws +/* import ( "fmt" "testing" @@ -52,7 +53,7 @@ func TestAccAWSDBInstance(t *testing.T) { } func testAccCheckAWSDBInstanceDestroy(s *terraform.State) error { - conn := testAccProvider.rdsconn + conn := testAccProvider.Meta().(*AWSClient).rdsconn for _, rs := range s.RootModule().Resources { if rs.Type != "aws_db_instance" { @@ -115,7 +116,7 @@ func testAccCheckAWSDBInstanceExists(n string, v *rds.DBInstance) resource.TestC return fmt.Errorf("No DB Instance ID is set") } - conn := testAccProvider.rdsconn + conn := testAccProvider.Meta().(*AWSClient).rdsconn opts := rds.DescribeDBInstances{ DBInstanceIdentifier: rs.Primary.ID, @@ -165,3 +166,4 @@ resource "aws_db_instance" "bar" { parameter_group_name = "default.mysql5.6" } ` +*/ diff --git a/builtin/providers/aws/resource_aws_db_parameter_group.go b/builtin/providers/aws/resource_aws_db_parameter_group.go index af5d0b32d..7253e7793 100644 --- a/builtin/providers/aws/resource_aws_db_parameter_group.go +++ b/builtin/providers/aws/resource_aws_db_parameter_group.go @@ -56,18 +56,8 @@ func resourceAwsDbParameterGroup() *schema.Resource { } } -func resourceAwsDbParameterHash(v interface{}) int { - var buf bytes.Buffer - m := v.(map[string]interface{}) - buf.WriteString(fmt.Sprintf("%s-", m["name"].(string))) - buf.WriteString(fmt.Sprintf("%s-", m["value"].(string))) - - return hashcode.String(buf.String()) -} - func resourceAwsDbParameterGroupCreate(d *schema.ResourceData, meta interface{}) error { - p := meta.(*ResourceProvider) - rdsconn := p.rdsconn + rdsconn := meta.(*AWSClient).rdsconn createOpts := rds.CreateDBParameterGroup{ DBParameterGroupName: d.Get("name").(string), @@ -93,65 +83,8 @@ func resourceAwsDbParameterGroupCreate(d *schema.ResourceData, meta interface{}) return resourceAwsDbParameterGroupUpdate(d, meta) } -func resourceAwsDbParameterGroupUpdate(d *schema.ResourceData, meta interface{}) error { - p := meta.(*ResourceProvider) - rdsconn := p.rdsconn - - d.Partial(true) - - if d.HasChange("parameter") { - o, n := d.GetChange("parameter") - if o == nil { - o = new(schema.Set) - } - if n == nil { - n = new(schema.Set) - } - - os := o.(*schema.Set) - ns := n.(*schema.Set) - - // Expand the "parameter" set to goamz compat []rds.Parameter - parameters, err := expandParameters(ns.Difference(os).List()) - if err != nil { - return err - } - - if len(parameters) > 0 { - modifyOpts := rds.ModifyDBParameterGroup{ - DBParameterGroupName: d.Get("name").(string), - Parameters: parameters, - } - - log.Printf("[DEBUG] Modify DB Parameter Group: %#v", modifyOpts) - _, err = rdsconn.ModifyDBParameterGroup(&modifyOpts) - if err != nil { - return fmt.Errorf("Error modifying DB Parameter Group: %s", err) - } - } - d.SetPartial("parameter") - } - - d.Partial(false) - - return resourceAwsDbParameterGroupRead(d, meta) -} - -func resourceAwsDbParameterGroupDelete(d *schema.ResourceData, meta interface{}) error { - stateConf := &resource.StateChangeConf{ - Pending: []string{"pending"}, - Target: "destroyed", - Refresh: resourceDbParameterGroupDeleteRefreshFunc(d, meta), - Timeout: 3 * time.Minute, - MinTimeout: 1 * time.Second, - } - _, err := stateConf.WaitForState() - return err -} - func resourceAwsDbParameterGroupRead(d *schema.ResourceData, meta interface{}) error { - p := meta.(*ResourceProvider) - rdsconn := p.rdsconn + rdsconn := meta.(*AWSClient).rdsconn describeOpts := rds.DescribeDBParameterGroups{ DBParameterGroupName: d.Id(), @@ -187,11 +120,65 @@ func resourceAwsDbParameterGroupRead(d *schema.ResourceData, meta interface{}) e return nil } -func resourceDbParameterGroupDeleteRefreshFunc( +func resourceAwsDbParameterGroupUpdate(d *schema.ResourceData, meta interface{}) error { + rdsconn := meta.(*AWSClient).rdsconn + + d.Partial(true) + + if d.HasChange("parameter") { + o, n := d.GetChange("parameter") + if o == nil { + o = new(schema.Set) + } + if n == nil { + n = new(schema.Set) + } + + os := o.(*schema.Set) + ns := n.(*schema.Set) + + // Expand the "parameter" set to goamz compat []rds.Parameter + parameters, err := expandParameters(ns.Difference(os).List()) + if err != nil { + return err + } + + if len(parameters) > 0 { + modifyOpts := rds.ModifyDBParameterGroup{ + DBParameterGroupName: d.Get("name").(string), + Parameters: parameters, + } + + log.Printf("[DEBUG] Modify DB Parameter Group: %#v", modifyOpts) + _, err = rdsconn.ModifyDBParameterGroup(&modifyOpts) + if err != nil { + return fmt.Errorf("Error modifying DB Parameter Group: %s", err) + } + } + d.SetPartial("parameter") + } + + d.Partial(false) + + return resourceAwsDbParameterGroupRead(d, meta) +} + +func resourceAwsDbParameterGroupDelete(d *schema.ResourceData, meta interface{}) error { + stateConf := &resource.StateChangeConf{ + Pending: []string{"pending"}, + Target: "destroyed", + Refresh: resourceAwsDbParameterGroupDeleteRefreshFunc(d, meta), + Timeout: 3 * time.Minute, + MinTimeout: 1 * time.Second, + } + _, err := stateConf.WaitForState() + return err +} + +func resourceAwsDbParameterGroupDeleteRefreshFunc( d *schema.ResourceData, meta interface{}) resource.StateRefreshFunc { - p := meta.(*ResourceProvider) - rdsconn := p.rdsconn + rdsconn := meta.(*AWSClient).rdsconn return func() (interface{}, string, error) { @@ -213,3 +200,12 @@ func resourceDbParameterGroupDeleteRefreshFunc( return d, "destroyed", nil } } + +func resourceAwsDbParameterHash(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + buf.WriteString(fmt.Sprintf("%s-", m["name"].(string))) + buf.WriteString(fmt.Sprintf("%s-", m["value"].(string))) + + return hashcode.String(buf.String()) +} diff --git a/builtin/providers/aws/resource_aws_db_parameter_group_test.go b/builtin/providers/aws/resource_aws_db_parameter_group_test.go index 93f61cd68..bf730e156 100644 --- a/builtin/providers/aws/resource_aws_db_parameter_group_test.go +++ b/builtin/providers/aws/resource_aws_db_parameter_group_test.go @@ -105,7 +105,7 @@ func TestAccAWSDBParameterGroupOnly(t *testing.T) { } func testAccCheckAWSDBParameterGroupDestroy(s *terraform.State) error { - conn := testAccProvider.rdsconn + conn := testAccProvider.Meta().(*AWSClient).rdsconn for _, rs := range s.RootModule().Resources { if rs.Type != "aws_db_parameter_group" { @@ -168,7 +168,7 @@ func testAccCheckAWSDBParameterGroupExists(n string, v *rds.DBParameterGroup) re return fmt.Errorf("No DB Parameter Group ID is set") } - conn := testAccProvider.rdsconn + conn := testAccProvider.Meta().(*AWSClient).rdsconn opts := rds.DescribeDBParameterGroups{ DBParameterGroupName: rs.Primary.ID, diff --git a/builtin/providers/aws/resource_aws_db_security_group.go b/builtin/providers/aws/resource_aws_db_security_group.go index a4cb1c308..56a74cba5 100644 --- a/builtin/providers/aws/resource_aws_db_security_group.go +++ b/builtin/providers/aws/resource_aws_db_security_group.go @@ -1,59 +1,102 @@ package aws +/* import ( "fmt" "log" "time" "github.com/hashicorp/terraform/flatmap" - "github.com/hashicorp/terraform/helper/config" - "github.com/hashicorp/terraform/helper/diff" "github.com/hashicorp/terraform/helper/multierror" "github.com/hashicorp/terraform/helper/resource" - "github.com/hashicorp/terraform/terraform" + "github.com/hashicorp/terraform/helper/schema" "github.com/mitchellh/goamz/rds" ) -func resource_aws_db_security_group_create( - s *terraform.InstanceState, - d *terraform.InstanceDiff, - meta interface{}) (*terraform.InstanceState, error) { - p := meta.(*ResourceProvider) - conn := p.rdsconn +func resourceAwsDbSecurityGroup() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsDbSecurityGroupCreate, + Read: resourceAwsDbSecurityGroupRead, + Delete: resourceAwsDbSecurityGroupDelete, - // Merge the diff into the state so that we have all the attributes - // properly. - rs := s.MergeDiff(d) + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "description": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "ingress": &schema.Schema{ + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cidr": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "security_group_name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "security_group_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + + "security_group_owner_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func resourceAwsDbSecurityGroupCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).rdsconn var err error var errs []error opts := rds.CreateDBSecurityGroup{ - DBSecurityGroupName: rs.Attributes["name"], - DBSecurityGroupDescription: rs.Attributes["description"], + DBSecurityGroupName: d.Get("name").(string), + DBSecurityGroupDescription: d.Get("description").(string), } log.Printf("[DEBUG] DB Security Group create configuration: %#v", opts) _, err = conn.CreateDBSecurityGroup(&opts) if err != nil { - return nil, fmt.Errorf("Error creating DB Security Group: %s", err) + return fmt.Errorf("Error creating DB Security Group: %s", err) } - rs.ID = rs.Attributes["name"] + d.SetId(d.Get("name").(string)) - log.Printf("[INFO] DB Security Group ID: %s", rs.ID) + log.Printf("[INFO] DB Security Group ID: %s", d.Id()) - v, err := resource_aws_db_security_group_retrieve(rs.ID, conn) + sg, err := resourceAwsDbSecurityGroupRetrieve(d, meta) if err != nil { - return rs, err + return err } - if _, ok := rs.Attributes["ingress.#"]; ok { - ingresses := flatmap.Expand( - rs.Attributes, "ingress").([]interface{}) - - for _, ing := range ingresses { - err = authorize_ingress_rule(ing, v.Name, conn) + rules := d.Get("ingress.#").(int) + if rules > 0 { + for i := 0; i < ssh_keys; i++ { + key := fmt.Sprintf("ingress.%d", i) + err = resourceAwsDbSecurityGroupAuthorizeRule(d.Get(key), sg.Name, conn) if err != nil { errs = append(errs, err) @@ -61,7 +104,7 @@ func resource_aws_db_security_group_create( } if len(errs) > 0 { - return rs, &multierror.Error{Errors: errs} + return &multierror.Error{Errors: errs} } } @@ -71,92 +114,29 @@ func resource_aws_db_security_group_create( stateConf := &resource.StateChangeConf{ Pending: []string{"authorizing"}, Target: "authorized", - Refresh: DBSecurityGroupStateRefreshFunc(rs.ID, conn), + Refresh: DBSecurityGroupStateRefreshFunc(d.Id(), conn), Timeout: 10 * time.Minute, } // Wait, catching any errors _, err = stateConf.WaitForState() if err != nil { - return rs, err - } - - return resource_aws_db_security_group_update_state(rs, v) -} - -func resource_aws_db_security_group_update( - s *terraform.InstanceState, - d *terraform.InstanceDiff, - meta interface{}) (*terraform.InstanceState, error) { - panic("Cannot update DB security group") -} - -func resource_aws_db_security_group_destroy( - s *terraform.InstanceState, - meta interface{}) error { - p := meta.(*ResourceProvider) - conn := p.rdsconn - - log.Printf("[DEBUG] DB Security Group destroy: %v", s.ID) - - opts := rds.DeleteDBSecurityGroup{DBSecurityGroupName: s.ID} - - log.Printf("[DEBUG] DB Security Group destroy configuration: %v", opts) - _, err := conn.DeleteDBSecurityGroup(&opts) - - if err != nil { - newerr, ok := err.(*rds.Error) - if ok && newerr.Code == "InvalidDBSecurityGroup.NotFound" { - return nil - } return err } - return nil + return resourceAwsDbSecurityGroupRead(d, meta) } -func resource_aws_db_security_group_refresh( - s *terraform.InstanceState, - meta interface{}) (*terraform.InstanceState, error) { - p := meta.(*ResourceProvider) - conn := p.rdsconn - - v, err := resource_aws_db_security_group_retrieve(s.ID, conn) +func resourceAwsDbSecurityGroupRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).rdsconn + sg, err := resourceAwsDbSecurityGroupRetrieve(d, meta) if err != nil { - return s, err + return err } - return resource_aws_db_security_group_update_state(s, v) -} - -func resource_aws_db_security_group_diff( - s *terraform.InstanceState, - c *terraform.ResourceConfig, - meta interface{}) (*terraform.InstanceDiff, error) { - - b := &diff.ResourceBuilder{ - Attrs: map[string]diff.AttrType{ - "name": diff.AttrTypeCreate, - "description": diff.AttrTypeCreate, - "ingress": diff.AttrTypeCreate, - }, - - ComputedAttrs: []string{ - "ingress_cidr", - "ingress_security_groups", - }, - } - - return b.Diff(s, c) -} - -func resource_aws_db_security_group_update_state( - s *terraform.InstanceState, - v *rds.DBSecurityGroup) (*terraform.InstanceState, error) { - - s.Attributes["name"] = v.Name - s.Attributes["description"] = v.Description + d.Set("name", sg.Name) + d.Set("description", sg.Description) // Flatten our group values toFlatten := make(map[string]interface{}) @@ -173,12 +153,35 @@ func resource_aws_db_security_group_update_state( s.Attributes[k] = v } - return s, nil + return nil } -func resource_aws_db_security_group_retrieve(id string, conn *rds.Rds) (*rds.DBSecurityGroup, error) { +func resourceAwsDbSecurityGroupDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).rdsconn + + log.Printf("[DEBUG] DB Security Group destroy: %v", d.Id()) + + opts := rds.DeleteDBSecurityGroup{DBSecurityGroupName: d.Id()} + + log.Printf("[DEBUG] DB Security Group destroy configuration: %v", opts) + _, err := conn.DeleteDBSecurityGroup(&opts) + + if err != nil { + newerr, ok := err.(*rds.Error) + if ok && newerr.Code == "InvalidDBSecurityGroup.NotFound" { + return nil + } + return err + } + + return nil +} + +func resourceAwsDbSecurityGroupRetrieve(d *schema.ResourceData, meta interface{}) (*rds.DBSecurityGroup, error) { + conn := meta.(*AWSClient).rdsconn + opts := rds.DescribeDBSecurityGroups{ - DBSecurityGroupName: id, + DBSecurityGroupName: d.Id(), } log.Printf("[DEBUG] DB Security Group describe configuration: %#v", opts) @@ -190,7 +193,7 @@ func resource_aws_db_security_group_retrieve(id string, conn *rds.Rds) (*rds.DBS } if len(resp.DBSecurityGroups) != 1 || - resp.DBSecurityGroups[0].Name != id { + resp.DBSecurityGroups[0].Name != d.Id() { if err != nil { return nil, fmt.Errorf("Unable to find DB Security Group: %#v", resp.DBSecurityGroups) } @@ -202,20 +205,15 @@ func resource_aws_db_security_group_retrieve(id string, conn *rds.Rds) (*rds.DBS } // Authorizes the ingress rule on the db security group -func authorize_ingress_rule(ingress interface{}, dbSecurityGroupName string, conn *rds.Rds) error { +func resourceAwsDbSecurityGroupAuthorizeRule(ingress interface{}, dbSecurityGroupName string, conn *rds.Rds) error { ing := ingress.(map[string]interface{}) opts := rds.AuthorizeDBSecurityGroupIngress{ DBSecurityGroupName: dbSecurityGroupName, } - if attr, ok := ing["cidr"].(string); ok && attr != "" { - opts.Cidr = attr - } - - if attr, ok := ing["security_group_name"].(string); ok && attr != "" { - opts.EC2SecurityGroupName = attr - } + opts.Cidr = ing["cidr"].(string) + opts.EC2SecurityGroupName = ing["security_group_name"].(string) if attr, ok := ing["security_group_id"].(string); ok && attr != "" { opts.EC2SecurityGroupId = attr @@ -236,23 +234,7 @@ func authorize_ingress_rule(ingress interface{}, dbSecurityGroupName string, con return nil } -func resource_aws_db_security_group_validation() *config.Validator { - return &config.Validator{ - Required: []string{ - "name", - "description", - }, - Optional: []string{ - "ingress.*", - "ingress.*.cidr", - "ingress.*.security_group_name", - "ingress.*.security_group_id", - "ingress.*.security_group_owner_id", - }, - } -} - -func DBSecurityGroupStateRefreshFunc(id string, conn *rds.Rds) resource.StateRefreshFunc { +func resourceAwsDbSecurityGroupStateRefreshFunc(id string, conn *rds.Rds) resource.StateRefreshFunc { return func() (interface{}, string, error) { v, err := resource_aws_db_security_group_retrieve(id, conn) @@ -273,3 +255,4 @@ func DBSecurityGroupStateRefreshFunc(id string, conn *rds.Rds) resource.StateRef return v, "authorized", nil } } +*/ diff --git a/builtin/providers/aws/resource_aws_db_security_group_test.go b/builtin/providers/aws/resource_aws_db_security_group_test.go index f81ac2537..d5ada2a2d 100644 --- a/builtin/providers/aws/resource_aws_db_security_group_test.go +++ b/builtin/providers/aws/resource_aws_db_security_group_test.go @@ -1,5 +1,6 @@ package aws +/* import ( "fmt" "testing" @@ -37,7 +38,7 @@ func TestAccAWSDBSecurityGroup(t *testing.T) { } func testAccCheckAWSDBSecurityGroupDestroy(s *terraform.State) error { - conn := testAccProvider.rdsconn + conn := testAccProvider.Meta().(*AWSClient).rdsconn for _, rs := range s.RootModule().Resources { if rs.Type != "aws_db_security_group" { @@ -107,7 +108,7 @@ func testAccCheckAWSDBSecurityGroupExists(n string, v *rds.DBSecurityGroup) reso return fmt.Errorf("No DB Security Group ID is set") } - conn := testAccProvider.rdsconn + conn := testAccProvider.Meta().(*AWSClient).rdsconn opts := rds.DescribeDBSecurityGroups{ DBSecurityGroupName: rs.Primary.ID, @@ -140,3 +141,4 @@ resource "aws_db_security_group" "bar" { } } ` +*/ diff --git a/builtin/providers/aws/resource_aws_db_subnet_group.go b/builtin/providers/aws/resource_aws_db_subnet_group.go index b67f5eddc..b9aa1de17 100644 --- a/builtin/providers/aws/resource_aws_db_subnet_group.go +++ b/builtin/providers/aws/resource_aws_db_subnet_group.go @@ -15,7 +15,6 @@ func resourceAwsDbSubnetGroup() *schema.Resource { return &schema.Resource{ Create: resourceAwsDbSubnetGroupCreate, Read: resourceAwsDbSubnetGroupRead, - Update: nil, Delete: resourceAwsDbSubnetGroupDelete, Schema: map[string]*schema.Schema{ @@ -45,8 +44,7 @@ func resourceAwsDbSubnetGroup() *schema.Resource { } func resourceAwsDbSubnetGroupCreate(d *schema.ResourceData, meta interface{}) error { - p := meta.(*ResourceProvider) - rdsconn := p.rdsconn + rdsconn := meta.(*AWSClient).rdsconn subnetIdsSet := d.Get("subnet_ids").(*schema.Set) subnetIds := make([]string, subnetIdsSet.Len()) @@ -71,21 +69,8 @@ func resourceAwsDbSubnetGroupCreate(d *schema.ResourceData, meta interface{}) er return resourceAwsDbSubnetGroupRead(d, meta) } -func resourceAwsDbSubnetGroupDelete(d *schema.ResourceData, meta interface{}) error { - stateConf := &resource.StateChangeConf{ - Pending: []string{"pending"}, - Target: "destroyed", - Refresh: resourceDbSubnetGroupDeleteRefreshFunc(d, meta), - Timeout: 3 * time.Minute, - MinTimeout: 1 * time.Second, - } - _, err := stateConf.WaitForState() - return err -} - func resourceAwsDbSubnetGroupRead(d *schema.ResourceData, meta interface{}) error { - p := meta.(*ResourceProvider) - rdsconn := p.rdsconn + rdsconn := meta.(*AWSClient).rdsconn describeOpts := rds.DescribeDBSubnetGroups{ DBSubnetGroupName: d.Id(), @@ -107,11 +92,22 @@ func resourceAwsDbSubnetGroupRead(d *schema.ResourceData, meta interface{}) erro return nil } -func resourceDbSubnetGroupDeleteRefreshFunc( +func resourceAwsDbSubnetGroupDelete(d *schema.ResourceData, meta interface{}) error { + stateConf := &resource.StateChangeConf{ + Pending: []string{"pending"}, + Target: "destroyed", + Refresh: resourceAwsDbSubnetGroupDeleteRefreshFunc(d, meta), + Timeout: 3 * time.Minute, + MinTimeout: 1 * time.Second, + } + _, err := stateConf.WaitForState() + return err +} + +func resourceAwsDbSubnetGroupDeleteRefreshFunc( d *schema.ResourceData, meta interface{}) resource.StateRefreshFunc { - p := meta.(*ResourceProvider) - rdsconn := p.rdsconn + rdsconn := meta.(*AWSClient).rdsconn return func() (interface{}, string, error) { diff --git a/builtin/providers/aws/resource_aws_db_subnet_group_test.go b/builtin/providers/aws/resource_aws_db_subnet_group_test.go index 3f7d8a632..184df9c9a 100644 --- a/builtin/providers/aws/resource_aws_db_subnet_group_test.go +++ b/builtin/providers/aws/resource_aws_db_subnet_group_test.go @@ -34,7 +34,7 @@ func TestAccAWSDbSubnetGroup(t *testing.T) { } func testAccCheckDbSubnetGroupDestroy(s *terraform.State) error { - conn := testAccProvider.rdsconn + conn := testAccProvider.Meta().(*AWSClient).rdsconn for _, rs := range s.RootModule().Resources { if rs.Type != "aws_db_subnet_group" { @@ -75,7 +75,7 @@ func testAccCheckDbSubnetGroupExists(n string, v *rds.DBSubnetGroup) resource.Te return fmt.Errorf("No ID is set") } - conn := testAccProvider.rdsconn + conn := testAccProvider.Meta().(*AWSClient).rdsconn resp, err := conn.DescribeDBSubnetGroups(&rds.DescribeDBSubnetGroups{rs.Primary.ID}) if err != nil { return err diff --git a/builtin/providers/aws/resource_aws_eip.go b/builtin/providers/aws/resource_aws_eip.go index 5a70ebacb..8f86941e0 100644 --- a/builtin/providers/aws/resource_aws_eip.go +++ b/builtin/providers/aws/resource_aws_eip.go @@ -8,7 +8,6 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" - //"github.com/hashicorp/terraform/terraform" "github.com/mitchellh/goamz/ec2" ) @@ -60,8 +59,7 @@ func resourceAwsEip() *schema.Resource { } func resourceAwsEipCreate(d *schema.ResourceData, meta interface{}) error { - p := meta.(*ResourceProvider) - ec2conn := p.ec2conn + ec2conn := meta.(*AWSClient).ec2conn // By default, we're not in a VPC domainOpt := "" @@ -97,9 +95,55 @@ func resourceAwsEipCreate(d *schema.ResourceData, meta interface{}) error { return resourceAwsEipUpdate(d, meta) } +func resourceAwsEipRead(d *schema.ResourceData, meta interface{}) error { + ec2conn := meta.(*AWSClient).ec2conn + + domain := resourceAwsEipDomain(d) + id := d.Id() + + assocIds := []string{} + publicIps := []string{} + if domain == "vpc" { + assocIds = []string{id} + } else { + publicIps = []string{id} + } + + log.Printf( + "[DEBUG] EIP describe configuration: %#v, %#v (domain: %s)", + assocIds, publicIps, domain) + + describeAddresses, err := ec2conn.Addresses(publicIps, assocIds, nil) + if err != nil { + if ec2err, ok := err.(*ec2.Error); ok && ec2err.Code == "InvalidAllocationID.NotFound" { + d.SetId("") + return nil + } + + return fmt.Errorf("Error retrieving EIP: %s", err) + } + + // Verify AWS returned our EIP + if len(describeAddresses.Addresses) != 1 || + describeAddresses.Addresses[0].AllocationId != id || + describeAddresses.Addresses[0].PublicIp != id { + if err != nil { + return fmt.Errorf("Unable to find EIP: %#v", describeAddresses.Addresses) + } + } + + address := describeAddresses.Addresses[0] + + d.Set("association_id", address.AssociationId) + d.Set("instance", address.InstanceId) + d.Set("public_ip", address.PublicIp) + d.Set("private_ip", address.PrivateIpAddress) + + return nil +} + func resourceAwsEipUpdate(d *schema.ResourceData, meta interface{}) error { - p := meta.(*ResourceProvider) - ec2conn := p.ec2conn + ec2conn := meta.(*AWSClient).ec2conn domain := resourceAwsEipDomain(d) @@ -132,8 +176,7 @@ func resourceAwsEipUpdate(d *schema.ResourceData, meta interface{}) error { } func resourceAwsEipDelete(d *schema.ResourceData, meta interface{}) error { - p := meta.(*ResourceProvider) - ec2conn := p.ec2conn + ec2conn := meta.(*AWSClient).ec2conn if err := resourceAwsEipRead(d, meta); err != nil { return err @@ -183,54 +226,6 @@ func resourceAwsEipDelete(d *schema.ResourceData, meta interface{}) error { }) } -func resourceAwsEipRead(d *schema.ResourceData, meta interface{}) error { - p := meta.(*ResourceProvider) - ec2conn := p.ec2conn - - domain := resourceAwsEipDomain(d) - id := d.Id() - - assocIds := []string{} - publicIps := []string{} - if domain == "vpc" { - assocIds = []string{id} - } else { - publicIps = []string{id} - } - - log.Printf( - "[DEBUG] EIP describe configuration: %#v, %#v (domain: %s)", - assocIds, publicIps, domain) - - describeAddresses, err := ec2conn.Addresses(publicIps, assocIds, nil) - if err != nil { - if ec2err, ok := err.(*ec2.Error); ok && ec2err.Code == "InvalidAllocationID.NotFound" { - d.SetId("") - return nil - } - - return fmt.Errorf("Error retrieving EIP: %s", err) - } - - // Verify AWS returned our EIP - if len(describeAddresses.Addresses) != 1 || - describeAddresses.Addresses[0].AllocationId != id || - describeAddresses.Addresses[0].PublicIp != id { - if err != nil { - return fmt.Errorf("Unable to find EIP: %#v", describeAddresses.Addresses) - } - } - - address := describeAddresses.Addresses[0] - - d.Set("association_id", address.AssociationId) - d.Set("instance", address.InstanceId) - d.Set("public_ip", address.PublicIp) - d.Set("private_ip", address.PrivateIpAddress) - - return nil -} - func resourceAwsEipDomain(d *schema.ResourceData) string { if v, ok := d.GetOk("domain"); ok { return v.(string) diff --git a/builtin/providers/aws/resource_aws_eip_test.go b/builtin/providers/aws/resource_aws_eip_test.go index b0272bd52..0a72af4f4 100644 --- a/builtin/providers/aws/resource_aws_eip_test.go +++ b/builtin/providers/aws/resource_aws_eip_test.go @@ -57,7 +57,7 @@ func TestAccAWSEIP_instance(t *testing.T) { } func testAccCheckAWSEIPDestroy(s *terraform.State) error { - conn := testAccProvider.ec2conn + conn := testAccProvider.Meta().(*AWSClient).ec2conn for _, rs := range s.RootModule().Resources { if rs.Type != "aws_eip" { @@ -112,7 +112,7 @@ func testAccCheckAWSEIPExists(n string, res *ec2.Address) resource.TestCheckFunc return fmt.Errorf("No EIP ID is set") } - conn := testAccProvider.ec2conn + conn := testAccProvider.Meta().(*AWSClient).ec2conn if strings.Contains(rs.Primary.ID, "eipalloc") { describe, err := conn.Addresses([]string{}, []string{rs.Primary.ID}, nil) diff --git a/builtin/providers/aws/resource_aws_elb.go b/builtin/providers/aws/resource_aws_elb.go index 353a675d6..c08b3d6da 100644 --- a/builtin/providers/aws/resource_aws_elb.go +++ b/builtin/providers/aws/resource_aws_elb.go @@ -150,36 +150,8 @@ func resourceAwsElb() *schema.Resource { } } -func resourceAwsElbHealthCheckHash(v interface{}) int { - var buf bytes.Buffer - m := v.(map[string]interface{}) - buf.WriteString(fmt.Sprintf("%d-", m["healthy_threshold"].(int))) - buf.WriteString(fmt.Sprintf("%d-", m["unhealthy_threshold"].(int))) - buf.WriteString(fmt.Sprintf("%s-", m["target"].(string))) - buf.WriteString(fmt.Sprintf("%d-", m["interval"].(int))) - buf.WriteString(fmt.Sprintf("%d-", m["timeout"].(int))) - - return hashcode.String(buf.String()) -} - -func resourceAwsElbListenerHash(v interface{}) int { - var buf bytes.Buffer - m := v.(map[string]interface{}) - buf.WriteString(fmt.Sprintf("%d-", m["instance_port"].(int))) - buf.WriteString(fmt.Sprintf("%s-", m["instance_protocol"].(string))) - buf.WriteString(fmt.Sprintf("%d-", m["lb_port"].(int))) - buf.WriteString(fmt.Sprintf("%s-", m["lb_protocol"].(string))) - - if v, ok := m["ssl_certificate_id"]; ok { - buf.WriteString(fmt.Sprintf("%s-", v.(string))) - } - - return hashcode.String(buf.String()) -} - func resourceAwsElbCreate(d *schema.ResourceData, meta interface{}) error { - p := meta.(*ResourceProvider) - elbconn := p.elbconn + elbconn := meta.(*AWSClient).elbconn // Expand the "listener" set to goamz compat []elb.Listener listeners, err := expandListeners(d.Get("listener").(*schema.Set).List()) @@ -250,9 +222,49 @@ func resourceAwsElbCreate(d *schema.ResourceData, meta interface{}) error { return resourceAwsElbUpdate(d, meta) } +func resourceAwsElbRead(d *schema.ResourceData, meta interface{}) error { + elbconn := meta.(*AWSClient).elbconn + + // Retrieve the ELB properties for updating the state + describeElbOpts := &elb.DescribeLoadBalancer{ + Names: []string{d.Id()}, + } + + describeResp, err := elbconn.DescribeLoadBalancers(describeElbOpts) + if err != nil { + if ec2err, ok := err.(*elb.Error); ok && ec2err.Code == "LoadBalancerNotFound" { + // The ELB is gone now, so just remove it from the state + d.SetId("") + return nil + } + + return fmt.Errorf("Error retrieving ELB: %s", err) + } + if len(describeResp.LoadBalancers) != 1 { + return fmt.Errorf("Unable to find ELB: %#v", describeResp.LoadBalancers) + } + + lb := describeResp.LoadBalancers[0] + + d.Set("name", lb.LoadBalancerName) + d.Set("dns_name", lb.DNSName) + d.Set("internal", lb.Scheme == "internal") + d.Set("instances", flattenInstances(lb.Instances)) + d.Set("listener", flattenListeners(lb.Listeners)) + d.Set("security_groups", lb.SecurityGroups) + d.Set("subnets", lb.Subnets) + + // There's only one health check, so save that to state as we + // currently can + if lb.HealthCheck.Target != "" { + d.Set("health_check", flattenHealthCheck(lb.HealthCheck)) + } + + return nil +} + func resourceAwsElbUpdate(d *schema.ResourceData, meta interface{}) error { - p := meta.(*ResourceProvider) - elbconn := p.elbconn + elbconn := meta.(*AWSClient).elbconn d.Partial(true) @@ -297,8 +309,7 @@ func resourceAwsElbUpdate(d *schema.ResourceData, meta interface{}) error { } func resourceAwsElbDelete(d *schema.ResourceData, meta interface{}) error { - p := meta.(*ResourceProvider) - elbconn := p.elbconn + elbconn := meta.(*AWSClient).elbconn log.Printf("[INFO] Deleting ELB: %s", d.Id()) @@ -313,44 +324,29 @@ func resourceAwsElbDelete(d *schema.ResourceData, meta interface{}) error { return nil } -func resourceAwsElbRead(d *schema.ResourceData, meta interface{}) error { - p := meta.(*ResourceProvider) - elbconn := p.elbconn +func resourceAwsElbHealthCheckHash(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + buf.WriteString(fmt.Sprintf("%d-", m["healthy_threshold"].(int))) + buf.WriteString(fmt.Sprintf("%d-", m["unhealthy_threshold"].(int))) + buf.WriteString(fmt.Sprintf("%s-", m["target"].(string))) + buf.WriteString(fmt.Sprintf("%d-", m["interval"].(int))) + buf.WriteString(fmt.Sprintf("%d-", m["timeout"].(int))) - // Retrieve the ELB properties for updating the state - describeElbOpts := &elb.DescribeLoadBalancer{ - Names: []string{d.Id()}, - } - - describeResp, err := elbconn.DescribeLoadBalancers(describeElbOpts) - if err != nil { - if ec2err, ok := err.(*elb.Error); ok && ec2err.Code == "LoadBalancerNotFound" { - // The ELB is gone now, so just remove it from the state - d.SetId("") - return nil - } - - return fmt.Errorf("Error retrieving ELB: %s", err) - } - if len(describeResp.LoadBalancers) != 1 { - return fmt.Errorf("Unable to find ELB: %#v", describeResp.LoadBalancers) - } - - lb := describeResp.LoadBalancers[0] - - d.Set("name", lb.LoadBalancerName) - d.Set("dns_name", lb.DNSName) - d.Set("internal", lb.Scheme == "internal") - d.Set("instances", flattenInstances(lb.Instances)) - d.Set("listener", flattenListeners(lb.Listeners)) - d.Set("security_groups", lb.SecurityGroups) - d.Set("subnets", lb.Subnets) - - // There's only one health check, so save that to state as we - // currently can - if lb.HealthCheck.Target != "" { - d.Set("health_check", flattenHealthCheck(lb.HealthCheck)) - } - - return nil + return hashcode.String(buf.String()) +} + +func resourceAwsElbListenerHash(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + buf.WriteString(fmt.Sprintf("%d-", m["instance_port"].(int))) + buf.WriteString(fmt.Sprintf("%s-", m["instance_protocol"].(string))) + buf.WriteString(fmt.Sprintf("%d-", m["lb_port"].(int))) + buf.WriteString(fmt.Sprintf("%s-", m["lb_protocol"].(string))) + + if v, ok := m["ssl_certificate_id"]; ok { + buf.WriteString(fmt.Sprintf("%s-", v.(string))) + } + + return hashcode.String(buf.String()) } diff --git a/builtin/providers/aws/resource_aws_elb_test.go b/builtin/providers/aws/resource_aws_elb_test.go index 73a1503dd..05078b673 100644 --- a/builtin/providers/aws/resource_aws_elb_test.go +++ b/builtin/providers/aws/resource_aws_elb_test.go @@ -114,7 +114,7 @@ func TestAccAWSELB_HealthCheck(t *testing.T) { }) } func testAccCheckAWSELBDestroy(s *terraform.State) error { - conn := testAccProvider.elbconn + conn := testAccProvider.Meta().(*AWSClient).elbconn for _, rs := range s.RootModule().Resources { if rs.Type != "aws_elb" { @@ -222,7 +222,7 @@ func testAccCheckAWSELBExists(n string, res *elb.LoadBalancer) resource.TestChec return fmt.Errorf("No ELB ID is set") } - conn := testAccProvider.elbconn + conn := testAccProvider.Meta().(*AWSClient).elbconn describe, err := conn.DescribeLoadBalancers(&elb.DescribeLoadBalancer{ Names: []string{rs.Primary.ID}, diff --git a/builtin/providers/aws/resource_aws_instance.go b/builtin/providers/aws/resource_aws_instance.go index 87f31d695..b5a6be105 100644 --- a/builtin/providers/aws/resource_aws_instance.go +++ b/builtin/providers/aws/resource_aws_instance.go @@ -177,8 +177,7 @@ func resourceAwsInstance() *schema.Resource { } func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error { - p := meta.(*ResourceProvider) - ec2conn := p.ec2conn + ec2conn := meta.(*AWSClient).ec2conn // Figure out user data userData := "" @@ -288,74 +287,8 @@ func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error { return resourceAwsInstanceUpdate(d, meta) } -func resourceAwsInstanceUpdate(d *schema.ResourceData, meta interface{}) error { - p := meta.(*ResourceProvider) - ec2conn := p.ec2conn - - modify := false - opts := new(ec2.ModifyInstance) - - if v, ok := d.GetOk("source_dest_check"); ok { - opts.SourceDestCheck = v.(bool) - opts.SetSourceDestCheck = true - modify = true - } - - if modify { - log.Printf("[INFO] Modifing instance %s: %#v", d.Id(), opts) - if _, err := ec2conn.ModifyInstance(d.Id(), opts); err != nil { - return err - } - - // TODO(mitchellh): wait for the attributes we modified to - // persist the change... - } - - if err := setTags(ec2conn, d); err != nil { - return err - } else { - d.SetPartial("tags") - } - - return nil -} - -func resourceAwsInstanceDelete(d *schema.ResourceData, meta interface{}) error { - p := meta.(*ResourceProvider) - ec2conn := p.ec2conn - - log.Printf("[INFO] Terminating instance: %s", d.Id()) - if _, err := ec2conn.TerminateInstances([]string{d.Id()}); err != nil { - return fmt.Errorf("Error terminating instance: %s", err) - } - - log.Printf( - "[DEBUG] Waiting for instance (%s) to become terminated", - d.Id()) - - stateConf := &resource.StateChangeConf{ - Pending: []string{"pending", "running", "shutting-down", "stopped", "stopping"}, - Target: "terminated", - Refresh: InstanceStateRefreshFunc(ec2conn, d.Id()), - Timeout: 10 * time.Minute, - Delay: 10 * time.Second, - MinTimeout: 3 * time.Second, - } - - _, err := stateConf.WaitForState() - if err != nil { - return fmt.Errorf( - "Error waiting for instance (%s) to terminate: %s", - d.Id(), err) - } - - d.SetId("") - return nil -} - func resourceAwsInstanceRead(d *schema.ResourceData, meta interface{}) error { - p := meta.(*ResourceProvider) - ec2conn := p.ec2conn + ec2conn := meta.(*AWSClient).ec2conn resp, err := ec2conn.Instances([]string{d.Id()}, ec2.NewFilter()) if err != nil { @@ -450,6 +383,69 @@ func resourceAwsInstanceRead(d *schema.ResourceData, meta interface{}) error { return nil } +func resourceAwsInstanceUpdate(d *schema.ResourceData, meta interface{}) error { + ec2conn := meta.(*AWSClient).ec2conn + + modify := false + opts := new(ec2.ModifyInstance) + + if v, ok := d.GetOk("source_dest_check"); ok { + opts.SourceDestCheck = v.(bool) + opts.SetSourceDestCheck = true + modify = true + } + + if modify { + log.Printf("[INFO] Modifing instance %s: %#v", d.Id(), opts) + if _, err := ec2conn.ModifyInstance(d.Id(), opts); err != nil { + return err + } + + // TODO(mitchellh): wait for the attributes we modified to + // persist the change... + } + + if err := setTags(ec2conn, d); err != nil { + return err + } else { + d.SetPartial("tags") + } + + return nil +} + +func resourceAwsInstanceDelete(d *schema.ResourceData, meta interface{}) error { + ec2conn := meta.(*AWSClient).ec2conn + + log.Printf("[INFO] Terminating instance: %s", d.Id()) + if _, err := ec2conn.TerminateInstances([]string{d.Id()}); err != nil { + return fmt.Errorf("Error terminating instance: %s", err) + } + + log.Printf( + "[DEBUG] Waiting for instance (%s) to become terminated", + d.Id()) + + stateConf := &resource.StateChangeConf{ + Pending: []string{"pending", "running", "shutting-down", "stopped", "stopping"}, + Target: "terminated", + Refresh: InstanceStateRefreshFunc(ec2conn, d.Id()), + Timeout: 10 * time.Minute, + Delay: 10 * time.Second, + MinTimeout: 3 * time.Second, + } + + _, err := stateConf.WaitForState() + if err != nil { + return fmt.Errorf( + "Error waiting for instance (%s) to terminate: %s", + d.Id(), err) + } + + d.SetId("") + return nil +} + // InstanceStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch // an EC2 instance. func InstanceStateRefreshFunc(conn *ec2.EC2, instanceID string) resource.StateRefreshFunc { diff --git a/builtin/providers/aws/resource_aws_instance_test.go b/builtin/providers/aws/resource_aws_instance_test.go index fc72ff452..7a735b7b2 100644 --- a/builtin/providers/aws/resource_aws_instance_test.go +++ b/builtin/providers/aws/resource_aws_instance_test.go @@ -189,7 +189,7 @@ func TestAccInstance_tags(t *testing.T) { } func testAccCheckInstanceDestroy(s *terraform.State) error { - conn := testAccProvider.ec2conn + conn := testAccProvider.Meta().(*AWSClient).ec2conn for _, rs := range s.RootModule().Resources { if rs.Type != "aws_instance" { @@ -231,7 +231,7 @@ func testAccCheckInstanceExists(n string, i *ec2.Instance) resource.TestCheckFun return fmt.Errorf("No ID is set") } - conn := testAccProvider.ec2conn + conn := testAccProvider.Meta().(*AWSClient).ec2conn resp, err := conn.Instances( []string{rs.Primary.ID}, ec2.NewFilter()) if err != nil { diff --git a/builtin/providers/aws/resource_aws_internet_gateway.go b/builtin/providers/aws/resource_aws_internet_gateway.go index fc23ebb06..0dac15781 100644 --- a/builtin/providers/aws/resource_aws_internet_gateway.go +++ b/builtin/providers/aws/resource_aws_internet_gateway.go @@ -5,84 +5,88 @@ import ( "log" "time" - "github.com/hashicorp/terraform/helper/diff" "github.com/hashicorp/terraform/helper/resource" - "github.com/hashicorp/terraform/terraform" + "github.com/hashicorp/terraform/helper/schema" "github.com/mitchellh/goamz/ec2" ) -func resource_aws_internet_gateway_create( - s *terraform.InstanceState, - d *terraform.InstanceDiff, - meta interface{}) (*terraform.InstanceState, error) { - p := meta.(*ResourceProvider) - ec2conn := p.ec2conn +func resourceAwsInternetGateway() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsInternetGatewayCreate, + Read: resourceAwsInternetGatewayRead, + Update: resourceAwsInternetGatewayUpdate, + Delete: resourceAwsInternetGatewayDelete, + + Schema: map[string]*schema.Schema{ + "vpc_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + } +} + +func resourceAwsInternetGatewayCreate(d *schema.ResourceData, meta interface{}) error { + ec2conn := meta.(*AWSClient).ec2conn // Create the gateway log.Printf("[DEBUG] Creating internet gateway") resp, err := ec2conn.CreateInternetGateway(nil) if err != nil { - return nil, fmt.Errorf("Error creating subnet: %s", err) + return fmt.Errorf("Error creating internet gateway: %s", err) } // Get the ID and store it ig := &resp.InternetGateway - s.ID = ig.InternetGatewayId - log.Printf("[INFO] InternetGateway ID: %s", s.ID) + d.SetId(ig.InternetGatewayId) + log.Printf("[INFO] InternetGateway ID: %s", d.Id()) - // Update our attributes and return - return resource_aws_internet_gateway_update(s, d, meta) + // Attach the new gateway to the correct vpc + return resourceAwsInternetGatewayAttach(d, meta) } -func resource_aws_internet_gateway_update( - s *terraform.InstanceState, - d *terraform.InstanceDiff, - meta interface{}) (*terraform.InstanceState, error) { - p := meta.(*ResourceProvider) - ec2conn := p.ec2conn +func resourceAwsInternetGatewayRead(d *schema.ResourceData, meta interface{}) error { + ec2conn := meta.(*AWSClient).ec2conn - // Merge the diff so we have the latest attributes - rs := s.MergeDiff(d) + igRaw, _, err := IGStateRefreshFunc(ec2conn, d.Id())() + if err != nil { + return err + } + if igRaw == nil { + // Seems we have lost our internet gateway + d.SetId("") + return nil + } - // A note on the states below: the AWS docs (as of July, 2014) say - // that the states would be: attached, attaching, detached, detaching, - // but when running, I noticed that the state is usually "available" when - // it is attached. + ig := igRaw.(*ec2.InternetGateway) + d.Set("vpc_id", ig.Attachments[0].VpcId) + return nil +} + +func resourceAwsInternetGatewayUpdate(d *schema.ResourceData, meta interface{}) error { // If we're already attached, detach it first - if err := resource_aws_internet_gateway_detach(ec2conn, s); err != nil { - return s, err - } - - // Set the VPC ID to empty since we're detached at this point - delete(rs.Attributes, "vpc_id") - - if attr, ok := d.Attributes["vpc_id"]; ok && attr.New != "" { - err := resource_aws_internet_gateway_attach(ec2conn, s, attr.New) - if err != nil { - return rs, err - } - - rs.Attributes["vpc_id"] = attr.New - } - - return resource_aws_internet_gateway_update_state(rs, nil) -} - -func resource_aws_internet_gateway_destroy( - s *terraform.InstanceState, - meta interface{}) error { - p := meta.(*ResourceProvider) - ec2conn := p.ec2conn - - // Detach if it is attached - if err := resource_aws_internet_gateway_detach(ec2conn, s); err != nil { + if err := resourceAwsInternetGatewayDetach(d, meta); err != nil { return err } - log.Printf("[INFO] Deleting Internet Gateway: %s", s.ID) + // Attach the gateway to the new vpc + return resourceAwsInternetGatewayAttach(d, meta) +} + +func resourceAwsInternetGatewayDelete(d *schema.ResourceData, meta interface{}) error { + ec2conn := meta.(*AWSClient).ec2conn + + // Detach if it is attached + if err := resourceAwsInternetGatewayDetach(d, meta); err != nil { + return err + } + + log.Printf("[INFO] Deleting Internet Gateway: %s", d.Id()) + return resource.Retry(5*time.Minute, func() error { - _, err := ec2conn.DeleteInternetGateway(s.ID) + _, err := ec2conn.DeleteInternetGateway(d.Id()) if err != nil { ec2err, ok := err.(*ec2.Error) if !ok { @@ -103,96 +107,67 @@ func resource_aws_internet_gateway_destroy( }) // Wait for the internet gateway to actually delete - log.Printf("[DEBUG] Waiting for internet gateway (%s) to delete", s.ID) + log.Printf("[DEBUG] Waiting for internet gateway (%s) to delete", d.Id()) stateConf := &resource.StateChangeConf{ Pending: []string{"available"}, Target: "", - Refresh: IGStateRefreshFunc(ec2conn, s.ID), + Refresh: IGStateRefreshFunc(ec2conn, d.Id()), Timeout: 10 * time.Minute, } if _, err := stateConf.WaitForState(); err != nil { return fmt.Errorf( "Error waiting for internet gateway (%s) to destroy: %s", - s.ID, err) + d.Id(), err) } return nil } -func resource_aws_internet_gateway_refresh( - s *terraform.InstanceState, - meta interface{}) (*terraform.InstanceState, error) { - p := meta.(*ResourceProvider) - ec2conn := p.ec2conn +func resourceAwsInternetGatewayAttach(d *schema.ResourceData, meta interface{}) error { + ec2conn := meta.(*AWSClient).ec2conn - igRaw, _, err := IGStateRefreshFunc(ec2conn, s.ID)() - if err != nil { - return s, err - } - if igRaw == nil { - return nil, nil - } - - ig := igRaw.(*ec2.InternetGateway) - return resource_aws_internet_gateway_update_state(s, ig) -} - -func resource_aws_internet_gateway_diff( - s *terraform.InstanceState, - c *terraform.ResourceConfig, - meta interface{}) (*terraform.InstanceDiff, error) { - b := &diff.ResourceBuilder{ - Attrs: map[string]diff.AttrType{ - "vpc_id": diff.AttrTypeUpdate, - }, - } - - return b.Diff(s, c) -} - -func resource_aws_internet_gateway_attach( - ec2conn *ec2.EC2, - s *terraform.InstanceState, - vpcId string) error { log.Printf( "[INFO] Attaching Internet Gateway '%s' to VPC '%s'", - s.ID, - vpcId) - _, err := ec2conn.AttachInternetGateway(s.ID, vpcId) + d.Id(), + d.Get("vpc_id").(string)) + + _, err := ec2conn.AttachInternetGateway(d.Id(), d.Get("vpc_id").(string)) if err != nil { return err } + // A note on the states below: the AWS docs (as of July, 2014) say + // that the states would be: attached, attaching, detached, detaching, + // but when running, I noticed that the state is usually "available" when + // it is attached. + // Wait for it to be fully attached before continuing - log.Printf("[DEBUG] Waiting for internet gateway (%s) to attach", s.ID) + log.Printf("[DEBUG] Waiting for internet gateway (%s) to attach", d.Id()) stateConf := &resource.StateChangeConf{ Pending: []string{"detached", "attaching"}, Target: "available", - Refresh: IGAttachStateRefreshFunc(ec2conn, s.ID, "available"), + Refresh: IGAttachStateRefreshFunc(ec2conn, d.Id(), "available"), Timeout: 1 * time.Minute, } if _, err := stateConf.WaitForState(); err != nil { return fmt.Errorf( "Error waiting for internet gateway (%s) to attach: %s", - s.ID, err) + d.Id(), err) } return nil } -func resource_aws_internet_gateway_detach( - ec2conn *ec2.EC2, - s *terraform.InstanceState) error { - if s.Attributes["vpc_id"] == "" { - return nil - } +func resourceAwsInternetGatewayDetach(d *schema.ResourceData, meta interface{}) error { + ec2conn := meta.(*AWSClient).ec2conn log.Printf( "[INFO] Detaching Internet Gateway '%s' from VPC '%s'", - s.ID, - s.Attributes["vpc_id"]) + d.Id(), + d.Get("vpc_id").(string)) + wait := true - _, err := ec2conn.DetachInternetGateway(s.ID, s.Attributes["vpc_id"]) + _, err := ec2conn.DetachInternetGateway(d.Id(), d.Get("vpc_id").(string)) if err != nil { ec2err, ok := err.(*ec2.Error) if ok { @@ -210,40 +185,32 @@ func resource_aws_internet_gateway_detach( } } - delete(s.Attributes, "vpc_id") - if !wait { return nil } // Wait for it to be fully detached before continuing - log.Printf("[DEBUG] Waiting for internet gateway (%s) to detach", s.ID) + log.Printf("[DEBUG] Waiting for internet gateway (%s) to detach", d.Id()) stateConf := &resource.StateChangeConf{ Pending: []string{"attached", "detaching", "available"}, Target: "detached", - Refresh: IGAttachStateRefreshFunc(ec2conn, s.ID, "detached"), + Refresh: IGAttachStateRefreshFunc(ec2conn, d.Id(), "detached"), Timeout: 1 * time.Minute, } if _, err := stateConf.WaitForState(); err != nil { return fmt.Errorf( "Error waiting for internet gateway (%s) to detach: %s", - s.ID, err) + d.Id(), err) } return nil } -func resource_aws_internet_gateway_update_state( - s *terraform.InstanceState, - ig *ec2.InternetGateway) (*terraform.InstanceState, error) { - return s, nil -} - // IGStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch // an internet gateway. -func IGStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc { +func IGStateRefreshFunc(ec2conn *ec2.EC2, id string) resource.StateRefreshFunc { return func() (interface{}, string, error) { - resp, err := conn.DescribeInternetGateways([]string{id}, ec2.NewFilter()) + resp, err := ec2conn.DescribeInternetGateways([]string{id}, ec2.NewFilter()) if err != nil { ec2err, ok := err.(*ec2.Error) if ok && ec2err.Code == "InvalidInternetGatewayID.NotFound" { diff --git a/builtin/providers/aws/resource_aws_internet_gateway_test.go b/builtin/providers/aws/resource_aws_internet_gateway_test.go index a5974cf25..47a57c6a4 100644 --- a/builtin/providers/aws/resource_aws_internet_gateway_test.go +++ b/builtin/providers/aws/resource_aws_internet_gateway_test.go @@ -55,7 +55,7 @@ func TestAccAWSInternetGateway(t *testing.T) { } func testAccCheckInternetGatewayDestroy(s *terraform.State) error { - conn := testAccProvider.ec2conn + conn := testAccProvider.Meta().(*AWSClient).ec2conn for _, rs := range s.RootModule().Resources { if rs.Type != "aws_internet_gateway" { @@ -97,7 +97,7 @@ func testAccCheckInternetGatewayExists(n string, ig *ec2.InternetGateway) resour return fmt.Errorf("No ID is set") } - conn := testAccProvider.ec2conn + conn := testAccProvider.Meta().(*AWSClient).ec2conn resp, err := conn.DescribeInternetGateways( []string{rs.Primary.ID}, ec2.NewFilter()) if err != nil { diff --git a/builtin/providers/aws/resource_aws_launch_configuration.go b/builtin/providers/aws/resource_aws_launch_configuration.go index 0cb80c395..fabc41a4d 100644 --- a/builtin/providers/aws/resource_aws_launch_configuration.go +++ b/builtin/providers/aws/resource_aws_launch_configuration.go @@ -80,8 +80,7 @@ func resourceAwsLaunchConfiguration() *schema.Resource { } func resourceAwsLaunchConfigurationCreate(d *schema.ResourceData, meta interface{}) error { - p := meta.(*ResourceProvider) - autoscalingconn := p.autoscalingconn + autoscalingconn := meta.(*AWSClient).autoscalingconn var createLaunchConfigurationOpts autoscaling.CreateLaunchConfiguration createLaunchConfigurationOpts.Name = d.Get("name").(string) @@ -112,28 +111,8 @@ func resourceAwsLaunchConfigurationCreate(d *schema.ResourceData, meta interface }) } -func resourceAwsLaunchConfigurationDelete(d *schema.ResourceData, meta interface{}) error { - p := meta.(*ResourceProvider) - autoscalingconn := p.autoscalingconn - - log.Printf("[DEBUG] Launch Configuration destroy: %v", d.Id()) - _, err := autoscalingconn.DeleteLaunchConfiguration( - &autoscaling.DeleteLaunchConfiguration{Name: d.Id()}) - if err != nil { - autoscalingerr, ok := err.(*autoscaling.Error) - if ok && autoscalingerr.Code == "InvalidConfiguration.NotFound" { - return nil - } - - return err - } - - return nil -} - func resourceAwsLaunchConfigurationRead(d *schema.ResourceData, meta interface{}) error { - p := meta.(*ResourceProvider) - autoscalingconn := p.autoscalingconn + autoscalingconn := meta.(*AWSClient).autoscalingconn describeOpts := autoscaling.DescribeLaunchConfigurations{ Names: []string{d.Id()}, @@ -167,3 +146,21 @@ func resourceAwsLaunchConfigurationRead(d *schema.ResourceData, meta interface{} return nil } + +func resourceAwsLaunchConfigurationDelete(d *schema.ResourceData, meta interface{}) error { + autoscalingconn := meta.(*AWSClient).autoscalingconn + + log.Printf("[DEBUG] Launch Configuration destroy: %v", d.Id()) + _, err := autoscalingconn.DeleteLaunchConfiguration( + &autoscaling.DeleteLaunchConfiguration{Name: d.Id()}) + if err != nil { + autoscalingerr, ok := err.(*autoscaling.Error) + if ok && autoscalingerr.Code == "InvalidConfiguration.NotFound" { + return nil + } + + return err + } + + return nil +} diff --git a/builtin/providers/aws/resource_aws_launch_configuration_test.go b/builtin/providers/aws/resource_aws_launch_configuration_test.go index 820cdf723..83a2c997b 100644 --- a/builtin/providers/aws/resource_aws_launch_configuration_test.go +++ b/builtin/providers/aws/resource_aws_launch_configuration_test.go @@ -35,7 +35,7 @@ func TestAccAWSLaunchConfiguration(t *testing.T) { } func testAccCheckAWSLaunchConfigurationDestroy(s *terraform.State) error { - conn := testAccProvider.autoscalingconn + conn := testAccProvider.Meta().(*AWSClient).autoscalingconn for _, rs := range s.RootModule().Resources { if rs.Type != "aws_launch_configuration" { @@ -96,7 +96,7 @@ func testAccCheckAWSLaunchConfigurationExists(n string, res *autoscaling.LaunchC return fmt.Errorf("No Launch Configuration ID is set") } - conn := testAccProvider.autoscalingconn + conn := testAccProvider.Meta().(*AWSClient).autoscalingconn describeOpts := autoscaling.DescribeLaunchConfigurations{ Names: []string{rs.Primary.ID}, diff --git a/builtin/providers/aws/resource_aws_route53_record.go b/builtin/providers/aws/resource_aws_route53_record.go index 31ec0d91f..167405e9d 100644 --- a/builtin/providers/aws/resource_aws_route53_record.go +++ b/builtin/providers/aws/resource_aws_route53_record.go @@ -3,45 +3,62 @@ package aws import ( "fmt" "log" - "strconv" "strings" "time" - "github.com/hashicorp/terraform/flatmap" - "github.com/hashicorp/terraform/helper/config" - "github.com/hashicorp/terraform/helper/diff" "github.com/hashicorp/terraform/helper/resource" - "github.com/hashicorp/terraform/terraform" + "github.com/hashicorp/terraform/helper/schema" "github.com/mitchellh/goamz/route53" ) -func resource_aws_r53_record_validation() *config.Validator { - return &config.Validator{ - Required: []string{ - "zone_id", - "name", - "type", - "ttl", - "records.*", +func resourceAwsRoute53Record() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsRoute53RecordCreate, + Read: resourceAwsRoute53RecordRead, + Delete: resourceAwsRoute53RecordDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "type": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "zone_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "ttl": &schema.Schema{ + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + + "records": &schema.Schema{ + Type: schema.TypeList, + Elem: &schema.Schema{Type: schema.TypeString}, + Required: true, + ForceNew: true, + }, }, } } -func resource_aws_r53_record_create( - s *terraform.InstanceState, - d *terraform.InstanceDiff, - meta interface{}) (*terraform.InstanceState, error) { - p := meta.(*ResourceProvider) - conn := p.route53 - - // Merge the diff into the state so that we have all the attributes - // properly. - rs := s.MergeDiff(d) +func resourceAwsRoute53RecordCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).route53 // Get the record - rec, err := resource_aws_r53_build_record_set(rs) + rec, err := resourceAwsRoute53RecordBuildSet(d) if err != nil { - return rs, err + return err } // Create the new records. We abuse StateChangeConf for this to @@ -56,9 +73,10 @@ func resource_aws_r53_record_create( }, }, } - zone := rs.Attributes["zone_id"] + zone := d.Get("zone_id").(string) log.Printf("[DEBUG] Creating resource records for zone: %s, name: %s", - zone, rs.Attributes["name"]) + zone, d.Get("name").(string)) + wait := resource.StateChangeConf{ Pending: []string{"rejected"}, Target: "accepted", @@ -79,14 +97,15 @@ func resource_aws_r53_record_create( return resp.ChangeInfo, "accepted", nil }, } + respRaw, err := wait.WaitForState() if err != nil { - return rs, err + return err } changeInfo := respRaw.(route53.ChangeInfo) // Generate an ID - rs.ID = fmt.Sprintf("%s_%s_%s", zone, rs.Attributes["name"], rs.Attributes["type"]) + d.SetId(fmt.Sprintf("%s_%s_%s", zone, d.Get("name").(string), d.Get("type").(string))) // Wait until we are done wait = resource.StateChangeConf{ @@ -96,47 +115,63 @@ func resource_aws_r53_record_create( Timeout: 10 * time.Minute, MinTimeout: 5 * time.Second, Refresh: func() (result interface{}, state string, err error) { - return resource_aws_r53_wait(conn, changeInfo.ID) + return resourceAwsRoute53Wait(conn, changeInfo.ID) }, } _, err = wait.WaitForState() if err != nil { - return rs, err + return err } - return rs, nil + + return nil } -func resource_aws_r53_build_record_set(s *terraform.InstanceState) (*route53.ResourceRecordSet, error) { - // Parse the TTL - ttl, err := strconv.ParseInt(s.Attributes["ttl"], 10, 32) +func resourceAwsRoute53RecordRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).route53 + + zone := d.Get("zone_id").(string) + lopts := &route53.ListOpts{ + Name: d.Get("name").(string), + Type: d.Get("type").(string), + } + resp, err := conn.ListResourceRecordSets(zone, lopts) if err != nil { - return nil, err + return err } - // Expand the records - recRaw := flatmap.Expand(s.Attributes, "records") - var records []string - for _, raw := range recRaw.([]interface{}) { - records = append(records, raw.(string)) + // Scan for a matching record + found := false + for _, record := range resp.Records { + if route53.FQDN(record.Name) != route53.FQDN(lopts.Name) { + continue + } + if strings.ToUpper(record.Type) != strings.ToUpper(lopts.Type) { + continue + } + + found = true + + for i, rec := range record.Records { + key := fmt.Sprintf("records.%d", i) + d.Set(key, rec) + } + d.Set("ttl", record.TTL) + + break } - rec := &route53.ResourceRecordSet{ - Name: s.Attributes["name"], - Type: s.Attributes["type"], - TTL: int(ttl), - Records: records, + if !found { + d.SetId("") } - return rec, nil + + return nil } -func resource_aws_r53_record_destroy( - s *terraform.InstanceState, - meta interface{}) error { - p := meta.(*ResourceProvider) - conn := p.route53 +func resourceAwsRoute53RecordDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).route53 - // Get the record - rec, err := resource_aws_r53_build_record_set(s) + // Get the records + rec, err := resourceAwsRoute53RecordBuildSet(d) if err != nil { return err } @@ -151,9 +186,10 @@ func resource_aws_r53_record_destroy( }, }, } - zone := s.Attributes["zone_id"] + zone := d.Get("zone_id").(string) log.Printf("[DEBUG] Deleting resource records for zone: %s, name: %s", - zone, s.Attributes["name"]) + zone, d.Get("name").(string)) + wait := resource.StateChangeConf{ Pending: []string{"rejected"}, Target: "accepted", @@ -179,6 +215,7 @@ func resource_aws_r53_record_destroy( return 42, "accepted", nil }, } + if _, err := wait.WaitForState(); err != nil { return err } @@ -186,68 +223,19 @@ func resource_aws_r53_record_destroy( return nil } -func resource_aws_r53_record_refresh( - s *terraform.InstanceState, - meta interface{}) (*terraform.InstanceState, error) { - p := meta.(*ResourceProvider) - conn := p.route53 - - zone := s.Attributes["zone_id"] - lopts := &route53.ListOpts{ - Name: s.Attributes["name"], - Type: s.Attributes["type"], - } - resp, err := conn.ListResourceRecordSets(zone, lopts) - if err != nil { - return s, err +func resourceAwsRoute53RecordBuildSet(d *schema.ResourceData) (*route53.ResourceRecordSet, error) { + recs := d.Get("records.#").(int) + records := make([]string, 0, recs) + for i := 0; i < recs; i++ { + key := fmt.Sprintf("records.%d", i) + records = append(records, d.Get(key).(string)) } - // Scan for a matching record - found := false - for _, record := range resp.Records { - if route53.FQDN(record.Name) != route53.FQDN(lopts.Name) { - continue - } - if strings.ToUpper(record.Type) != strings.ToUpper(lopts.Type) { - continue - } - - found = true - resource_aws_r53_record_update_state(s, &record) - break + rec := &route53.ResourceRecordSet{ + Name: d.Get("name").(string), + Type: d.Get("type").(string), + TTL: d.Get("ttl").(int), + Records: records, } - if !found { - s.ID = "" - } - return s, nil -} - -func resource_aws_r53_record_update_state( - s *terraform.InstanceState, - rec *route53.ResourceRecordSet) { - - flatRec := flatmap.Flatten(map[string]interface{}{ - "records": rec.Records, - }) - for k, v := range flatRec { - s.Attributes[k] = v - } - - s.Attributes["ttl"] = strconv.FormatInt(int64(rec.TTL), 10) -} - -func resource_aws_r53_record_diff( - s *terraform.InstanceState, - c *terraform.ResourceConfig, - meta interface{}) (*terraform.InstanceDiff, error) { - b := &diff.ResourceBuilder{ - Attrs: map[string]diff.AttrType{ - "zone_id": diff.AttrTypeCreate, - "name": diff.AttrTypeCreate, - "type": diff.AttrTypeCreate, - "ttl": diff.AttrTypeUpdate, - "records": diff.AttrTypeUpdate, - }, - } - return b.Diff(s, c) + return rec, nil } diff --git a/builtin/providers/aws/resource_aws_route53_record_test.go b/builtin/providers/aws/resource_aws_route53_record_test.go index c22fc994f..3f72a9297 100644 --- a/builtin/providers/aws/resource_aws_route53_record_test.go +++ b/builtin/providers/aws/resource_aws_route53_record_test.go @@ -27,7 +27,7 @@ func TestAccRoute53Record(t *testing.T) { } func testAccCheckRoute53RecordDestroy(s *terraform.State) error { - conn := testAccProvider.route53 + conn := testAccProvider.Meta().(*AWSClient).route53 for _, rs := range s.RootModule().Resources { if rs.Type != "aws_route53_record" { continue @@ -56,7 +56,7 @@ func testAccCheckRoute53RecordDestroy(s *terraform.State) error { func testAccCheckRoute53RecordExists(n string) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := testAccProvider.route53 + conn := testAccProvider.Meta().(*AWSClient).route53 rs, ok := s.RootModule().Resources[n] if !ok { return fmt.Errorf("Not found: %s", n) diff --git a/builtin/providers/aws/resource_aws_route53_zone.go b/builtin/providers/aws/resource_aws_route53_zone.go index 871fe112e..4a5027899 100644 --- a/builtin/providers/aws/resource_aws_route53_zone.go +++ b/builtin/providers/aws/resource_aws_route53_zone.go @@ -5,46 +5,49 @@ import ( "strings" "time" - "github.com/hashicorp/terraform/helper/config" - "github.com/hashicorp/terraform/helper/diff" "github.com/hashicorp/terraform/helper/resource" - "github.com/hashicorp/terraform/terraform" + "github.com/hashicorp/terraform/helper/schema" "github.com/mitchellh/goamz/route53" ) -func resource_aws_r53_zone_validation() *config.Validator { - return &config.Validator{ - Required: []string{ - "name", +func resourceAwsRoute53Zone() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsRoute53ZoneCreate, + Read: resourceAwsRoute53ZoneRead, + Delete: resourceAwsRoute53ZoneDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "zone_id": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, }, } } -func resource_aws_r53_zone_create( - s *terraform.InstanceState, - d *terraform.InstanceDiff, - meta interface{}) (*terraform.InstanceState, error) { - p := meta.(*ResourceProvider) - r53 := p.route53 - - // Merge the diff into the state so that we have all the attributes - // properly. - rs := s.MergeDiff(d) +func resourceAwsRoute53ZoneCreate(d *schema.ResourceData, meta interface{}) error { + r53 := meta.(*AWSClient).route53 req := &route53.CreateHostedZoneRequest{ - Name: rs.Attributes["name"], + Name: d.Get("name").(string), Comment: "Managed by Terraform", } log.Printf("[DEBUG] Creating Route53 hosted zone: %s", req.Name) resp, err := r53.CreateHostedZone(req) if err != nil { - return rs, err + return err } // Store the zone_id zone := route53.CleanZoneID(resp.HostedZone.ID) - rs.ID = zone - rs.Attributes["zone_id"] = zone + d.Set("zone_id", zone) + d.SetId(zone) // Wait until we are done initializing wait := resource.StateChangeConf{ @@ -54,71 +57,50 @@ func resource_aws_r53_zone_create( Timeout: 10 * time.Minute, MinTimeout: 2 * time.Second, Refresh: func() (result interface{}, state string, err error) { - return resource_aws_r53_wait(r53, resp.ChangeInfo.ID) + return resourceAwsRoute53Wait(r53, resp.ChangeInfo.ID) }, } _, err = wait.WaitForState() - if err != nil { - return rs, err - } - return rs, nil -} - -// resource_aws_r53_wait checks the status of a change -func resource_aws_r53_wait(r53 *route53.Route53, ref string) (result interface{}, state string, err error) { - status, err := r53.GetChange(ref) - if err != nil { - return nil, "UNKNOWN", err - } - return true, status, nil -} - -func resource_aws_r53_zone_destroy( - s *terraform.InstanceState, - meta interface{}) error { - p := meta.(*ResourceProvider) - r53 := p.route53 - - log.Printf("[DEBUG] Deleting Route53 hosted zone: %s (ID: %s)", - s.Attributes["name"], s.Attributes["zone_id"]) - _, err := r53.DeleteHostedZone(s.Attributes["zone_id"]) if err != nil { return err } return nil } -func resource_aws_r53_zone_refresh( - s *terraform.InstanceState, - meta interface{}) (*terraform.InstanceState, error) { - p := meta.(*ResourceProvider) - r53 := p.route53 +func resourceAwsRoute53ZoneRead(d *schema.ResourceData, meta interface{}) error { + r53 := meta.(*AWSClient).route53 - _, err := r53.GetHostedZone(s.Attributes["zone_id"]) + _, err := r53.GetHostedZone(d.Id()) if err != nil { // Handle a deleted zone if strings.Contains(err.Error(), "404") { - s.ID = "" - return s, nil + d.SetId("") + return nil } - return s, err + return err } - return s, nil + + return nil } -func resource_aws_r53_zone_diff( - s *terraform.InstanceState, - c *terraform.ResourceConfig, - meta interface{}) (*terraform.InstanceDiff, error) { +func resourceAwsRoute53ZoneDelete(d *schema.ResourceData, meta interface{}) error { + r53 := meta.(*AWSClient).route53 - b := &diff.ResourceBuilder{ - Attrs: map[string]diff.AttrType{ - "name": diff.AttrTypeCreate, - }, - - ComputedAttrs: []string{ - "zone_id", - }, + log.Printf("[DEBUG] Deleting Route53 hosted zone: %s (ID: %s)", + d.Get("name").(string), d.Id()) + _, err := r53.DeleteHostedZone(d.Id()) + if err != nil { + return err } - return b.Diff(s, c) + + return nil +} + +// resourceAwsRoute53Wait checks the status of a change +func resourceAwsRoute53Wait(r53 *route53.Route53, ref string) (result interface{}, state string, err error) { + status, err := r53.GetChange(ref) + if err != nil { + return nil, "UNKNOWN", err + } + return true, status, nil } diff --git a/builtin/providers/aws/resource_aws_route53_zone_test.go b/builtin/providers/aws/resource_aws_route53_zone_test.go index 45475a1a3..d55e208e8 100644 --- a/builtin/providers/aws/resource_aws_route53_zone_test.go +++ b/builtin/providers/aws/resource_aws_route53_zone_test.go @@ -25,7 +25,7 @@ func TestAccRoute53Zone(t *testing.T) { } func testAccCheckRoute53ZoneDestroy(s *terraform.State) error { - conn := testAccProvider.route53 + conn := testAccProvider.Meta().(*AWSClient).route53 for _, rs := range s.RootModule().Resources { if rs.Type != "aws_route53_zone" { continue @@ -50,7 +50,7 @@ func testAccCheckRoute53ZoneExists(n string) resource.TestCheckFunc { return fmt.Errorf("No hosted zone ID is set") } - conn := testAccProvider.route53 + conn := testAccProvider.Meta().(*AWSClient).route53 _, err := conn.GetHostedZone(rs.Primary.ID) if err != nil { return fmt.Errorf("Hosted zone err: %v", err) diff --git a/builtin/providers/aws/resource_aws_route_table.go b/builtin/providers/aws/resource_aws_route_table.go index b444a083c..c197b9e3a 100644 --- a/builtin/providers/aws/resource_aws_route_table.go +++ b/builtin/providers/aws/resource_aws_route_table.go @@ -1,146 +1,169 @@ package aws import ( + "bytes" "fmt" "log" - "reflect" "time" - "github.com/hashicorp/terraform/flatmap" - "github.com/hashicorp/terraform/helper/diff" + "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/resource" - "github.com/hashicorp/terraform/terraform" + "github.com/hashicorp/terraform/helper/schema" "github.com/mitchellh/goamz/ec2" ) -func resource_aws_route_table_create( - s *terraform.InstanceState, - d *terraform.InstanceDiff, - meta interface{}) (*terraform.InstanceState, error) { - p := meta.(*ResourceProvider) - ec2conn := p.ec2conn +func resourceAwsRouteTable() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsRouteTableCreate, + Read: resourceAwsRouteTableRead, + Delete: resourceAwsRouteTableDelete, + + Schema: map[string]*schema.Schema{ + "vpc_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "route": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cidr_block": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "gateway_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + + "instance_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + }, + }, + Set: resourceAwsRouteTableHash, + }, + }, + } +} + +func resourceAwsRouteTableCreate(d *schema.ResourceData, meta interface{}) error { + ec2conn := meta.(*AWSClient).ec2conn // Create the routing table createOpts := &ec2.CreateRouteTable{ - VpcId: d.Attributes["vpc_id"].New, + VpcId: d.Get("vpc_id").(string), } log.Printf("[DEBUG] RouteTable create config: %#v", createOpts) + resp, err := ec2conn.CreateRouteTable(createOpts) if err != nil { - return nil, fmt.Errorf("Error creating route table: %s", err) + return fmt.Errorf("Error creating route table: %s", err) } // Get the ID and store it rt := &resp.RouteTable - s.ID = rt.RouteTableId - log.Printf("[INFO] Route Table ID: %s", s.ID) + d.SetId(rt.RouteTableId) + log.Printf("[INFO] Route Table ID: %s", d.Id()) // Wait for the route table to become available log.Printf( "[DEBUG] Waiting for route table (%s) to become available", - s.ID) + d.Id()) stateConf := &resource.StateChangeConf{ Pending: []string{"pending"}, Target: "ready", - Refresh: RouteTableStateRefreshFunc(ec2conn, s.ID), + Refresh: resourceAwsRouteTableStateRefreshFunc(ec2conn, d.Id()), Timeout: 1 * time.Minute, } if _, err := stateConf.WaitForState(); err != nil { - return s, fmt.Errorf( + return fmt.Errorf( "Error waiting for route table (%s) to become available: %s", - s.ID, err) + d.Id(), err) } - // Update our routes - return resource_aws_route_table_update(s, d, meta) + return resourceAwsRouteTableRead(d, meta) } -func resource_aws_route_table_update( - s *terraform.InstanceState, - d *terraform.InstanceDiff, - meta interface{}) (*terraform.InstanceState, error) { - p := meta.(*ResourceProvider) - ec2conn := p.ec2conn +func resourceAwsRouteTableRead(d *schema.ResourceData, meta interface{}) error { + ec2conn := meta.(*AWSClient).ec2conn - // Our resulting state - rs := s.MergeDiff(d) - - // Get our routes out of the merge - oldroutes := flatmap.Expand(s.Attributes, "route") - routes := flatmap.Expand(s.MergeDiff(d).Attributes, "route") - - // Determine the route operations we need to perform - ops := routeTableOps(oldroutes, routes) - if len(ops) == 0 { - return s, nil + rtRaw, _, err := resourceAwsRouteTableStateRefreshFunc(ec2conn, d.Id())() + if err != nil { + return err + } + if rtRaw == nil { + return nil } - // Go through each operation, performing each one at a time. - // We store the updated state on each operation so that if any - // individual operation fails, we can return a valid partial state. - var err error - resultRoutes := make([]map[string]string, 0, len(ops)) - for _, op := range ops { - switch op.Op { - case routeTableOpCreate: + rt := rtRaw.(ec2.RouteTable) + d.Set("vpc_id", rt.VpcId) + + // TODO: Add some code to also update the route set + + return nil +} + +func resourceAwsRouteTableUpdate(d *schema.ResourceData, meta interface{}) error { + ec2conn := meta.(*AWSClient).ec2conn + + // Check if the route set as a whole has changed + if d.HasChange("route") { + o, n := d.GetChange("route") + ors := o.(*schema.Set).Difference(n.(*schema.Set)) + nrs := n.(*schema.Set).Difference(o.(*schema.Set)) + + // Now first loop through all the old routes and delete any obsolete ones + for _, route := range ors.List() { + m := route.(map[string]interface{}) + + // Delete the route as it no longer exists in the config + _, err := ec2conn.DeleteRoute( + d.Id(), m["cidr_block"].(string)) + if err != nil { + return err + } + } + + // Make sure we save the state of the currently configured rules + routes := o.(*schema.Set).Intersection(n.(*schema.Set)) + d.Set("route", routes) + + // Then loop through al the newly configured routes and create them + for _, route := range nrs.List() { + m := route.(map[string]interface{}) + opts := ec2.CreateRoute{ - RouteTableId: s.ID, - DestinationCidrBlock: op.Route.DestinationCidrBlock, - GatewayId: op.Route.GatewayId, - InstanceId: op.Route.InstanceId, + RouteTableId: d.Id(), + DestinationCidrBlock: m["cidr_block"].(string), + GatewayId: m["gateway_id"].(string), + InstanceId: m["instance_id"].(string), } - _, err = ec2conn.CreateRoute(&opts) - case routeTableOpReplace: - opts := ec2.ReplaceRoute{ - RouteTableId: s.ID, - DestinationCidrBlock: op.Route.DestinationCidrBlock, - GatewayId: op.Route.GatewayId, - InstanceId: op.Route.InstanceId, + _, err := ec2conn.CreateRoute(&opts) + if err != nil { + return err } - _, err = ec2conn.ReplaceRoute(&opts) - case routeTableOpDelete: - _, err = ec2conn.DeleteRoute( - s.ID, op.Route.DestinationCidrBlock) - } - - if err != nil { - // Exit early so we can return what we've done so far - break - } - - // If we didn't delete the route, append it to the list of routes - // we have. - if op.Op != routeTableOpDelete { - resultMap := map[string]string{"cidr_block": op.Route.DestinationCidrBlock} - if op.Route.GatewayId != "" { - resultMap["gateway_id"] = op.Route.GatewayId - } else if op.Route.InstanceId != "" { - resultMap["instance_id"] = op.Route.InstanceId - } - - resultRoutes = append(resultRoutes, resultMap) + routes.Add(route) + d.Set("route", routes) } } - // Update our state with the settings - flatmap.Map(rs.Attributes).Merge(flatmap.Flatten(map[string]interface{}{ - "route": resultRoutes, - })) - - return rs, err + return resourceAwsRouteTableRead(d, meta) } -func resource_aws_route_table_destroy( - s *terraform.InstanceState, - meta interface{}) error { - p := meta.(*ResourceProvider) - ec2conn := p.ec2conn +func resourceAwsRouteTableDelete(d *schema.ResourceData, meta interface{}) error { + ec2conn := meta.(*AWSClient).ec2conn // First request the routing table since we'll have to disassociate // all the subnets first. - rtRaw, _, err := RouteTableStateRefreshFunc(ec2conn, s.ID)() + rtRaw, _, err := resourceAwsRouteTableStateRefreshFunc(ec2conn, d.Id())() if err != nil { return err } @@ -158,8 +181,8 @@ func resource_aws_route_table_destroy( } // Delete the route table - log.Printf("[INFO] Deleting Route Table: %s", s.ID) - if _, err := ec2conn.DeleteRouteTable(s.ID); err != nil { + log.Printf("[INFO] Deleting Route Table: %s", d.Id()) + if _, err := ec2conn.DeleteRouteTable(d.Id()); err != nil { ec2err, ok := err.(*ec2.Error) if ok && ec2err.Code == "InvalidRouteTableID.NotFound" { return nil @@ -171,147 +194,42 @@ func resource_aws_route_table_destroy( // Wait for the route table to really destroy log.Printf( "[DEBUG] Waiting for route table (%s) to become destroyed", - s.ID) + d.Id()) + stateConf := &resource.StateChangeConf{ Pending: []string{"ready"}, Target: "", - Refresh: RouteTableStateRefreshFunc(ec2conn, s.ID), + Refresh: resourceAwsRouteTableStateRefreshFunc(ec2conn, d.Id()), Timeout: 1 * time.Minute, } if _, err := stateConf.WaitForState(); err != nil { return fmt.Errorf( "Error waiting for route table (%s) to become destroyed: %s", - s.ID, err) + d.Id(), err) } return nil } -func resource_aws_route_table_refresh( - s *terraform.InstanceState, - meta interface{}) (*terraform.InstanceState, error) { - p := meta.(*ResourceProvider) - ec2conn := p.ec2conn +func resourceAwsRouteTableHash(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + buf.WriteString(fmt.Sprintf("%s-", m["cidr_block"].(string))) - rtRaw, _, err := RouteTableStateRefreshFunc(ec2conn, s.ID)() - if err != nil { - return s, err - } - if rtRaw == nil { - return nil, nil + if v, ok := m["gateway_id"]; ok { + buf.WriteString(fmt.Sprintf("%s-", v.(string))) } - rt := rtRaw.(*ec2.RouteTable) - return resource_aws_route_table_update_state(s, rt) + if v, ok := m["instance_id"]; ok { + buf.WriteString(fmt.Sprintf("%s-", v.(string))) + } + + return hashcode.String(buf.String()) } -func resource_aws_route_table_diff( - s *terraform.InstanceState, - c *terraform.ResourceConfig, - meta interface{}) (*terraform.InstanceDiff, error) { - b := &diff.ResourceBuilder{ - Attrs: map[string]diff.AttrType{ - "vpc_id": diff.AttrTypeCreate, - "route": diff.AttrTypeUpdate, - }, - } - - return b.Diff(s, c) -} - -func resource_aws_route_table_update_state( - s *terraform.InstanceState, - rt *ec2.RouteTable) (*terraform.InstanceState, error) { - s.Attributes["vpc_id"] = rt.VpcId - - return s, nil -} - -// routeTableOp represents a minor operation on the routing table. -// This tells us what we should do to the routing table. -type routeTableOp struct { - Op routeTableOpType - Route ec2.Route -} - -// routeTableOpType is the type of operation related to a route that -// can be operated on a routing table. -type routeTableOpType byte - -const ( - routeTableOpCreate routeTableOpType = iota - routeTableOpReplace - routeTableOpDelete -) - -// routeTableOps takes the old and new routes from flatmap.Expand -// and returns a set of operations that must be performed in order -// to get to the desired state. -func routeTableOps(a interface{}, b interface{}) []routeTableOp { - // Build up the actual ec2.Route objects - oldRoutes := make(map[string]ec2.Route) - newRoutes := make(map[string]ec2.Route) - for i, raws := range []interface{}{a, b} { - result := oldRoutes - if i == 1 { - result = newRoutes - } - if raws == nil { - continue - } - - for _, raw := range raws.([]interface{}) { - m := raw.(map[string]interface{}) - r := ec2.Route{ - DestinationCidrBlock: m["cidr_block"].(string), - } - if v, ok := m["gateway_id"]; ok { - r.GatewayId = v.(string) - } - if v, ok := m["instance_id"]; ok { - r.InstanceId = v.(string) - } - - result[r.DestinationCidrBlock] = r - } - } - - // Now, start building up the ops - ops := make([]routeTableOp, 0, len(newRoutes)) - for n, r := range newRoutes { - op := routeTableOpCreate - if oldR, ok := oldRoutes[n]; ok { - if reflect.DeepEqual(r, oldR) { - // No changes! - continue - } - - op = routeTableOpReplace - } - - ops = append(ops, routeTableOp{ - Op: op, - Route: r, - }) - } - - // Determine what routes we need to delete - for _, op := range ops { - delete(oldRoutes, op.Route.DestinationCidrBlock) - } - for _, r := range oldRoutes { - ops = append(ops, routeTableOp{ - Op: routeTableOpDelete, - Route: r, - }) - } - - return ops -} - -// RouteTableStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch +// resourceAwsRouteTableStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch // a RouteTable. -func RouteTableStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc { +func resourceAwsRouteTableStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc { return func() (interface{}, string, error) { resp, err := conn.DescribeRouteTables([]string{id}, ec2.NewFilter()) if err != nil { diff --git a/builtin/providers/aws/resource_aws_route_table_association.go b/builtin/providers/aws/resource_aws_route_table_association.go index c8932f749..846836008 100644 --- a/builtin/providers/aws/resource_aws_route_table_association.go +++ b/builtin/providers/aws/resource_aws_route_table_association.go @@ -4,77 +4,121 @@ import ( "fmt" "log" - "github.com/hashicorp/terraform/helper/diff" - "github.com/hashicorp/terraform/terraform" + "github.com/hashicorp/terraform/helper/schema" "github.com/mitchellh/goamz/ec2" ) -func resource_aws_route_table_association_create( - s *terraform.InstanceState, - d *terraform.InstanceDiff, - meta interface{}) (*terraform.InstanceState, error) { - p := meta.(*ResourceProvider) - ec2conn := p.ec2conn - rs := s.MergeDiff(d) +func resourceAwsRouteTableAssociation() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsRouteTableAssociationCreate, + Read: resourceAwsRouteTableAssociationRead, + Update: resourceAwsRouteTableAssociationUpdate, + Delete: resourceAwsRouteTableAssociationDelete, + + Schema: map[string]*schema.Schema{ + "subnet_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "route_table_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + }, + } +} + +func resourceAwsRouteTableAssociationCreate(d *schema.ResourceData, meta interface{}) error { + ec2conn := meta.(*AWSClient).ec2conn log.Printf( "[INFO] Creating route table association: %s => %s", - rs.Attributes["subnet_id"], - rs.Attributes["route_table_id"]) + d.Get("subnet_id").(string), + d.Get("route_table_id").(string)) + resp, err := ec2conn.AssociateRouteTable( - rs.Attributes["route_table_id"], - rs.Attributes["subnet_id"]) + d.Get("route_table_id").(string), + d.Get("subnet_id").(string)) + if err != nil { - return nil, err + return err } // Set the ID and return - rs.ID = resp.AssociationId - log.Printf("[INFO] Association ID: %s", rs.ID) + d.SetId(resp.AssociationId) + log.Printf("[INFO] Association ID: %s", d.Id()) - return rs, nil + return nil } -func resource_aws_route_table_association_update( - s *terraform.InstanceState, - d *terraform.InstanceDiff, - meta interface{}) (*terraform.InstanceState, error) { - p := meta.(*ResourceProvider) - ec2conn := p.ec2conn +func resourceAwsRouteTableAssociationRead(d *schema.ResourceData, meta interface{}) error { + ec2conn := meta.(*AWSClient).ec2conn + + // Get the routing table that this association belongs to + rtRaw, _, err := resourceAwsRouteTableStateRefreshFunc( + ec2conn, d.Get("route_table_id").(string))() + if err != nil { + return err + } + if rtRaw == nil { + return nil + } + rt := rtRaw.(*ec2.RouteTable) + + // Inspect that the association exists + found := false + for _, a := range rt.Associations { + if a.AssociationId == d.Id() { + found = true + d.Set("subnet_id", a.SubnetId) + break + } + } + + if !found { + // It seems it doesn't exist anymore, so clear the ID + d.SetId("") + } + + return nil +} + +func resourceAwsRouteTableAssociationUpdate(d *schema.ResourceData, meta interface{}) error { + ec2conn := meta.(*AWSClient).ec2conn - rs := s.MergeDiff(d) log.Printf( - "[INFO] Replacing route table association: %s => %s", - rs.Attributes["subnet_id"], - rs.Attributes["route_table_id"]) + "[INFO] Creating route table association: %s => %s", + d.Get("subnet_id").(string), + d.Get("route_table_id").(string)) + resp, err := ec2conn.ReassociateRouteTable( - rs.ID, - rs.Attributes["route_table_id"]) + d.Id(), + d.Get("route_table_id").(string)) + if err != nil { ec2err, ok := err.(*ec2.Error) if ok && ec2err.Code == "InvalidAssociationID.NotFound" { // Not found, so just create a new one - return resource_aws_route_table_association_create(s, d, meta) + return resourceAwsRouteTableAssociationCreate(d, meta) } - return s, err + return err } // Update the ID - rs.ID = resp.AssociationId - log.Printf("[INFO] Association ID: %s", rs.ID) + d.SetId(resp.AssociationId) + log.Printf("[INFO] Association ID: %s", d.Id()) - return rs, nil + return nil } -func resource_aws_route_table_association_destroy( - s *terraform.InstanceState, - meta interface{}) error { - p := meta.(*ResourceProvider) - ec2conn := p.ec2conn +func resourceAwsRouteTableAssociationDelete(d *schema.ResourceData, meta interface{}) error { + ec2conn := meta.(*AWSClient).ec2conn - log.Printf("[INFO] Deleting route table association: %s", s.ID) - if _, err := ec2conn.DisassociateRouteTable(s.ID); err != nil { + log.Printf("[INFO] Deleting route table association: %s", d.Id()) + if _, err := ec2conn.DisassociateRouteTable(d.Id()); err != nil { ec2err, ok := err.(*ec2.Error) if ok && ec2err.Code == "InvalidAssociationID.NotFound" { return nil @@ -85,50 +129,3 @@ func resource_aws_route_table_association_destroy( return nil } - -func resource_aws_route_table_association_refresh( - s *terraform.InstanceState, - meta interface{}) (*terraform.InstanceState, error) { - p := meta.(*ResourceProvider) - ec2conn := p.ec2conn - - // Get the routing table that this association belongs to - rtRaw, _, err := RouteTableStateRefreshFunc( - ec2conn, s.Attributes["route_table_id"])() - if err != nil { - return s, err - } - if rtRaw == nil { - return nil, nil - } - rt := rtRaw.(*ec2.RouteTable) - - // Inspect that the association exists - found := false - for _, a := range rt.Associations { - if a.AssociationId == s.ID { - found = true - s.Attributes["subnet_id"] = a.SubnetId - break - } - } - if !found { - return nil, nil - } - - return s, nil -} - -func resource_aws_route_table_association_diff( - s *terraform.InstanceState, - c *terraform.ResourceConfig, - meta interface{}) (*terraform.InstanceDiff, error) { - b := &diff.ResourceBuilder{ - Attrs: map[string]diff.AttrType{ - "subnet_id": diff.AttrTypeCreate, - "route_table_id": diff.AttrTypeUpdate, - }, - } - - return b.Diff(s, c) -} diff --git a/builtin/providers/aws/resource_aws_route_table_association_test.go b/builtin/providers/aws/resource_aws_route_table_association_test.go index bc670cdf5..079fb41f8 100644 --- a/builtin/providers/aws/resource_aws_route_table_association_test.go +++ b/builtin/providers/aws/resource_aws_route_table_association_test.go @@ -37,7 +37,7 @@ func TestAccAWSRouteTableAssociation(t *testing.T) { } func testAccCheckRouteTableAssociationDestroy(s *terraform.State) error { - conn := testAccProvider.ec2conn + conn := testAccProvider.Meta().(*AWSClient).ec2conn for _, rs := range s.RootModule().Resources { if rs.Type != "aws_route_table_association" { @@ -81,7 +81,7 @@ func testAccCheckRouteTableAssociationExists(n string, v *ec2.RouteTable) resour return fmt.Errorf("No ID is set") } - conn := testAccProvider.ec2conn + conn := testAccProvider.Meta().(*AWSClient).ec2conn resp, err := conn.DescribeRouteTables( []string{rs.Primary.Attributes["route_table_id"]}, ec2.NewFilter()) if err != nil { diff --git a/builtin/providers/aws/resource_aws_route_table_test.go b/builtin/providers/aws/resource_aws_route_table_test.go index 2fbe2433f..45cae1fb8 100644 --- a/builtin/providers/aws/resource_aws_route_table_test.go +++ b/builtin/providers/aws/resource_aws_route_table_test.go @@ -122,7 +122,7 @@ func TestAccAWSRouteTable_instance(t *testing.T) { } func testAccCheckRouteTableDestroy(s *terraform.State) error { - conn := testAccProvider.ec2conn + conn := testAccProvider.Meta().(*AWSClient).ec2conn for _, rs := range s.RootModule().Resources { if rs.Type != "aws_route_table" { @@ -164,7 +164,7 @@ func testAccCheckRouteTableExists(n string, v *ec2.RouteTable) resource.TestChec return fmt.Errorf("No ID is set") } - conn := testAccProvider.ec2conn + conn := testAccProvider.Meta().(*AWSClient).ec2conn resp, err := conn.DescribeRouteTables( []string{rs.Primary.ID}, ec2.NewFilter()) if err != nil { diff --git a/builtin/providers/aws/resource_aws_s3_bucket.go b/builtin/providers/aws/resource_aws_s3_bucket.go index 36a9f4f0f..2e01078f7 100644 --- a/builtin/providers/aws/resource_aws_s3_bucket.go +++ b/builtin/providers/aws/resource_aws_s3_bucket.go @@ -4,90 +4,70 @@ import ( "fmt" "log" - "github.com/hashicorp/terraform/helper/config" - "github.com/hashicorp/terraform/helper/diff" - "github.com/hashicorp/terraform/terraform" + "github.com/hashicorp/terraform/helper/schema" "github.com/mitchellh/goamz/s3" ) -func resource_aws_s3_bucket_validation() *config.Validator { - return &config.Validator{ - Required: []string{ - "bucket", - }, - Optional: []string{ - "acl", +func resourceAwsS3Bucket() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsS3BucketCreate, + Read: resourceAwsS3BucketRead, + Delete: resourceAwsS3BucketDelete, + + Schema: map[string]*schema.Schema{ + "bucket": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "acl": &schema.Schema{ + Type: schema.TypeString, + Default: "private", + Optional: true, + ForceNew: true, + }, }, } } -func resource_aws_s3_bucket_create( - s *terraform.InstanceState, - d *terraform.InstanceDiff, - meta interface{}) (*terraform.InstanceState, error) { - p := meta.(*ResourceProvider) - s3conn := p.s3conn +func resourceAwsS3BucketCreate(d *schema.ResourceData, meta interface{}) error { + s3conn := meta.(*AWSClient).s3conn - // Merge the diff into the state so that we have all the attributes - // properly. - rs := s.MergeDiff(d) - - // Get the bucket and optional acl - bucket := rs.Attributes["bucket"] - acl := "private" - if other, ok := rs.Attributes["acl"]; ok { - acl = other - } + // Get the bucket and acl + bucket := d.Get("bucket").(string) + acl := d.Get("acl").(string) log.Printf("[DEBUG] S3 bucket create: %s, ACL: %s", bucket, acl) s3Bucket := s3conn.Bucket(bucket) err := s3Bucket.PutBucket(s3.ACL(acl)) if err != nil { - return nil, fmt.Errorf("Error creating S3 bucket: %s", err) + return fmt.Errorf("Error creating S3 bucket: %s", err) } // Assign the bucket name as the resource ID - rs.ID = bucket - return rs, nil + d.SetId(bucket) + + return nil } -func resource_aws_s3_bucket_destroy( - s *terraform.InstanceState, - meta interface{}) error { - p := meta.(*ResourceProvider) - s3conn := p.s3conn +func resourceAwsS3BucketRead(d *schema.ResourceData, meta interface{}) error { + s3conn := meta.(*AWSClient).s3conn - name := s.Attributes["bucket"] - bucket := s3conn.Bucket(name) - - log.Printf("[DEBUG] S3 Delete Bucket: %s", name) - return bucket.DelBucket() -} - -func resource_aws_s3_bucket_refresh( - s *terraform.InstanceState, - meta interface{}) (*terraform.InstanceState, error) { - p := meta.(*ResourceProvider) - s3conn := p.s3conn - - bucket := s3conn.Bucket(s.Attributes["bucket"]) + bucket := s3conn.Bucket(d.Id()) resp, err := bucket.Head("/") if err != nil { - return s, err + return err } defer resp.Body.Close() - return s, nil + return nil } -func resource_aws_s3_bucket_diff( - s *terraform.InstanceState, - c *terraform.ResourceConfig, - meta interface{}) (*terraform.InstanceDiff, error) { +func resourceAwsS3BucketDelete(d *schema.ResourceData, meta interface{}) error { + s3conn := meta.(*AWSClient).s3conn - b := &diff.ResourceBuilder{ - Attrs: map[string]diff.AttrType{ - "bucket": diff.AttrTypeCreate, - }, - } - return b.Diff(s, c) + log.Printf("[DEBUG] S3 Delete Bucket: %s", d.Id()) + bucket := s3conn.Bucket(d.Id()) + + return bucket.DelBucket() } diff --git a/builtin/providers/aws/resource_aws_s3_bucket_test.go b/builtin/providers/aws/resource_aws_s3_bucket_test.go index 09eed24aa..9296df0d0 100644 --- a/builtin/providers/aws/resource_aws_s3_bucket_test.go +++ b/builtin/providers/aws/resource_aws_s3_bucket_test.go @@ -25,7 +25,7 @@ func TestAccAWSS3Bucket(t *testing.T) { } func testAccCheckAWSS3BucketDestroy(s *terraform.State) error { - conn := testAccProvider.s3conn + conn := testAccProvider.Meta().(*AWSClient).s3conn for _, rs := range s.RootModule().Resources { if rs.Type != "aws_s3_bucket" { @@ -53,7 +53,7 @@ func testAccCheckAWSS3BucketExists(n string) resource.TestCheckFunc { return fmt.Errorf("No S3 Bucket ID is set") } - conn := testAccProvider.s3conn + conn := testAccProvider.Meta().(*AWSClient).s3conn bucket := conn.Bucket(rs.Primary.ID) resp, err := bucket.Head("/") if err != nil { diff --git a/builtin/providers/aws/resource_aws_security_group.go b/builtin/providers/aws/resource_aws_security_group.go index 4be7ca484..87fce98ff 100644 --- a/builtin/providers/aws/resource_aws_security_group.go +++ b/builtin/providers/aws/resource_aws_security_group.go @@ -94,46 +94,8 @@ func resourceAwsSecurityGroup() *schema.Resource { } } -func resourceAwsSecurityGroupIngressHash(v interface{}) int { - var buf bytes.Buffer - m := v.(map[string]interface{}) - buf.WriteString(fmt.Sprintf("%d-", m["from_port"].(int))) - buf.WriteString(fmt.Sprintf("%d-", m["to_port"].(int))) - buf.WriteString(fmt.Sprintf("%s-", m["protocol"].(string))) - - // We need to make sure to sort the strings below so that we always - // generate the same hash code no matter what is in the set. - if v, ok := m["cidr_blocks"]; ok { - vs := v.([]interface{}) - s := make([]string, len(vs)) - for i, raw := range vs { - s[i] = raw.(string) - } - sort.Strings(s) - - for _, v := range s { - buf.WriteString(fmt.Sprintf("%s-", v)) - } - } - if v, ok := m["security_groups"]; ok { - vs := v.(*schema.Set).List() - s := make([]string, len(vs)) - for i, raw := range vs { - s[i] = raw.(string) - } - sort.Strings(s) - - for _, v := range s { - buf.WriteString(fmt.Sprintf("%s-", v)) - } - } - - return hashcode.String(buf.String()) -} - func resourceAwsSecurityGroupCreate(d *schema.ResourceData, meta interface{}) error { - p := meta.(*ResourceProvider) - ec2conn := p.ec2conn + ec2conn := meta.(*AWSClient).ec2conn securityGroupOpts := ec2.SecurityGroup{ Name: d.Get("name").(string), @@ -177,103 +139,8 @@ func resourceAwsSecurityGroupCreate(d *schema.ResourceData, meta interface{}) er return resourceAwsSecurityGroupUpdate(d, meta) } -func resourceAwsSecurityGroupUpdate(d *schema.ResourceData, meta interface{}) error { - p := meta.(*ResourceProvider) - ec2conn := p.ec2conn - - sgRaw, _, err := SGStateRefreshFunc(ec2conn, d.Id())() - if err != nil { - return err - } - if sgRaw == nil { - d.SetId("") - return nil - } - group := sgRaw.(*ec2.SecurityGroupInfo).SecurityGroup - - if d.HasChange("ingress") { - o, n := d.GetChange("ingress") - if o == nil { - o = new(schema.Set) - } - if n == nil { - n = new(schema.Set) - } - - os := o.(*schema.Set) - ns := n.(*schema.Set) - - remove := expandIPPerms(d.Id(), os.Difference(ns).List()) - add := expandIPPerms(d.Id(), ns.Difference(os).List()) - - // TODO: We need to handle partial state better in the in-between - // in this update. - - // TODO: It'd be nicer to authorize before removing, but then we have - // to deal with complicated unrolling to get individual CIDR blocks - // to avoid authorizing already authorized sources. Removing before - // adding is easier here, and Terraform should be fast enough to - // not have service issues. - - if len(remove) > 0 { - // Revoke the old rules - _, err = ec2conn.RevokeSecurityGroup(group, remove) - if err != nil { - return fmt.Errorf("Error authorizing security group ingress rules: %s", err) - } - } - - if len(add) > 0 { - // Authorize the new rules - _, err := ec2conn.AuthorizeSecurityGroup(group, add) - if err != nil { - return fmt.Errorf("Error authorizing security group ingress rules: %s", err) - } - } - } - - if err := setTags(ec2conn, d); err != nil { - return err - } else { - d.SetPartial("tags") - } - - return nil -} - -func resourceAwsSecurityGroupDelete(d *schema.ResourceData, meta interface{}) error { - p := meta.(*ResourceProvider) - ec2conn := p.ec2conn - - log.Printf("[DEBUG] Security Group destroy: %v", d.Id()) - - return resource.Retry(5*time.Minute, func() error { - _, err := ec2conn.DeleteSecurityGroup(ec2.SecurityGroup{Id: d.Id()}) - if err != nil { - ec2err, ok := err.(*ec2.Error) - if !ok { - return err - } - - switch ec2err.Code { - case "InvalidGroup.NotFound": - return nil - case "DependencyViolation": - // If it is a dependency violation, we want to retry - return err - default: - // Any other error, we want to quit the retry loop immediately - return resource.RetryError{err} - } - } - - return nil - }) -} - func resourceAwsSecurityGroupRead(d *schema.ResourceData, meta interface{}) error { - p := meta.(*ResourceProvider) - ec2conn := p.ec2conn + ec2conn := meta.(*AWSClient).ec2conn sgRaw, _, err := SGStateRefreshFunc(ec2conn, d.Id())() if err != nil { @@ -348,6 +215,135 @@ func resourceAwsSecurityGroupRead(d *schema.ResourceData, meta interface{}) erro return nil } +func resourceAwsSecurityGroupUpdate(d *schema.ResourceData, meta interface{}) error { + ec2conn := meta.(*AWSClient).ec2conn + + sgRaw, _, err := SGStateRefreshFunc(ec2conn, d.Id())() + if err != nil { + return err + } + if sgRaw == nil { + d.SetId("") + return nil + } + group := sgRaw.(*ec2.SecurityGroupInfo).SecurityGroup + + if d.HasChange("ingress") { + o, n := d.GetChange("ingress") + if o == nil { + o = new(schema.Set) + } + if n == nil { + n = new(schema.Set) + } + + os := o.(*schema.Set) + ns := n.(*schema.Set) + + remove := expandIPPerms(d.Id(), os.Difference(ns).List()) + add := expandIPPerms(d.Id(), ns.Difference(os).List()) + + // TODO: We need to handle partial state better in the in-between + // in this update. + + // TODO: It'd be nicer to authorize before removing, but then we have + // to deal with complicated unrolling to get individual CIDR blocks + // to avoid authorizing already authorized sources. Removing before + // adding is easier here, and Terraform should be fast enough to + // not have service issues. + + if len(remove) > 0 { + // Revoke the old rules + _, err = ec2conn.RevokeSecurityGroup(group, remove) + if err != nil { + return fmt.Errorf("Error authorizing security group ingress rules: %s", err) + } + } + + if len(add) > 0 { + // Authorize the new rules + _, err := ec2conn.AuthorizeSecurityGroup(group, add) + if err != nil { + return fmt.Errorf("Error authorizing security group ingress rules: %s", err) + } + } + } + + if err := setTags(ec2conn, d); err != nil { + return err + } else { + d.SetPartial("tags") + } + + return nil +} + +func resourceAwsSecurityGroupDelete(d *schema.ResourceData, meta interface{}) error { + ec2conn := meta.(*AWSClient).ec2conn + + log.Printf("[DEBUG] Security Group destroy: %v", d.Id()) + + return resource.Retry(5*time.Minute, func() error { + _, err := ec2conn.DeleteSecurityGroup(ec2.SecurityGroup{Id: d.Id()}) + if err != nil { + ec2err, ok := err.(*ec2.Error) + if !ok { + return err + } + + switch ec2err.Code { + case "InvalidGroup.NotFound": + return nil + case "DependencyViolation": + // If it is a dependency violation, we want to retry + return err + default: + // Any other error, we want to quit the retry loop immediately + return resource.RetryError{err} + } + } + + return nil + }) +} + +func resourceAwsSecurityGroupIngressHash(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + buf.WriteString(fmt.Sprintf("%d-", m["from_port"].(int))) + buf.WriteString(fmt.Sprintf("%d-", m["to_port"].(int))) + buf.WriteString(fmt.Sprintf("%s-", m["protocol"].(string))) + + // We need to make sure to sort the strings below so that we always + // generate the same hash code no matter what is in the set. + if v, ok := m["cidr_blocks"]; ok { + vs := v.([]interface{}) + s := make([]string, len(vs)) + for i, raw := range vs { + s[i] = raw.(string) + } + sort.Strings(s) + + for _, v := range s { + buf.WriteString(fmt.Sprintf("%s-", v)) + } + } + if v, ok := m["security_groups"]; ok { + vs := v.(*schema.Set).List() + s := make([]string, len(vs)) + for i, raw := range vs { + s[i] = raw.(string) + } + sort.Strings(s) + + for _, v := range s { + buf.WriteString(fmt.Sprintf("%s-", v)) + } + } + + return hashcode.String(buf.String()) +} + // SGStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch // a security group. func SGStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc { diff --git a/builtin/providers/aws/resource_aws_security_group_test.go b/builtin/providers/aws/resource_aws_security_group_test.go index 5ff4d4c49..8ee38f89e 100644 --- a/builtin/providers/aws/resource_aws_security_group_test.go +++ b/builtin/providers/aws/resource_aws_security_group_test.go @@ -174,7 +174,7 @@ func TestAccAWSSecurityGroup_Change(t *testing.T) { } func testAccCheckAWSSecurityGroupDestroy(s *terraform.State) error { - conn := testAccProvider.ec2conn + conn := testAccProvider.Meta().(*AWSClient).ec2conn for _, rs := range s.RootModule().Resources { if rs.Type != "aws_security_group" { @@ -221,7 +221,7 @@ func testAccCheckAWSSecurityGroupExists(n string, group *ec2.SecurityGroupInfo) return fmt.Errorf("No Security Group is set") } - conn := testAccProvider.ec2conn + conn := testAccProvider.Meta().(*AWSClient).ec2conn sgs := []ec2.SecurityGroup{ ec2.SecurityGroup{ Id: rs.Primary.ID, diff --git a/builtin/providers/aws/resource_aws_subnet.go b/builtin/providers/aws/resource_aws_subnet.go index 1db1aa6f0..7bb88f58f 100644 --- a/builtin/providers/aws/resource_aws_subnet.go +++ b/builtin/providers/aws/resource_aws_subnet.go @@ -49,8 +49,7 @@ func resourceAwsSubnet() *schema.Resource { } func resourceAwsSubnetCreate(d *schema.ResourceData, meta interface{}) error { - p := meta.(*ResourceProvider) - ec2conn := p.ec2conn + ec2conn := meta.(*AWSClient).ec2conn createOpts := &ec2.CreateSubnet{ AvailabilityZone: d.Get("availability_zone").(string), @@ -90,8 +89,7 @@ func resourceAwsSubnetCreate(d *schema.ResourceData, meta interface{}) error { } func resourceAwsSubnetRead(d *schema.ResourceData, meta interface{}) error { - p := meta.(*ResourceProvider) - ec2conn := p.ec2conn + ec2conn := meta.(*AWSClient).ec2conn resp, err := ec2conn.DescribeSubnets([]string{d.Id()}, ec2.NewFilter()) @@ -114,8 +112,7 @@ func resourceAwsSubnetRead(d *schema.ResourceData, meta interface{}) error { } func resourceAwsSubnetUpdate(d *schema.ResourceData, meta interface{}) error { - p := meta.(*ResourceProvider) - ec2conn := p.ec2conn + ec2conn := meta.(*AWSClient).ec2conn d.Partial(true) @@ -148,8 +145,7 @@ func resourceAwsSubnetUpdate(d *schema.ResourceData, meta interface{}) error { } func resourceAwsSubnetDelete(d *schema.ResourceData, meta interface{}) error { - p := meta.(*ResourceProvider) - ec2conn := p.ec2conn + ec2conn := meta.(*AWSClient).ec2conn log.Printf("[INFO] Deleting subnet: %s", d.Id()) if _, err := ec2conn.DeleteSubnet(d.Id()); err != nil { diff --git a/builtin/providers/aws/resource_aws_subnet_test.go b/builtin/providers/aws/resource_aws_subnet_test.go index 3941891f9..461c27269 100644 --- a/builtin/providers/aws/resource_aws_subnet_test.go +++ b/builtin/providers/aws/resource_aws_subnet_test.go @@ -42,7 +42,7 @@ func TestAccAWSSubnet(t *testing.T) { } func testAccCheckSubnetDestroy(s *terraform.State) error { - conn := testAccProvider.ec2conn + conn := testAccProvider.Meta().(*AWSClient).ec2conn for _, rs := range s.RootModule().Resources { if rs.Type != "aws_subnet" { @@ -84,7 +84,7 @@ func testAccCheckSubnetExists(n string, v *ec2.Subnet) resource.TestCheckFunc { return fmt.Errorf("No ID is set") } - conn := testAccProvider.ec2conn + conn := testAccProvider.Meta().(*AWSClient).ec2conn resp, err := conn.DescribeSubnets( []string{rs.Primary.ID}, ec2.NewFilter()) if err != nil { diff --git a/builtin/providers/aws/resource_aws_vpc.go b/builtin/providers/aws/resource_aws_vpc.go index e399384bd..da04543ff 100644 --- a/builtin/providers/aws/resource_aws_vpc.go +++ b/builtin/providers/aws/resource_aws_vpc.go @@ -47,8 +47,7 @@ func resourceAwsVpc() *schema.Resource { } func resourceAwsVpcCreate(d *schema.ResourceData, meta interface{}) error { - p := meta.(*ResourceProvider) - ec2conn := p.ec2conn + ec2conn := meta.(*AWSClient).ec2conn // Create the VPC createOpts := &ec2.CreateVpc{ @@ -89,73 +88,8 @@ func resourceAwsVpcCreate(d *schema.ResourceData, meta interface{}) error { return resourceAwsVpcUpdate(d, meta) } -func resourceAwsVpcUpdate(d *schema.ResourceData, meta interface{}) error { - p := meta.(*ResourceProvider) - ec2conn := p.ec2conn - - // Turn on partial mode - d.Partial(true) - - if d.HasChange("enable_dns_hostnames") { - options := new(ec2.ModifyVpcAttribute) - options.EnableDnsHostnames = d.Get("enable_dns_hostnames").(bool) - options.SetEnableDnsHostnames = true - - log.Printf( - "[INFO] Modifying enable_dns_hostnames vpc attribute for %s: %#v", - d.Id(), options) - if _, err := ec2conn.ModifyVpcAttribute(d.Id(), options); err != nil { - return err - } - - d.SetPartial("enable_dns_hostnames") - } - - if d.HasChange("enable_dns_support") { - options := new(ec2.ModifyVpcAttribute) - options.EnableDnsSupport = d.Get("enable_dns_support").(bool) - options.SetEnableDnsSupport = true - - log.Printf( - "[INFO] Modifying enable_dns_support vpc attribute for %s: %#v", - d.Id(), options) - if _, err := ec2conn.ModifyVpcAttribute(d.Id(), options); err != nil { - return err - } - - d.SetPartial("enable_dns_support") - } - - if err := setTags(ec2conn, d); err != nil { - return err - } else { - d.SetPartial("tags") - } - - d.Partial(false) - return resourceAwsVpcRead(d, meta) -} - -func resourceAwsVpcDelete(d *schema.ResourceData, meta interface{}) error { - p := meta.(*ResourceProvider) - ec2conn := p.ec2conn - - log.Printf("[INFO] Deleting VPC: %s", d.Id()) - if _, err := ec2conn.DeleteVpc(d.Id()); err != nil { - ec2err, ok := err.(*ec2.Error) - if ok && ec2err.Code == "InvalidVpcID.NotFound" { - return nil - } - - return fmt.Errorf("Error deleting VPC: %s", err) - } - - return nil -} - func resourceAwsVpcRead(d *schema.ResourceData, meta interface{}) error { - p := meta.(*ResourceProvider) - ec2conn := p.ec2conn + ec2conn := meta.(*AWSClient).ec2conn // Refresh the VPC state vpcRaw, _, err := VPCStateRefreshFunc(ec2conn, d.Id())() @@ -201,6 +135,68 @@ func resourceAwsVpcRead(d *schema.ResourceData, meta interface{}) error { return nil } +func resourceAwsVpcUpdate(d *schema.ResourceData, meta interface{}) error { + ec2conn := meta.(*AWSClient).ec2conn + + // Turn on partial mode + d.Partial(true) + + if d.HasChange("enable_dns_hostnames") { + options := new(ec2.ModifyVpcAttribute) + options.EnableDnsHostnames = d.Get("enable_dns_hostnames").(bool) + options.SetEnableDnsHostnames = true + + log.Printf( + "[INFO] Modifying enable_dns_hostnames vpc attribute for %s: %#v", + d.Id(), options) + if _, err := ec2conn.ModifyVpcAttribute(d.Id(), options); err != nil { + return err + } + + d.SetPartial("enable_dns_hostnames") + } + + if d.HasChange("enable_dns_support") { + options := new(ec2.ModifyVpcAttribute) + options.EnableDnsSupport = d.Get("enable_dns_support").(bool) + options.SetEnableDnsSupport = true + + log.Printf( + "[INFO] Modifying enable_dns_support vpc attribute for %s: %#v", + d.Id(), options) + if _, err := ec2conn.ModifyVpcAttribute(d.Id(), options); err != nil { + return err + } + + d.SetPartial("enable_dns_support") + } + + if err := setTags(ec2conn, d); err != nil { + return err + } else { + d.SetPartial("tags") + } + + d.Partial(false) + return resourceAwsVpcRead(d, meta) +} + +func resourceAwsVpcDelete(d *schema.ResourceData, meta interface{}) error { + ec2conn := meta.(*AWSClient).ec2conn + + log.Printf("[INFO] Deleting VPC: %s", d.Id()) + if _, err := ec2conn.DeleteVpc(d.Id()); err != nil { + ec2err, ok := err.(*ec2.Error) + if ok && ec2err.Code == "InvalidVpcID.NotFound" { + return nil + } + + return fmt.Errorf("Error deleting VPC: %s", err) + } + + return nil +} + // VPCStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch // a VPC. func VPCStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc { diff --git a/builtin/providers/aws/resource_aws_vpc_test.go b/builtin/providers/aws/resource_aws_vpc_test.go index 86c85655e..4ef2e77aa 100644 --- a/builtin/providers/aws/resource_aws_vpc_test.go +++ b/builtin/providers/aws/resource_aws_vpc_test.go @@ -91,7 +91,7 @@ func TestAccVpcUpdate(t *testing.T) { } func testAccCheckVpcDestroy(s *terraform.State) error { - conn := testAccProvider.ec2conn + conn := testAccProvider.Meta().(*AWSClient).ec2conn for _, rs := range s.RootModule().Resources { if rs.Type != "aws_vpc" { @@ -142,7 +142,7 @@ func testAccCheckVpcExists(n string, vpc *ec2.VPC) resource.TestCheckFunc { return fmt.Errorf("No VPC ID is set") } - conn := testAccProvider.ec2conn + conn := testAccProvider.Meta().(*AWSClient).ec2conn resp, err := conn.DescribeVpcs([]string{rs.Primary.ID}, ec2.NewFilter()) if err != nil { return err diff --git a/builtin/providers/aws/resource_provider.go b/builtin/providers/aws/resource_provider.go deleted file mode 100644 index 4f7818743..000000000 --- a/builtin/providers/aws/resource_provider.go +++ /dev/null @@ -1,135 +0,0 @@ -package aws - -import ( - "log" - - "github.com/hashicorp/terraform/helper/config" - "github.com/hashicorp/terraform/helper/multierror" - "github.com/hashicorp/terraform/helper/schema" - "github.com/hashicorp/terraform/terraform" - "github.com/mitchellh/goamz/autoscaling" - "github.com/mitchellh/goamz/ec2" - "github.com/mitchellh/goamz/elb" - "github.com/mitchellh/goamz/rds" - "github.com/mitchellh/goamz/route53" - "github.com/mitchellh/goamz/s3" -) - -type ResourceProvider struct { - Config Config - - ec2conn *ec2.EC2 - elbconn *elb.ELB - autoscalingconn *autoscaling.AutoScaling - s3conn *s3.S3 - rdsconn *rds.Rds - route53 *route53.Route53 - - // This is the schema.Provider. Eventually this will replace much - // of this structure. For now it is an element of it for compatiblity. - p *schema.Provider -} - -func (p *ResourceProvider) Input( - input terraform.UIInput, - c *terraform.ResourceConfig) (*terraform.ResourceConfig, error) { - return Provider().Input(input, c) -} - -func (p *ResourceProvider) Validate(c *terraform.ResourceConfig) ([]string, []error) { - return Provider().Validate(c) -} - -func (p *ResourceProvider) ValidateResource( - t string, c *terraform.ResourceConfig) ([]string, []error) { - prov := Provider() - if _, ok := prov.ResourcesMap[t]; ok { - return prov.ValidateResource(t, c) - } - - return resourceMap.Validate(t, c) -} - -func (p *ResourceProvider) Configure(c *terraform.ResourceConfig) error { - if _, err := config.Decode(&p.Config, c.Config); err != nil { - return err - } - - // Get the auth and region. This can fail if keys/regions were not - // specified and we're attempting to use the environment. - var errs []error - log.Println("[INFO] Building AWS auth structure") - auth, err := p.Config.AWSAuth() - if err != nil { - errs = append(errs, err) - } - - log.Println("[INFO] Building AWS region structure") - region, err := p.Config.AWSRegion() - if err != nil { - errs = append(errs, err) - } - - if len(errs) == 0 { - log.Println("[INFO] Initializing EC2 connection") - p.ec2conn = ec2.New(auth, region) - log.Println("[INFO] Initializing ELB connection") - p.elbconn = elb.New(auth, region) - log.Println("[INFO] Initializing AutoScaling connection") - p.autoscalingconn = autoscaling.New(auth, region) - log.Println("[INFO] Initializing S3 connection") - p.s3conn = s3.New(auth, region) - log.Println("[INFO] Initializing RDS connection") - p.rdsconn = rds.New(auth, region) - log.Println("[INFO] Initializing Route53 connection") - p.route53 = route53.New(auth, region) - } - - if len(errs) > 0 { - return &multierror.Error{Errors: errs} - } - - // Create the provider, set the meta - p.p = Provider() - p.p.SetMeta(p) - - return nil -} - -func (p *ResourceProvider) Apply( - info *terraform.InstanceInfo, - s *terraform.InstanceState, - d *terraform.InstanceDiff) (*terraform.InstanceState, error) { - if _, ok := p.p.ResourcesMap[info.Type]; ok { - return p.p.Apply(info, s, d) - } - - return resourceMap.Apply(info, s, d, p) -} - -func (p *ResourceProvider) Diff( - info *terraform.InstanceInfo, - s *terraform.InstanceState, - c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { - if _, ok := p.p.ResourcesMap[info.Type]; ok { - return p.p.Diff(info, s, c) - } - - return resourceMap.Diff(info, s, c, p) -} - -func (p *ResourceProvider) Refresh( - info *terraform.InstanceInfo, - s *terraform.InstanceState) (*terraform.InstanceState, error) { - if _, ok := p.p.ResourcesMap[info.Type]; ok { - return p.p.Refresh(info, s) - } - - return resourceMap.Refresh(info, s, p) -} - -func (p *ResourceProvider) Resources() []terraform.ResourceType { - result := resourceMap.Resources() - result = append(result, Provider().Resources()...) - return result -} diff --git a/builtin/providers/aws/resource_provider_test.go b/builtin/providers/aws/resource_provider_test.go deleted file mode 100644 index b376f62b8..000000000 --- a/builtin/providers/aws/resource_provider_test.go +++ /dev/null @@ -1,95 +0,0 @@ -package aws - -import ( - "log" - "os" - "reflect" - "testing" - - "github.com/hashicorp/terraform/config" - "github.com/hashicorp/terraform/terraform" -) - -var testAccProviders map[string]terraform.ResourceProvider -var testAccProvider *ResourceProvider - -func init() { - testAccProvider = new(ResourceProvider) - testAccProviders = map[string]terraform.ResourceProvider{ - "aws": testAccProvider, - } -} - -func TestResourceProvider_impl(t *testing.T) { - var _ terraform.ResourceProvider = new(ResourceProvider) -} - -func TestResourceProvider_Configure(t *testing.T) { - rp := new(ResourceProvider) - - raw := map[string]interface{}{ - "access_key": "foo", - "secret_key": "bar", - "region": "us-east-1", - } - - rawConfig, err := config.NewRawConfig(raw) - if err != nil { - t.Fatalf("err: %s", err) - } - - err = rp.Configure(terraform.NewResourceConfig(rawConfig)) - if err != nil { - t.Fatalf("err: %s", err) - } - - expected := Config{ - AccessKey: "foo", - SecretKey: "bar", - Region: "us-east-1", - } - - if !reflect.DeepEqual(rp.Config, expected) { - t.Fatalf("bad: %#v", rp.Config) - } - - if rp.p == nil { - t.Fatal("provider should be set") - } - if !reflect.DeepEqual(rp, rp.p.Meta()) { - t.Fatalf("meta should be set") - } -} - -func TestResourceProvider_ConfigureBadRegion(t *testing.T) { - rp := new(ResourceProvider) - - raw := map[string]interface{}{ - "access_key": "foo", - "secret_key": "bar", - "region": "blah", - } - - rawConfig, err := config.NewRawConfig(raw) - if err != nil { - t.Fatalf("err: %s", err) - } - - err = rp.Configure(terraform.NewResourceConfig(rawConfig)) - if err == nil { - t.Fatalf("should have err: bad region") - } -} - -func testAccPreCheck(t *testing.T) { - if v := os.Getenv("AWS_ACCESS_KEY"); v == "" { - t.Fatal("AWS_ACCESS_KEY must be set for acceptance tests") - } - if v := os.Getenv("AWS_SECRET_KEY"); v == "" { - t.Fatal("AWS_SECRET_KEY must be set for acceptance tests") - } - if v := os.Getenv("AWS_REGION"); v == "" { - log.Println("[INFO] Test: Using us-west-2 as test region") - os.Setenv("AWS_REGION", "us-west-2") - } -} diff --git a/builtin/providers/aws/resources.go b/builtin/providers/aws/resources.go deleted file mode 100644 index e34235bb0..000000000 --- a/builtin/providers/aws/resources.go +++ /dev/null @@ -1,98 +0,0 @@ -package aws - -import ( - "github.com/hashicorp/terraform/helper/config" - "github.com/hashicorp/terraform/helper/resource" -) - -// resourceMap is the mapping of resources we support to their basic -// operations. This makes it easy to implement new resource types. -var resourceMap *resource.Map - -func init() { - resourceMap = &resource.Map{ - Mapping: map[string]resource.Resource{ - "aws_db_instance": resource.Resource{ - ConfigValidator: resource_aws_db_instance_validation(), - Create: resource_aws_db_instance_create, - Destroy: resource_aws_db_instance_destroy, - Diff: resource_aws_db_instance_diff, - Refresh: resource_aws_db_instance_refresh, - Update: resource_aws_db_instance_update, - }, - - "aws_db_security_group": resource.Resource{ - ConfigValidator: resource_aws_db_security_group_validation(), - Create: resource_aws_db_security_group_create, - Destroy: resource_aws_db_security_group_destroy, - Diff: resource_aws_db_security_group_diff, - Refresh: resource_aws_db_security_group_refresh, - }, - - "aws_internet_gateway": resource.Resource{ - Create: resource_aws_internet_gateway_create, - Destroy: resource_aws_internet_gateway_destroy, - Diff: resource_aws_internet_gateway_diff, - Refresh: resource_aws_internet_gateway_refresh, - Update: resource_aws_internet_gateway_update, - }, - - "aws_route_table": resource.Resource{ - ConfigValidator: &config.Validator{ - Required: []string{ - "vpc_id", - }, - Optional: []string{ - "route.*.cidr_block", - "route.*.gateway_id", - "route.*.instance_id", - }, - }, - Create: resource_aws_route_table_create, - Destroy: resource_aws_route_table_destroy, - Diff: resource_aws_route_table_diff, - Refresh: resource_aws_route_table_refresh, - Update: resource_aws_route_table_update, - }, - - "aws_route_table_association": resource.Resource{ - ConfigValidator: &config.Validator{ - Required: []string{ - "route_table_id", - "subnet_id", - }, - }, - Create: resource_aws_route_table_association_create, - Destroy: resource_aws_route_table_association_destroy, - Diff: resource_aws_route_table_association_diff, - Refresh: resource_aws_route_table_association_refresh, - Update: resource_aws_route_table_association_update, - }, - - "aws_route53_record": resource.Resource{ - ConfigValidator: resource_aws_r53_record_validation(), - Create: resource_aws_r53_record_create, - Destroy: resource_aws_r53_record_destroy, - Diff: resource_aws_r53_record_diff, - Refresh: resource_aws_r53_record_refresh, - Update: resource_aws_r53_record_create, - }, - - "aws_route53_zone": resource.Resource{ - ConfigValidator: resource_aws_r53_zone_validation(), - Create: resource_aws_r53_zone_create, - Destroy: resource_aws_r53_zone_destroy, - Diff: resource_aws_r53_zone_diff, - Refresh: resource_aws_r53_zone_refresh, - }, - - "aws_s3_bucket": resource.Resource{ - ConfigValidator: resource_aws_s3_bucket_validation(), - Create: resource_aws_s3_bucket_create, - Destroy: resource_aws_s3_bucket_destroy, - Diff: resource_aws_s3_bucket_diff, - Refresh: resource_aws_s3_bucket_refresh, - }, - }, - } -}