Skip to content

Commit

Permalink
New Resource: aws_iot_thing
Browse files Browse the repository at this point in the history
  • Loading branch information
radeksimko committed Mar 1, 2018
1 parent 7223d8f commit 43de0d2
Show file tree
Hide file tree
Showing 5 changed files with 404 additions and 0 deletions.
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": resourceAwsIotThing(),
"aws_iot_thing_type": resourceAwsIotThingType(),
"aws_iot_topic_rule": resourceAwsIotTopicRule(),
"aws_key_pair": resourceAwsKeyPair(),
Expand Down
162 changes: 162 additions & 0 deletions aws/resource_aws_iot_thing.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package aws

import (
"log"

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

func resourceAwsIotThing() *schema.Resource {
return &schema.Resource{
Create: resourceAwsIotThingCreate,
Read: resourceAwsIotThingRead,
Update: resourceAwsIotThingUpdate,
Delete: resourceAwsIotThingDelete,

Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringLenBetween(1, 128),
},
"attributes": {
Type: schema.TypeMap,
Optional: true,
},
"thing_type_name": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringLenBetween(1, 128),
},
"default_client_id": {
Type: schema.TypeString,
Computed: true,
},
"version": {
Type: schema.TypeInt,
Computed: true,
},
"arn": {
Type: schema.TypeString,
Computed: true,
},
},
}
}

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

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

if v, ok := d.GetOk("thing_type_name"); ok {
params.ThingTypeName = aws.String(v.(string))
}
if v, ok := d.GetOk("attributes"); ok {
params.AttributePayload = &iot.AttributePayload{
Attributes: stringMapToPointers(v.(map[string]interface{})),
}
}

log.Printf("[DEBUG] Creating IoT Thing: %s", params)
out, err := conn.CreateThing(params)
if err != nil {
return err
}

d.SetId(*out.ThingName)

return resourceAwsIotThingRead(d, meta)
}

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

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

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

log.Printf("[DEBUG] Received IoT Thing: %s", out)

d.Set("arn", out.ThingArn)
d.Set("name", out.ThingName)
d.Set("attributes", aws.StringValueMap(out.Attributes))
d.Set("default_client_id", out.DefaultClientId)
d.Set("thing_type_name", out.ThingTypeName)
d.Set("version", out.Version)

return nil
}

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

params := &iot.UpdateThingInput{
ThingName: aws.String(d.Get("name").(string)),
}
if d.HasChange("thing_type_name") {
if v, ok := d.GetOk("thing_type_name"); ok {
params.ThingTypeName = aws.String(v.(string))
} else {
params.RemoveThingType = aws.Bool(true)
}
}
if d.HasChange("attributes") {
attributes := map[string]*string{}

if v, ok := d.GetOk("attributes"); ok {
if m, ok := v.(map[string]interface{}); ok {
attributes = stringMapToPointers(m)
}
}
params.AttributePayload = &iot.AttributePayload{
Attributes: attributes,
}
}

_, err := conn.UpdateThing(params)
if err != nil {
return err
}

return resourceAwsIotThingRead(d, meta)
}

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

params := &iot.DeleteThingInput{
ThingName: aws.String(d.Id()),
}
log.Printf("[DEBUG] Deleting IoT Thing: %s", params)

_, err := conn.DeleteThing(params)
if err != nil {
if isAWSErr(err, iot.ErrCodeResourceNotFoundException, "") {
return nil
}
return err
}

return nil
}
194 changes: 194 additions & 0 deletions aws/resource_aws_iot_thing_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
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 TestAccAWSIotThing_basic(t *testing.T) {
var thing iot.DescribeThingOutput
rString := acctest.RandString(8)
thingName := fmt.Sprintf("tf_acc_thing_%s", rString)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSIotThingDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSIotThingConfig_basic(thingName),
Check: resource.ComposeTestCheckFunc(
testAccCheckIotThingExists("aws_iot_thing.test", &thing),
resource.TestCheckResourceAttr("aws_iot_thing.test", "name", thingName),
resource.TestCheckResourceAttr("aws_iot_thing.test", "attributes.%", "0"),
resource.TestCheckResourceAttr("aws_iot_thing.test", "thing_type_name", ""),
resource.TestCheckResourceAttrSet("aws_iot_thing.test", "arn"),
resource.TestCheckResourceAttrSet("aws_iot_thing.test", "default_client_id"),
resource.TestCheckResourceAttrSet("aws_iot_thing.test", "version"),
),
},
},
})
}

