diff --git a/.changelog/21219.txt b/.changelog/21219.txt new file mode 100644 index 00000000000..4870302226c --- /dev/null +++ b/.changelog/21219.txt @@ -0,0 +1,67 @@ +```release-note:note +data-source/aws_security_groups: If no security groups match the specified criteria an empty list is returned (previously an error was raised) +``` + +```release-note:note +data-source/aws_route_tables: The type of the `ids` attribute has changed from Set to List. If no route tables match the specified criteria an empty list is returned (previously an error was raised) +``` + +```release-note:note +data-source/aws_network_interfaces: The type of the `ids` attribute has changed from Set to List. If no network interfaces match the specified criteria an empty list is returned (previously an error was raised) +``` + +```release-note:note +data-source/aws_network_acls: The type of the `ids` attribute has changed from Set to List. If no NACLs match the specified criteria an empty list is returned (previously an error was raised) +``` + +```release-note:note +data-source/aws_ec2_transit_gateway_route_tables: The type of the `ids` attribute has changed from Set to List. If no transit gateway route tables match the specified criteria an empty list is returned (previously an error was raised) +``` + +```release-note:note +data-source/aws_ec2_coip_pools: The type of the `pool_ids` attribute has changed from Set to List. If no COIP pools match the specified criteria an empty list is returned (previously an error was raised) +``` + +```release-note:note +data-source/aws_ec2_local_gateway_route_tables: The type of the `ids` attribute has changed from Set to List. If no local gateway route tables match the specified criteria an empty list is returned (previously an error was raised) +``` + +```release-note:note +data-source/aws_ec2_local_gateway_virtual_interface_groups: The type of the `ids` and `local_gateway_virtual_interface_ids` attributes has changed from Set to List. If no local gateway virtual interface groups match the specified criteria an empty list is returned (previously an error was raised) +``` + +```release-note:note +data-source/aws_ec2_local_gateways: The type of the `ids` attribute has changed from Set to List. If no local gateways match the specified criteria an empty list is returned (previously an error was raised) +``` + +```release-note:note +data-source/aws_ebs_volumes: The type of the `ids` attribute has changed from Set to List. If no volumes match the specified criteria an empty list is returned (previously an error was raised) +``` + +```release-note:note +data-source/aws_cognito_user_pools: The type of the `ids` and `arns` attributes has changed from Set to List. If no volumes match the specified criteria an empty list is returned (previously an error was raised) +``` + +```release-note:note +data-source/aws_ip_ranges: If no ranges match the specified criteria an empty list is returned (previously an error was raised) +``` + +```release-note:note +data-source/aws_efs_access_points: The type of the `ids` and `arns` attributes has changed from Set to List. If no access points match the specified criteria an empty list is returned (previously an error was raised) +``` + +```release-note:note +data-source/aws_emr_release_labels: The type of the `ids` attribute has changed from Set to List. If no release labels match the specified criteria an empty list is returned (previously an error was raised) +``` + +```release-note:note +data-source/aws_inspector_rules_packages: If no rules packages match the specified criteria an empty list is returned (previously an error was raised) +``` + +```release-note:note +data-source/aws_db_event_categories: The type of the `ids` attribute has changed from Set to List. If no event categories match the specified criteria an empty list is returned (previously an error was raised) +``` + +```release-note:note +data-source/aws_ssoadmin_instances: The type of the `identity_store_ids` and `arns` attributes has changed from Set to List. If no instances match the specified criteria an empty list is returned (previously an error was raised) +``` \ No newline at end of file diff --git a/.changelog/5055.txt b/.changelog/5055.txt new file mode 100644 index 00000000000..da9fa294d9c --- /dev/null +++ b/.changelog/5055.txt @@ -0,0 +1,3 @@ +```release-note:note +data-source/aws_instances: If no instances match the specified criteria an empty list is returned (previously an error was raised) +``` \ No newline at end of file diff --git a/internal/service/cognitoidp/user_pools_data_source.go b/internal/service/cognitoidp/user_pools_data_source.go index 0c29cc0e82a..30ed24abdd4 100644 --- a/internal/service/cognitoidp/user_pools_data_source.go +++ b/internal/service/cognitoidp/user_pools_data_source.go @@ -13,85 +13,86 @@ import ( func DataSourceUserPools() *schema.Resource { return &schema.Resource{ Read: dataSourceUserPoolsRead, + Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - }, - "ids": { - Type: schema.TypeSet, + "arns": { + Type: schema.TypeList, Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, }, - "arns": { - Type: schema.TypeSet, + "ids": { + Type: schema.TypeList, Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, }, + "name": { + Type: schema.TypeString, + Required: true, + }, }, } } func dataSourceUserPoolsRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).CognitoIDPConn - name := d.Get("name").(string) - var ids []string - var arns []string - pools, err := getAllCognitoUserPools(conn) + output, err := findUserPoolDescriptionTypes(conn) + if err != nil { - return fmt.Errorf("Error listing cognito user pools: %w", err) + return fmt.Errorf("error reading Cognito User Pools: %w", err) } - for _, pool := range pools { - if name == aws.StringValue(pool.Name) { - id := aws.StringValue(pool.Id) - arn := arn.ARN{ - Partition: meta.(*conns.AWSClient).Partition, - Service: "cognito-idp", - Region: meta.(*conns.AWSClient).Region, - AccountID: meta.(*conns.AWSClient).AccountID, - Resource: fmt.Sprintf("userpool/%s", id), - }.String() - - ids = append(ids, id) - arns = append(arns, arn) + + name := d.Get("name").(string) + var arns, userPoolIDs []string + + for _, v := range output { + if name != aws.StringValue(v.Name) { + continue } - } - if len(ids) == 0 { - return fmt.Errorf("No cognito user pool found with name: %s", name) + userPoolID := aws.StringValue(v.Id) + arn := arn.ARN{ + Partition: meta.(*conns.AWSClient).Partition, + Service: cognitoidentityprovider.ServiceName, + Region: meta.(*conns.AWSClient).Region, + AccountID: meta.(*conns.AWSClient).AccountID, + Resource: fmt.Sprintf("userpool/%s", userPoolID), + }.String() + + userPoolIDs = append(userPoolIDs, userPoolID) + arns = append(arns, arn) } d.SetId(name) - d.Set("ids", ids) + d.Set("ids", userPoolIDs) d.Set("arns", arns) return nil } -func getAllCognitoUserPools(conn *cognitoidentityprovider.CognitoIdentityProvider) ([]*cognitoidentityprovider.UserPoolDescriptionType, error) { - var pools []*cognitoidentityprovider.UserPoolDescriptionType - var nextToken string +func findUserPoolDescriptionTypes(conn *cognitoidentityprovider.CognitoIdentityProvider) ([]*cognitoidentityprovider.UserPoolDescriptionType, error) { + input := &cognitoidentityprovider.ListUserPoolsInput{ + MaxResults: aws.Int64(60), + } + var output []*cognitoidentityprovider.UserPoolDescriptionType - for { - input := &cognitoidentityprovider.ListUserPoolsInput{ - // MaxResults Valid Range: Minimum value of 1. Maximum value of 60 - MaxResults: aws.Int64(60), - } - if nextToken != "" { - input.NextToken = aws.String(nextToken) - } - out, err := conn.ListUserPools(input) - if err != nil { - return pools, err + err := conn.ListUserPoolsPages(input, func(page *cognitoidentityprovider.ListUserPoolsOutput, lastPage bool) bool { + if page == nil { + return !lastPage } - pools = append(pools, out.UserPools...) - if out.NextToken == nil { - break + for _, v := range page.UserPools { + if v != nil { + output = append(output, v) + } } - nextToken = aws.StringValue(out.NextToken) + + return !lastPage + }) + + if err != nil { + return nil, err } - return pools, nil + return output, nil } diff --git a/internal/service/cognitoidp/user_pools_data_source_test.go b/internal/service/cognitoidp/user_pools_data_source_test.go index ecce4ddc6bd..1a5eb7cbd4a 100644 --- a/internal/service/cognitoidp/user_pools_data_source_test.go +++ b/internal/service/cognitoidp/user_pools_data_source_test.go @@ -2,7 +2,6 @@ package cognitoidp_test import ( "fmt" - "regexp" "testing" "github.com/aws/aws-sdk-go/service/cognitoidentityprovider" @@ -12,44 +11,43 @@ import ( ) func TestAccCognitoIDPUserPoolsDataSource_basic(t *testing.T) { - rName := fmt.Sprintf("tf_acc_ds_cognito_user_pools_%s", sdkacctest.RandString(7)) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t); testAccPreCheckIdentityProvider(t) }, ErrorCheck: acctest.ErrorCheck(t, cognitoidentityprovider.EndpointsID), Providers: acctest.Providers, Steps: []resource.TestStep{ { - Config: testAccUserPoolsDataSourceConfig_basic(rName), + Config: testAccUserPoolsDataSourceConfig(rName), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("data.aws_cognito_user_pools.selected", "ids.#", "2"), - resource.TestCheckResourceAttr("data.aws_cognito_user_pools.selected", "arns.#", "2"), + resource.TestCheckResourceAttr("data.aws_cognito_user_pools.test", "arns.#", "2"), + resource.TestCheckResourceAttr("data.aws_cognito_user_pools.test", "ids.#", "2"), + resource.TestCheckResourceAttr("data.aws_cognito_user_pools.empty", "arns.#", "0"), + resource.TestCheckResourceAttr("data.aws_cognito_user_pools.empty", "ids.#", "0"), ), }, - { - Config: testAccUserPoolsDataSourceConfig_notFound(rName), - ExpectError: regexp.MustCompile(`No cognito user pool found with name:`), - }, }, }) } -func testAccUserPoolsDataSourceConfig_basic(rName string) string { +func testAccUserPoolsDataSourceConfig(rName string) string { return fmt.Sprintf(` -resource "aws_cognito_user_pool" "main" { +resource "aws_cognito_user_pool" "test" { count = 2 - name = "%s" + name = %[1]q } -data "aws_cognito_user_pools" "selected" { - name = aws_cognito_user_pool.main.*.name[0] -} -`, rName) +data "aws_cognito_user_pools" "test" { + name = %[1]q + + depends_on = [aws_cognito_user_pool.test[0], aws_cognito_user_pool.test[1]] } -func testAccUserPoolsDataSourceConfig_notFound(rName string) string { - return fmt.Sprintf(` -data "aws_cognito_user_pools" "selected" { - name = "%s-not-found" +data "aws_cognito_user_pools" "empty" { + name = "not.%[1]s" + + depends_on = [aws_cognito_user_pool.test[0], aws_cognito_user_pool.test[1]] } `, rName) } diff --git a/internal/service/ec2/coip_pools_data_source.go b/internal/service/ec2/coip_pools_data_source.go index e2d35941279..1c8a2848afd 100644 --- a/internal/service/ec2/coip_pools_data_source.go +++ b/internal/service/ec2/coip_pools_data_source.go @@ -13,17 +13,15 @@ import ( func DataSourceCoIPPools() *schema.Resource { return &schema.Resource{ Read: dataSourceCoIPPoolsRead, - Schema: map[string]*schema.Schema{ - "filter": CustomFiltersSchema(), - - "tags": tftags.TagsSchemaComputed(), + Schema: map[string]*schema.Schema{ + "filter": DataSourceFiltersSchema(), "pool_ids": { - Type: schema.TypeSet, + Type: schema.TypeList, Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, - Set: schema.HashString, }, + "tags": tftags.TagsSchemaComputed(), }, } } @@ -31,59 +29,34 @@ func DataSourceCoIPPools() *schema.Resource { func dataSourceCoIPPoolsRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).EC2Conn - req := &ec2.DescribeCoipPoolsInput{} - - if tags, tagsOk := d.GetOk("tags"); tagsOk { - req.Filters = append(req.Filters, BuildTagFilterList( - Tags(tftags.New(tags.(map[string]interface{}))), - )...) - } - - if filters, filtersOk := d.GetOk("filter"); filtersOk { - req.Filters = append(req.Filters, BuildCustomFilterList( - filters.(*schema.Set), - )...) - } - if len(req.Filters) == 0 { - // Don't send an empty filters list; the EC2 API won't accept it. - req.Filters = nil - } + input := &ec2.DescribeCoipPoolsInput{} - var coipPools []*ec2.CoipPool + input.Filters = append(input.Filters, BuildTagFilterList( + Tags(tftags.New(d.Get("tags").(map[string]interface{}))), + )...) - err := conn.DescribeCoipPoolsPages(req, func(page *ec2.DescribeCoipPoolsOutput, lastPage bool) bool { - if page == nil { - return !lastPage - } + input.Filters = append(input.Filters, BuildFiltersDataSource( + d.Get("filter").(*schema.Set), + )...) - coipPools = append(coipPools, page.CoipPools...) + if len(input.Filters) == 0 { + input.Filters = nil + } - return !lastPage - }) + output, err := FindCOIPPools(conn, input) if err != nil { - return fmt.Errorf("error describing EC2 COIP Pools: %w", err) - } - - if len(coipPools) == 0 { - return fmt.Errorf("no matching EC2 COIP Pools found") + return fmt.Errorf("error reading EC2 COIP Pools: %w", err) } var poolIDs []string - for _, coipPool := range coipPools { - if coipPool == nil { - continue - } - - poolIDs = append(poolIDs, aws.StringValue(coipPool.PoolId)) + for _, v := range output { + poolIDs = append(poolIDs, aws.StringValue(v.PoolId)) } d.SetId(meta.(*conns.AWSClient).Region) - - if err := d.Set("pool_ids", poolIDs); err != nil { - return fmt.Errorf("error setting pool_ids: %w", err) - } + d.Set("pool_ids", poolIDs) return nil } diff --git a/internal/service/ec2/ebs_volumes_data_source.go b/internal/service/ec2/ebs_volumes_data_source.go index bc9f90cb121..fd5f38b41ab 100644 --- a/internal/service/ec2/ebs_volumes_data_source.go +++ b/internal/service/ec2/ebs_volumes_data_source.go @@ -1,10 +1,9 @@ package ec2 import ( - "errors" "fmt" - "log" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -15,16 +14,13 @@ func DataSourceEBSVolumes() *schema.Resource { return &schema.Resource{ Read: dataSourceEBSVolumesRead, Schema: map[string]*schema.Schema{ - "filter": CustomFiltersSchema(), - - "tags": tftags.TagsSchema(), - + "filter": DataSourceFiltersSchema(), "ids": { - Type: schema.TypeSet, + Type: schema.TypeList, Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, - Set: schema.HashString, }, + "tags": tftags.TagsSchema(), }, } } @@ -32,45 +28,34 @@ func DataSourceEBSVolumes() *schema.Resource { func dataSourceEBSVolumesRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).EC2Conn - req := &ec2.DescribeVolumesInput{} + input := &ec2.DescribeVolumesInput{} - if tags, tagsOk := d.GetOk("tags"); tagsOk { - req.Filters = append(req.Filters, BuildTagFilterList( - Tags(tftags.New(tags.(map[string]interface{}))), - )...) - } + input.Filters = append(input.Filters, BuildTagFilterList( + Tags(tftags.New(d.Get("tags").(map[string]interface{}))), + )...) - if filters, filtersOk := d.GetOk("filter"); filtersOk { - req.Filters = append(req.Filters, BuildCustomFilterList( - filters.(*schema.Set), - )...) - } + input.Filters = append(input.Filters, BuildFiltersDataSource( + d.Get("filter").(*schema.Set), + )...) - if len(req.Filters) == 0 { - req.Filters = nil + if len(input.Filters) == 0 { + input.Filters = nil } - log.Printf("[DEBUG] DescribeVolumes %s\n", req) - resp, err := conn.DescribeVolumes(req) - if err != nil { - return fmt.Errorf("error describing EC2 Volumes: %w", err) - } + output, err := FindEBSVolumes(conn, input) - if resp == nil || len(resp.Volumes) == 0 { - return errors.New("no matching volumes found") + if err != nil { + return fmt.Errorf("error reading EC2 Volumes: %w", err) } - volumes := make([]string, 0) + var volumeIDs []string - for _, volume := range resp.Volumes { - volumes = append(volumes, *volume.VolumeId) + for _, v := range output { + volumeIDs = append(volumeIDs, aws.StringValue(v.VolumeId)) } d.SetId(meta.(*conns.AWSClient).Region) - - if err := d.Set("ids", volumes); err != nil { - return fmt.Errorf("error setting ids: %w", err) - } + d.Set("ids", volumeIDs) return nil } diff --git a/internal/service/ec2/ebs_volumes_data_source_test.go b/internal/service/ec2/ebs_volumes_data_source_test.go index 26d60fa7e0c..e71ffcce07d 100644 --- a/internal/service/ec2/ebs_volumes_data_source_test.go +++ b/internal/service/ec2/ebs_volumes_data_source_test.go @@ -11,7 +11,8 @@ import ( ) func TestAccEC2EBSVolumesDataSource_basic(t *testing.T) { - rInt := sdkacctest.RandIntRange(0, 256) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, ec2.EndpointsID), @@ -19,47 +20,56 @@ func TestAccEC2EBSVolumesDataSource_basic(t *testing.T) { CheckDestroy: testAccCheckVolumeDestroy, Steps: []resource.TestStep{ { - Config: testAccEBSVolumeIDsDataSourceConfig(rInt), - }, - { - Config: testAccEBSVolumeIDsWithDataSourceDataSourceConfig(rInt), + Config: testAccEBSVolumeIDsDataSourceConfig(rName), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("data.aws_ebs_volumes.subject_under_test", "ids.#", "2"), + resource.TestCheckResourceAttr("data.aws_ebs_volumes.by_tags", "ids.#", "2"), + resource.TestCheckResourceAttr("data.aws_ebs_volumes.by_filter", "ids.#", "1"), + resource.TestCheckResourceAttr("data.aws_ebs_volumes.empty", "ids.#", "0"), ), }, - { - // Force the destroy to not refresh the data source (leading to an error) - Config: testAccEBSVolumeIDsDataSourceConfig(rInt), - }, }, }) } -func testAccEBSVolumeIDsWithDataSourceDataSourceConfig(rInt int) string { - return fmt.Sprintf(` -%s - -data "aws_ebs_volumes" "subject_under_test" { - tags = { - TestIdentifierSet = "testAccDataSourceAwsEbsVolumes-%d" - } -} -`, testAccEBSVolumeIDsDataSourceConfig(rInt), rInt) -} - -func testAccEBSVolumeIDsDataSourceConfig(rInt int) string { - return acctest.ConfigAvailableAZsNoOptIn() + fmt.Sprintf(` +func testAccEBSVolumeIDsDataSourceConfig(rName string) string { + return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(` data "aws_region" "current" {} -resource "aws_ebs_volume" "volume" { +resource "aws_ebs_volume" "test" { count = 2 availability_zone = data.aws_availability_zones.available.names[0] size = 1 tags = { - TestIdentifierSet = "testAccDataSourceAwsEbsVolumes-%d" + Name = %[1]q + } +} + +data "aws_ebs_volumes" "by_tags" { + tags = { + Name = %[1]q + } + + depends_on = [aws_ebs_volume.test[0], aws_ebs_volume.test[1]] +} + +data "aws_ebs_volumes" "by_filter" { + filter { + name = "volume-id" + values = [aws_ebs_volume.test[0].id] + } + + depends_on = [aws_ebs_volume.test[0], aws_ebs_volume.test[1]] +} + +data "aws_ebs_volumes" "empty" { + filter { + name = "create-time" + values = ["2000-01-01T00:00:00.000Z"] } + + depends_on = [aws_ebs_volume.test[0], aws_ebs_volume.test[1]] } -`, rInt) +`, rName)) } diff --git a/internal/service/ec2/errors.go b/internal/service/ec2/errors.go index ccc0a84e218..7676b522414 100644 --- a/internal/service/ec2/errors.go +++ b/internal/service/ec2/errors.go @@ -43,6 +43,7 @@ const ( ErrCodeInvalidPermissionMalformed = "InvalidPermission.Malformed" ErrCodeInvalidPermissionNotFound = "InvalidPermission.NotFound" ErrCodeInvalidPlacementGroupUnknown = "InvalidPlacementGroup.Unknown" + ErrCodeInvalidPoolIDNotFound = "InvalidPoolID.NotFound" ErrCodeInvalidPrefixListIDNotFound = "InvalidPrefixListID.NotFound" ErrCodeInvalidRouteNotFound = "InvalidRoute.NotFound" ErrCodeInvalidRouteTableIDNotFound = "InvalidRouteTableID.NotFound" @@ -56,6 +57,7 @@ const ( ErrCodeInvalidSubnetIdNotFound = "InvalidSubnetId.NotFound" ErrCodeInvalidTransitGatewayAttachmentIDNotFound = "InvalidTransitGatewayAttachmentID.NotFound" ErrCodeInvalidTransitGatewayIDNotFound = "InvalidTransitGatewayID.NotFound" + ErrCodeInvalidVolumeNotFound = "InvalidVolume.NotFound" ErrCodeInvalidVpcCidrBlockAssociationIDNotFound = "InvalidVpcCidrBlockAssociationID.NotFound" ErrCodeInvalidVpcEndpointIdNotFound = "InvalidVpcEndpointId.NotFound" ErrCodeInvalidVpcEndpointNotFound = "InvalidVpcEndpoint.NotFound" diff --git a/internal/service/ec2/find.go b/internal/service/ec2/find.go index 9b746ea7f05..490d3bb2e6a 100644 --- a/internal/service/ec2/find.go +++ b/internal/service/ec2/find.go @@ -79,6 +79,104 @@ func FindClientVPNRouteByID(conn *ec2.EC2, routeID string) (*ec2.DescribeClientV return FindClientVPNRoute(conn, endpointID, targetSubnetID, destinationCidr) } +func FindCOIPPools(conn *ec2.EC2, input *ec2.DescribeCoipPoolsInput) ([]*ec2.CoipPool, error) { + var output []*ec2.CoipPool + + err := conn.DescribeCoipPoolsPages(input, func(page *ec2.DescribeCoipPoolsOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, v := range page.CoipPools { + if v != nil { + output = append(output, v) + } + } + + return !lastPage + }) + + if tfawserr.ErrCodeEquals(err, ErrCodeInvalidPoolIDNotFound) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + return output, nil +} + +func FindCOIPPool(conn *ec2.EC2, input *ec2.DescribeCoipPoolsInput) (*ec2.CoipPool, error) { + output, err := FindCOIPPools(conn, input) + + if err != nil { + return nil, err + } + + if len(output) == 0 || output[0] == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + if count := len(output); count > 1 { + return nil, tfresource.NewTooManyResultsError(count, input) + } + + return output[0], nil +} + +func FindEBSVolumes(conn *ec2.EC2, input *ec2.DescribeVolumesInput) ([]*ec2.Volume, error) { + var output []*ec2.Volume + + err := conn.DescribeVolumesPages(input, func(page *ec2.DescribeVolumesOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, v := range page.Volumes { + if v != nil { + output = append(output, v) + } + } + + return !lastPage + }) + + if tfawserr.ErrCodeEquals(err, ErrCodeInvalidVolumeNotFound) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + return output, nil +} + +func FindEBSVolume(conn *ec2.EC2, input *ec2.DescribeVolumesInput) (*ec2.Volume, error) { + output, err := FindEBSVolumes(conn, input) + + if err != nil { + return nil, err + } + + if len(output) == 0 || output[0] == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + if count := len(output); count > 1 { + return nil, tfresource.NewTooManyResultsError(count, input) + } + + return output[0], nil +} + func FindEIPs(conn *ec2.EC2, input *ec2.DescribeAddressesInput) ([]*ec2.Address, error) { var addresses []*ec2.Address @@ -160,23 +258,211 @@ func FindHost(conn *ec2.EC2, input *ec2.DescribeHostsInput) (*ec2.Host, error) { return host, nil } -// FindInstanceByID looks up a Instance by ID. When not found, returns nil and potentially an API error. +func FindInstances(conn *ec2.EC2, input *ec2.DescribeInstancesInput) ([]*ec2.Instance, error) { + var output []*ec2.Instance + + err := conn.DescribeInstancesPages(input, func(page *ec2.DescribeInstancesOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, v := range page.Reservations { + if v != nil { + for _, v := range v.Instances { + if v != nil { + output = append(output, v) + } + } + } + } + + return !lastPage + }) + + if tfawserr.ErrCodeEquals(err, ErrCodeInvalidInstanceIDNotFound) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + return output, nil +} + +func FindInstance(conn *ec2.EC2, input *ec2.DescribeInstancesInput) (*ec2.Instance, error) { + output, err := FindInstances(conn, input) + + if err != nil { + return nil, err + } + + if len(output) == 0 || output[0] == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + if count := len(output); count > 1 { + return nil, tfresource.NewTooManyResultsError(count, input) + } + + return output[0], nil +} + func FindInstanceByID(conn *ec2.EC2, id string) (*ec2.Instance, error) { input := &ec2.DescribeInstancesInput{ InstanceIds: aws.StringSlice([]string{id}), } - output, err := conn.DescribeInstances(input) + output, err := FindInstance(conn, input) if err != nil { return nil, err } - if output == nil || len(output.Reservations) == 0 || output.Reservations[0] == nil || len(output.Reservations[0].Instances) == 0 || output.Reservations[0].Instances[0] == nil { - return nil, nil + if state := aws.StringValue(output.State.Name); state == ec2.InstanceStateNameTerminated { + return nil, &resource.NotFoundError{ + Message: state, + LastRequest: input, + } + } + + // Eventual consistency check. + if aws.StringValue(output.InstanceId) != id { + return nil, &resource.NotFoundError{ + LastRequest: input, + } } - return output.Reservations[0].Instances[0], nil + return output, nil +} + +func FindLocalGatewayRouteTables(conn *ec2.EC2, input *ec2.DescribeLocalGatewayRouteTablesInput) ([]*ec2.LocalGatewayRouteTable, error) { + var output []*ec2.LocalGatewayRouteTable + + err := conn.DescribeLocalGatewayRouteTablesPages(input, func(page *ec2.DescribeLocalGatewayRouteTablesOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, v := range page.LocalGatewayRouteTables { + if v != nil { + output = append(output, v) + } + } + + return !lastPage + }) + + if err != nil { + return nil, err + } + + return output, nil +} + +func FindLocalGatewayRouteTable(conn *ec2.EC2, input *ec2.DescribeLocalGatewayRouteTablesInput) (*ec2.LocalGatewayRouteTable, error) { + output, err := FindLocalGatewayRouteTables(conn, input) + + if err != nil { + return nil, err + } + + if len(output) == 0 || output[0] == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + if count := len(output); count > 1 { + return nil, tfresource.NewTooManyResultsError(count, input) + } + + return output[0], nil +} + +func FindLocalGatewayVirtualInterfaceGroups(conn *ec2.EC2, input *ec2.DescribeLocalGatewayVirtualInterfaceGroupsInput) ([]*ec2.LocalGatewayVirtualInterfaceGroup, error) { + var output []*ec2.LocalGatewayVirtualInterfaceGroup + + err := conn.DescribeLocalGatewayVirtualInterfaceGroupsPages(input, func(page *ec2.DescribeLocalGatewayVirtualInterfaceGroupsOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, v := range page.LocalGatewayVirtualInterfaceGroups { + if v != nil { + output = append(output, v) + } + } + + return !lastPage + }) + + if err != nil { + return nil, err + } + + return output, nil +} + +func FindLocalGatewayVirtualInterfaceGroup(conn *ec2.EC2, input *ec2.DescribeLocalGatewayVirtualInterfaceGroupsInput) (*ec2.LocalGatewayVirtualInterfaceGroup, error) { + output, err := FindLocalGatewayVirtualInterfaceGroups(conn, input) + + if err != nil { + return nil, err + } + + if len(output) == 0 || output[0] == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + if count := len(output); count > 1 { + return nil, tfresource.NewTooManyResultsError(count, input) + } + + return output[0], nil +} + +func FindLocalGateways(conn *ec2.EC2, input *ec2.DescribeLocalGatewaysInput) ([]*ec2.LocalGateway, error) { + var output []*ec2.LocalGateway + + err := conn.DescribeLocalGatewaysPages(input, func(page *ec2.DescribeLocalGatewaysOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, v := range page.LocalGateways { + if v != nil { + output = append(output, v) + } + } + + return !lastPage + }) + + if err != nil { + return nil, err + } + + return output, nil +} + +func FindLocalGateway(conn *ec2.EC2, input *ec2.DescribeLocalGatewaysInput) (*ec2.LocalGateway, error) { + output, err := FindLocalGateways(conn, input) + + if err != nil { + return nil, err + } + + if len(output) == 0 || output[0] == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + if count := len(output); count > 1 { + return nil, tfresource.NewTooManyResultsError(count, input) + } + + return output[0], nil } func FindNetworkACL(conn *ec2.EC2, input *ec2.DescribeNetworkAclsInput) (*ec2.NetworkAcl, error) { @@ -1668,6 +1954,37 @@ func FindTransitGatewayAttachmentByID(conn *ec2.EC2, id string) (*ec2.TransitGat return output, nil } +func FindTransitGatewayRouteTables(conn *ec2.EC2, input *ec2.DescribeTransitGatewayRouteTablesInput) ([]*ec2.TransitGatewayRouteTable, error) { + var output []*ec2.TransitGatewayRouteTable + + err := conn.DescribeTransitGatewayRouteTablesPages(input, func(page *ec2.DescribeTransitGatewayRouteTablesOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, v := range page.TransitGatewayRouteTables { + if v != nil { + output = append(output, v) + } + } + + return !lastPage + }) + + if tfawserr.ErrCodeEquals(err, ErrCodeInvalidRouteTableIDNotFound) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + return output, nil +} + func FindDHCPOptions(conn *ec2.EC2, input *ec2.DescribeDhcpOptionsInput) (*ec2.DhcpOptions, error) { output, err := FindDHCPOptionses(conn, input) diff --git a/internal/service/ec2/instances_data_source.go b/internal/service/ec2/instances_data_source.go index b318933eae9..4f9d9b2e1e0 100644 --- a/internal/service/ec2/instances_data_source.go +++ b/internal/service/ec2/instances_data_source.go @@ -2,7 +2,6 @@ package ec2 import ( "fmt" - "log" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" @@ -18,29 +17,21 @@ func DataSourceInstances() *schema.Resource { Read: dataSourceInstancesRead, Schema: map[string]*schema.Schema{ - "filter": DataSourceFiltersSchema(), + "filter": DataSourceFiltersSchema(), + "ids": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, "instance_tags": tftags.TagsSchemaComputed(), "instance_state_names": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: validation.StringInSlice([]string{ - ec2.InstanceStateNamePending, - ec2.InstanceStateNameRunning, - ec2.InstanceStateNameShuttingDown, - ec2.InstanceStateNameStopped, - ec2.InstanceStateNameStopping, - ec2.InstanceStateNameTerminated, - }, false), + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice(ec2.InstanceStateName_Values(), false), }, }, - - "ids": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, "private_ips": { Type: schema.TypeList, Computed: true, @@ -58,75 +49,54 @@ func DataSourceInstances() *schema.Resource { func dataSourceInstancesRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).EC2Conn - filters, filtersOk := d.GetOk("filter") - tags, tagsOk := d.GetOk("instance_tags") - - if !filtersOk && !tagsOk { - return fmt.Errorf("One of filters or instance_tags must be assigned") - } - - instanceStateNames := []*string{aws.String(ec2.InstanceStateNameRunning)} - if v, ok := d.GetOk("instance_state_names"); ok && len(v.(*schema.Set).List()) > 0 { - instanceStateNames = flex.ExpandStringSet(v.(*schema.Set)) - } - params := &ec2.DescribeInstancesInput{ - Filters: []*ec2.Filter{ - { - Name: aws.String("instance-state-name"), - Values: instanceStateNames, - }, - }, + input := &ec2.DescribeInstancesInput{} + + if v, ok := d.GetOk("instance_state_names"); ok && v.(*schema.Set).Len() > 0 { + input.Filters = append(input.Filters, &ec2.Filter{ + Name: aws.String("instance-state-name"), + Values: flex.ExpandStringSet(v.(*schema.Set)), + }) + } else { + input.Filters = append(input.Filters, &ec2.Filter{ + Name: aws.String("instance-state-name"), + Values: aws.StringSlice([]string{ec2.InstanceStateNameRunning}), + }) } - if filtersOk { - params.Filters = append(params.Filters, - BuildFiltersDataSource(filters.(*schema.Set))...) - } - if tagsOk { - params.Filters = append(params.Filters, BuildTagFilterList( - Tags(tftags.New(tags.(map[string]interface{}))), - )...) - } + input.Filters = append(input.Filters, BuildTagFilterList( + Tags(tftags.New(d.Get("instance_tags").(map[string]interface{}))), + )...) - log.Printf("[DEBUG] Reading EC2 instances: %s", params) - - var instanceIds, privateIps, publicIps []string - err := conn.DescribeInstancesPages(params, func(resp *ec2.DescribeInstancesOutput, lastPage bool) bool { - for _, res := range resp.Reservations { - for _, instance := range res.Instances { - instanceIds = append(instanceIds, *instance.InstanceId) - if instance.PrivateIpAddress != nil { - privateIps = append(privateIps, *instance.PrivateIpAddress) - } - if instance.PublicIpAddress != nil { - publicIps = append(publicIps, *instance.PublicIpAddress) - } - } - } - return !lastPage - }) - if err != nil { - return err - } + input.Filters = append(input.Filters, BuildFiltersDataSource( + d.Get("filter").(*schema.Set), + )...) - if len(instanceIds) < 1 { - return fmt.Errorf("Your query returned no results. Please change your search criteria and try again.") + if len(input.Filters) == 0 { + input.Filters = nil } - log.Printf("[DEBUG] Found %d instances via given filter", len(instanceIds)) + output, err := FindInstances(conn, input) - d.SetId(meta.(*conns.AWSClient).Region) - - err = d.Set("ids", instanceIds) if err != nil { - return err + return fmt.Errorf("error reading EC2 Instances: %w", err) } - err = d.Set("private_ips", privateIps) - if err != nil { - return err + var instanceIDs, privateIPs, publicIPs []string + + for _, v := range output { + instanceIDs = append(instanceIDs, aws.StringValue(v.InstanceId)) + if privateIP := aws.StringValue(v.PrivateIpAddress); privateIP != "" { + privateIPs = append(privateIPs, privateIP) + } + if publicIP := aws.StringValue(v.PublicIpAddress); publicIP != "" { + publicIPs = append(publicIPs, publicIP) + } } - err = d.Set("public_ips", publicIps) - return err + d.SetId(meta.(*conns.AWSClient).Region) + d.Set("ids", instanceIDs) + d.Set("private_ips", privateIPs) + d.Set("public_ips", publicIPs) + + return nil } diff --git a/internal/service/ec2/instances_data_source_test.go b/internal/service/ec2/instances_data_source_test.go index 9f4c2ca7887..9a0d7e9a6dd 100644 --- a/internal/service/ec2/instances_data_source_test.go +++ b/internal/service/ec2/instances_data_source_test.go @@ -67,6 +67,26 @@ func TestAccEC2InstancesDataSource_instanceStateNames(t *testing.T) { }) } +func TestAccEC2InstancesDataSource_empty(t *testing.T) { + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, ec2.EndpointsID), + Providers: acctest.Providers, + Steps: []resource.TestStep{ + { + Config: testAccInstancesDataSourceConfig_empty(rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.aws_instances.test", "ids.#", "0"), + resource.TestCheckResourceAttr("data.aws_instances.test", "private_ips.#", "0"), + resource.TestCheckResourceAttr("data.aws_instances.test", "public_ips.#", "0"), + ), + }, + }, + }) +} + func testAccInstancesDataSourceConfig_ids(rName string) string { return acctest.ConfigCompose( acctest.ConfigLatestAmazonLinuxHvmEbsAmi(), @@ -78,7 +98,7 @@ resource "aws_instance" "test" { instance_type = data.aws_ec2_instance_type_offering.available.instance_type tags = { - Name = %q + Name = %[1]q } } @@ -129,7 +149,7 @@ resource "aws_instance" "test" { instance_type = data.aws_ec2_instance_type_offering.available.instance_type tags = { - Name = %q + Name = %[1]q } } @@ -143,3 +163,13 @@ data "aws_instances" "test" { } `, rName)) } + +func testAccInstancesDataSourceConfig_empty(rName string) string { + return fmt.Sprintf(` +data "aws_instances" "test" { + instance_tags = { + Name = %[1]q + } +} +`, rName) +} diff --git a/internal/service/ec2/local_gateway_route_tables_data_source.go b/internal/service/ec2/local_gateway_route_tables_data_source.go index d16d24e4ec4..4db12ee2680 100644 --- a/internal/service/ec2/local_gateway_route_tables_data_source.go +++ b/internal/service/ec2/local_gateway_route_tables_data_source.go @@ -13,17 +13,15 @@ import ( func DataSourceLocalGatewayRouteTables() *schema.Resource { return &schema.Resource{ Read: dataSourceLocalGatewayRouteTablesRead, - Schema: map[string]*schema.Schema{ - "filter": CustomFiltersSchema(), - - "tags": tftags.TagsSchemaComputed(), + Schema: map[string]*schema.Schema{ + "filter": DataSourceFiltersSchema(), "ids": { - Type: schema.TypeSet, + Type: schema.TypeList, Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, - Set: schema.HashString, }, + "tags": tftags.TagsSchemaComputed(), }, } } @@ -31,55 +29,34 @@ func DataSourceLocalGatewayRouteTables() *schema.Resource { func dataSourceLocalGatewayRouteTablesRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).EC2Conn - req := &ec2.DescribeLocalGatewayRouteTablesInput{} + input := &ec2.DescribeLocalGatewayRouteTablesInput{} - req.Filters = append(req.Filters, BuildTagFilterList( + input.Filters = append(input.Filters, BuildTagFilterList( Tags(tftags.New(d.Get("tags").(map[string]interface{}))), )...) - req.Filters = append(req.Filters, BuildCustomFilterList( + input.Filters = append(input.Filters, BuildFiltersDataSource( d.Get("filter").(*schema.Set), )...) - if len(req.Filters) == 0 { - // Don't send an empty filters list; the EC2 API won't accept it. - req.Filters = nil - } - - var localGatewayRouteTables []*ec2.LocalGatewayRouteTable - err := conn.DescribeLocalGatewayRouteTablesPages(req, func(page *ec2.DescribeLocalGatewayRouteTablesOutput, lastPage bool) bool { - if page == nil { - return !lastPage - } - - localGatewayRouteTables = append(localGatewayRouteTables, page.LocalGatewayRouteTables...) + if len(input.Filters) == 0 { + input.Filters = nil + } - return !lastPage - }) + output, err := FindLocalGatewayRouteTables(conn, input) if err != nil { - return fmt.Errorf("error describing EC2 Local Gateway Route Tables: %w", err) - } - - if len(localGatewayRouteTables) == 0 { - return fmt.Errorf("no matching EC2 Local Gateway Route Tables found") + return fmt.Errorf("error reading EC2 Local Gateway Route Tables: %w", err) } - var ids []string + var routeTableIDs []string - for _, localGatewayRouteTable := range localGatewayRouteTables { - if localGatewayRouteTable == nil { - continue - } - - ids = append(ids, aws.StringValue(localGatewayRouteTable.LocalGatewayRouteTableId)) + for _, v := range output { + routeTableIDs = append(routeTableIDs, aws.StringValue(v.LocalGatewayRouteTableId)) } d.SetId(meta.(*conns.AWSClient).Region) - - if err := d.Set("ids", ids); err != nil { - return fmt.Errorf("error setting ids: %w", err) - } + d.Set("ids", routeTableIDs) return nil } diff --git a/internal/service/ec2/local_gateway_virtual_interface_groups_data_source.go b/internal/service/ec2/local_gateway_virtual_interface_groups_data_source.go index 7a61ecbe88d..b4481123d48 100644 --- a/internal/service/ec2/local_gateway_virtual_interface_groups_data_source.go +++ b/internal/service/ec2/local_gateway_virtual_interface_groups_data_source.go @@ -3,6 +3,7 @@ package ec2 import ( "fmt" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -14,14 +15,14 @@ func DataSourceLocalGatewayVirtualInterfaceGroups() *schema.Resource { Read: dataSourceLocalGatewayVirtualInterfaceGroupsRead, Schema: map[string]*schema.Schema{ - "filter": CustomFiltersSchema(), + "filter": DataSourceFiltersSchema(), "ids": { - Type: schema.TypeSet, + Type: schema.TypeList, Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, }, "local_gateway_virtual_interface_ids": { - Type: schema.TypeSet, + Type: schema.TypeList, Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, }, @@ -39,55 +40,30 @@ func dataSourceLocalGatewayVirtualInterfaceGroupsRead(d *schema.ResourceData, me Tags(tftags.New(d.Get("tags").(map[string]interface{}))), )...) - input.Filters = append(input.Filters, BuildCustomFilterList( + input.Filters = append(input.Filters, BuildFiltersDataSource( d.Get("filter").(*schema.Set), )...) if len(input.Filters) == 0 { - // Don't send an empty filters list; the EC2 API won't accept it. input.Filters = nil } - var localGatewayVirtualInterfaceGroups []*ec2.LocalGatewayVirtualInterfaceGroup - - err := conn.DescribeLocalGatewayVirtualInterfaceGroupsPages(input, func(page *ec2.DescribeLocalGatewayVirtualInterfaceGroupsOutput, lastPage bool) bool { - if page == nil { - return !lastPage - } - - localGatewayVirtualInterfaceGroups = append(localGatewayVirtualInterfaceGroups, page.LocalGatewayVirtualInterfaceGroups...) - - return !lastPage - }) + output, err := FindLocalGatewayVirtualInterfaceGroups(conn, input) if err != nil { - return fmt.Errorf("error describing EC2 Local Gateway Virtual Interface Groups: %w", err) - } - - if len(localGatewayVirtualInterfaceGroups) == 0 { - return fmt.Errorf("no matching EC2 Local Gateway Virtual Interface Groups found") + return fmt.Errorf("error reading EC2 Local Gateway Virtual Interface Groups: %w", err) } - var ids, localGatewayVirtualInterfaceIds []*string + var groupIDs, interfaceIDs []string - for _, group := range localGatewayVirtualInterfaceGroups { - if group == nil { - continue - } - - ids = append(ids, group.LocalGatewayVirtualInterfaceGroupId) - localGatewayVirtualInterfaceIds = append(localGatewayVirtualInterfaceIds, group.LocalGatewayVirtualInterfaceIds...) + for _, v := range output { + groupIDs = append(groupIDs, aws.StringValue(v.LocalGatewayVirtualInterfaceGroupId)) + interfaceIDs = append(interfaceIDs, aws.StringValueSlice(v.LocalGatewayVirtualInterfaceIds)...) } d.SetId(meta.(*conns.AWSClient).Region) - - if err := d.Set("ids", ids); err != nil { - return fmt.Errorf("error setting ids: %w", err) - } - - if err := d.Set("local_gateway_virtual_interface_ids", localGatewayVirtualInterfaceIds); err != nil { - return fmt.Errorf("error setting local_gateway_virtual_interface_ids: %w", err) - } + d.Set("ids", groupIDs) + d.Set("local_gateway_virtual_interface_ids", interfaceIDs) return nil } diff --git a/internal/service/ec2/local_gateways_data_source.go b/internal/service/ec2/local_gateways_data_source.go index cf46eae5255..9a4ae075086 100644 --- a/internal/service/ec2/local_gateways_data_source.go +++ b/internal/service/ec2/local_gateways_data_source.go @@ -14,16 +14,13 @@ func DataSourceLocalGateways() *schema.Resource { return &schema.Resource{ Read: dataSourceLocalGatewaysRead, Schema: map[string]*schema.Schema{ - "filter": CustomFiltersSchema(), - - "tags": tftags.TagsSchemaComputed(), - + "filter": DataSourceFiltersSchema(), "ids": { - Type: schema.TypeSet, + Type: schema.TypeList, Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, - Set: schema.HashString, }, + "tags": tftags.TagsSchemaComputed(), }, } } @@ -31,59 +28,34 @@ func DataSourceLocalGateways() *schema.Resource { func dataSourceLocalGatewaysRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).EC2Conn - req := &ec2.DescribeLocalGatewaysInput{} + input := &ec2.DescribeLocalGatewaysInput{} - if tags, tagsOk := d.GetOk("tags"); tagsOk { - req.Filters = append(req.Filters, BuildTagFilterList( - Tags(tftags.New(tags.(map[string]interface{}))), - )...) - } - - if filters, filtersOk := d.GetOk("filter"); filtersOk { - req.Filters = append(req.Filters, BuildCustomFilterList( - filters.(*schema.Set), - )...) - } - if len(req.Filters) == 0 { - // Don't send an empty filters list; the EC2 API won't accept it. - req.Filters = nil - } + input.Filters = append(input.Filters, BuildTagFilterList( + Tags(tftags.New(d.Get("tags").(map[string]interface{}))), + )...) - var localGateways []*ec2.LocalGateway + input.Filters = append(input.Filters, BuildFiltersDataSource( + d.Get("filter").(*schema.Set), + )...) - err := conn.DescribeLocalGatewaysPages(req, func(page *ec2.DescribeLocalGatewaysOutput, lastPage bool) bool { - if page == nil { - return !lastPage - } - - localGateways = append(localGateways, page.LocalGateways...) + if len(input.Filters) == 0 { + input.Filters = nil + } - return !lastPage - }) + output, err := FindLocalGateways(conn, input) if err != nil { - return fmt.Errorf("error describing EC2 Local Gateways: %w", err) - } - - if len(localGateways) == 0 { - return fmt.Errorf("no matching EC2 Local Gateways found") + return fmt.Errorf("error reading EC2 Local Gateways: %w", err) } - var ids []string + var gatewayIDs []string - for _, localGateway := range localGateways { - if localGateway == nil { - continue - } - - ids = append(ids, aws.StringValue(localGateway.LocalGatewayId)) + for _, v := range output { + gatewayIDs = append(gatewayIDs, aws.StringValue(v.LocalGatewayId)) } d.SetId(meta.(*conns.AWSClient).Region) - - if err := d.Set("ids", ids); err != nil { - return fmt.Errorf("error setting ids: %w", err) - } + d.Set("ids", gatewayIDs) return nil } diff --git a/internal/service/ec2/network_acls_data_source.go b/internal/service/ec2/network_acls_data_source.go index 26bd24308a3..7de247c6ace 100644 --- a/internal/service/ec2/network_acls_data_source.go +++ b/internal/service/ec2/network_acls_data_source.go @@ -1,9 +1,7 @@ package ec2 import ( - "errors" "fmt" - "log" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" @@ -16,21 +14,17 @@ func DataSourceNetworkACLs() *schema.Resource { return &schema.Resource{ Read: dataSourceNetworkACLsRead, Schema: map[string]*schema.Schema{ - "filter": CustomFiltersSchema(), - + "filter": DataSourceFiltersSchema(), + "ids": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, "tags": tftags.TagsSchemaComputed(), - "vpc_id": { Type: schema.TypeString, Optional: true, }, - - "ids": { - Type: schema.TypeSet, - Computed: true, - Elem: &schema.Schema{Type: schema.TypeString}, - Set: schema.HashString, - }, }, } } @@ -38,57 +32,42 @@ func DataSourceNetworkACLs() *schema.Resource { func dataSourceNetworkACLsRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).EC2Conn - req := &ec2.DescribeNetworkAclsInput{} + input := &ec2.DescribeNetworkAclsInput{} if v, ok := d.GetOk("vpc_id"); ok { - req.Filters = BuildAttributeFilterList( + input.Filters = append(input.Filters, BuildAttributeFilterList( map[string]string{ "vpc-id": v.(string), }, - ) + )...) } - filters, filtersOk := d.GetOk("filter") - tags, tagsOk := d.GetOk("tags") + input.Filters = append(input.Filters, BuildTagFilterList( + Tags(tftags.New(d.Get("tags").(map[string]interface{}))), + )...) - if tagsOk { - req.Filters = append(req.Filters, BuildTagFilterList( - Tags(tftags.New(tags.(map[string]interface{}))), - )...) - } + input.Filters = append(input.Filters, BuildFiltersDataSource( + d.Get("filter").(*schema.Set), + )...) - if filtersOk { - req.Filters = append(req.Filters, BuildCustomFilterList( - filters.(*schema.Set), - )...) + if len(input.Filters) == 0 { + input.Filters = nil } - if len(req.Filters) == 0 { - // Don't send an empty filters list; the EC2 API won't accept it. - req.Filters = nil - } + output, err := FindNetworkACLs(conn, input) - log.Printf("[DEBUG] DescribeNetworkAcls %s\n", req) - resp, err := conn.DescribeNetworkAcls(req) if err != nil { - return err - } - - if resp == nil || len(resp.NetworkAcls) == 0 { - return errors.New("no matching network ACLs found") + return fmt.Errorf("error reading EC2 Network ACLs: %w", err) } - networkAcls := make([]string, 0) + var naclIDs []string - for _, networkAcl := range resp.NetworkAcls { - networkAcls = append(networkAcls, aws.StringValue(networkAcl.NetworkAclId)) + for _, v := range output { + naclIDs = append(naclIDs, aws.StringValue(v.NetworkAclId)) } d.SetId(meta.(*conns.AWSClient).Region) - - if err := d.Set("ids", networkAcls); err != nil { - return fmt.Errorf("Error setting network ACL ids: %w", err) - } + d.Set("ids", naclIDs) return nil } diff --git a/internal/service/ec2/network_acls_data_source_test.go b/internal/service/ec2/network_acls_data_source_test.go index 59c76a62494..692413f3ca4 100644 --- a/internal/service/ec2/network_acls_data_source_test.go +++ b/internal/service/ec2/network_acls_data_source_test.go @@ -2,7 +2,6 @@ package ec2_test import ( "fmt" - "regexp" "testing" "github.com/aws/aws-sdk-go/service/ec2" @@ -12,7 +11,7 @@ import ( ) func TestAccEC2NetworkACLsDataSource_basic(t *testing.T) { - rName := sdkacctest.RandString(5) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) dataSourceName := "data.aws_network_acls.test" resource.ParallelTest(t, resource.TestCase{ @@ -21,15 +20,10 @@ func TestAccEC2NetworkACLsDataSource_basic(t *testing.T) { Providers: acctest.Providers, CheckDestroy: testAccCheckVpcDestroy, Steps: []resource.TestStep{ - { - // Ensure at least 1 network ACL exists. We cannot use depends_on. - Config: testAccNetworkACLsDataSourceConfig_Base(rName), - }, { Config: testAccNetworkACLsDataSourceConfig_basic(rName), Check: resource.ComposeTestCheckFunc( - // At least 1 - resource.TestMatchResourceAttr(dataSourceName, "ids.#", regexp.MustCompile(`^[1-9][0-9]*`)), + acctest.CheckResourceAttrGreaterThanValue(dataSourceName, "ids.#", "1"), ), }, }, @@ -37,7 +31,7 @@ func TestAccEC2NetworkACLsDataSource_basic(t *testing.T) { } func TestAccEC2NetworkACLsDataSource_filter(t *testing.T) { - rName := sdkacctest.RandString(5) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) dataSourceName := "data.aws_network_acls.test" resource.ParallelTest(t, resource.TestCase{ @@ -57,7 +51,7 @@ func TestAccEC2NetworkACLsDataSource_filter(t *testing.T) { } func TestAccEC2NetworkACLsDataSource_tags(t *testing.T) { - rName := sdkacctest.RandString(5) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) dataSourceName := "data.aws_network_acls.test" resource.ParallelTest(t, resource.TestCase{ @@ -77,7 +71,7 @@ func TestAccEC2NetworkACLsDataSource_tags(t *testing.T) { } func TestAccEC2NetworkACLsDataSource_vpcID(t *testing.T) { - rName := sdkacctest.RandString(5) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) dataSourceName := "data.aws_network_acls.test" resource.ParallelTest(t, resource.TestCase{ @@ -97,59 +91,97 @@ func TestAccEC2NetworkACLsDataSource_vpcID(t *testing.T) { }) } +func TestAccEC2NetworkACLsDataSource_empty(t *testing.T) { + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + dataSourceName := "data.aws_network_acls.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, ec2.EndpointsID), + Providers: acctest.Providers, + CheckDestroy: testAccCheckVpcDestroy, + Steps: []resource.TestStep{ + { + Config: testAccNetworkACLsDataSourceConfig_Empty(rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(dataSourceName, "ids.#", "0"), + ), + }, + }, + }) +} + func testAccNetworkACLsDataSourceConfig_Base(rName string) string { return fmt.Sprintf(` resource "aws_vpc" "test" { cidr_block = "10.0.0.0/16" tags = { - Name = "testacc-acl-%[1]s" + Name = %[1]q } } -resource "aws_network_acl" "acl" { +resource "aws_network_acl" "test" { count = 2 vpc_id = aws_vpc.test.id tags = { - Name = "testacc-acl-%[1]s" + Name = %[1]q } } `, rName) } func testAccNetworkACLsDataSourceConfig_basic(rName string) string { - return testAccNetworkACLsDataSourceConfig_Base(rName) + ` -data "aws_network_acls" "test" {} -` + return acctest.ConfigCompose(testAccNetworkACLsDataSourceConfig_Base(rName), ` +data "aws_network_acls" "test" { + depends_on = [aws_network_acl.test[0], aws_network_acl.test[1]] +} +`) } func testAccNetworkACLsDataSourceConfig_Filter(rName string) string { - return testAccNetworkACLsDataSourceConfig_Base(rName) + ` + return acctest.ConfigCompose(testAccNetworkACLsDataSourceConfig_Base(rName), ` data "aws_network_acls" "test" { filter { name = "network-acl-id" - values = [aws_network_acl.acl[0].id] + values = [aws_network_acl.test[0].id] } + + depends_on = [aws_network_acl.test[0], aws_network_acl.test[1]] } -` +`) } func testAccNetworkACLsDataSourceConfig_Tags(rName string) string { - return testAccNetworkACLsDataSourceConfig_Base(rName) + ` + return acctest.ConfigCompose(testAccNetworkACLsDataSourceConfig_Base(rName), ` data "aws_network_acls" "test" { tags = { - Name = aws_network_acl.acl[0].tags.Name + Name = aws_network_acl.test[0].tags.Name } + + depends_on = [aws_network_acl.test[0], aws_network_acl.test[1]] } -` +`) } func testAccNetworkACLsDataSourceConfig_VPCID(rName string) string { - return testAccNetworkACLsDataSourceConfig_Base(rName) + ` + return acctest.ConfigCompose(testAccNetworkACLsDataSourceConfig_Base(rName), ` data "aws_network_acls" "test" { - vpc_id = aws_network_acl.acl[0].vpc_id + vpc_id = aws_network_acl.test[0].vpc_id + + depends_on = [aws_network_acl.test[0], aws_network_acl.test[1]] +} +`) } -` + +func testAccNetworkACLsDataSourceConfig_Empty(rName string) string { + return fmt.Sprintf(` +data "aws_network_acls" "test" { + tags = { + Name = %[1]q + } +} +`, rName) } diff --git a/internal/service/ec2/network_interfaces_data_source.go b/internal/service/ec2/network_interfaces_data_source.go index 129d92a48f5..fcbe3e8ecfb 100644 --- a/internal/service/ec2/network_interfaces_data_source.go +++ b/internal/service/ec2/network_interfaces_data_source.go @@ -1,7 +1,6 @@ package ec2 import ( - "errors" "fmt" "github.com/aws/aws-sdk-go/aws" @@ -14,13 +13,13 @@ import ( func DataSourceNetworkInterfaces() *schema.Resource { return &schema.Resource{ Read: dataSourceNetworkInterfacesRead, + Schema: map[string]*schema.Schema{ - "filter": CustomFiltersSchema(), + "filter": DataSourceFiltersSchema(), "ids": { - Type: schema.TypeSet, + Type: schema.TypeList, Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, - Set: schema.HashString, }, "tags": tftags.TagsSchemaComputed(), }, @@ -32,17 +31,13 @@ func dataSourceNetworkInterfacesRead(d *schema.ResourceData, meta interface{}) e input := &ec2.DescribeNetworkInterfacesInput{} - if v, ok := d.GetOk("tags"); ok { - input.Filters = BuildTagFilterList( - Tags(tftags.New(v.(map[string]interface{}))), - ) - } + input.Filters = append(input.Filters, BuildTagFilterList( + Tags(tftags.New(d.Get("tags").(map[string]interface{}))), + )...) - if v, ok := d.GetOk("filter"); ok { - input.Filters = append(input.Filters, BuildCustomFilterList( - v.(*schema.Set), - )...) - } + input.Filters = append(input.Filters, BuildFiltersDataSource( + d.Get("filter").(*schema.Set), + )...) if len(input.Filters) == 0 { input.Filters = nil @@ -56,19 +51,12 @@ func dataSourceNetworkInterfacesRead(d *schema.ResourceData, meta interface{}) e return fmt.Errorf("error reading EC2 Network Interfaces: %w", err) } - if len(output) == 0 { - return errors.New("no matching network interfaces found") - } - for _, v := range output { networkInterfaceIDs = append(networkInterfaceIDs, aws.StringValue(v.NetworkInterfaceId)) } d.SetId(meta.(*conns.AWSClient).Region) - - if err := d.Set("ids", networkInterfaceIDs); err != nil { - return fmt.Errorf("error setting ids: %w", err) - } + d.Set("ids", networkInterfaceIDs) return nil } diff --git a/internal/service/ec2/network_interfaces_data_source_test.go b/internal/service/ec2/network_interfaces_data_source_test.go index 81f7759999d..cb65c331c3b 100644 --- a/internal/service/ec2/network_interfaces_data_source_test.go +++ b/internal/service/ec2/network_interfaces_data_source_test.go @@ -48,6 +48,25 @@ func TestAccEC2NetworkInterfacesDataSource_tags(t *testing.T) { }) } +func TestAccEC2NetworkInterfacesDataSource_empty(t *testing.T) { + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, ec2.EndpointsID), + Providers: acctest.Providers, + CheckDestroy: testAccCheckVpcDestroy, + Steps: []resource.TestStep{ + { + Config: testAccNetworkInterfacesDataSourceConfig_Empty(rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.aws_network_interfaces.test", "ids.#", "0"), + ), + }, + }, + }) +} + func testAccNetworkInterfacesDataSourceConfig_Base(rName string) string { return fmt.Sprintf(` resource "aws_vpc" "test" { @@ -105,3 +124,13 @@ data "aws_network_interfaces" "test" { } `) } + +func testAccNetworkInterfacesDataSourceConfig_Empty(rName string) string { + return fmt.Sprintf(` +data "aws_network_interfaces" "test" { + tags = { + Name = %[1]q + } +} +`, rName) +} diff --git a/internal/service/ec2/route_tables_data_source.go b/internal/service/ec2/route_tables_data_source.go index f82bf0763ce..1779aa1e74b 100644 --- a/internal/service/ec2/route_tables_data_source.go +++ b/internal/service/ec2/route_tables_data_source.go @@ -2,7 +2,6 @@ package ec2 import ( "fmt" - "log" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" @@ -14,23 +13,19 @@ import ( func DataSourceRouteTables() *schema.Resource { return &schema.Resource{ Read: dataSourceRouteTablesRead, - Schema: map[string]*schema.Schema{ - - "filter": CustomFiltersSchema(), + Schema: map[string]*schema.Schema{ + "filter": DataSourceFiltersSchema(), + "ids": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, "tags": tftags.TagsSchemaComputed(), - "vpc_id": { Type: schema.TypeString, Optional: true, }, - - "ids": { - Type: schema.TypeSet, - Computed: true, - Elem: &schema.Schema{Type: schema.TypeString}, - Set: schema.HashString, - }, }, } } @@ -38,45 +33,42 @@ func DataSourceRouteTables() *schema.Resource { func dataSourceRouteTablesRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).EC2Conn - req := &ec2.DescribeRouteTablesInput{} + input := &ec2.DescribeRouteTablesInput{} if v, ok := d.GetOk("vpc_id"); ok { - req.Filters = BuildAttributeFilterList( + input.Filters = append(input.Filters, BuildAttributeFilterList( map[string]string{ "vpc-id": v.(string), }, - ) + )...) } - req.Filters = append(req.Filters, BuildTagFilterList( + input.Filters = append(input.Filters, BuildTagFilterList( Tags(tftags.New(d.Get("tags").(map[string]interface{}))), )...) - req.Filters = append(req.Filters, BuildCustomFilterList( + input.Filters = append(input.Filters, BuildFiltersDataSource( d.Get("filter").(*schema.Set), )...) - log.Printf("[DEBUG] DescribeRouteTables %s\n", req) - resp, err := conn.DescribeRouteTables(req) - if err != nil { - return err + if len(input.Filters) == 0 { + input.Filters = nil } - if resp == nil || len(resp.RouteTables) == 0 { - return fmt.Errorf("no matching route tables found for vpc with id %s", d.Get("vpc_id").(string)) + output, err := FindRouteTables(conn, input) + + if err != nil { + return fmt.Errorf("error reading EC2 Route Tables: %w", err) } - routeTables := make([]string, 0) + var routeTableIDs []string - for _, routeTable := range resp.RouteTables { - routeTables = append(routeTables, aws.StringValue(routeTable.RouteTableId)) + for _, v := range output { + routeTableIDs = append(routeTableIDs, aws.StringValue(v.RouteTableId)) } d.SetId(meta.(*conns.AWSClient).Region) - - if err = d.Set("ids", routeTables); err != nil { - return fmt.Errorf("error setting ids: %w", err) - } + d.Set("ids", routeTableIDs) return nil } diff --git a/internal/service/ec2/route_tables_data_source_test.go b/internal/service/ec2/route_tables_data_source_test.go index 49a36f2c6bb..5fad885e1ab 100644 --- a/internal/service/ec2/route_tables_data_source_test.go +++ b/internal/service/ec2/route_tables_data_source_test.go @@ -11,7 +11,8 @@ import ( ) func TestAccEC2RouteTablesDataSource_basic(t *testing.T) { - rInt := sdkacctest.RandIntRange(0, 256) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, ec2.EndpointsID), @@ -19,154 +20,107 @@ func TestAccEC2RouteTablesDataSource_basic(t *testing.T) { CheckDestroy: testAccCheckVpcDestroy, Steps: []resource.TestStep{ { - Config: testAccRouteTablesDataSourceConfig(rInt), - }, - { - Config: testAccRouteTablesWithDataSourceDataSourceConfig(rInt), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("data.aws_route_tables.test", "ids.#", "5"), - resource.TestCheckResourceAttr("data.aws_route_tables.private", "ids.#", "3"), - resource.TestCheckResourceAttr("data.aws_route_tables.test2", "ids.#", "1"), - resource.TestCheckResourceAttr("data.aws_route_tables.filter_test", "ids.#", "2"), + Config: testAccRouteTablesDataSourceConfig(rName), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("data.aws_route_tables.by_vpc_id", "ids.#", "2"), // Add the default route table. + resource.TestCheckResourceAttr("data.aws_route_tables.by_tags", "ids.#", "2"), + resource.TestCheckResourceAttr("data.aws_route_tables.by_filter", "ids.#", "6"), // Add the default route tables. + resource.TestCheckResourceAttr("data.aws_route_tables.empty", "ids.#", "0"), ), }, }, }) } -func testAccRouteTablesWithDataSourceDataSourceConfig(rInt int) string { +func testAccRouteTablesDataSourceConfig(rName string) string { return fmt.Sprintf(` -resource "aws_vpc" "test" { - cidr_block = "172.%d.0.0/16" +resource "aws_vpc" "test1" { + cidr_block = "172.16.0.0/16" tags = { - Name = "terraform-testacc-route-tables-data-source" + Name = %[1]q } } resource "aws_vpc" "test2" { - cidr_block = "172.%d.0.0/16" + cidr_block = "172.16.0.0/16" tags = { - Name = "terraform-test2acc-route-tables-data-source" + Name = %[1]q } } -resource "aws_route_table" "test_public_a" { - vpc_id = aws_vpc.test.id +resource "aws_route_table" "test1_public" { + vpc_id = aws_vpc.test1.id tags = { - Name = "tf-acc-route-tables-data-source-public-a" + Name = %[1]q Tier = "Public" Component = "Frontend" } } -resource "aws_route_table" "test_private_a" { - vpc_id = aws_vpc.test.id +resource "aws_route_table" "test1_private1" { + vpc_id = aws_vpc.test1.id tags = { - Name = "tf-acc-route-tables-data-source-private-a" + Name = %[1]q Tier = "Private" Component = "Database" } } -resource "aws_route_table" "test_private_b" { - vpc_id = aws_vpc.test.id +resource "aws_route_table" "test1_private2" { + vpc_id = aws_vpc.test1.id tags = { - Name = "tf-acc-route-tables-data-source-private-b" + Name = %[1]q Tier = "Private" - Component = "Backend-1" + Component = "AppServer" } } -resource "aws_route_table" "test_private_c" { - vpc_id = aws_vpc.test.id - - tags = { - Name = "tf-acc-route-tables-data-source-private-c" - Tier = "Private" - Component = "Backend-2" - } -} - -data "aws_route_tables" "test" { - vpc_id = aws_vpc.test.id -} - -data "aws_route_tables" "test2" { +resource "aws_route_table" "test2_public" { vpc_id = aws_vpc.test2.id -} - -data "aws_route_tables" "private" { - vpc_id = aws_vpc.test.id tags = { - Tier = "Private" + Name = %[1]q + Tier = "Public" + Component = "Frontend" } } -data "aws_route_tables" "filter_test" { - vpc_id = aws_vpc.test.id +data "aws_route_tables" "by_vpc_id" { + vpc_id = aws_vpc.test2.id - filter { - name = "tag:Component" - values = ["Backend*"] - } + depends_on = [aws_route_table.test1_public, aws_route_table.test1_private1, aws_route_table.test1_private2, aws_route_table.test2_public] } -`, rInt, rInt) -} - -func testAccRouteTablesDataSourceConfig(rInt int) string { - return fmt.Sprintf(` -resource "aws_vpc" "test" { - cidr_block = "172.%d.0.0/16" +data "aws_route_tables" "by_tags" { tags = { - Name = "terraform-testacc-route-tables-data-source" + Tier = "Public" } -} - -resource "aws_route_table" "test_public_a" { - vpc_id = aws_vpc.test.id - tags = { - Name = "tf-acc-route-tables-data-source-public-a" - Tier = "Public" - Component = "Frontend" - } + depends_on = [aws_route_table.test1_public, aws_route_table.test1_private1, aws_route_table.test1_private2, aws_route_table.test2_public] } -resource "aws_route_table" "test_private_a" { - vpc_id = aws_vpc.test.id - - tags = { - Name = "tf-acc-route-tables-data-source-private-a" - Tier = "Private" - Component = "Database" +data "aws_route_tables" "by_filter" { + filter { + name = "vpc-id" + values = [aws_vpc.test1.id, aws_vpc.test2.id] } -} - -resource "aws_route_table" "test_private_b" { - vpc_id = aws_vpc.test.id - tags = { - Name = "tf-acc-route-tables-data-source-private-b" - Tier = "Private" - Component = "Backend-1" - } + depends_on = [aws_route_table.test1_public, aws_route_table.test1_private1, aws_route_table.test1_private2, aws_route_table.test2_public] } -resource "aws_route_table" "test_private_c" { - vpc_id = aws_vpc.test.id +data "aws_route_tables" "empty" { + vpc_id = aws_vpc.test2.id tags = { - Name = "tf-acc-route-tables-data-source-private-c" - Tier = "Private" - Component = "Backend-2" + Tier = "Private" } + + depends_on = [aws_route_table.test1_public, aws_route_table.test1_private1, aws_route_table.test1_private2, aws_route_table.test2_public] } -`, rInt) +`, rName) } diff --git a/internal/service/ec2/security_groups_data_source.go b/internal/service/ec2/security_groups_data_source.go index f651645efb0..8dc1cc5e8c5 100644 --- a/internal/service/ec2/security_groups_data_source.go +++ b/internal/service/ec2/security_groups_data_source.go @@ -2,7 +2,6 @@ package ec2 import ( "fmt" - "log" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/arn" @@ -17,20 +16,19 @@ func DataSourceSecurityGroups() *schema.Resource { Read: dataSourceSecurityGroupsRead, Schema: map[string]*schema.Schema{ - "filter": DataSourceFiltersSchema(), - "tags": tftags.TagsSchemaComputed(), - - "ids": { + "arns": { Type: schema.TypeList, Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, }, - "vpc_ids": { + "filter": DataSourceFiltersSchema(), + "ids": { Type: schema.TypeList, Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, }, - "arns": { + "tags": tftags.TagsSchemaComputed(), + "vpc_ids": { Type: schema.TypeList, Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, @@ -41,75 +39,46 @@ func DataSourceSecurityGroups() *schema.Resource { func dataSourceSecurityGroupsRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).EC2Conn - req := &ec2.DescribeSecurityGroupsInput{} - filters, filtersOk := d.GetOk("filter") - tags, tagsOk := d.GetOk("tags") + input := &ec2.DescribeSecurityGroupsInput{} - if !filtersOk && !tagsOk { - return fmt.Errorf("One of filters or tags must be assigned") - } + input.Filters = append(input.Filters, BuildTagFilterList( + Tags(tftags.New(d.Get("tags").(map[string]interface{}))), + )...) - if filtersOk { - req.Filters = append(req.Filters, - BuildFiltersDataSource(filters.(*schema.Set))...) - } - if tagsOk { - req.Filters = append(req.Filters, BuildTagFilterList( - Tags(tftags.New(tags.(map[string]interface{}))), - )...) - } + input.Filters = append(input.Filters, BuildFiltersDataSource( + d.Get("filter").(*schema.Set), + )...) - log.Printf("[DEBUG] Reading Security Groups with request: %s", req) - - var ids, vpcIds, arns []string - for { - resp, err := conn.DescribeSecurityGroups(req) - if err != nil { - return fmt.Errorf("error reading security groups: %w", err) - } - - for _, sg := range resp.SecurityGroups { - ids = append(ids, aws.StringValue(sg.GroupId)) - vpcIds = append(vpcIds, aws.StringValue(sg.VpcId)) - - arn := arn.ARN{ - Partition: meta.(*conns.AWSClient).Partition, - Service: ec2.ServiceName, - Region: meta.(*conns.AWSClient).Region, - AccountID: aws.StringValue(sg.OwnerId), - Resource: fmt.Sprintf("security-group/%s", aws.StringValue(sg.GroupId)), - }.String() - - arns = append(arns, arn) - } - - if resp.NextToken == nil { - break - } - req.NextToken = resp.NextToken + if len(input.Filters) == 0 { + input.Filters = nil } - if len(ids) < 1 { - return fmt.Errorf("Your query returned no results. Please change your search criteria and try again.") - } + output, err := FindSecurityGroups(conn, input) - log.Printf("[DEBUG] Found %d security groups via given filter: %s", len(ids), req) - - d.SetId(meta.(*conns.AWSClient).Region) - - err := d.Set("ids", ids) if err != nil { - return err + return fmt.Errorf("error reading EC2 Security Groups: %w", err) } - if err = d.Set("vpc_ids", vpcIds); err != nil { - return fmt.Errorf("error setting vpc_ids: %s", err) + var arns, securityGroupIDs, vpcIDs []string + + for _, v := range output { + arn := arn.ARN{ + Partition: meta.(*conns.AWSClient).Partition, + Service: ec2.ServiceName, + Region: meta.(*conns.AWSClient).Region, + AccountID: aws.StringValue(v.OwnerId), + Resource: fmt.Sprintf("security-group/%s", aws.StringValue(v.GroupId)), + }.String() + arns = append(arns, arn) + securityGroupIDs = append(securityGroupIDs, aws.StringValue(v.GroupId)) + vpcIDs = append(vpcIDs, aws.StringValue(v.VpcId)) } - if err = d.Set("arns", arns); err != nil { - return fmt.Errorf("error setting arns: %s", err) - } + d.SetId(meta.(*conns.AWSClient).Region) + d.Set("arns", arns) + d.Set("ids", securityGroupIDs) + d.Set("vpc_ids", vpcIDs) return nil } diff --git a/internal/service/ec2/security_groups_data_source_test.go b/internal/service/ec2/security_groups_data_source_test.go index fba42a1bf1e..803b3139f5d 100644 --- a/internal/service/ec2/security_groups_data_source_test.go +++ b/internal/service/ec2/security_groups_data_source_test.go @@ -11,19 +11,20 @@ import ( ) func TestAccEC2SecurityGroupsDataSource_tag(t *testing.T) { - rInt := sdkacctest.RandInt() - dataSourceName := "data.aws_security_groups.by_tag" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + dataSourceName := "data.aws_security_groups.test" + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, ec2.EndpointsID), Providers: acctest.Providers, Steps: []resource.TestStep{ { - Config: testAccSecurityGroupsDataSourceConfig_tag(rInt), + Config: testAccSecurityGroupsDataSourceConfig_tag(rName), Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(dataSourceName, "arns.#", "3"), resource.TestCheckResourceAttr(dataSourceName, "ids.#", "3"), resource.TestCheckResourceAttr(dataSourceName, "vpc_ids.#", "3"), - resource.TestCheckResourceAttr(dataSourceName, "arns.#", "3"), ), }, }, @@ -31,83 +32,116 @@ func TestAccEC2SecurityGroupsDataSource_tag(t *testing.T) { } func TestAccEC2SecurityGroupsDataSource_filter(t *testing.T) { - rInt := sdkacctest.RandInt() - dataSourceName := "data.aws_security_groups.by_filter" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + dataSourceName := "data.aws_security_groups.test" + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, ec2.EndpointsID), Providers: acctest.Providers, Steps: []resource.TestStep{ { - Config: testAccSecurityGroupsDataSourceConfig_filter(rInt), + Config: testAccSecurityGroupsDataSourceConfig_filter(rName), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr(dataSourceName, "ids.#", "3"), - resource.TestCheckResourceAttr(dataSourceName, "vpc_ids.#", "3"), - resource.TestCheckResourceAttr(dataSourceName, "arns.#", "3"), + resource.TestCheckResourceAttr(dataSourceName, "arns.#", "1"), + resource.TestCheckResourceAttr(dataSourceName, "ids.#", "1"), + resource.TestCheckResourceAttr(dataSourceName, "vpc_ids.#", "1"), ), }, }, }) } -func testAccSecurityGroupsDataSourceConfig_tag(rInt int) string { +func TestAccEC2SecurityGroupsDataSource_empty(t *testing.T) { + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + dataSourceName := "data.aws_security_groups.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, ec2.EndpointsID), + Providers: acctest.Providers, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceAwsSecurityGroupsConfig_empty(rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(dataSourceName, "arns.#", "0"), + resource.TestCheckResourceAttr(dataSourceName, "ids.#", "0"), + resource.TestCheckResourceAttr(dataSourceName, "vpc_ids.#", "0"), + ), + }, + }, + }) +} + +func testAccSecurityGroupsDataSourceConfig_tag(rName string) string { return fmt.Sprintf(` -resource "aws_vpc" "test_tag" { +resource "aws_vpc" "test" { cidr_block = "172.16.0.0/16" tags = { - Name = "terraform-testacc-security-group-data-source" + Name = %[1]q } } resource "aws_security_group" "test" { count = 3 - vpc_id = aws_vpc.test_tag.id - name = "tf-%[1]d-${count.index}" + vpc_id = aws_vpc.test.id + name = "%[1]s-${count.index}" tags = { - Seed = "%[1]d" + Name = %[1]q } } -data "aws_security_groups" "by_tag" { +data "aws_security_groups" "test" { tags = { - Seed = aws_security_group.test[0].tags["Seed"] + Name = %[1]q } + + depends_on = [aws_security_group.test[0], aws_security_group.test[1], aws_security_group.test[2]] } -`, rInt) +`, rName) } -func testAccSecurityGroupsDataSourceConfig_filter(rInt int) string { +func testAccSecurityGroupsDataSourceConfig_filter(rName string) string { return fmt.Sprintf(` -resource "aws_vpc" "test_filter" { +resource "aws_vpc" "test" { cidr_block = "172.16.0.0/16" tags = { - Name = "terraform-testacc-security-group-data-source" + Name = %[1]q } } resource "aws_security_group" "test" { - count = 3 - vpc_id = aws_vpc.test_filter.id - name = "tf-%[1]d-${count.index}" + vpc_id = aws_vpc.test.id + name = %[1]q tags = { - Seed = "%[1]d" + Name = %[1]q } } -data "aws_security_groups" "by_filter" { +data "aws_security_groups" "test" { filter { name = "vpc-id" - values = [aws_vpc.test_filter.id] + values = [aws_vpc.test.id] } filter { name = "group-name" - values = ["tf-${aws_security_group.test[0].tags["Seed"]}-*"] + values = [aws_security_group.test.name] + } +} +`, rName) +} + +func testAccDataSourceAwsSecurityGroupsConfig_empty(rName string) string { + return fmt.Sprintf(` +data "aws_security_groups" "test" { + tags = { + Name = %[1]q } } -`, rInt) +`, rName) } diff --git a/internal/service/ec2/status.go b/internal/service/ec2/status.go index cbe7289dff7..f752f7aa0cd 100644 --- a/internal/service/ec2/status.go +++ b/internal/service/ec2/status.go @@ -218,7 +218,7 @@ func StatusInstanceIAMInstanceProfile(conn *ec2.EC2, id string) resource.StateRe return func() (interface{}, string, error) { instance, err := FindInstanceByID(conn, id) - if tfawserr.ErrCodeEquals(err, ErrCodeInvalidInstanceIDNotFound) { + if tfresource.NotFound(err) { return nil, "", nil } @@ -226,10 +226,6 @@ func StatusInstanceIAMInstanceProfile(conn *ec2.EC2, id string) resource.StateRe return nil, "", err } - if instance == nil { - return nil, "", nil - } - if instance.IamInstanceProfile == nil || instance.IamInstanceProfile.Arn == nil { return instance, "", nil } diff --git a/internal/service/ec2/transit_gateway_data_source_test.go b/internal/service/ec2/transit_gateway_data_source_test.go index 9b3d5c800aa..8cafb8d3e57 100644 --- a/internal/service/ec2/transit_gateway_data_source_test.go +++ b/internal/service/ec2/transit_gateway_data_source_test.go @@ -31,7 +31,9 @@ func TestAccEC2TransitGatewayDataSource_serial(t *testing.T) { }, "RouteTables": { "basic": testAccTransitGatewayRouteTablesDataSource_basic, - "Filter": testAccTransitGatewayRouteTablesDataSource_Filter, + "Filter": testAccTransitGatewayRouteTablesDataSource_filter, + "Tags": testAccTransitGatewayRouteTablesDataSource_tags, + "Empty": testAccTransitGatewayRouteTablesDataSource_empty, }, "VpcAttachment": { "Filter": testAccTransitGatewayVPCAttachmentDataSource_Filter, diff --git a/internal/service/ec2/transit_gateway_route_tables_data_source.go b/internal/service/ec2/transit_gateway_route_tables_data_source.go index 294050898b8..be60da44063 100644 --- a/internal/service/ec2/transit_gateway_route_tables_data_source.go +++ b/internal/service/ec2/transit_gateway_route_tables_data_source.go @@ -15,15 +15,12 @@ func DataSourceTransitGatewayRouteTables() *schema.Resource { Read: dataSourceTransitGatewayRouteTablesRead, Schema: map[string]*schema.Schema{ - "filter": CustomFiltersSchema(), - + "filter": DataSourceFiltersSchema(), "ids": { - Type: schema.TypeSet, + Type: schema.TypeList, Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, - Set: schema.HashString, }, - "tags": tftags.TagsSchemaComputed(), }, } @@ -38,50 +35,28 @@ func dataSourceTransitGatewayRouteTablesRead(d *schema.ResourceData, meta interf Tags(tftags.New(d.Get("tags").(map[string]interface{}))), )...) - input.Filters = append(input.Filters, BuildCustomFilterList( + input.Filters = append(input.Filters, BuildFiltersDataSource( d.Get("filter").(*schema.Set), )...) if len(input.Filters) == 0 { - // Don't send an empty filters list; the EC2 API won't accept it. input.Filters = nil } - var transitGatewayRouteTables []*ec2.TransitGatewayRouteTable - - err := conn.DescribeTransitGatewayRouteTablesPages(input, func(page *ec2.DescribeTransitGatewayRouteTablesOutput, lastPage bool) bool { - if page == nil { - return !lastPage - } - - transitGatewayRouteTables = append(transitGatewayRouteTables, page.TransitGatewayRouteTables...) - - return !lastPage - }) + output, err := FindTransitGatewayRouteTables(conn, input) if err != nil { - return fmt.Errorf("error describing EC2 Transit Gateway Route Tables: %w", err) + return fmt.Errorf("error reading EC2 Transit Gateway Route Tables: %w", err) } - if len(transitGatewayRouteTables) == 0 { - return fmt.Errorf("no matching EC2 Transit Gateway Route Tables found") - } - - var ids []string - - for _, transitGatewayRouteTable := range transitGatewayRouteTables { - if transitGatewayRouteTable == nil { - continue - } + var routeTableIDs []string - ids = append(ids, aws.StringValue(transitGatewayRouteTable.TransitGatewayRouteTableId)) + for _, v := range output { + routeTableIDs = append(routeTableIDs, aws.StringValue(v.TransitGatewayRouteTableId)) } d.SetId(meta.(*conns.AWSClient).Region) - - if err = d.Set("ids", ids); err != nil { - return fmt.Errorf("error setting ids: %w", err) - } + d.Set("ids", routeTableIDs) return nil } diff --git a/internal/service/ec2/transit_gateway_route_tables_data_source_test.go b/internal/service/ec2/transit_gateway_route_tables_data_source_test.go index d33e61175dc..3e3bdfff874 100644 --- a/internal/service/ec2/transit_gateway_route_tables_data_source_test.go +++ b/internal/service/ec2/transit_gateway_route_tables_data_source_test.go @@ -1,14 +1,17 @@ package ec2_test import ( + "fmt" "testing" "github.com/aws/aws-sdk-go/service/ec2" + sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-provider-aws/internal/acctest" ) func testAccTransitGatewayRouteTablesDataSource_basic(t *testing.T) { + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) dataSourceName := "data.aws_ec2_transit_gateway_route_tables.test" resource.Test(t, resource.TestCase{ @@ -17,7 +20,7 @@ func testAccTransitGatewayRouteTablesDataSource_basic(t *testing.T) { Providers: acctest.Providers, Steps: []resource.TestStep{ { - Config: testAccTransitGatewayRouteTablesDataSourceConfig, + Config: testAccTransitGatewayRouteTablesDataSourceConfig(rName), Check: resource.ComposeTestCheckFunc( acctest.CheckResourceAttrGreaterThanValue(dataSourceName, "ids.#", "0"), ), @@ -26,7 +29,8 @@ func testAccTransitGatewayRouteTablesDataSource_basic(t *testing.T) { }) } -func testAccTransitGatewayRouteTablesDataSource_Filter(t *testing.T) { +func testAccTransitGatewayRouteTablesDataSource_filter(t *testing.T) { + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) dataSourceName := "data.aws_ec2_transit_gateway_route_tables.test" resource.Test(t, resource.TestCase{ @@ -35,32 +39,89 @@ func testAccTransitGatewayRouteTablesDataSource_Filter(t *testing.T) { Providers: acctest.Providers, Steps: []resource.TestStep{ { - Config: testAccTransitGatewayRouteTablesTransitGatewayFilterDataSource, + Config: testAccTransitGatewayRouteTablesTransitGatewayFilterDataSource(rName), Check: resource.ComposeTestCheckFunc( - acctest.CheckResourceAttrGreaterThanValue(dataSourceName, "ids.#", "0"), + resource.TestCheckResourceAttr(dataSourceName, "ids.#", "2"), + ), + }, + }, + }) +} + +func testAccTransitGatewayRouteTablesDataSource_tags(t *testing.T) { + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + dataSourceName := "data.aws_ec2_transit_gateway_route_tables.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t); testAccPreCheckTransitGateway(t) }, + ErrorCheck: acctest.ErrorCheck(t, ec2.EndpointsID), + Providers: acctest.Providers, + Steps: []resource.TestStep{ + { + Config: testAccTransitGatewayRouteTablesTransitGatewayTagsDataSource(rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(dataSourceName, "ids.#", "1"), + ), + }, + }, + }) +} + +func testAccTransitGatewayRouteTablesDataSource_empty(t *testing.T) { + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + dataSourceName := "data.aws_ec2_transit_gateway_route_tables.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t); testAccPreCheckTransitGateway(t) }, + ErrorCheck: acctest.ErrorCheck(t, ec2.EndpointsID), + Providers: acctest.Providers, + Steps: []resource.TestStep{ + { + Config: testAccTransitGatewayRouteTablesTransitGatewayEmptyDataSource(rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(dataSourceName, "ids.#", "0"), ), }, }, }) } -const testAccTransitGatewayRouteTablesDataSourceConfig = ` -resource "aws_ec2_transit_gateway" "test" {} +func testAccTransitGatewayRouteTablesDataSourceConfig(rName string) string { + return fmt.Sprintf(` +resource "aws_ec2_transit_gateway" "test" { + tags = { + Name = %[1]q + } +} resource "aws_ec2_transit_gateway_route_table" "test" { transit_gateway_id = aws_ec2_transit_gateway.test.id + + tags = { + Name = %[1]q + } } data "aws_ec2_transit_gateway_route_tables" "test" { depends_on = [aws_ec2_transit_gateway_route_table.test] } -` +`, rName) +} -const testAccTransitGatewayRouteTablesTransitGatewayFilterDataSource = ` -resource "aws_ec2_transit_gateway" "test" {} +func testAccTransitGatewayRouteTablesTransitGatewayFilterDataSource(rName string) string { + return fmt.Sprintf(` +resource "aws_ec2_transit_gateway" "test" { + tags = { + Name = %[1]q + } +} resource "aws_ec2_transit_gateway_route_table" "test" { transit_gateway_id = aws_ec2_transit_gateway.test.id + + tags = { + Name = %[1]q + } } data "aws_ec2_transit_gateway_route_tables" "test" { @@ -71,4 +132,41 @@ data "aws_ec2_transit_gateway_route_tables" "test" { depends_on = [aws_ec2_transit_gateway_route_table.test] } -` +`, rName) +} + +func testAccTransitGatewayRouteTablesTransitGatewayTagsDataSource(rName string) string { + return fmt.Sprintf(` +resource "aws_ec2_transit_gateway" "test" { + tags = { + Name = %[1]q + } +} + +resource "aws_ec2_transit_gateway_route_table" "test" { + transit_gateway_id = aws_ec2_transit_gateway.test.id + + tags = { + Name = %[1]q + } +} + +data "aws_ec2_transit_gateway_route_tables" "test" { + tags = { + Name = %[1]q + } + + depends_on = [aws_ec2_transit_gateway_route_table.test] +} +`, rName) +} + +func testAccTransitGatewayRouteTablesTransitGatewayEmptyDataSource(rName string) string { + return fmt.Sprintf(` +data "aws_ec2_transit_gateway_route_tables" "test" { + tags = { + Name = %[1]q + } +} +`, rName) +} diff --git a/internal/service/efs/access_points_data_source.go b/internal/service/efs/access_points_data_source.go index 3fc36a150d1..44b4abf0516 100644 --- a/internal/service/efs/access_points_data_source.go +++ b/internal/service/efs/access_points_data_source.go @@ -16,7 +16,7 @@ func DataSourceAccessPoints() *schema.Resource { Schema: map[string]*schema.Schema{ "arns": { - Type: schema.TypeSet, + Type: schema.TypeList, Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, }, @@ -26,7 +26,7 @@ func DataSourceAccessPoints() *schema.Resource { ValidateFunc: validation.StringIsNotEmpty, }, "ids": { - Type: schema.TypeSet, + Type: schema.TypeList, Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, }, @@ -37,47 +37,51 @@ func DataSourceAccessPoints() *schema.Resource { func dataSourceAccessPointsRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).EFSConn - fileSystemId := d.Get("file_system_id").(string) + fileSystemID := d.Get("file_system_id").(string) input := &efs.DescribeAccessPointsInput{ - FileSystemId: aws.String(fileSystemId), + FileSystemId: aws.String(fileSystemID), } - var accessPoints []*efs.AccessPointDescription + output, err := findAccessPointDescriptions(conn, input) + + if err != nil { + return fmt.Errorf("error reading EFS Access Points: %w", err) + } + + var accessPointIDs, arns []string + + for _, v := range output { + accessPointIDs = append(accessPointIDs, aws.StringValue(v.AccessPointId)) + arns = append(arns, aws.StringValue(v.AccessPointArn)) + } + + d.SetId(fileSystemID) + d.Set("arns", arns) + d.Set("ids", accessPointIDs) + + return nil +} + +func findAccessPointDescriptions(conn *efs.EFS, input *efs.DescribeAccessPointsInput) ([]*efs.AccessPointDescription, error) { + var output []*efs.AccessPointDescription err := conn.DescribeAccessPointsPages(input, func(page *efs.DescribeAccessPointsOutput, lastPage bool) bool { if page == nil { return !lastPage } - accessPoints = append(accessPoints, page.AccessPoints...) + for _, v := range page.AccessPoints { + if v != nil { + output = append(output, v) + } + } return !lastPage }) if err != nil { - return fmt.Errorf("error reading EFS Access Points for File System (%s): %w", fileSystemId, err) + return nil, err } - if len(accessPoints) == 0 { - return fmt.Errorf("no matching EFS Access Points for File System (%s) found", fileSystemId) - } - - d.SetId(fileSystemId) - - var arns, ids []string - - for _, accessPoint := range accessPoints { - arns = append(arns, aws.StringValue(accessPoint.AccessPointArn)) - ids = append(ids, aws.StringValue(accessPoint.AccessPointId)) - } - - if err := d.Set("arns", arns); err != nil { - return fmt.Errorf("error setting arns: %w", err) - } - - if err := d.Set("ids", ids); err != nil { - return fmt.Errorf("error setting ids: %w", err) - } - - return nil + return output, nil } diff --git a/internal/service/efs/access_points_data_source_test.go b/internal/service/efs/access_points_data_source_test.go index 9e87e146a34..9ae9a1b3f4d 100644 --- a/internal/service/efs/access_points_data_source_test.go +++ b/internal/service/efs/access_points_data_source_test.go @@ -28,6 +28,26 @@ func TestAccEFSAccessPointsDataSource_basic(t *testing.T) { }) } +func TestAccEFSAccessPointsDataSource_empty(t *testing.T) { + dataSourceName := "data.aws_efs_access_points.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, efs.EndpointsID), + Providers: acctest.Providers, + CheckDestroy: testAccCheckEfsAccessPointDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAccessPointsEmptyDataSourceConfig(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(dataSourceName, "arns.#", "0"), + resource.TestCheckResourceAttr(dataSourceName, "ids.#", "0"), + ), + }, + }, + }) +} + func testAccAccessPointsDataSourceConfig() string { return ` resource "aws_efs_file_system" "test" {} @@ -41,3 +61,13 @@ data "aws_efs_access_points" "test" { } ` } + +func testAccAccessPointsEmptyDataSourceConfig() string { + return ` +resource "aws_efs_file_system" "test" {} + +data "aws_efs_access_points" "test" { + file_system_id = aws_efs_file_system.test.id +} +` +} diff --git a/internal/service/emr/release_labels_data_source.go b/internal/service/emr/release_labels_data_source.go index b863bac5584..b8b92b9f8b5 100644 --- a/internal/service/emr/release_labels_data_source.go +++ b/internal/service/emr/release_labels_data_source.go @@ -10,12 +10,12 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" - "github.com/hashicorp/terraform-provider-aws/internal/flex" ) func DataSourceReleaseLabels() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceReleaseLabelsRead, + Schema: map[string]*schema.Schema{ "filters": { Type: schema.TypeList, @@ -35,7 +35,7 @@ func DataSourceReleaseLabels() *schema.Resource { }, }, "release_labels": { - Type: schema.TypeSet, + Type: schema.TypeList, Elem: &schema.Schema{Type: schema.TypeString}, Computed: true, }, @@ -52,17 +52,20 @@ func dataSourceReleaseLabelsRead(ctx context.Context, d *schema.ResourceData, me input.Filters = expandReleaseLabelsFilters(v.([]interface{})) } - out, err := conn.ListReleaseLabels(input) + output, err := findReleaseLabels(conn, input) + if err != nil { - return diag.FromErr(fmt.Errorf("error reading EMR Release Label: %w", err)) + return diag.FromErr(fmt.Errorf("error reading EMR Release Labels: %w", err)) } - if len(out.ReleaseLabels) == 0 { - return diag.Errorf("no EMR release labels found") - } + releaseLabels := aws.StringValueSlice(output) - d.SetId(strings.Join(aws.StringValueSlice(out.ReleaseLabels), ",")) - d.Set("release_labels", flex.FlattenStringSet(out.ReleaseLabels)) + if len(releaseLabels) == 0 { + d.SetId(",") + } else { + d.SetId(strings.Join(releaseLabels, ",")) + } + d.Set("release_labels", releaseLabels) return nil } @@ -85,3 +88,26 @@ func expandReleaseLabelsFilters(filters []interface{}) *emr.ReleaseLabelFilter { return app } + +func findReleaseLabels(conn *emr.EMR, input *emr.ListReleaseLabelsInput) ([]*string, error) { + var output []*string + + err := conn.ListReleaseLabelsPages(input, func(page *emr.ListReleaseLabelsOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + for _, v := range page.ReleaseLabels { + if v != nil { + output = append(output, v) + } + } + + return !lastPage + }) + + if err != nil { + return nil, err + } + + return output, nil +} diff --git a/internal/service/emr/release_labels_data_source_test.go b/internal/service/emr/release_labels_data_source_test.go index ca2836f04a0..646adf090a6 100644 --- a/internal/service/emr/release_labels_data_source_test.go +++ b/internal/service/emr/release_labels_data_source_test.go @@ -84,6 +84,25 @@ func TestAccEMRReleaseLabels_full(t *testing.T) { }) } +func TestAccEMRReleaseLabels_empty(t *testing.T) { + dataSourceResourceName := "data.aws_emr_release_labels.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, emr.EndpointsID), + ProviderFactories: acctest.ProviderFactories, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: testAccReleaseLabelsDataSourceConfigEmpty(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(dataSourceResourceName, "release_labels.#", "0"), + ), + }, + }, + }) +} + func testAccReleaseLabelsDataSourceConfigBasic() string { return ` data "aws_emr_release_labels" "test" {} @@ -120,3 +139,13 @@ data "aws_emr_release_labels" "test" { } ` } + +func testAccReleaseLabelsDataSourceConfigEmpty() string { + return ` +data "aws_emr_release_labels" "test" { + filters { + prefix = "emr-0" + } +} +` +} diff --git a/internal/service/inspector/rules_packages_data_source.go b/internal/service/inspector/rules_packages_data_source.go index 5e92d5dc240..6ab5d66e0e6 100644 --- a/internal/service/inspector/rules_packages_data_source.go +++ b/internal/service/inspector/rules_packages_data_source.go @@ -1,11 +1,10 @@ package inspector import ( - "errors" "fmt" - "log" "sort" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/inspector" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -28,30 +27,42 @@ func DataSourceRulesPackages() *schema.Resource { func dataSourceRulesPackagesRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).InspectorConn - log.Printf("[DEBUG] Reading Rules Packages.") + output, err := findRulesPackageArns(conn) - var arns []string + if err != nil { + return fmt.Errorf("error reading Inspector Rules Packages: %w", err) + } + + arns := aws.StringValueSlice(output) + sort.Strings(arns) + d.SetId(meta.(*conns.AWSClient).Region) + d.Set("arns", arns) + + return nil +} + +func findRulesPackageArns(conn *inspector.Inspector) ([]*string, error) { input := &inspector.ListRulesPackagesInput{} + var output []*string err := conn.ListRulesPackagesPages(input, func(page *inspector.ListRulesPackagesOutput, lastPage bool) bool { - for _, arn := range page.RulesPackageArns { - arns = append(arns, *arn) + if page == nil { + return !lastPage + } + + for _, v := range page.RulesPackageArns { + if v != nil { + output = append(output, v) + } } + return !lastPage }) - if err != nil { - return fmt.Errorf("Error fetching Rules Packages: %w", err) - } - if len(arns) == 0 { - return errors.New("No rules packages found.") + if err != nil { + return nil, err } - d.SetId(meta.(*conns.AWSClient).Region) - - sort.Strings(arns) - d.Set("arns", arns) - - return nil + return output, nil } diff --git a/internal/service/meta/ip_ranges_data_source.go b/internal/service/meta/ip_ranges_data_source.go index ba208bb09f4..2ad149767f3 100644 --- a/internal/service/meta/ip_ranges_data_source.go +++ b/internal/service/meta/ip_ranges_data_source.go @@ -160,10 +160,6 @@ func dataSourceIPRangesRead(d *schema.ResourceData, meta interface{}) error { } } - if len(ipPrefixes) == 0 && len(ipv6Prefixes) == 0 { - return fmt.Errorf("No IP ranges result from filters from (%s)", url) - } - sort.Strings(ipPrefixes) if err := d.Set("cidr_blocks", ipPrefixes); err != nil { diff --git a/internal/service/meta/ip_ranges_data_source_test.go b/internal/service/meta/ip_ranges_data_source_test.go index e3dbe2fdf6a..547afd791d6 100644 --- a/internal/service/meta/ip_ranges_data_source_test.go +++ b/internal/service/meta/ip_ranges_data_source_test.go @@ -27,6 +27,8 @@ func TestAccMetaIPRangesDataSource_basic(t *testing.T) { testAccIPRangesCheckAttributes("data.aws_ip_ranges.some"), testAccIPRangesCheckCIDRBlocksAttribute("data.aws_ip_ranges.some", "cidr_blocks"), testAccIPRangesCheckCIDRBlocksAttribute("data.aws_ip_ranges.some", "ipv6_cidr_blocks"), + resource.TestCheckResourceAttr("data.aws_ip_ranges.none", "cidr_blocks.#", "0"), + resource.TestCheckResourceAttr("data.aws_ip_ranges.none", "ipv6_cidr_blocks.#", "0"), ), }, }, @@ -163,6 +165,11 @@ data "aws_ip_ranges" "some" { regions = ["eu-west-1", "eu-central-1"] services = ["ec2"] } + +data "aws_ip_ranges" "none" { + regions = ["mars-1"] + services = ["blueorigin"] +} ` // lintignore:AWSAT003 diff --git a/internal/service/rds/event_categories_data_source.go b/internal/service/rds/event_categories_data_source.go index 433f5106478..7fbce681efb 100644 --- a/internal/service/rds/event_categories_data_source.go +++ b/internal/service/rds/event_categories_data_source.go @@ -2,11 +2,11 @@ package rds import ( "fmt" - "log" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/rds" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" ) @@ -15,15 +15,15 @@ func DataSourceEventCategories() *schema.Resource { Read: dataSourceEventCategoriesRead, Schema: map[string]*schema.Schema{ - "source_type": { - Type: schema.TypeString, - Optional: true, - }, "event_categories": { - Type: schema.TypeSet, + Type: schema.TypeList, Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, - Set: schema.HashString, + }, + "source_type": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice(rds.SourceType_Values(), false), }, }, } @@ -32,35 +32,45 @@ func DataSourceEventCategories() *schema.Resource { func dataSourceEventCategoriesRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).RDSConn - req := &rds.DescribeEventCategoriesInput{} + input := &rds.DescribeEventCategoriesInput{} - if sourceType := d.Get("source_type").(string); sourceType != "" { - req.SourceType = aws.String(sourceType) + if v, ok := d.GetOk("source_type"); ok { + input.SourceType = aws.String(v.(string)) } - log.Printf("[DEBUG] Describe Event Categories %s\n", req) - resp, err := conn.DescribeEventCategories(req) - if err != nil { - return err - } + output, err := findEventCategoriesMaps(conn, input) - if resp == nil || len(resp.EventCategoriesMapList) == 0 { - return fmt.Errorf("Event Categories not found") + if err != nil { + return fmt.Errorf("error reading RDS Event Categories: %w", err) } - eventCategories := make([]string, 0) + var eventCategories []string - for _, eventMap := range resp.EventCategoriesMapList { - for _, v := range eventMap.EventCategories { - eventCategories = append(eventCategories, aws.StringValue(v)) - } + for _, v := range output { + eventCategories = append(eventCategories, aws.StringValueSlice(v.EventCategories)...) } d.SetId(meta.(*conns.AWSClient).Region) - if err := d.Set("event_categories", eventCategories); err != nil { - return fmt.Errorf("Error setting Event Categories: %w", err) - } + d.Set("event_categories", eventCategories) return nil } + +func findEventCategoriesMaps(conn *rds.RDS, input *rds.DescribeEventCategoriesInput) ([]*rds.EventCategoriesMap, error) { + var output []*rds.EventCategoriesMap + + page, err := conn.DescribeEventCategories(input) + + if err != nil { + return nil, err + } + + for _, v := range page.EventCategoriesMapList { + if v != nil { + output = append(output, v) + } + } + + return output, nil +} diff --git a/internal/service/ssoadmin/instances_data_source.go b/internal/service/ssoadmin/instances_data_source.go index a97e418c82d..5bafb16dfc3 100644 --- a/internal/service/ssoadmin/instances_data_source.go +++ b/internal/service/ssoadmin/instances_data_source.go @@ -15,13 +15,12 @@ func DataSourceInstances() *schema.Resource { Schema: map[string]*schema.Schema{ "arns": { - Type: schema.TypeSet, + Type: schema.TypeList, Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, }, - "identity_store_ids": { - Type: schema.TypeSet, + Type: schema.TypeList, Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, }, @@ -32,39 +31,47 @@ func DataSourceInstances() *schema.Resource { func dataSourceInstancesRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).SSOAdminConn - var instances []*ssoadmin.InstanceMetadata - var arns, ids []string + output, err := findInstanceMetadatas(conn) + + if err != nil { + return fmt.Errorf("error reading SSO Instances: %w", err) + } + + var identityStoreIDs, arns []string + + for _, v := range output { + identityStoreIDs = append(identityStoreIDs, aws.StringValue(v.IdentityStoreId)) + arns = append(arns, aws.StringValue(v.InstanceArn)) + } + + d.SetId(meta.(*conns.AWSClient).Region) + d.Set("arns", arns) + d.Set("identity_store_ids", identityStoreIDs) + + return nil +} - err := conn.ListInstancesPages(&ssoadmin.ListInstancesInput{}, func(page *ssoadmin.ListInstancesOutput, lastPage bool) bool { +func findInstanceMetadatas(conn *ssoadmin.SSOAdmin) ([]*ssoadmin.InstanceMetadata, error) { + input := &ssoadmin.ListInstancesInput{} + var output []*ssoadmin.InstanceMetadata + + err := conn.ListInstancesPages(input, func(page *ssoadmin.ListInstancesOutput, lastPage bool) bool { if page == nil { return !lastPage } - instances = append(instances, page.Instances...) + for _, v := range page.Instances { + if v != nil { + output = append(output, v) + } + } return !lastPage }) if err != nil { - return fmt.Errorf("error reading SSO Instances: %w", err) - } - - if len(instances) == 0 { - return fmt.Errorf("error reading SSO Instance: no instances found") - } - - for _, instance := range instances { - arns = append(arns, aws.StringValue(instance.InstanceArn)) - ids = append(ids, aws.StringValue(instance.IdentityStoreId)) - } - - d.SetId(meta.(*conns.AWSClient).Region) - if err := d.Set("arns", arns); err != nil { - return fmt.Errorf("error setting arns: %w", err) - } - if err := d.Set("identity_store_ids", ids); err != nil { - return fmt.Errorf("error setting identity_store_ids: %w", err) + return nil, err } - return nil + return output, nil } diff --git a/website/docs/d/network_acls.html.markdown b/website/docs/d/network_acls.html.markdown index 5a0d21ce824..e53660d74e7 100644 --- a/website/docs/d/network_acls.html.markdown +++ b/website/docs/d/network_acls.html.markdown @@ -70,4 +70,4 @@ which take the following arguments: ## Attributes Reference * `id` - AWS Region. -* `ids` - A list of all the network ACL ids found. This data source will fail if none are found. +* `ids` - A list of all the network ACL ids found. diff --git a/website/docs/d/network_interfaces.html.markdown b/website/docs/d/network_interfaces.html.markdown index 0a5a6ec6c6b..fd0be09ca7f 100644 --- a/website/docs/d/network_interfaces.html.markdown +++ b/website/docs/d/network_interfaces.html.markdown @@ -68,5 +68,5 @@ which take the following arguments: ## Attributes Reference * `id` - AWS Region. -* `ids` - A list of all the network interface ids found. This data source will fail if none are found. +* `ids` - A list of all the network interface ids found. diff --git a/website/docs/d/route_tables.html.markdown b/website/docs/d/route_tables.html.markdown index e874be58192..37e88521e60 100644 --- a/website/docs/d/route_tables.html.markdown +++ b/website/docs/d/route_tables.html.markdown @@ -55,4 +55,4 @@ which take the following arguments: ## Attributes Reference * `id` - AWS Region. -* `ids` - A set of all the route table ids found. This data source will fail if none are found. +* `ids` - A set of all the route table ids found. diff --git a/website/docs/d/vpcs.html.markdown b/website/docs/d/vpcs.html.markdown index 56b26fcc18a..8cd42429528 100644 --- a/website/docs/d/vpcs.html.markdown +++ b/website/docs/d/vpcs.html.markdown @@ -71,4 +71,4 @@ which take the following arguments: ## Attributes Reference * `id` - AWS Region. -* `ids` - A list of all the VPC Ids found. This data source will fail if none are found. +* `ids` - A list of all the VPC Ids found.