Skip to content

Commit

Permalink
Merge pull request #3302 from Ninir/f-iot-thing-type
Browse files Browse the repository at this point in the history
New Resource: `aws_iot_thing_type`
  • Loading branch information
Ninir authored Feb 20, 2018
2 parents 078ec17 + 9427513 commit 8ff7a12
Show file tree
Hide file tree
Showing 8 changed files with 446 additions and 0 deletions.
29 changes: 29 additions & 0 deletions aws/import_aws_iot_thing_type_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package aws

import (
"testing"

"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
)

func TestAccAWSIotThingType_importBasic(t *testing.T) {
resourceName := "aws_iot_thing_type.foo"
rInt := acctest.RandInt()

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSIotThingTypeDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSIotThingTypeConfig_basic(rInt),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}
1 change: 1 addition & 0 deletions aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ func Provider() terraform.ResourceProvider {
"aws_internet_gateway": resourceAwsInternetGateway(),
"aws_iot_certificate": resourceAwsIotCertificate(),
"aws_iot_policy": resourceAwsIotPolicy(),
"aws_iot_thing_type": resourceAwsIotThingType(),
"aws_iot_topic_rule": resourceAwsIotTopicRule(),
"aws_key_pair": resourceAwsKeyPair(),
"aws_kinesis_firehose_delivery_stream": resourceAwsKinesisFirehoseDeliveryStream(),
Expand Down
202 changes: 202 additions & 0 deletions aws/resource_aws_iot_thing_type.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
package aws

import (
"log"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/iot"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
)

// https://docs.aws.amazon.com/iot/latest/apireference/API_CreateThingType.html
func resourceAwsIotThingType() *schema.Resource {
return &schema.Resource{
Create: resourceAwsIotThingTypeCreate,
Read: resourceAwsIotThingTypeRead,
Update: resourceAwsIotThingTypeUpdate,
Delete: resourceAwsIotThingTypeDelete,

Importer: &schema.ResourceImporter{
State: func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
d.Set("name", d.Id())
return []*schema.ResourceData{d}, nil
},
},

Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validateIotThingTypeName,
},
"properties": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"description": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: validateIotThingTypeDescription,
},
"searchable_attributes": {
Type: schema.TypeSet,
Optional: true,
Computed: true,
ForceNew: true,
MaxItems: 3,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validateIotThingTypeSearchableAttribute,
},
},
},
},
},
"deprecated": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"arn": {
Type: schema.TypeString,
Computed: true,
},
},
}
}

func resourceAwsIotThingTypeCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).iotconn

params := &iot.CreateThingTypeInput{
ThingTypeName: aws.String(d.Get("name").(string)),
}

if v, ok := d.GetOk("properties"); ok {
configs := v.([]interface{})
config, ok := configs[0].(map[string]interface{})

if ok && config != nil {
params.ThingTypeProperties = expandIotThingTypeProperties(config)
}
}

log.Printf("[DEBUG] Creating IoT Thing Type: %s", params)
out, err := conn.CreateThingType(params)

if err != nil {
return err
}

d.SetId(*out.ThingTypeName)

if v := d.Get("deprecated").(bool); v {
params := &iot.DeprecateThingTypeInput{
ThingTypeName: aws.String(d.Id()),
UndoDeprecate: aws.Bool(false),
}

log.Printf("[DEBUG] Deprecating IoT Thing Type: %s", params)
_, err := conn.DeprecateThingType(params)

if err != nil {
return err
}
}

return resourceAwsIotThingTypeRead(d, meta)
}

func resourceAwsIotThingTypeRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).iotconn

params := &iot.DescribeThingTypeInput{
ThingTypeName: aws.String(d.Id()),
}
log.Printf("[DEBUG] Reading IoT Thing Type: %s", params)
out, err := conn.DescribeThingType(params)

if err != nil {
if isAWSErr(err, iot.ErrCodeResourceNotFoundException, "") {
log.Printf("[WARN] IoT Thing Type %q not found, removing from state", d.Id())
d.SetId("")
}
return err
}

if out.ThingTypeMetadata != nil {
d.Set("deprecated", out.ThingTypeMetadata.Deprecated)
}

d.Set("arn", out.ThingTypeArn)
d.Set("properties", flattenIotThingTypeProperties(out.ThingTypeProperties))

return nil
}

func resourceAwsIotThingTypeUpdate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).iotconn

if d.HasChange("deprecated") {
params := &iot.DeprecateThingTypeInput{
ThingTypeName: aws.String(d.Id()),
UndoDeprecate: aws.Bool(!d.Get("deprecated").(bool)),
}

log.Printf("[DEBUG] Updating IoT Thing Type: %s", params)
_, err := conn.DeprecateThingType(params)

if err != nil {
return err
}
}

return resourceAwsIotThingTypeRead(d, meta)
}

func resourceAwsIotThingTypeDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).iotconn