func TestAccAWSIotThing_full(t *testing.T) {
var thing iot.DescribeThingOutput
rString := acctest.RandString(8)
thingName := fmt.Sprintf("tf_acc_thing_%s", rString)
typeName := fmt.Sprintf("tf_acc_type_%s", rString)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSIotThingDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSIotThingConfig_full(thingName, typeName, "42"),
Check: resource.ComposeTestCheckFunc(
testAccCheckIotThingExists("aws_iot_thing.test", &thing),
resource.TestCheckResourceAttr("aws_iot_thing.test", "name", thingName),
resource.TestCheckResourceAttr("aws_iot_thing.test", "thing_type_name", typeName),
resource.TestCheckResourceAttr("aws_iot_thing.test", "attributes.%", "3"),
resource.TestCheckResourceAttr("aws_iot_thing.test", "attributes.One", "11111"),
resource.TestCheckResourceAttr("aws_iot_thing.test", "attributes.Two", "TwoTwo"),
resource.TestCheckResourceAttr("aws_iot_thing.test", "attributes.Answer", "42"),
resource.TestCheckResourceAttrSet("aws_iot_thing.test", "arn"),
resource.TestCheckResourceAttrSet("aws_iot_thing.test", "default_client_id"),
resource.TestCheckResourceAttrSet("aws_iot_thing.test", "version"),
),
},
{ // Update attribute
Config: testAccAWSIotThingConfig_full(thingName, typeName, "differentOne"),
Check: resource.ComposeTestCheckFunc(
testAccCheckIotThingExists("aws_iot_thing.test", &thing),
resource.TestCheckResourceAttr("aws_iot_thing.test", "name", thingName),
resource.TestCheckResourceAttr("aws_iot_thing.test", "thing_type_name", typeName),
resource.TestCheckResourceAttr("aws_iot_thing.test", "attributes.%", "3"),
resource.TestCheckResourceAttr("aws_iot_thing.test", "attributes.One", "11111"),
resource.TestCheckResourceAttr("aws_iot_thing.test", "attributes.Two", "TwoTwo"),
resource.TestCheckResourceAttr("aws_iot_thing.test", "attributes.Answer", "differentOne"),
resource.TestCheckResourceAttrSet("aws_iot_thing.test", "arn"),
resource.TestCheckResourceAttrSet("aws_iot_thing.test", "default_client_id"),
resource.TestCheckResourceAttrSet("aws_iot_thing.test", "version"),
),
},
{ // Remove thing type association
Config: testAccAWSIotThingConfig_basic(thingName),
Check: resource.ComposeTestCheckFunc(
testAccCheckIotThingExists("aws_iot_thing.test", &thing),
resource.TestCheckResourceAttr("aws_iot_thing.test", "name", thingName),
resource.TestCheckResourceAttr("aws_iot_thing.test", "attributes.%", "0"),
resource.TestCheckResourceAttr("aws_iot_thing.test", "thing_type_name", ""),
resource.TestCheckResourceAttrSet("aws_iot_thing.test", "arn"),
resource.TestCheckResourceAttrSet("aws_iot_thing.test", "default_client_id"),
resource.TestCheckResourceAttrSet("aws_iot_thing.test", "version"),
),
},
},
})
}

func TestAccAWSIotThing_importBasic(t *testing.T) {
resourceName := "aws_iot_thing.test"
rString := acctest.RandString(8)
thingName := fmt.Sprintf("tf_acc_thing_%s", rString)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSIotThingTypeDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSIotThingConfig_basic(thingName),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func testAccCheckIotThingExists(n string, thing *iot.DescribeThingOutput) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}

if rs.Primary.ID == "" {
return fmt.Errorf("No IoT Thing ID is set")
}

conn := testAccProvider.Meta().(*AWSClient).iotconn
params := &iot.DescribeThingInput{
ThingName: aws.String(rs.Primary.ID),
}
resp, err := conn.DescribeThing(params)
if err != nil {
return err
}

*thing = *resp

return nil
}
}

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

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

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

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

}

return nil
}

func testAccAWSIotThingConfig_basic(thingName string) string {
return fmt.Sprintf(`
resource "aws_iot_thing" "test" {
name = "%s"
}
`, thingName)
}

func testAccAWSIotThingConfig_full(thingName, typeName, answer string) string {
return fmt.Sprintf(`
resource "aws_iot_thing" "test" {
name = "%s"
attributes {
One = "11111"
Two = "TwoTwo"
Answer = "%s"
}
thing_type_name = "${aws_iot_thing_type.test.name}"
}
resource "aws_iot_thing_type" "test" {
name = "%s"
}
`, thingName, answer, typeName)
}
3 changes: 3 additions & 0 deletions website/aws.erb
Original file line number Diff line number Diff line change
Expand Up @@ -1167,6 +1167,9 @@
<li<%= sidebar_current("docs-aws-resource-iot-topic-rule") %>>
<a href="/docs/providers/aws/r/iot_topic_rule.html">aws_iot_topic_rule</a>
</li>
<li<%= sidebar_current("docs-aws-resource-iot-thing") %>>
<a href="/docs/providers/aws/r/iot_thing.html">aws_iot_thing</a>
</li>
<li<%= sidebar_current("docs-aws-resource-iot-thing-type") %>>
<a href="/docs/providers/aws/r/iot_thing_type.html">aws_iot_thing_type</a>
</li>
Expand Down
Loading

0 comments on commit 43de0d2

Please sign in to comment.