From 61064720482ef101158670fc17922ca156369799 Mon Sep 17 00:00:00 2001 From: Atsushi Ishibashi Date: Thu, 2 Nov 2017 21:05:51 +0900 Subject: [PATCH 1/4] New Resource: aws_dx_lag --- aws/config.go | 3 + aws/provider.go | 1 + aws/resource_aws_dx_lag.go | 162 ++++++++++++++++++++++++++++ aws/resource_aws_dx_lag_test.go | 77 +++++++++++++ aws/validators.go | 18 ++++ aws/validators_test.go | 27 +++++ website/aws.erb | 9 ++ website/docs/r/dx_lag.html.markdown | 39 +++++++ 8 files changed, 336 insertions(+) create mode 100644 aws/resource_aws_dx_lag.go create mode 100644 aws/resource_aws_dx_lag_test.go create mode 100644 website/docs/r/dx_lag.html.markdown diff --git a/aws/config.go b/aws/config.go index 9fc0a088bcc1..843c7a58dc93 100644 --- a/aws/config.go +++ b/aws/config.go @@ -33,6 +33,7 @@ import ( "github.com/aws/aws-sdk-go/service/configservice" "github.com/aws/aws-sdk-go/service/databasemigrationservice" "github.com/aws/aws-sdk-go/service/devicefarm" + "github.com/aws/aws-sdk-go/service/directconnect" "github.com/aws/aws-sdk-go/service/directoryservice" "github.com/aws/aws-sdk-go/service/dynamodb" "github.com/aws/aws-sdk-go/service/ec2" @@ -180,6 +181,7 @@ type AWSClient struct { wafregionalconn *wafregional.WAFRegional iotconn *iot.IoT batchconn *batch.Batch + dxconn *directconnect.DirectConnect } func (c *AWSClient) S3() *s3.S3 { @@ -390,6 +392,7 @@ func (c *Config) Client() (interface{}, error) { client.wafconn = waf.New(sess) client.wafregionalconn = wafregional.New(sess) client.batchconn = batch.New(sess) + client.dxconn = directconnect.New(sess) // Workaround for https://github.com/aws/aws-sdk-go/issues/1376 client.kinesisconn.Handlers.Retry.PushBack(func(r *request.Request) { diff --git a/aws/provider.go b/aws/provider.go index c0ad59f1747a..e24d6451bbea 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -307,6 +307,7 @@ func Provider() terraform.ResourceProvider { "aws_dms_replication_instance": resourceAwsDmsReplicationInstance(), "aws_dms_replication_subnet_group": resourceAwsDmsReplicationSubnetGroup(), "aws_dms_replication_task": resourceAwsDmsReplicationTask(), + "aws_dx_lag": resourceAwsDxLag(), "aws_dynamodb_table": resourceAwsDynamoDbTable(), "aws_ebs_snapshot": resourceAwsEbsSnapshot(), "aws_ebs_volume": resourceAwsEbsVolume(), diff --git a/aws/resource_aws_dx_lag.go b/aws/resource_aws_dx_lag.go new file mode 100644 index 000000000000..800625612b0e --- /dev/null +++ b/aws/resource_aws_dx_lag.go @@ -0,0 +1,162 @@ +package aws + +import ( + "fmt" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/directconnect" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAwsDxLag() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsDxLagCreate, + Read: resourceAwsDxLagRead, + Update: resourceAwsDxLagUpdate, + Delete: resourceAwsDxLagDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "band_width": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateDxLagBandWidth, + }, + "location": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "num_connections": &schema.Schema{ + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + "force_destroy": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + }, + } +} + +func resourceAwsDxLagCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).dxconn + + input := &directconnect.CreateLagInput{ + ConnectionsBandwidth: aws.String(d.Get("band_width").(string)), + LagName: aws.String(d.Get("name").(string)), + Location: aws.String(d.Get("location").(string)), + NumberOfConnections: aws.Int64(int64(d.Get("num_connections").(int))), + } + resp, err := conn.CreateLag(input) + if err != nil { + return err + } + d.SetId(*resp.LagId) + return resourceAwsDxLagRead(d, meta) +} + +func resourceAwsDxLagRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).dxconn + + lagId := d.Id() + input := &directconnect.DescribeLagsInput{ + LagId: aws.String(lagId), + } + resp, err := conn.DescribeLags(input) + if err != nil { + d.SetId("") + return err + } + if len(resp.Lags) != 1 { + d.SetId("") + return fmt.Errorf("[ERROR] Number of DX Lag (%s) isn't one, got %d", lagId, len(resp.Lags)) + } + if d.Id() != *resp.Lags[0].LagId { + d.SetId("") + return fmt.Errorf("[ERROR] DX Lag (%s) not found", lagId) + } + return nil +} + +func resourceAwsDxLagUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).dxconn + + input := &directconnect.UpdateLagInput{ + LagId: aws.String(d.Id()), + } + if d.HasChange("name") { + input.LagName = aws.String(d.Get("name").(string)) + } + _, err := conn.UpdateLag(input) + if err != nil { + return err + } + return nil +} + +func resourceAwsDxLagDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).dxconn + + if d.Get("force_destroy").(bool) { + input := &directconnect.DescribeLagsInput{ + LagId: aws.String(d.Id()), + } + resp, err := conn.DescribeLags(input) + if err != nil { + return err + } + lag := resp.Lags[0] + for _, v := range lag.Connections { + dcinput := &directconnect.DeleteConnectionInput{ + ConnectionId: v.ConnectionId, + } + if _, err := conn.DeleteConnection(dcinput); err != nil { + return err + } + } + } + + input := &directconnect.DeleteLagInput{ + LagId: aws.String(d.Id()), + } + _, err := conn.DeleteLag(input) + if err != nil { + return err + } + deleteStateConf := &resource.StateChangeConf{ + Pending: []string{directconnect.LagStateAvailable, directconnect.LagStateRequested, directconnect.LagStatePending}, + Target: []string{directconnect.LagStateDeleted, directconnect.LagStateDeleting}, + Refresh: dxLagRefreshStateFunc(conn, d.Id()), + Timeout: 10 * time.Minute, + Delay: 10 * time.Second, + MinTimeout: 3 * time.Second, + } + _, err = deleteStateConf.WaitForState() + if err != nil { + return fmt.Errorf("Error waiting for Dx Lag (%s) to be deleted: %s", d.Id(), err) + } + d.SetId("") + return nil +} + +func dxLagRefreshStateFunc(conn *directconnect.DirectConnect, lagId string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + input := &directconnect.DescribeLagsInput{ + LagId: aws.String(lagId), + } + resp, err := conn.DescribeLags(input) + if err != nil { + return nil, "failed", err + } + return resp, *resp.Lags[0].LagState, nil + } +} diff --git a/aws/resource_aws_dx_lag_test.go b/aws/resource_aws_dx_lag_test.go new file mode 100644 index 000000000000..e86808fb6c1c --- /dev/null +++ b/aws/resource_aws_dx_lag_test.go @@ -0,0 +1,77 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/directconnect" + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAwsDxLag_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsDxLagDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDxLagConfig(acctest.RandString(5)), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsDxLagExists("aws_dx_lag.hoge"), + ), + }, + }, + }) +} + +func testAccCheckAwsDxLagDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).dxconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_dx_lag" { + continue + } + + input := &directconnect.DescribeLagsInput{ + LagId: aws.String(rs.Primary.ID), + } + + resp, err := conn.DescribeLags(input) + if err != nil { + return err + } + for _, v := range resp.Lags { + if *v.LagId == rs.Primary.ID && !(*v.LagState == directconnect.LagStateDeleting || *v.LagState == directconnect.LagStateDeleted) { + return fmt.Errorf("[DESTROY ERROR] Dx Lag (%s) found", rs.Primary.ID) + } + } + } + + return nil +} + +func testAccCheckAwsDxLagExists(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + _, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + return nil + } +} + +func testAccDxLagConfig(rName string) string { + return fmt.Sprintf(` + resource "aws_dx_lag" "hoge" { + name = "tf-dx-lag-%s" + band_width = "1Gbps" + location = "EqDC2" + num_connections = 2 + force_destroy = true + } + `, rName) +} diff --git a/aws/validators.go b/aws/validators.go index 14b48919b1ab..b89fe2f7e793 100644 --- a/aws/validators.go +++ b/aws/validators.go @@ -1651,3 +1651,21 @@ func validateCognitoRoles(v map[string]interface{}, k string) (errors []error) { return } + +func validateDxLagBandWidth(v interface{}, k string) (ws []string, errors []error) { + val, ok := v.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %s to be string", k)) + return + } + + validBandWidth := []string{"1Gbps", "10Gbps"} + for _, str := range validBandWidth { + if val == str { + return + } + } + + errors = append(errors, fmt.Errorf("expected %s to be one of %v, got %s", k, validBandWidth, val)) + return +} diff --git a/aws/validators_test.go b/aws/validators_test.go index 2e9dae246b92..f1ae77f509b8 100644 --- a/aws/validators_test.go +++ b/aws/validators_test.go @@ -2603,3 +2603,30 @@ func TestValidateCognitoRoleMappingsType(t *testing.T) { } } } + +func TestValidateDxLagBandWidth(t *testing.T) { + validValues := []string{ + "1Gbps", + "10Gbps", + } + + for _, s := range validValues { + _, errors := validateDxLagBandWidth(s, "match_type") + if len(errors) > 0 { + t.Fatalf("%s should be a valid Direct Connect Lag Bandwidth: %v", s, errors) + } + } + + invalidValues := []string{ + "1gbps", + "10GBPS", + "invalid character", + } + + for _, s := range invalidValues { + _, errors := validateDxLagBandWidth(s, "match_type") + if len(errors) == 0 { + t.Fatalf("%s should not be a valid Direct Connect Lag Bandwidth: %v", s, errors) + } + } +} diff --git a/website/aws.erb b/website/aws.erb index 09f08082a860..54f5f3276c51 100644 --- a/website/aws.erb +++ b/website/aws.erb @@ -512,6 +512,15 @@ + > + Direct Connect Resources + + + > Directory Service Resources