Skip to content

Commit

Permalink
Merge pull request #36590 from hashicorp/b-ec2-instance-change-type
Browse files Browse the repository at this point in the history
ec2/instance: Replace instance when architecture changes
  • Loading branch information
YakDriver authored Mar 26, 2024
2 parents e705bad + 69292e5 commit 60895b4
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 12 deletions.
3 changes: 3 additions & 0 deletions .changelog/36590.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
resource/aws_instance: Replace an instance when an `instance_type` change also requires an architecture change, such as x86_64 to arm64
```
45 changes: 45 additions & 0 deletions internal/service/ec2/ec2_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -901,6 +901,40 @@ func ResourceInstance() *schema.Resource {
customdiff.ForceNewIf("user_data_base64", func(_ context.Context, diff *schema.ResourceDiff, meta interface{}) bool {
return diff.Get("user_data_replace_on_change").(bool)
}),
customdiff.ForceNewIf("instance_type", func(ctx context.Context, diff *schema.ResourceDiff, meta interface{}) bool {
conn := meta.(*conns.AWSClient).EC2Conn(ctx)

_, ok := diff.GetOk("instance_type")

if diff.Id() == "" || !diff.HasChange("instance_type") || !ok {
return false
}

o, n := diff.GetChange("instance_type")
it1, err := FindInstanceTypeByName(ctx, conn, o.(string))
if err != nil {
return false
}

it2, err := FindInstanceTypeByName(ctx, conn, n.(string))
if err != nil {
return false
}

if it1 == nil || it2 == nil {
return false
}

if aws.StringValue(it1.InstanceType) == aws.StringValue(it2.InstanceType) {
return false
}

if hasCommonElement(it1.ProcessorInfo.SupportedArchitectures, it2.ProcessorInfo.SupportedArchitectures) {
return false
}

return true
}),
),
}
}
Expand Down Expand Up @@ -3984,3 +4018,14 @@ func ParseInstanceType(s string) (*InstanceType, error) {
Size: matches[5],
}, nil
}

func hasCommonElement(slice1 []*string, slice2 []*string) bool {
for _, str1 := range slice1 {
for _, str2 := range slice2 {
if aws.StringValue(str1) == aws.StringValue(str2) {
return true
}
}
}
return false
}
66 changes: 54 additions & 12 deletions internal/service/ec2/ec2_instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2259,7 +2259,7 @@ func TestAccEC2Instance_changeInstanceType(t *testing.T) {
CheckDestroy: testAccCheckInstanceDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAccInstanceConfig_smallType(rName),
Config: testAccInstanceConfig_type(rName, "t2.medium"),
Check: resource.ComposeTestCheckFunc(
testAccCheckInstanceExists(ctx, resourceName, &before),
resource.TestCheckResourceAttr(resourceName, "instance_type", "t2.medium"),
Expand All @@ -2272,7 +2272,7 @@ func TestAccEC2Instance_changeInstanceType(t *testing.T) {
ImportStateVerifyIgnore: []string{"user_data_replace_on_change"},
},
{
Config: testAccInstanceConfig_updateType(rName),
Config: testAccInstanceConfig_type(rName, "t2.large"),
Check: resource.ComposeTestCheckFunc(
testAccCheckInstanceExists(ctx, resourceName, &after),
testAccCheckInstanceNotRecreated(&before, &after),
Expand All @@ -2283,6 +2283,38 @@ func TestAccEC2Instance_changeInstanceType(t *testing.T) {
})
}

func TestAccEC2Instance_changeInstanceTypeReplace(t *testing.T) {
ctx := acctest.Context(t)
var before ec2.Instance
var after ec2.Instance
resourceName := "aws_instance.test"
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, names.EC2ServiceID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckInstanceDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAccInstanceConfig_typeReplace(rName, "m5.2xlarge"),
Check: resource.ComposeTestCheckFunc(
testAccCheckInstanceExists(ctx, resourceName, &before),
resource.TestCheckResourceAttr(resourceName, "instance_type", "m5.2xlarge"),
),
},
{
Config: testAccInstanceConfig_typeReplace(rName, "m6g.2xlarge"),
Check: resource.ComposeTestCheckFunc(
testAccCheckInstanceExists(ctx, resourceName, &after),
testAccCheckInstanceRecreated(&before, &after),
resource.TestCheckResourceAttr(resourceName, "instance_type", "m6g.2xlarge"),
),
},
},
})
}

func TestAccEC2Instance_changeInstanceTypeAndUserData(t *testing.T) {
ctx := acctest.Context(t)
var v ec2.Instance
Expand Down Expand Up @@ -6195,7 +6227,7 @@ resource "aws_instance" "test" {
`, rName, filename))
}

func testAccInstanceConfig_smallType(rName string) string {
func testAccInstanceConfig_type(rName, instanceType string) string {
return acctest.ConfigCompose(
acctest.ConfigLatestAmazonLinux2HVMEBSX8664AMI(),
testAccInstanceVPCConfig(rName, false, 0),
Expand All @@ -6204,31 +6236,41 @@ resource "aws_instance" "test" {
ami = data.aws_ami.amzn2-ami-minimal-hvm-ebs-x86_64.id
subnet_id = aws_subnet.test.id
instance_type = "t2.medium"
instance_type = %[1]q
tags = {
Name = %[1]q
Name = %[2]q
}
}
`, rName))
`, instanceType, rName))
}

func testAccInstanceConfig_updateType(rName string) string {
func testAccInstanceConfig_typeReplace(rName, instanceType string) string {
arch := acctest.ConfigLatestAmazonLinux2HVMEBSX8664AMI()
archs := "x86_64"
if strings.HasPrefix(instanceType, "m6g.") {
arch = acctest.ConfigLatestAmazonLinux2HVMEBSARM64AMI()
archs = "arm64"
}
return acctest.ConfigCompose(
acctest.ConfigLatestAmazonLinux2HVMEBSX8664AMI(),
arch,
testAccInstanceVPCConfig(rName, false, 0),
fmt.Sprintf(`
resource "aws_instance" "test" {
ami = data.aws_ami.amzn2-ami-minimal-hvm-ebs-x86_64.id
ami = data.aws_ami.amzn2-ami-minimal-hvm-ebs-%[3]s.id
subnet_id = aws_subnet.test.id
instance_type = "t2.large"
instance_type = %[1]q
tags = {
Name = %[1]q
Name = %[2]q
}
lifecycle {
ignore_changes = [ami]
}
}
`, rName))
`, instanceType, rName, archs))
}

func testAccInstanceConfig_typeAndUserData(rName, instanceType, userData string) string {
Expand Down

0 comments on commit 60895b4

Please sign in to comment.