github.com/mponton/terratest@v0.44.0/modules/aws/region.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "os" 6 7 "github.com/aws/aws-sdk-go/aws" 8 "github.com/aws/aws-sdk-go/service/ec2" 9 "github.com/aws/aws-sdk-go/service/ssm" 10 "github.com/mponton/terratest/modules/collections" 11 "github.com/mponton/terratest/modules/logger" 12 "github.com/mponton/terratest/modules/random" 13 "github.com/mponton/terratest/modules/testing" 14 ) 15 16 // You can set this environment variable to force Terratest to use a specific region rather than a random one. This is 17 // convenient when iterating locally. 18 const regionOverrideEnvVarName = "TERRATEST_REGION" 19 20 // AWS API calls typically require an AWS region. We typically require the user to set one explicitly, but in some 21 // cases, this doesn't make sense (e.g., for fetching the lsit of regions in an account), so for those cases, we use 22 // this region as a default. 23 const defaultRegion = "us-east-1" 24 25 // Reference for launch dates: https://aws.amazon.com/about-aws/global-infrastructure/ 26 var stableRegions = []string{ 27 "us-east-1", // Launched 2006 28 "us-east-2", // Launched 2016 29 "us-west-1", // Launched 2009 30 "us-west-2", // Launched 2011 31 "ca-central-1", // Launched 2016 32 "sa-east-1", // Launched 2011 33 "eu-west-1", // Launched 2007 34 "eu-west-2", // Launched 2016 35 "eu-west-3", // Launched 2017 36 "eu-central-1", // Launched 2014 37 "ap-southeast-1", // Launched 2010 38 "ap-southeast-2", // Launched 2012 39 "ap-northeast-1", // Launched 2011 40 "ap-northeast-2", // Launched 2016 41 "ap-south-1", // Launched 2016 42 "eu-north-1", // Launched 2018 43 } 44 45 // GetRandomStableRegion gets a randomly chosen AWS region that is considered stable. Like GetRandomRegion, you can 46 // further restrict the stable region list using approvedRegions and forbiddenRegions. We consider stable regions to be 47 // those that have been around for at least 1 year. 48 // Note that regions in the approvedRegions list that are not considered stable are ignored. 49 func GetRandomStableRegion(t testing.TestingT, approvedRegions []string, forbiddenRegions []string) string { 50 regionsToPickFrom := stableRegions 51 if len(approvedRegions) > 0 { 52 regionsToPickFrom = collections.ListIntersection(regionsToPickFrom, approvedRegions) 53 } 54 if len(forbiddenRegions) > 0 { 55 regionsToPickFrom = collections.ListSubtract(regionsToPickFrom, forbiddenRegions) 56 } 57 return GetRandomRegion(t, regionsToPickFrom, nil) 58 } 59 60 // GetRandomRegion gets a randomly chosen AWS region. If approvedRegions is not empty, this will be a region from the approvedRegions 61 // list; otherwise, this method will fetch the latest list of regions from the AWS APIs and pick one of those. If 62 // forbiddenRegions is not empty, this method will make sure the returned region is not in the forbiddenRegions list. 63 func GetRandomRegion(t testing.TestingT, approvedRegions []string, forbiddenRegions []string) string { 64 region, err := GetRandomRegionE(t, approvedRegions, forbiddenRegions) 65 if err != nil { 66 t.Fatal(err) 67 } 68 return region 69 } 70 71 // GetRandomRegionE gets a randomly chosen AWS region. If approvedRegions is not empty, this will be a region from the approvedRegions 72 // list; otherwise, this method will fetch the latest list of regions from the AWS APIs and pick one of those. If 73 // forbiddenRegions is not empty, this method will make sure the returned region is not in the forbiddenRegions list. 74 func GetRandomRegionE(t testing.TestingT, approvedRegions []string, forbiddenRegions []string) (string, error) { 75 regionFromEnvVar := os.Getenv(regionOverrideEnvVarName) 76 if regionFromEnvVar != "" { 77 logger.Logf(t, "Using AWS region %s from environment variable %s", regionFromEnvVar, regionOverrideEnvVarName) 78 return regionFromEnvVar, nil 79 } 80 81 regionsToPickFrom := approvedRegions 82 83 if len(regionsToPickFrom) == 0 { 84 allRegions, err := GetAllAwsRegionsE(t) 85 if err != nil { 86 return "", err 87 } 88 regionsToPickFrom = allRegions 89 } 90 91 regionsToPickFrom = collections.ListSubtract(regionsToPickFrom, forbiddenRegions) 92 region := random.RandomString(regionsToPickFrom) 93 94 logger.Logf(t, "Using region %s", region) 95 return region, nil 96 } 97 98 // GetAllAwsRegions gets the list of AWS regions available in this account. 99 func GetAllAwsRegions(t testing.TestingT) []string { 100 out, err := GetAllAwsRegionsE(t) 101 if err != nil { 102 t.Fatal(err) 103 } 104 return out 105 } 106 107 // GetAllAwsRegionsE gets the list of AWS regions available in this account. 108 func GetAllAwsRegionsE(t testing.TestingT) ([]string, error) { 109 logger.Log(t, "Looking up all AWS regions available in this account") 110 111 ec2Client, err := NewEc2ClientE(t, defaultRegion) 112 if err != nil { 113 return nil, err 114 } 115 116 out, err := ec2Client.DescribeRegions(&ec2.DescribeRegionsInput{}) 117 if err != nil { 118 return nil, err 119 } 120 121 regions := []string{} 122 for _, region := range out.Regions { 123 regions = append(regions, aws.StringValue(region.RegionName)) 124 } 125 126 return regions, nil 127 } 128 129 // GetAvailabilityZones gets the Availability Zones for a given AWS region. Note that for certain regions (e.g. us-east-1), different AWS 130 // accounts have access to different availability zones. 131 func GetAvailabilityZones(t testing.TestingT, region string) []string { 132 out, err := GetAvailabilityZonesE(t, region) 133 if err != nil { 134 t.Fatal(err) 135 } 136 return out 137 } 138 139 // GetAvailabilityZonesE gets the Availability Zones for a given AWS region. Note that for certain regions (e.g. us-east-1), different AWS 140 // accounts have access to different availability zones. 141 func GetAvailabilityZonesE(t testing.TestingT, region string) ([]string, error) { 142 logger.Logf(t, "Looking up all availability zones available in this account for region %s", region) 143 144 ec2Client, err := NewEc2ClientE(t, region) 145 if err != nil { 146 return nil, err 147 } 148 149 resp, err := ec2Client.DescribeAvailabilityZones(&ec2.DescribeAvailabilityZonesInput{}) 150 if err != nil { 151 return nil, err 152 } 153 154 var out []string 155 for _, availabilityZone := range resp.AvailabilityZones { 156 out = append(out, aws.StringValue(availabilityZone.ZoneName)) 157 } 158 159 return out, nil 160 } 161 162 // GetRegionsForService gets all AWS regions in which a service is available. 163 func GetRegionsForService(t testing.TestingT, serviceName string) []string { 164 out, err := GetRegionsForServiceE(t, serviceName) 165 if err != nil { 166 t.Fatal(err) 167 } 168 return out 169 } 170 171 // GetRegionsForService gets all AWS regions in which a service is available and returns errors. 172 // See https://docs.aws.amazon.com/systems-manager/latest/userguide/parameter-store-public-parameters-global-infrastructure.html 173 func GetRegionsForServiceE(t testing.TestingT, serviceName string) ([]string, error) { 174 // These values are available in any region, defaulting to us-east-1 since it's the oldest 175 ssmClient, err := NewSsmClientE(t, "us-east-1") 176 177 if err != nil { 178 return nil, err 179 } 180 181 paramPath := "/aws/service/global-infrastructure/services/%s/regions" 182 req, resp := ssmClient.GetParametersByPathRequest(&ssm.GetParametersByPathInput{ 183 Path: aws.String(fmt.Sprintf(paramPath, serviceName)), 184 }) 185 186 ssmErr := req.Send() 187 if ssmErr != nil { 188 return nil, err 189 } 190 191 var availableRegions []string 192 for _, p := range resp.Parameters { 193 availableRegions = append(availableRegions, *p.Value) 194 } 195 196 return availableRegions, nil 197 } 198 199 // GetRandomRegionForService retrieves a list of AWS regions in which a service is available 200 // Then returns one region randomly from the list 201 func GetRandomRegionForService(t testing.TestingT, serviceName string) string { 202 availableRegions, err := GetRegionsForServiceE(t, serviceName) 203 204 if err != nil { 205 t.Fatal(err) 206 } 207 208 return GetRandomRegion(t, availableRegions, nil) 209 }