Skip to content

Commit

Permalink
Add function to check TypeSet pairs
Browse files Browse the repository at this point in the history
  • Loading branch information
YakDriver committed Jul 21, 2020
1 parent d9f833b commit ef590c5
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 37 deletions.
98 changes: 72 additions & 26 deletions aws/internal/tfawsresource/testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,46 +95,92 @@ func TestCheckTypeSetElemNestedAttrs(res, attr string, values map[string]string)
}
}

// instanceState returns the primary instance state for the given
// resource name in the root module.
func instanceState(s *terraform.State, name string) (*terraform.InstanceState, error) {
ms := s.RootModule()
rs, ok := ms.Resources[name]
if !ok {
return nil, fmt.Errorf("Not found: %s in %s", name, ms.Path)
}

is := rs.Primary
if is == nil {
return nil, fmt.Errorf("No primary instance: %s in %s", name, ms.Path)
}

return is, nil
}

func testCheckTypeSetElem(is *terraform.InstanceState, attr, value string) error {
attrParts := strings.Split(attr, ".")
if attrParts[len(attrParts)-1] != sentinelIndex {
return fmt.Errorf("%q does not end with the special value %q", attr, sentinelIndex)
}
for stateKey, stateValue := range is.Attributes {
if stateValue == value {
stateKeyParts := strings.Split(stateKey, ".")
if len(stateKeyParts) == len(attrParts) {
for i := range attrParts {
if attrParts[i] != stateKeyParts[i] && attrParts[i] != sentinelIndex {
break
}
if i == len(attrParts)-1 {
return nil
}
}
}
}
}

return fmt.Errorf("no TypeSet element %q, with value %q in state: %#v", attr, value, is.Attributes)
}

