diff --git a/aws/data_source_aws_storagegateway_local_disk.go b/aws/data_source_aws_storagegateway_local_disk.go new file mode 100644 index 00000000000..fda3fbe05f0 --- /dev/null +++ b/aws/data_source_aws_storagegateway_local_disk.go @@ -0,0 +1,76 @@ +package aws + +import ( + "errors" + "fmt" + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/storagegateway" + "github.com/hashicorp/terraform/helper/schema" +) + +func dataSourceAwsStorageGatewayLocalDisk() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAwsStorageGatewayLocalDiskRead, + + Schema: map[string]*schema.Schema{ + "disk_id": { + Type: schema.TypeString, + Computed: true, + }, + "disk_path": { + Type: schema.TypeString, + Required: true, + }, + "gateway_arn": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateArn, + }, + }, + } +} + +func dataSourceAwsStorageGatewayLocalDiskRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).storagegatewayconn + + input := &storagegateway.ListLocalDisksInput{ + GatewayARN: aws.String(d.Get("gateway_arn").(string)), + } + + log.Printf("[DEBUG] Reading Storage Gateway Local Disk: %s", input) + output, err := conn.ListLocalDisks(input) + if err != nil { + return fmt.Errorf("error reading Storage Gateway Local Disk: %s", err) + } + + if output == nil || len(output.Disks) == 0 { + return errors.New("no results found for query, try adjusting your search criteria") + } + + diskPath := d.Get("disk_path").(string) + var matchingDisks []*storagegateway.Disk + + for _, disk := range output.Disks { + if aws.StringValue(disk.DiskPath) == diskPath { + matchingDisks = append(matchingDisks, disk) + } + } + + if len(matchingDisks) == 0 { + return errors.New("no results found for query, try adjusting your search criteria") + } + + if len(matchingDisks) > 1 { + return errors.New("multiple results found for query, try adjusting your search criteria") + } + + disk := matchingDisks[0] + + d.SetId(aws.StringValue(disk.DiskId)) + d.Set("disk_id", disk.DiskId) + d.Set("disk_path", disk.DiskPath) + + return nil +} diff --git a/aws/data_source_aws_storagegateway_local_disk_test.go b/aws/data_source_aws_storagegateway_local_disk_test.go new file mode 100644 index 00000000000..bf31af61d91 --- /dev/null +++ b/aws/data_source_aws_storagegateway_local_disk_test.go @@ -0,0 +1,97 @@ +package aws + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSStorageGatewayLocalDiskDataSource_DiskPath(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + dataSourceName := "data.aws_storagegateway_local_disk.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccAWSStorageGatewayLocalDiskDataSourceConfig_DiskPath_NonExistent(rName), + ExpectError: regexp.MustCompile(`no results found`), + }, + { + Config: testAccAWSStorageGatewayLocalDiskDataSourceConfig_DiskPath(rName), + Check: resource.ComposeTestCheckFunc( + testAccAWSStorageGatewayLocalDiskDataSourceExists(dataSourceName), + resource.TestCheckResourceAttrSet(dataSourceName, "disk_id"), + ), + }, + }, + }) +} + +func testAccAWSStorageGatewayLocalDiskDataSourceExists(dataSourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + _, ok := s.RootModule().Resources[dataSourceName] + if !ok { + return fmt.Errorf("not found: %s", dataSourceName) + } + + return nil + } +} + +func testAccAWSStorageGatewayLocalDiskDataSourceConfig_DiskPath(rName string) string { + return testAccAWSStorageGatewayGatewayConfig_GatewayType_FileS3(rName) + fmt.Sprintf(` +resource "aws_ebs_volume" "test" { + availability_zone = "${aws_instance.test.availability_zone}" + size = "10" + type = "gp2" + + tags { + Name = %q + } +} + +resource "aws_volume_attachment" "test" { + device_name = "/dev/xvdb" + force_detach = true + instance_id = "${aws_instance.test.id}" + volume_id = "${aws_ebs_volume.test.id}" +} + +data "aws_storagegateway_local_disk" "test" { + disk_path = "${aws_volume_attachment.test.device_name}" + gateway_arn = "${aws_storagegateway_gateway.test.arn}" +} +`, rName) +} + +func testAccAWSStorageGatewayLocalDiskDataSourceConfig_DiskPath_NonExistent(rName string) string { + return testAccAWSStorageGatewayGatewayConfig_GatewayType_FileS3(rName) + fmt.Sprintf(` +resource "aws_ebs_volume" "test" { + availability_zone = "${aws_instance.test.availability_zone}" + size = "10" + type = "gp2" + + tags { + Name = %q + } +} + +resource "aws_volume_attachment" "test" { + device_name = "/dev/xvdb" + force_detach = true + instance_id = "${aws_instance.test.id}" + volume_id = "${aws_ebs_volume.test.id}" +} + +data "aws_storagegateway_local_disk" "test" { + disk_path = "/dev/xvdz" + gateway_arn = "${aws_storagegateway_gateway.test.arn}" +} +`, rName) +} diff --git a/aws/provider.go b/aws/provider.go index a9293181ad7..d91222e5430 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -250,6 +250,7 @@ func Provider() terraform.ResourceProvider { "aws_sns_topic": dataSourceAwsSnsTopic(), "aws_sqs_queue": dataSourceAwsSqsQueue(), "aws_ssm_parameter": dataSourceAwsSsmParameter(), + "aws_storagegateway_local_disk": dataSourceAwsStorageGatewayLocalDisk(), "aws_subnet": dataSourceAwsSubnet(), "aws_subnet_ids": dataSourceAwsSubnetIDs(), "aws_vpcs": dataSourceAwsVpcs(), @@ -586,6 +587,7 @@ func Provider() terraform.ResourceProvider { "aws_ssm_resource_data_sync": resourceAwsSsmResourceDataSync(), "aws_storagegateway_gateway": resourceAwsStorageGatewayGateway(), "aws_storagegateway_nfs_file_share": resourceAwsStorageGatewayNfsFileShare(), + "aws_storagegateway_working_storage": resourceAwsStorageGatewayWorkingStorage(), "aws_spot_datafeed_subscription": resourceAwsSpotDataFeedSubscription(), "aws_spot_instance_request": resourceAwsSpotInstanceRequest(), "aws_spot_fleet_request": resourceAwsSpotFleetRequest(), diff --git a/aws/resource_aws_storagegateway_working_storage.go b/aws/resource_aws_storagegateway_working_storage.go new file mode 100644 index 00000000000..52c5787c3c5 --- /dev/null +++ b/aws/resource_aws_storagegateway_working_storage.go @@ -0,0 +1,131 @@ +package aws + +import ( + "fmt" + "log" + "strings" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/arn" + "github.com/aws/aws-sdk-go/service/storagegateway" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAwsStorageGatewayWorkingStorage() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsStorageGatewayWorkingStorageCreate, + Read: resourceAwsStorageGatewayWorkingStorageRead, + Delete: schema.Noop, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "disk_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "gateway_arn": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateArn, + }, + }, + } +} + +func resourceAwsStorageGatewayWorkingStorageCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).storagegatewayconn + + diskID := d.Get("disk_id").(string) + gatewayARN := d.Get("gateway_arn").(string) + + input := &storagegateway.AddWorkingStorageInput{ + DiskIds: []*string{aws.String(diskID)}, + GatewayARN: aws.String(gatewayARN), + } + + log.Printf("[DEBUG] Adding Storage Gateway working storage: %s", input) + _, err := conn.AddWorkingStorage(input) + if err != nil { + return fmt.Errorf("error adding Storage Gateway working storage: %s", err) + } + + d.SetId(fmt.Sprintf("%s:%s", gatewayARN, diskID)) + + return resourceAwsStorageGatewayWorkingStorageRead(d, meta) +} + +func resourceAwsStorageGatewayWorkingStorageRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).storagegatewayconn + + gatewayARN, diskID, err := decodeStorageGatewayWorkingStorageID(d.Id()) + if err != nil { + return err + } + + input := &storagegateway.DescribeWorkingStorageInput{ + GatewayARN: aws.String(gatewayARN), + } + + log.Printf("[DEBUG] Reading Storage Gateway working storage: %s", input) + output, err := conn.DescribeWorkingStorage(input) + if err != nil { + if isAWSErrStorageGatewayGatewayNotFound(err) { + log.Printf("[WARN] Storage Gateway working storage %q not found - removing from state", d.Id()) + d.SetId("") + return nil + } + return fmt.Errorf("error reading Storage Gateway working storage: %s", err) + } + + if output == nil || len(output.DiskIds) == 0 { + log.Printf("[WARN] Storage Gateway working storage %q not found - removing from state", d.Id()) + d.SetId("") + return nil + } + + found := false + for _, existingDiskID := range output.DiskIds { + if aws.StringValue(existingDiskID) == diskID { + found = true + break + } + } + + if !found { + log.Printf("[WARN] Storage Gateway working storage %q not found - removing from state", d.Id()) + d.SetId("") + return nil + } + + d.Set("disk_id", diskID) + d.Set("gateway_arn", gatewayARN) + + return nil +} + +func decodeStorageGatewayWorkingStorageID(id string) (string, string, error) { + // id = arn:aws:storagegateway:us-east-1:123456789012:gateway/sgw-12345678:pci-0000:03:00.0-scsi-0:0:0:0 + idFormatErr := fmt.Errorf("expected ID in form of GatewayARN:DiskId, received: %s", id) + gatewayARNAndDisk, err := arn.Parse(id) + if err != nil { + return "", "", idFormatErr + } + // gatewayARNAndDisk.Resource = gateway/sgw-12345678:pci-0000:03:00.0-scsi-0:0:0:0 + resourceParts := strings.SplitN(gatewayARNAndDisk.Resource, ":", 2) + if len(resourceParts) != 2 { + return "", "", idFormatErr + } + // resourceParts = ["gateway/sgw-12345678", "pci-0000:03:00.0-scsi-0:0:0:0"] + gatewayARN := &arn.ARN{ + AccountID: gatewayARNAndDisk.AccountID, + Partition: gatewayARNAndDisk.Partition, + Region: gatewayARNAndDisk.Region, + Service: gatewayARNAndDisk.Service, + Resource: resourceParts[0], + } + return gatewayARN.String(), resourceParts[1], nil +} diff --git a/aws/resource_aws_storagegateway_working_storage_test.go b/aws/resource_aws_storagegateway_working_storage_test.go new file mode 100644 index 00000000000..7351cfa46cf --- /dev/null +++ b/aws/resource_aws_storagegateway_working_storage_test.go @@ -0,0 +1,165 @@ +package aws + +import ( + "fmt" + "regexp" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/storagegateway" + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestDecodeStorageGatewayWorkingStorageID(t *testing.T) { + var testCases = []struct { + Input string + ExpectedGatewayARN string + ExpectedDiskID string + ErrCount int + }{ + { + Input: "arn:aws:storagegateway:us-east-1:123456789012:gateway/sgw-12345678:pci-0000:03:00.0-scsi-0:0:0:0", + ExpectedGatewayARN: "arn:aws:storagegateway:us-east-1:123456789012:gateway/sgw-12345678", + ExpectedDiskID: "pci-0000:03:00.0-scsi-0:0:0:0", + ErrCount: 0, + }, + { + Input: "sgw-12345678:pci-0000:03:00.0-scsi-0:0:0:0", + ErrCount: 1, + }, + { + Input: "example:pci-0000:03:00.0-scsi-0:0:0:0", + ErrCount: 1, + }, + { + Input: "arn:aws:storagegateway:us-east-1:123456789012:gateway/sgw-12345678", + ErrCount: 1, + }, + { + Input: "pci-0000:03:00.0-scsi-0:0:0:0", + ErrCount: 1, + }, + { + Input: "gateway/sgw-12345678", + ErrCount: 1, + }, + { + Input: "sgw-12345678", + ErrCount: 1, + }, + } + + for _, tc := range testCases { + gatewayARN, diskID, err := decodeStorageGatewayWorkingStorageID(tc.Input) + if tc.ErrCount == 0 && err != nil { + t.Fatalf("expected %q not to trigger an error, received: %s", tc.Input, err) + } + if tc.ErrCount > 0 && err == nil { + t.Fatalf("expected %q to trigger an error", tc.Input) + } + if gatewayARN != tc.ExpectedGatewayARN { + t.Fatalf("expected %q to return Gateway ARN %q, received: %s", tc.Input, tc.ExpectedGatewayARN, gatewayARN) + } + if diskID != tc.ExpectedDiskID { + t.Fatalf("expected %q to return Disk ID %q, received: %s", tc.Input, tc.ExpectedDiskID, diskID) + } + } +} + +func TestAccAWSStorageGatewayWorkingStorage_Basic(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_storagegateway_working_storage.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + // Storage Gateway API does not support removing working storages + // CheckDestroy: testAccCheckAWSStorageGatewayWorkingStorageDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSStorageGatewayWorkingStorageConfig_Basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSStorageGatewayWorkingStorageExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "disk_id"), + resource.TestMatchResourceAttr(resourceName, "gateway_arn", regexp.MustCompile(`^arn:[^:]+:storagegateway:[^:]+:[^:]+:gateway/sgw-.+$`)), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckAWSStorageGatewayWorkingStorageExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + conn := testAccProvider.Meta().(*AWSClient).storagegatewayconn + + gatewayARN, diskID, err := decodeStorageGatewayWorkingStorageID(rs.Primary.ID) + if err != nil { + return err + } + + input := &storagegateway.DescribeWorkingStorageInput{ + GatewayARN: aws.String(gatewayARN), + } + + output, err := conn.DescribeWorkingStorage(input) + + if err != nil { + return fmt.Errorf("error reading Storage Gateway working storage: %s", err) + } + + if output == nil || len(output.DiskIds) == 0 { + return fmt.Errorf("Storage Gateway working storage %q not found", rs.Primary.ID) + } + + for _, existingDiskID := range output.DiskIds { + if aws.StringValue(existingDiskID) == diskID { + return nil + } + } + + return fmt.Errorf("Storage Gateway working storage %q not found", rs.Primary.ID) + } +} + +func testAccAWSStorageGatewayWorkingStorageConfig_Basic(rName string) string { + return testAccAWSStorageGatewayGatewayConfig_GatewayType_Stored(rName) + fmt.Sprintf(` +resource "aws_ebs_volume" "test" { + availability_zone = "${aws_instance.test.availability_zone}" + size = "10" + type = "gp2" + + tags { + Name = %q + } +} + +resource "aws_volume_attachment" "test" { + device_name = "/dev/xvdc" + force_detach = true + instance_id = "${aws_instance.test.id}" + volume_id = "${aws_ebs_volume.test.id}" +} + +data "aws_storagegateway_local_disk" "test" { + disk_path = "${aws_volume_attachment.test.device_name}" + gateway_arn = "${aws_storagegateway_gateway.test.arn}" +} + +resource "aws_storagegateway_working_storage" "test" { + disk_id = "${data.aws_storagegateway_local_disk.test.id}" + gateway_arn = "${aws_storagegateway_gateway.test.arn}" +} +`, rName) +} diff --git a/website/aws.erb b/website/aws.erb index a1fa9231139..ffadd674dbc 100644 --- a/website/aws.erb +++ b/website/aws.erb @@ -338,6 +338,9 @@