// In order to delete an IoT Thing Type, you must deprecate it first and wait
// at least 5 minutes.
deprecateParams := &iot.DeprecateThingTypeInput{
ThingTypeName: aws.String(d.Id()),
}
log.Printf("[DEBUG] Deprecating IoT Thing Type: %s", deprecateParams)
_, err := conn.DeprecateThingType(deprecateParams)

if err != nil {
return err
}

deleteParams := &iot.DeleteThingTypeInput{
ThingTypeName: aws.String(d.Id()),
}
log.Printf("[DEBUG] Deleting IoT Thing Type: %s", deleteParams)

return resource.Retry(6*time.Minute, func() *resource.RetryError {
_, err := conn.DeleteThingType(deleteParams)

if err != nil {
if isAWSErr(err, iot.ErrCodeInvalidRequestException, "Please wait for 5 minutes after deprecation and then retry") {
return resource.RetryableError(err)
}

// As the delay post-deprecation is about 5 minutes, it may have been
// deleted in between, thus getting a Not Found Exception.
if isAWSErr(err, iot.ErrCodeResourceNotFoundException, "") {
return nil
}

return resource.NonRetryableError(err)
}

return nil
})
}
116 changes: 116 additions & 0 deletions aws/resource_aws_iot_thing_type_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package aws

import (
"fmt"
"testing"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/iot"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)

func TestAccAWSIotThingType_basic(t *testing.T) {
rInt := acctest.RandInt()

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSIotThingTypeDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSIotThingTypeConfig_basic(rInt),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet("aws_iot_thing_type.foo", "arn"),
resource.TestCheckResourceAttr("aws_iot_thing_type.foo", "name", fmt.Sprintf("tf_acc_iot_thing_type_%d", rInt)),
),
},
},
})
}

func TestAccAWSIotThingType_full(t *testing.T) {
rInt := acctest.RandInt()

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSIotThingTypeDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSIotThingTypeConfig_full(rInt),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet("aws_iot_thing_type.foo", "arn"),
resource.TestCheckResourceAttr("aws_iot_thing_type.foo", "properties.0.description", "MyDescription"),
resource.TestCheckResourceAttr("aws_iot_thing_type.foo", "properties.0.searchable_attributes.#", "3"),
resource.TestCheckResourceAttr("aws_iot_thing_type.foo", "deprecated", "true"),
),
},
{
Config: testAccAWSIotThingTypeConfig_fullUpdated(rInt),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("aws_iot_thing_type.foo", "deprecated", "false"),
),
},
},
})
}

func testAccCheckAWSIotThingTypeDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).iotconn

for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_iot_thing_type" {
continue
}

params := &iot.DescribeThingTypeInput{
ThingTypeName: aws.String(rs.Primary.ID),
}

_, err := conn.DescribeThingType(params)
if err == nil {
return fmt.Errorf("Expected IoT Thing Type to be destroyed, %s found", rs.Primary.ID)
}

}

return nil
}

func testAccAWSIotThingTypeConfig_basic(rName int) string {
return fmt.Sprintf(`
resource "aws_iot_thing_type" "foo" {
name = "tf_acc_iot_thing_type_%d"
}
`, rName)
}

func testAccAWSIotThingTypeConfig_full(rName int) string {
return fmt.Sprintf(`
resource "aws_iot_thing_type" "foo" {
name = "tf_acc_iot_thing_type_%d"
deprecated = true
properties {
description = "MyDescription"
searchable_attributes = ["foo", "bar", "baz"]
}
}
`, rName)
}

func testAccAWSIotThingTypeConfig_fullUpdated(rName int) string {
return fmt.Sprintf(`
resource "aws_iot_thing_type" "foo" {
name = "tf_acc_iot_thing_type_%d"
deprecated = false
properties {
description = "MyDescription"
searchable_attributes = ["foo", "bar", "baz"]
}
}
`, rName)
}
27 changes: 27 additions & 0 deletions aws/structure.go
Original file line number Diff line number Diff line change
Expand Up @@ -3678,3 +3678,30 @@ func flattenDynamoDbTableItemAttributes(attrs map[string]*dynamodb.AttributeValu

return rawBuffer.String(), nil
}

func expandIotThingTypeProperties(config map[string]interface{}) *iot.ThingTypeProperties {
properties := &iot.ThingTypeProperties{
SearchableAttributes: expandStringList(config["searchable_attributes"].(*schema.Set).List()),
}

if v, ok := config["description"]; ok && v.(string) != "" {
properties.ThingTypeDescription = aws.String(v.(string))
}

return properties
}

func flattenIotThingTypeProperties(s *iot.ThingTypeProperties) []map[string]interface{} {
m := map[string]interface{}{}

if s == nil {
return nil
}

if s.ThingTypeDescription != nil {
m["description"] = *s.ThingTypeDescription
}
m["searchable_attributes"] = flattenStringList(s.SearchableAttributes)

return []map[string]interface{}{m}
}
Loading

0 comments on commit 8ff7a12

Please sign in to comment.