// TestCheckTypeSetElemAttr is a resource.TestCheckFunc that accepts a resource
// name, an attribute path, which should use the sentinel value '*' for indexing
// into a TypeSet. The function verifies that an element matches the provided
// value.
//
// Use this function over SDK provided TestCheckFunctions when validating a
// TypeSet where its elements are a simple value
func TestCheckTypeSetElemAttr(res, attr, value string) resource.TestCheckFunc {
func TestCheckTypeSetElemAttr(name, attr, value string) resource.TestCheckFunc {
return func(s *terraform.State) error {
ms := s.RootModule()
rs, ok := ms.Resources[res]
if !ok {
return fmt.Errorf("Not found: %s in %s", res, ms.Path)
is, err := instanceState(s, name)
if err != nil {
return err
}

is := rs.Primary
if is == nil {
return fmt.Errorf("No primary instance: %s in %s", res, ms.Path)
err = testCheckTypeSetElem(is, attr, value)
if err != nil {
return fmt.Errorf("%q error: %s", name, err)
}

attrParts := strings.Split(attr, ".")
if attrParts[len(attrParts)-1] != sentinelIndex {
return fmt.Errorf("%q does not end with the special value %q", attr, sentinelIndex)
return nil
}
}

// TestCheckTypeSetElemAttrPair is a TestCheckFunc that verifies a pair of name/key
// combinations are equal where the first uses the sentinel value to index into a
// TypeSet.
//
// E.g., tfawsresource.TestCheckTypeSetElemAttrPair("aws_autoscaling_group.bar", "availability_zones.*", "data.aws_availability_zones.available", "names.0")
func TestCheckTypeSetElemAttrPair(nameFirst, keyFirst, nameSecond, keySecond string) resource.TestCheckFunc {
return func(s *terraform.State) error {
isFirst, err := instanceState(s, nameFirst)
if err != nil {
return err
}
for stateKey, stateValue := range is.Attributes {
if stateValue == value {
stateKeyParts := strings.Split(stateKey, ".")
if len(stateKeyParts) == len(attrParts) {
for i := range attrParts {
if attrParts[i] != stateKeyParts[i] && attrParts[i] != sentinelIndex {
break
}
if i == len(attrParts)-1 {
return nil
}
}
}
}

isSecond, err := instanceState(s, nameSecond)
if err != nil {
return err
}

vSecond, okSecond := isSecond.Attributes[keySecond]
if !okSecond {
return fmt.Errorf("%s: Attribute %q not set, cannot be checked against TypeSet", keySecond, nameSecond)
}

return fmt.Errorf("%q no TypeSet element %q, with value %q in state: %#v", res, attr, value, is.Attributes)
return testCheckTypeSetElem(isFirst, keyFirst, vSecond)
}
}
8 changes: 4 additions & 4 deletions aws/internal/tfawsresource/testing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ func TestTestCheckTypeSetElemAttr(t *testing.T) {
},
},
ExpectedError: func(err error) bool {
return strings.Contains(err.Error(), "\"example_thing.test\" no TypeSet element \"test.*\"")
return strings.Contains(err.Error(), "\"example_thing.test\" error: no TypeSet element \"test.*\"")
},
},
{
Expand Down Expand Up @@ -246,7 +246,7 @@ func TestTestCheckTypeSetElemAttr(t *testing.T) {
},
},
ExpectedError: func(err error) bool {
return strings.Contains(err.Error(), "\"example_thing.test\" no TypeSet element \"test.*\"")
return strings.Contains(err.Error(), "\"example_thing.test\" error: no TypeSet element \"test.*\"")
},
},
{
Expand Down Expand Up @@ -319,7 +319,7 @@ func TestTestCheckTypeSetElemAttr(t *testing.T) {
},
},
ExpectedError: func(err error) bool {
return strings.Contains(err.Error(), "\"example_thing.test\" no TypeSet element \"test.*\"")
return strings.Contains(err.Error(), "\"example_thing.test\" error: no TypeSet element \"test.*\"")
},
},
{
Expand Down Expand Up @@ -390,7 +390,7 @@ func TestTestCheckTypeSetElemAttr(t *testing.T) {
},
},
ExpectedError: func(err error) bool {
return strings.Contains(err.Error(), "\"example_thing.test\" no TypeSet element \"test.0.nested_test.*\"")
return strings.Contains(err.Error(), "\"example_thing.test\" error: no TypeSet element \"test.0.nested_test.*\"")
},
},
}
Expand Down
44 changes: 37 additions & 7 deletions aws/resource_aws_autoscaling_group_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ func TestAccAWSAutoScalingGroup_basic(t *testing.T) {
testAccCheckAWSAutoScalingGroupHealthyCapacity(&group, 2),
testAccCheckAWSAutoScalingGroupAttributes(&group, randName),
testAccMatchResourceAttrRegionalARN("aws_autoscaling_group.bar", "arn", "autoscaling", regexp.MustCompile(`autoScalingGroup:.+`)),
tfawsresource.TestCheckTypeSetElemAttr("aws_autoscaling_group.bar", "availability_zones.*", "us-west-2a"),
tfawsresource.TestCheckTypeSetElemAttrPair("aws_autoscaling_group.bar", "availability_zones.*", "data.aws_availability_zones.available", "names.0"),
resource.TestCheckResourceAttr("aws_autoscaling_group.bar", "default_cooldown", "300"),
resource.TestCheckResourceAttr("aws_autoscaling_group.bar", "desired_capacity", "4"),
resource.TestCheckResourceAttr("aws_autoscaling_group.bar", "enabled_metrics.#", "0"),
Expand Down Expand Up @@ -391,7 +391,7 @@ func TestAccAWSAutoScalingGroup_VpcUpdates(t *testing.T) {
testAccCheckAWSAutoScalingGroupExists("aws_autoscaling_group.bar", &group),
resource.TestCheckResourceAttr(
"aws_autoscaling_group.bar", "availability_zones.#", "1"),
tfawsresource.TestCheckTypeSetElemAttr("aws_autoscaling_group.bar", "availability_zones.*", "us-west-2a"),
tfawsresource.TestCheckTypeSetElemAttrPair("aws_autoscaling_group.bar", "availability_zones.*", "data.aws_availability_zones.available", "names.0"),
resource.TestCheckResourceAttr(
"aws_autoscaling_group.bar", "vpc_zone_identifier.#", "0"),
),
Expand All @@ -417,7 +417,7 @@ func TestAccAWSAutoScalingGroup_VpcUpdates(t *testing.T) {
testAccCheckAWSAutoScalingGroupAttributesVPCZoneIdentifier(&group),
resource.TestCheckResourceAttr(
"aws_autoscaling_group.bar", "availability_zones.#", "1"),
tfawsresource.TestCheckTypeSetElemAttr("aws_autoscaling_group.bar", "availability_zones.*", "us-west-2a"),
tfawsresource.TestCheckTypeSetElemAttrPair("aws_autoscaling_group.bar", "availability_zones.*", "data.aws_availability_zones.available", "names.0"),
resource.TestCheckResourceAttr(
"aws_autoscaling_group.bar", "vpc_zone_identifier.#", "1"),
),
Expand Down Expand Up @@ -2260,6 +2260,16 @@ resource "aws_autoscaling_group" "bar" {

func testAccAWSAutoScalingGroupConfig(name string) string {
return fmt.Sprintf(`
data "aws_availability_zones" "available" {
exclude_zone_ids = ["usw2-az4"]
state = "available"
filter {
name = "opt-in-status"
values = ["opt-in-not-required"]
}
}
data "aws_ami" "test_ami" {
most_recent = true
owners = ["amazon"]
Expand All @@ -2281,7 +2291,7 @@ resource "aws_placement_group" "test" {
}
resource "aws_autoscaling_group" "bar" {
availability_zones = ["us-west-2a"]
availability_zones = [data.aws_availability_zones.available.names[0]]
name = "%s"
max_size = 5
min_size = 2
Expand Down Expand Up @@ -2586,6 +2596,16 @@ resource "aws_autoscaling_group" "bar" {
`

const testAccAWSAutoScalingGroupConfigWithAZ = `
data "aws_availability_zones" "available" {
exclude_zone_ids = ["usw2-az4"]
state = "available"
filter {
name = "opt-in-status"
values = ["opt-in-not-required"]
}
}
resource "aws_vpc" "default" {
cidr_block = "10.0.0.0/16"
tags = {
Expand All @@ -2596,7 +2616,7 @@ resource "aws_vpc" "default" {
resource "aws_subnet" "main" {
vpc_id = "${aws_vpc.default.id}"
cidr_block = "10.0.1.0/24"
availability_zone = "us-west-2a"
availability_zone = data.aws_availability_zones.available.names[0]
tags = {
Name = "tf-acc-autoscaling-group-with-az"
}
Expand All @@ -2619,7 +2639,7 @@ resource "aws_launch_configuration" "foobar" {
resource "aws_autoscaling_group" "bar" {
availability_zones = [
"us-west-2a"
data.aws_availability_zones.available.names[0]
]
desired_capacity = 0
max_size = 0
Expand All @@ -2629,6 +2649,16 @@ resource "aws_autoscaling_group" "bar" {
`

const testAccAWSAutoScalingGroupConfigWithVPCIdent = `
data "aws_availability_zones" "available" {
exclude_zone_ids = ["usw2-az4"]
state = "available"
filter {
name = "opt-in-status"
values = ["opt-in-not-required"]
}
}
resource "aws_vpc" "default" {
cidr_block = "10.0.0.0/16"
tags = {
Expand All @@ -2639,7 +2669,7 @@ resource "aws_vpc" "default" {
resource "aws_subnet" "main" {
vpc_id = "${aws_vpc.default.id}"
cidr_block = "10.0.1.0/24"
availability_zone = "us-west-2a"
availability_zone = data.aws_availability_zones.available.names[0]
tags = {
Name = "tf-acc-autoscaling-group-with-vpc-id"
}
Expand Down

0 comments on commit ef590c5

Please sign in to comment.