github.com/darmach/terratest@v0.34.8-0.20210517103231-80931f95e3ff/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/gruntwork-io/terratest/modules/logger"
    11  	"github.com/gruntwork-io/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  		Owners:  []*string{aws.String(ownerId)},
   117  	}
   118  
   119  	out, err := ec2Client.DescribeImages(&input)
   120  	if err != nil {
   121  		return "", err
   122  	}
   123  
   124  	if len(out.Images) == 0 {
   125  		return "", NoImagesFound{Region: region, OwnerId: ownerId, Filters: filters}
   126  	}
   127  
   128  	mostRecentImage := mostRecentAMI(out.Images)
   129  	return aws.StringValue(mostRecentImage.ImageId), nil
   130  }
   131  
   132  // Image sorting code borrowed from: https://github.com/hashicorp/packer/blob/7f4112ba229309cfc0ebaa10ded2abdfaf1b22c8/builder/amazon/common/step_source_ami_info.go
   133  type imageSort []*ec2.Image
   134  
   135  func (a imageSort) Len() int      { return len(a) }
   136  func (a imageSort) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
   137  func (a imageSort) Less(i, j int) bool {
   138  	iTime, _ := time.Parse(time.RFC3339, *a[i].CreationDate)
   139  	jTime, _ := time.Parse(time.RFC3339, *a[j].CreationDate)
   140  	return iTime.Unix() < jTime.Unix()
   141  }
   142  
   143  // mostRecentAMI returns the most recent AMI out of a slice of images.
   144  func mostRecentAMI(images []*ec2.Image) *ec2.Image {
   145  	sortedImages := images
   146  	sort.Sort(imageSort(sortedImages))
   147  	return sortedImages[len(sortedImages)-1]
   148  }
   149  
   150  // GetUbuntu1404Ami gets the ID of the most recent Ubuntu 14.04 HVM x86_64 EBS GP2 AMI in the given region.
   151  func GetUbuntu1404Ami(t testing.TestingT, region string) string {
   152  	amiID, err := GetUbuntu1404AmiE(t, region)
   153  	if err != nil {
   154  		t.Fatal(err)
   155  	}
   156  	return amiID
   157  }
   158  
   159  // GetUbuntu1404AmiE gets the ID of the most recent Ubuntu 14.04 HVM x86_64 EBS GP2 AMI in the given region.
   160  func GetUbuntu1404AmiE(t testing.TestingT, region string) (string, error) {
   161  	filters := map[string][]string{
   162  		"name":                             {"*ubuntu-trusty-14.04-amd64-server-*"},
   163  		"virtualization-type":              {"hvm"},
   164  		"architecture":                     {"x86_64"},
   165  		"root-device-type":                 {"ebs"},
   166  		"block-device-mapping.volume-type": {"gp2"},
   167  	}
   168  
   169  	return GetMostRecentAmiIdE(t, region, CanonicalAccountId, filters)
   170  }
   171  
   172  // GetUbuntu1604Ami gets the ID of the most recent Ubuntu 16.04 HVM x86_64 EBS GP2 AMI in the given region.
   173  func GetUbuntu1604Ami(t testing.TestingT, region string) string {
   174  	amiID, err := GetUbuntu1604AmiE(t, region)
   175  	if err != nil {
   176  		t.Fatal(err)
   177  	}
   178  	return amiID
   179  }
   180  
   181  // GetUbuntu1604AmiE gets the ID of the most recent Ubuntu 16.04 HVM x86_64 EBS GP2 AMI in the given region.
   182  func GetUbuntu1604AmiE(t testing.TestingT, region string) (string, error) {
   183  	filters := map[string][]string{
   184  		"name":                             {"*ubuntu-xenial-16.04-amd64-server-*"},
   185  		"virtualization-type":              {"hvm"},
   186  		"architecture":                     {"x86_64"},
   187  		"root-device-type":                 {"ebs"},
   188  		"block-device-mapping.volume-type": {"gp2"},
   189  	}
   190  
   191  	return GetMostRecentAmiIdE(t, region, CanonicalAccountId, filters)
   192  }
   193  
   194  // GetCentos7Ami returns a CentOS 7 public AMI from the given region.
   195  // WARNING: you may have to accept the terms & conditions of this AMI in AWS MarketPlace for your AWS Account before
   196  // you can successfully launch the AMI.
   197  func GetCentos7Ami(t testing.TestingT, region string) string {
   198  	amiID, err := GetCentos7AmiE(t, region)
   199  	if err != nil {
   200  		t.Fatal(err)
   201  	}
   202  	return amiID
   203  }
   204  
   205  // GetCentos7AmiE returns a CentOS 7 public AMI from the given region.
   206  // WARNING: you may have to accept the terms & conditions of this AMI in AWS MarketPlace for your AWS Account before
   207  // you can successfully launch the AMI.
   208  func GetCentos7AmiE(t testing.TestingT, region string) (string, error) {
   209  	filters := map[string][]string{
   210  		"name":                             {"*CentOS Linux 7 x86_64 HVM EBS*"},
   211  		"virtualization-type":              {"hvm"},
   212  		"architecture":                     {"x86_64"},
   213  		"root-device-type":                 {"ebs"},
   214  		"block-device-mapping.volume-type": {"gp2"},
   215  	}
   216  
   217  	return GetMostRecentAmiIdE(t, region, CentOsAccountId, filters)
   218  }
   219  
   220  // GetAmazonLinuxAmi returns an Amazon Linux AMI HVM, SSD Volume Type public AMI for the given region.
   221  func GetAmazonLinuxAmi(t testing.TestingT, region string) string {
   222  	amiID, err := GetAmazonLinuxAmiE(t, region)
   223  	if err != nil {
   224  		t.Fatal(err)
   225  	}
   226  	return amiID
   227  }
   228  
   229  // GetAmazonLinuxAmiE returns an Amazon Linux AMI HVM, SSD Volume Type public AMI for the given region.
   230  func GetAmazonLinuxAmiE(t testing.TestingT, region string) (string, error) {
   231  	filters := map[string][]string{
   232  		"name":                             {"*amzn-ami-hvm-*-x86_64*"},
   233  		"virtualization-type":              {"hvm"},
   234  		"architecture":                     {"x86_64"},
   235  		"root-device-type":                 {"ebs"},
   236  		"block-device-mapping.volume-type": {"gp2"},
   237  	}
   238  
   239  	return GetMostRecentAmiIdE(t, region, AmazonAccountId, filters)
   240  }
   241  
   242  // GetEcsOptimizedAmazonLinuxAmi returns an Amazon ECS-Optimized Amazon Linux AMI for the given region. This AMI is useful for running an ECS cluster.
   243  func GetEcsOptimizedAmazonLinuxAmi(t testing.TestingT, region string) string {
   244  	amiID, err := GetEcsOptimizedAmazonLinuxAmiE(t, region)
   245  	if err != nil {
   246  		t.Fatal(err)
   247  	}
   248  	return amiID
   249  }
   250  
   251  // GetEcsOptimizedAmazonLinuxAmiE returns an Amazon ECS-Optimized Amazon Linux AMI for the given region. This AMI is useful for running an ECS cluster.
   252  func GetEcsOptimizedAmazonLinuxAmiE(t testing.TestingT, region string) (string, error) {
   253  	filters := map[string][]string{
   254  		"name":                             {"*amzn-ami*amazon-ecs-optimized*"},
   255  		"virtualization-type":              {"hvm"},
   256  		"architecture":                     {"x86_64"},
   257  		"root-device-type":                 {"ebs"},
   258  		"block-device-mapping.volume-type": {"gp2"},
   259  	}
   260  
   261  	return GetMostRecentAmiIdE(t, region, AmazonAccountId, filters)
   262  }
   263  
   264  // NoImagesFound is an error that occurs if no images were found.
   265  type NoImagesFound struct {
   266  	Region  string
   267  	OwnerId string
   268  	Filters map[string][]string
   269  }
   270  
   271  func (err NoImagesFound) Error() string {
   272  	return fmt.Sprintf("No AMIs found in %s for owner ID %s and filters: %v", err.Region, err.OwnerId, err.Filters)
   273  }