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  }