Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

d/aws_ami: Fix interface conversion: interface {} is types.ProductCodeValues, not string panic #37977

Merged
merged 3 commits into from
Jun 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/#####.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
data-source/aws_ami: Fix `interface conversion: interface {} is types.ProductCodeValues, not string` panic
```
105 changes: 35 additions & 70 deletions internal/service/ec2/ec2_ami_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@
package ec2

import (
"bytes"
"context"
"fmt"
"log"
"sort"
"time"

Expand All @@ -20,7 +18,6 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"github.com/hashicorp/terraform-provider-aws/internal/conns"
"github.com/hashicorp/terraform-provider-aws/internal/create"
"github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag"
"github.com/hashicorp/terraform-provider-aws/internal/flex"
tftags "github.com/hashicorp/terraform-provider-aws/internal/tags"
Expand Down Expand Up @@ -50,7 +47,6 @@ func dataSourceAMI() *schema.Resource {
"block_device_mappings": {
Type: schema.TypeSet,
Computed: true,
Set: amiBlockDeviceMappingHash,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
names.AttrDeviceName: {
Expand Down Expand Up @@ -170,7 +166,6 @@ func dataSourceAMI() *schema.Resource {
"product_codes": {
Type: schema.TypeSet,
Computed: true,
Set: amiProductCodesHash,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"product_code_id": {
Expand Down Expand Up @@ -349,48 +344,54 @@ func dataSourceAMIRead(ctx context.Context, d *schema.ResourceData, meta interfa
return diags
}

func flattenAMIBlockDeviceMappings(m []awstypes.BlockDeviceMapping) *schema.Set {
s := &schema.Set{
F: amiBlockDeviceMappingHash,
func flattenAMIBlockDeviceMappings(apiObjects []awstypes.BlockDeviceMapping) []interface{} {
if len(apiObjects) == 0 {
return nil
}
for _, v := range m {
mapping := map[string]interface{}{
names.AttrDeviceName: aws.ToString(v.DeviceName),
names.AttrVirtualName: aws.ToString(v.VirtualName),

var tfList []interface{}

for _, apiObject := range apiObjects {
tfMap := map[string]interface{}{
names.AttrDeviceName: aws.ToString(apiObject.DeviceName),
names.AttrVirtualName: aws.ToString(apiObject.VirtualName),
}

if v.Ebs != nil {
if apiObject := apiObject.Ebs; apiObject != nil {
ebs := map[string]interface{}{
names.AttrDeleteOnTermination: fmt.Sprintf("%t", aws.ToBool(v.Ebs.DeleteOnTermination)),
names.AttrEncrypted: fmt.Sprintf("%t", aws.ToBool(v.Ebs.Encrypted)),
names.AttrIOPS: fmt.Sprintf("%d", aws.ToInt32(v.Ebs.Iops)),
names.AttrThroughput: fmt.Sprintf("%d", aws.ToInt32(v.Ebs.Throughput)),
names.AttrVolumeSize: fmt.Sprintf("%d", aws.ToInt32(v.Ebs.VolumeSize)),
names.AttrSnapshotID: aws.ToString(v.Ebs.SnapshotId),
names.AttrVolumeType: v.Ebs.VolumeType,
names.AttrDeleteOnTermination: flex.BoolToStringValue(apiObject.DeleteOnTermination),
names.AttrEncrypted: flex.BoolToStringValue(apiObject.Encrypted),
names.AttrIOPS: flex.Int32ToStringValue(apiObject.Iops),
names.AttrSnapshotID: aws.ToString(apiObject.SnapshotId),
names.AttrThroughput: flex.Int32ToStringValue(apiObject.Throughput),
names.AttrVolumeSize: flex.Int32ToStringValue(apiObject.VolumeSize),
names.AttrVolumeType: apiObject.VolumeType,
}

mapping["ebs"] = ebs
tfMap["ebs"] = ebs
}

log.Printf("[DEBUG] aws_ami - adding block device mapping: %v", mapping)
s.Add(mapping)
tfList = append(tfList, tfMap)
}
return s

return tfList
}

func flattenAMIProductCodes(m []awstypes.ProductCode) *schema.Set {
s := &schema.Set{
F: amiProductCodesHash,
func flattenAMIProductCodes(apiObjects []awstypes.ProductCode) []interface{} {
if len(apiObjects) == 0 {
return nil
}
for _, v := range m {
code := map[string]interface{}{
"product_code_id": aws.ToString(v.ProductCodeId),
"product_code_type": v.ProductCodeType,
}
s.Add(code)

var tfList []interface{}

for _, apiObject := range apiObjects {
tfList = append(tfList, map[string]interface{}{
"product_code_id": aws.ToString(apiObject.ProductCodeId),
"product_code_type": apiObject.ProductCodeType,
})
}
return s

return tfList
}

func amiRootSnapshotId(image awstypes.Image) string {
Expand Down Expand Up @@ -420,39 +421,3 @@ func flattenAMIStateReason(m *awstypes.StateReason) map[string]interface{} {
}
return s
}

func amiBlockDeviceMappingHash(v interface{}) int {
var buf bytes.Buffer
// All keys added in alphabetical order.
m := v.(map[string]interface{})
buf.WriteString(fmt.Sprintf("%s-", m[names.AttrDeviceName].(string)))
if d, ok := m["ebs"]; ok {
if len(d.(map[string]interface{})) > 0 {
e := d.(map[string]interface{})
buf.WriteString(fmt.Sprintf("%s-", e[names.AttrDeleteOnTermination].(string)))
buf.WriteString(fmt.Sprintf("%s-", e[names.AttrEncrypted].(string)))
buf.WriteString(fmt.Sprintf("%s-", e[names.AttrIOPS].(string)))
buf.WriteString(fmt.Sprintf("%s-", e[names.AttrVolumeSize].(string)))
buf.WriteString(fmt.Sprintf("%s-", e[names.AttrVolumeType]))
}
}
if d, ok := m["no_device"]; ok {
buf.WriteString(fmt.Sprintf("%s-", d.(string)))
}
if d, ok := m[names.AttrVirtualName]; ok {
buf.WriteString(fmt.Sprintf("%s-", d.(string)))
}
if d, ok := m[names.AttrSnapshotID]; ok {
buf.WriteString(fmt.Sprintf("%s-", d.(string)))
}
return create.StringHashcode(buf.String())
}

func amiProductCodesHash(v interface{}) int {
var buf bytes.Buffer
m := v.(map[string]interface{})
// All keys added in alphabetical order.
buf.WriteString(fmt.Sprintf("%s-", m["product_code_id"].(string)))
buf.WriteString(fmt.Sprintf("%s-", m["product_code_type"].(string)))
return create.StringHashcode(buf.String())
}
32 changes: 32 additions & 0 deletions internal/service/ec2/ec2_ami_data_source_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,25 @@ func TestAccEC2AMIDataSource_gp3BlockDevice(t *testing.T) {
})
}

func TestAccEC2AMIDataSource_productCode(t *testing.T) {
ctx := acctest.Context(t)
datasourceName := "data.aws_ami.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, names.EC2ServiceID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccAMIDataSourceConfig_productCode,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(datasourceName, "product_codes.#", acctest.Ct1),
),
},
},
})
}

// testAccAMIDataSourceConfig_latestUbuntuBionicHVMInstanceStore returns the configuration for a data source that
// describes the latest Ubuntu 18.04 AMI using HVM virtualization and an instance store root device.
// The data source is named 'ubuntu-bionic-ami-hvm-instance-store'.
Expand Down Expand Up @@ -322,3 +341,16 @@ data "aws_ami" "test" {
}
`)
}

// Image with product code.
const testAccAMIDataSourceConfig_productCode = `
data "aws_ami" "test" {
most_recent = true
owners = ["679593333241"]

filter {
name = "name"
values = ["AwsMarketPublished_IBM App Connect v12.0.12.0 and IBM MQ v9.3.0.16 with RapidDeploy 5.1.12 -422d2ddd-3288-4067-be37-4e2a69450606"]
}
}
`
Loading