github.com/mponton/terratest@v0.44.0/modules/aws/ami.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "sort" 6 "time" 7 8 "github.com/aws/aws-sdk-go/aws" 9 "github.com/aws/aws-sdk-go/service/ec2" 10 "github.com/mponton/terratest/modules/logger" 11 "github.com/mponton/terratest/modules/testing" 12 ) 13 14 // These are commonly used AMI account IDs. 15 const ( 16 CanonicalAccountId = "099720109477" 17 CentOsAccountId = "679593333241" 18 AmazonAccountId = "amazon" 19 ) 20 21 // DeleteAmiAndAllSnapshots will delete the given AMI along with all EBS snapshots that backed that AMI 22 func DeleteAmiAndAllSnapshots(t testing.TestingT, region string, ami string) { 23 err := DeleteAmiAndAllSnapshotsE(t, region, ami) 24 if err != nil { 25 t.Fatal(err) 26 } 27 } 28 29 // DeleteAmiAndAllSnapshotsE will delete the given AMI along with all EBS snapshots that backed that AMI 30 func DeleteAmiAndAllSnapshotsE(t testing.TestingT, region string, ami string) error { 31 snapshots, err := GetEbsSnapshotsForAmiE(t, region, ami) 32 if err != nil { 33 return err 34 } 35 36 err = DeleteAmiE(t, region, ami) 37 if err != nil { 38 return err 39 } 40 41 for _, snapshot := range snapshots { 42 err = DeleteEbsSnapshotE(t, region, snapshot) 43 if err != nil { 44 return err 45 } 46 } 47 48 return nil 49 } 50 51 // GetEbsSnapshotsForAmi retrieves the EBS snapshots which back the given AMI 52 func GetEbsSnapshotsForAmi(t testing.TestingT, region string, ami string) []string { 53 snapshots, err := GetEbsSnapshotsForAmiE(t, region, ami) 54 if err != nil { 55 t.Fatal(err) 56 } 57 return snapshots 58 } 59 60 // GetEbsSnapshotsForAmi retrieves the EBS snapshots which back the given AMI 61 func GetEbsSnapshotsForAmiE(t testing.TestingT, region string, ami string) ([]string, error) { 62 logger.Logf(t, "Retrieving EBS snapshots backing AMI %s", ami) 63 ec2Client, err := NewEc2ClientE(t, region) 64 if err != nil { 65 return nil, err 66 } 67 68 images, err := ec2Client.DescribeImages(&ec2.DescribeImagesInput{ 69 ImageIds: []*string{ 70 aws.String(ami), 71 }, 72 }) 73 if err != nil { 74 return nil, err 75 } 76 77 var snapshots []string 78 for _, image := range images.Images { 79 for _, mapping := range image.BlockDeviceMappings { 80 if mapping.Ebs != nil && mapping.Ebs.SnapshotId != nil { 81 snapshots = append(snapshots, aws.StringValue(mapping.Ebs.SnapshotId)) 82 } 83 } 84 } 85 86 return snapshots, err 87 } 88 89 // GetMostRecentAmiId gets the ID of the most recent AMI in the given region that has the given owner and matches the given filters. Each 90 // filter should correspond to the name and values of a filter supported by DescribeImagesInput: 91 // https://docs.aws.amazon.com/sdk-for-go/api/service/ec2/#DescribeImagesInput 92 func GetMostRecentAmiId(t testing.TestingT, region string, ownerId string, filters map[string][]string) string { 93 amiID, err := GetMostRecentAmiIdE(t, region, ownerId, filters) 94 if err != nil { 95 t.Fatal(err) 96 } 97 return amiID 98 } 99 100 // GetMostRecentAmiIdE gets the ID of the most recent AMI in the given region that has the given owner and matches the given filters. Each 101 // filter should correspond to the name and values of a filter supported by DescribeImagesInput: 102 // https://docs.aws.amazon.com/sdk-for-go/api/service/ec2/#DescribeImagesInput 103 func GetMostRecentAmiIdE(t testing.TestingT, region string, ownerId string, filters map[string][]string) (string, error) { 104 ec2Client, err := NewEc2ClientE(t, region) 105 if err != nil { 106 return "", err 107 } 108 109 ec2Filters := []*ec2.Filter{} 110 for name, values := range filters { 111 ec2Filters = append(ec2Filters, &ec2.Filter{Name: aws.String(name), Values: aws.StringSlice(values)}) 112 } 113 114 input := ec2.DescribeImagesInput{ 115 Filters: ec2Filters, 116 IncludeDeprecated: aws.Bool(true), 117 Owners: []*string{aws.String(ownerId)}, 118 } 119 120 out, err := ec2Client.DescribeImages(&input) 121 if err != nil { 122 return "", err 123 } 124 125 if len(out.Images) == 0 { 126 return "", NoImagesFound{Region: region, OwnerId: ownerId, Filters: filters} 127 } 128 129 mostRecentImage := mostRecentAMI(out.Images) 130 return aws.StringValue(mostRecentImage.ImageId), nil 131 } 132 133 // Image sorting code borrowed from: https://github.com/hashicorp/packer/blob/7f4112ba229309cfc0ebaa10ded2abdfaf1b22c8/builder/amazon/common/step_source_ami_info.go 134 type imageSort []*ec2.Image 135 136 func (a imageSort) Len() int { return len(a) } 137 func (a imageSort) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 138 func (a imageSort) Less(i, j int) bool { 139 iTime, _ := time.Parse(time.RFC3339, *a[i].CreationDate) 140 jTime, _ := time.Parse(time.RFC3339, *a[j].CreationDate) 141 return iTime.Unix() < jTime.Unix() 142 } 143 144 // mostRecentAMI returns the most recent AMI out of a slice of images. 145 func mostRecentAMI(images []*ec2.Image) *ec2.Image { 146 sortedImages := images 147 sort.Sort(imageSort(sortedImages)) 148 return sortedImages[len(sortedImages)-1] 149 } 150 151 // GetUbuntu1404Ami gets the ID of the most recent Ubuntu 14.04 HVM x86_64 EBS GP2 AMI in the given region. 152 func GetUbuntu1404Ami(t testing.TestingT, region string) string { 153 amiID, err := GetUbuntu1404AmiE(t, region) 154 if err != nil { 155 t.Fatal(err) 156 } 157 return amiID 158 } 159 160 // GetUbuntu1404AmiE gets the ID of the most recent Ubuntu 14.04 HVM x86_64 EBS GP2 AMI in the given region. 161 func GetUbuntu1404AmiE(t testing.TestingT, region string) (string, error) { 162 filters := map[string][]string{ 163 "name": {"*ubuntu-trusty-14.04-amd64-server-*"}, 164 "virtualization-type": {"hvm"}, 165 "architecture": {"x86_64"}, 166 "root-device-type": {"ebs"}, 167 "block-device-mapping.volume-type": {"gp2"}, 168 } 169 170 return GetMostRecentAmiIdE(t, region, CanonicalAccountId, filters) 171 } 172 173 // GetUbuntu1604Ami gets the ID of the most recent Ubuntu 16.04 HVM x86_64 EBS GP2 AMI in the given region. 174 func GetUbuntu1604Ami(t testing.TestingT, region string) string { 175 amiID, err := GetUbuntu1604AmiE(t, region) 176 if err != nil { 177 t.Fatal(err) 178 } 179 return amiID 180 } 181 182 // GetUbuntu1604AmiE gets the ID of the most recent Ubuntu 16.04 HVM x86_64 EBS GP2 AMI in the given region. 183 func GetUbuntu1604AmiE(t testing.TestingT, region string) (string, error) { 184 filters := map[string][]string{ 185 "name": {"*ubuntu-xenial-16.04-amd64-server-*"}, 186 "virtualization-type": {"hvm"}, 187 "architecture": {"x86_64"}, 188 "root-device-type": {"ebs"}, 189 "block-device-mapping.volume-type": {"gp2"}, 190 } 191 192 return GetMostRecentAmiIdE(t, region, CanonicalAccountId, filters) 193 } 194 195 // GetCentos7Ami returns a CentOS 7 public AMI from the given region. 196 // WARNING: you may have to accept the terms & conditions of this AMI in AWS MarketPlace for your AWS Account before 197 // you can successfully launch the AMI. 198 func GetCentos7Ami(t testing.TestingT, region string) string { 199 amiID, err := GetCentos7AmiE(t, region) 200 if err != nil { 201 t.Fatal(err) 202 } 203 return amiID 204 } 205 206 // GetCentos7AmiE returns a CentOS 7 public AMI from the given region. 207 // WARNING: you may have to accept the terms & conditions of this AMI in AWS MarketPlace for your AWS Account before 208 // you can successfully launch the AMI. 209 func GetCentos7AmiE(t testing.TestingT, region string) (string, error) { 210 filters := map[string][]string{ 211 "name": {"*CentOS Linux 7 x86_64 HVM EBS*"}, 212 "virtualization-type": {"hvm"}, 213 "architecture": {"x86_64"}, 214 "root-device-type": {"ebs"}, 215 "block-device-mapping.volume-type": {"gp2"}, 216 } 217 218 return GetMostRecentAmiIdE(t, region, CentOsAccountId, filters) 219 } 220 221 // GetAmazonLinuxAmi returns an Amazon Linux AMI HVM, SSD Volume Type public AMI for the given region. 222 func GetAmazonLinuxAmi(t testing.TestingT, region string) string { 223 amiID, err := GetAmazonLinuxAmiE(t, region) 224 if err != nil { 225 t.Fatal(err) 226 } 227 return amiID 228 } 229 230 // GetAmazonLinuxAmiE returns an Amazon Linux AMI HVM, SSD Volume Type public AMI for the given region. 231 func GetAmazonLinuxAmiE(t testing.TestingT, region string) (string, error) { 232 filters := map[string][]string{ 233 "name": {"*amzn-ami-hvm-*-x86_64*"}, 234 "virtualization-type": {"hvm"}, 235 "architecture": {"x86_64"}, 236 "root-device-type": {"ebs"}, 237 "block-device-mapping.volume-type": {"gp2"}, 238 } 239 240 return GetMostRecentAmiIdE(t, region, AmazonAccountId, filters) 241 } 242 243 // GetEcsOptimizedAmazonLinuxAmi returns an Amazon ECS-Optimized Amazon Linux AMI for the given region. This AMI is useful for running an ECS cluster. 244 func GetEcsOptimizedAmazonLinuxAmi(t testing.TestingT, region string) string { 245 amiID, err := GetEcsOptimizedAmazonLinuxAmiE(t, region) 246 if err != nil { 247 t.Fatal(err) 248 } 249 return amiID 250 } 251 252 // GetEcsOptimizedAmazonLinuxAmiE returns an Amazon ECS-Optimized Amazon Linux AMI for the given region. This AMI is useful for running an ECS cluster. 253 func GetEcsOptimizedAmazonLinuxAmiE(t testing.TestingT, region string) (string, error) { 254 filters := map[string][]string{ 255 "name": {"*amzn-ami*amazon-ecs-optimized*"}, 256 "virtualization-type": {"hvm"}, 257 "architecture": {"x86_64"}, 258 "root-device-type": {"ebs"}, 259 "block-device-mapping.volume-type": {"gp2"}, 260 } 261 262 return GetMostRecentAmiIdE(t, region, AmazonAccountId, filters) 263 } 264 265 // NoImagesFound is an error that occurs if no images were found. 266 type NoImagesFound struct { 267 Region string 268 OwnerId string 269 Filters map[string][]string 270 } 271 272 func (err NoImagesFound) Error() string { 273 return fmt.Sprintf("No AMIs found in %s for owner ID %s and filters: %v", err.Region, err.OwnerId, err.Filters) 274 }