diff --git a/aws/resource_aws_appmesh_test.go b/aws/resource_aws_appmesh_test.go index ccc4769570b..d373d762abc 100644 --- a/aws/resource_aws_appmesh_test.go +++ b/aws/resource_aws_appmesh_test.go @@ -19,6 +19,7 @@ func TestAccAWSAppmesh(t *testing.T) { "basic": testAccAwsAppmeshVirtualNode_basic, "listenerHealthChecks": testAccAwsAppmeshVirtualNode_listenerHealthChecks, "logging": testAccAwsAppmeshVirtualNode_logging, + "tags": testAccAwsAppmeshVirtualNode_tags, }, "VirtualRouter": { "basic": testAccAwsAppmeshVirtualRouter_basic, diff --git a/aws/resource_aws_appmesh_virtual_node.go b/aws/resource_aws_appmesh_virtual_node.go index 9e2e0e33359..25c27a46215 100644 --- a/aws/resource_aws_appmesh_virtual_node.go +++ b/aws/resource_aws_appmesh_virtual_node.go @@ -261,6 +261,8 @@ func resourceAwsAppmeshVirtualNode() *schema.Resource { Type: schema.TypeString, Computed: true, }, + + "tags": tagsSchema(), }, } } @@ -272,6 +274,7 @@ func resourceAwsAppmeshVirtualNodeCreate(d *schema.ResourceData, meta interface{ MeshName: aws.String(d.Get("mesh_name").(string)), VirtualNodeName: aws.String(d.Get("name").(string)), Spec: expandAppmeshVirtualNodeSpec(d.Get("spec").([]interface{})), + Tags: tagsFromMapAppmesh(d.Get("tags").(map[string]interface{})), } log.Printf("[DEBUG] Creating App Mesh virtual node: %#v", req) @@ -316,6 +319,16 @@ func resourceAwsAppmeshVirtualNodeRead(d *schema.ResourceData, meta interface{}) return fmt.Errorf("error setting spec: %s", err) } + err = saveTagsAppmesh(conn, d, aws.StringValue(resp.VirtualNode.Metadata.Arn)) + if isAWSErr(err, appmesh.ErrCodeNotFoundException, "") { + log.Printf("[WARN] App Mesh virtual node (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + if err != nil { + return fmt.Errorf("error saving tags: %s", err) + } + return nil } @@ -337,6 +350,16 @@ func resourceAwsAppmeshVirtualNodeUpdate(d *schema.ResourceData, meta interface{ } } + err := setTagsAppmesh(conn, d, d.Get("arn").(string)) + if isAWSErr(err, appmesh.ErrCodeNotFoundException, "") { + log.Printf("[WARN] App Mesh virtual node (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + if err != nil { + return fmt.Errorf("error setting tags: %s", err) + } + return resourceAwsAppmeshVirtualNodeRead(d, meta) } diff --git a/aws/resource_aws_appmesh_virtual_node_test.go b/aws/resource_aws_appmesh_virtual_node_test.go index c64ce4499d7..d2ec40b6244 100644 --- a/aws/resource_aws_appmesh_virtual_node_test.go +++ b/aws/resource_aws_appmesh_virtual_node_test.go @@ -179,6 +179,59 @@ func testAccAwsAppmeshVirtualNode_logging(t *testing.T) { }) } +func testAccAwsAppmeshVirtualNode_tags(t *testing.T) { + var vn appmesh.VirtualNodeData + resourceName := "aws_appmesh_virtual_node.foo" + meshName := fmt.Sprintf("tf-test-mesh-%d", acctest.RandInt()) + vnName := fmt.Sprintf("tf-test-node-%d", acctest.RandInt()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAppmeshVirtualNodeDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAppmeshVirtualNodeConfig_tags(meshName, vnName, "foo", "bar", "good", "bad"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAppmeshVirtualNodeExists(resourceName, &vn), + resource.TestCheckResourceAttr( + resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr( + resourceName, "tags.foo", "bar"), + resource.TestCheckResourceAttr( + resourceName, "tags.good", "bad"), + ), + }, + { + Config: testAccAppmeshVirtualNodeConfig_tags(meshName, vnName, "foo2", "bar", "good", "bad2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAppmeshVirtualNodeExists(resourceName, &vn), + resource.TestCheckResourceAttr( + resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr( + resourceName, "tags.foo2", "bar"), + resource.TestCheckResourceAttr( + resourceName, "tags.good", "bad2"), + ), + }, + { + Config: testAccAppmeshVirtualNodeConfig_basic(meshName, vnName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAppmeshVirtualNodeExists(resourceName, &vn), + resource.TestCheckResourceAttr( + resourceName, "tags.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportStateId: fmt.Sprintf("%s/%s", meshName, vnName), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func testAccCheckAppmeshVirtualNodeDestroy(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).appmeshconn @@ -377,3 +430,23 @@ resource "aws_appmesh_virtual_node" "foo" { } `, meshName, vnName, path) } + +func testAccAppmeshVirtualNodeConfig_tags(meshName, vnName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { + return fmt.Sprintf(` +resource "aws_appmesh_mesh" "foo" { + name = %[1]q +} + +resource "aws_appmesh_virtual_node" "foo" { + name = %[2]q + mesh_name = "${aws_appmesh_mesh.foo.id}" + + spec {} + + tags = { + %[3]s = %[4]q + %[5]s = %[6]q + } +} +`, meshName, vnName, tagKey1, tagValue1, tagKey2, tagValue2) +} diff --git a/website/docs/r/appmesh_virtual_node.html.markdown b/website/docs/r/appmesh_virtual_node.html.markdown index ef5a7bcb650..c1d4c7b513e 100644 --- a/website/docs/r/appmesh_virtual_node.html.markdown +++ b/website/docs/r/appmesh_virtual_node.html.markdown @@ -137,6 +137,7 @@ The following arguments are supported: * `name` - (Required) The name to use for the virtual node. * `mesh_name` - (Required) The name of the service mesh in which to create the virtual node. * `spec` - (Required) The virtual node specification to apply. +* `tags` - (Optional) A mapping of tags to assign to the resource. The `spec` object supports the following: