github.com/darmach/terratest@v0.34.8-0.20210517103231-80931f95e3ff/modules/aws/ec2_test.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "strings" 6 "testing" 7 8 "github.com/aws/aws-sdk-go/aws" 9 "github.com/aws/aws-sdk-go/service/ec2" 10 11 "github.com/gruntwork-io/terratest/modules/random" 12 "github.com/stretchr/testify/assert" 13 "github.com/stretchr/testify/require" 14 ) 15 16 func TestGetEc2InstanceIdsByTag(t *testing.T) { 17 t.Parallel() 18 19 region := GetRandomStableRegion(t, nil, nil) 20 ids, err := GetEc2InstanceIdsByTagE(t, region, "Name", fmt.Sprintf("nonexistent-%s", random.UniqueId())) 21 require.NoError(t, err) 22 assert.Equal(t, 0, len(ids)) 23 } 24 25 func TestGetEc2InstanceIdsByFilters(t *testing.T) { 26 t.Parallel() 27 28 region := GetRandomStableRegion(t, nil, nil) 29 filters := map[string][]string{ 30 "instance-state-name": {"running", "shutting-down"}, 31 "tag:Name": {fmt.Sprintf("nonexistent-%s", random.UniqueId())}, 32 } 33 34 ids, err := GetEc2InstanceIdsByFiltersE(t, region, filters) 35 require.NoError(t, err) 36 assert.Equal(t, 0, len(ids)) 37 } 38 39 func TestGetRecommendedInstanceType(t *testing.T) { 40 t.Parallel() 41 42 testCases := []struct { 43 region string 44 instanceTypeOptions []string 45 }{ 46 {"eu-west-1", []string{"t2.micro", "t3.micro"}}, 47 {"ap-northeast-2", []string{"t2.micro", "t3.micro"}}, 48 {"us-east-1", []string{"t2.large", "t3.large"}}, 49 } 50 51 for _, testCase := range testCases { 52 // The following is necessary to make sure testCase's values don't get updated due to concurrency within the 53 // scope of t.Run(..) below. https://golang.org/doc/faq#closures_and_goroutines 54 testCase := testCase 55 56 t.Run(fmt.Sprintf("%s-%s", testCase.region, strings.Join(testCase.instanceTypeOptions, "-")), func(t *testing.T) { 57 t.Parallel() 58 instanceType := GetRecommendedInstanceType(t, testCase.region, testCase.instanceTypeOptions) 59 // We could hard-code the expected result (e.g., as of July, 2020, we expect eu-west-1 to return t2.micro 60 // and ap-northeast-2 to return t3.micro), but the result will likely change over time, so to avoid a 61 // brittle test, we simply check that we get _one_ result. Combined with the unit test below, this hopefully 62 // is enough to be confident this function works correctly. 63 assert.Contains(t, testCase.instanceTypeOptions, instanceType) 64 }) 65 } 66 } 67 68 func TestPickRecommendedInstanceTypeHappyPath(t *testing.T) { 69 testCases := []struct { 70 name string 71 availabilityZones []string 72 instanceTypeOfferings []*ec2.InstanceTypeOffering 73 instanceTypeOptions []string 74 expected string 75 }{ 76 { 77 "One AZ, one instance type, available in one offering", 78 []string{"us-east-1a"}, 79 offerings(map[string][]string{"us-east-1a": {"t2.micro"}}), 80 []string{"t2.micro"}, 81 "t2.micro", 82 }, 83 { 84 "Three AZs, one instance type, available in all three offerings", 85 []string{"us-east-1a", "us-east-1b", "us-east-1c"}, 86 offerings(map[string][]string{"us-east-1a": {"t2.micro"}, "us-east-1b": {"t2.micro"}, "us-east-1c": {"t2.micro"}}), 87 []string{"t2.micro"}, 88 "t2.micro", 89 }, 90 { 91 "Three AZs, two instance types, first one available in all three offerings, the other not available at all", 92 []string{"us-east-1a", "us-east-1b", "us-east-1c"}, 93 offerings(map[string][]string{"us-east-1a": {"t2.micro"}, "us-east-1b": {"t2.micro"}, "us-east-1c": {"t2.micro"}}), 94 []string{"t2.micro", "t3.micro"}, 95 "t2.micro", 96 }, 97 { 98 "Three AZs, two instance types, first one available in all three offerings, the other only available in one offering in an unrequested AZ", 99 []string{"us-east-1a", "us-east-1b", "us-east-1c"}, 100 offerings(map[string][]string{"us-east-1a": {"t2.micro"}, "us-east-1b": {"t2.micro"}, "us-east-1c": {"t2.micro"}, "us-east-1d": {"t3.micro"}}), 101 []string{"t2.micro", "t3.micro"}, 102 "t2.micro", 103 }, 104 { 105 "Three AZs, two instance types, first one available in all three offerings, the other one available in only two offerings", 106 []string{"us-east-1a", "us-east-1b", "us-east-1c"}, 107 offerings(map[string][]string{"us-east-1a": {"t2.micro", "t3.micro"}, "us-east-1b": {"t2.micro"}, "us-east-1c": {"t2.micro"}}), 108 []string{"t2.micro", "t3.micro"}, 109 "t2.micro", 110 }, 111 { 112 "Three AZs, three instance types, first one available in two offerings, second in all three offerings, third in two offerings", 113 []string{"us-east-1a", "us-east-1b", "us-east-1c"}, 114 offerings(map[string][]string{"us-east-1a": {"t2.micro", "t3.micro", "t3.small"}, "us-east-1b": {"t3.micro"}, "us-east-1c": {"t2.micro", "t3.micro", "t3.small"}}), 115 []string{"t2.micro", "t3.micro", "t3.small"}, 116 "t3.micro", 117 }, 118 } 119 120 for _, testCase := range testCases { 121 // The following is necessary to make sure testCase's values don't get updated due to concurrency within the 122 // scope of t.Run(..) below. https://golang.org/doc/faq#closures_and_goroutines 123 testCase := testCase 124 125 t.Run(testCase.name, func(t *testing.T) { 126 t.Parallel() 127 128 actual, err := pickRecommendedInstanceTypeE(testCase.availabilityZones, testCase.instanceTypeOfferings, testCase.instanceTypeOptions) 129 assert.NoError(t, err) 130 assert.Equal(t, testCase.expected, actual) 131 }) 132 } 133 } 134 135 func TestPickRecommendedInstanceTypeErrors(t *testing.T) { 136 testCases := []struct { 137 name string 138 availabilityZones []string 139 instanceTypeOfferings []*ec2.InstanceTypeOffering 140 instanceTypeOptions []string 141 }{ 142 { 143 "All params nil", 144 nil, 145 nil, 146 nil, 147 }, 148 { 149 "No AZs, one instance type, no offerings", 150 nil, 151 nil, 152 []string{"t2.micro"}, 153 }, 154 { 155 "One AZ, one instance type, no offerings", 156 []string{"us-east-1a"}, 157 nil, 158 []string{"t2.micro"}, 159 }, 160 { 161 "Two AZs, one instance type, available in only one offering", 162 []string{"us-east-1a", "us-east-1b"}, 163 offerings(map[string][]string{"us-east-1a": {"t2.micro"}}), 164 []string{"t2.micro"}, 165 }, 166 { 167 "Three AZs, two instance types, each available in only two of the three offerings", 168 []string{"us-east-1a", "us-east-1b", "us-east-1c"}, 169 offerings(map[string][]string{"us-east-1a": {"t2.micro"}, "us-east-1b": {"t2.micro", "t3.micro"}, "us-east-1c": {"t3.micro"}}), 170 []string{"t2.micro", "t3.micro"}, 171 }, 172 } 173 174 for _, testCase := range testCases { 175 // The following is necessary to make sure testCase's values don't 176 // get updated due to concurrency within the scope of t.Run(..) below 177 testCase := testCase 178 t.Run(testCase.name, func(t *testing.T) { 179 t.Parallel() 180 181 _, err := pickRecommendedInstanceTypeE(testCase.availabilityZones, testCase.instanceTypeOfferings, testCase.instanceTypeOptions) 182 assert.EqualError(t, err, NoInstanceTypeError{Azs: testCase.availabilityZones, InstanceTypeOptions: testCase.instanceTypeOptions}.Error()) 183 }) 184 } 185 } 186 187 func offerings(offerings map[string][]string) []*ec2.InstanceTypeOffering { 188 var out []*ec2.InstanceTypeOffering 189 190 for az, instanceTypes := range offerings { 191 for _, instanceType := range instanceTypes { 192 offering := &ec2.InstanceTypeOffering{ 193 InstanceType: aws.String(instanceType), 194 Location: aws.String(az), 195 LocationType: aws.String(ec2.LocationTypeAvailabilityZone), 196 } 197 out = append(out, offering) 198 } 199 } 200 201 return out 202 }