From 63875c2db6ce58224394ca32281d42c23a26be79 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 7 Dec 2018 09:15:54 -0500 Subject: [PATCH] Add aws_appmesh_virtual_node resource. --- aws/provider.go | 1 + aws/resource_aws_appmesh_test.go | 4 + aws/resource_aws_appmesh_virtual_node.go | 240 ++++++++++++++++ aws/resource_aws_virtual_node_test.go | 269 ++++++++++++++++++ aws/structure.go | 131 ++++++++- website/aws.erb | 3 + .../docs/r/appmesh_virtual_node.html.markdown | 77 +++++ 7 files changed, 712 insertions(+), 13 deletions(-) create mode 100644 aws/resource_aws_appmesh_virtual_node.go create mode 100644 aws/resource_aws_virtual_node_test.go create mode 100644 website/docs/r/appmesh_virtual_node.html.markdown diff --git a/aws/provider.go b/aws/provider.go index 9e18bbd00e1..ee2321c2122 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -320,6 +320,7 @@ func Provider() terraform.ResourceProvider { "aws_appautoscaling_policy": resourceAwsAppautoscalingPolicy(), "aws_appautoscaling_scheduled_action": resourceAwsAppautoscalingScheduledAction(), "aws_appmesh_mesh": resourceAwsAppmeshMesh(), + "aws_appmesh_virtual_node": resourceAwsAppmeshVirtualNode(), "aws_appmesh_virtual_router": resourceAwsAppmeshVirtualRouter(), "aws_appsync_api_key": resourceAwsAppsyncApiKey(), "aws_appsync_datasource": resourceAwsAppsyncDatasource(), diff --git a/aws/resource_aws_appmesh_test.go b/aws/resource_aws_appmesh_test.go index e6b2eaefce1..fc76e880674 100644 --- a/aws/resource_aws_appmesh_test.go +++ b/aws/resource_aws_appmesh_test.go @@ -9,6 +9,10 @@ func TestAccAWSAppmesh(t *testing.T) { "Mesh": { "basic": testAccAwsAppmeshMesh_basic, }, + "VirtualNode": { + "basic": testAccAwsAppmeshVirtualNode_basic, + "allAttributes": testAccAwsAppmeshVirtualNode_allAttributes, + }, "VirtualRouter": { "basic": testAccAwsAppmeshVirtualRouter_basic, }, diff --git a/aws/resource_aws_appmesh_virtual_node.go b/aws/resource_aws_appmesh_virtual_node.go new file mode 100644 index 00000000000..072cdac7c39 --- /dev/null +++ b/aws/resource_aws_appmesh_virtual_node.go @@ -0,0 +1,240 @@ +package aws + +import ( + "bytes" + "fmt" + "log" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/appmesh" + "github.com/hashicorp/terraform/helper/hashcode" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" +) + +func resourceAwsAppmeshVirtualNode() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsAppmeshVirtualNodeCreate, + Read: resourceAwsAppmeshVirtualNodeRead, + Update: resourceAwsAppmeshVirtualNodeUpdate, + Delete: resourceAwsAppmeshVirtualNodeDelete, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(1, 255), + }, + + "mesh_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(1, 255), + }, + + "spec": { + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "backends": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + }, + + "listener": { + Type: schema.TypeSet, + Optional: true, + MinItems: 0, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "port_mapping": { + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "port": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(1, 65535), + }, + + "protocol": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + appmesh.PortProtocolHttp, + appmesh.PortProtocolTcp, + }, false), + }, + }, + }, + }, + }, + }, + Set: appmeshVirtualNodeListenerHash, + }, + + "service_discovery": { + Type: schema.TypeList, + Optional: true, + MinItems: 0, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "dns": { + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "service_name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.NoZeroValues, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + + "arn": { + Type: schema.TypeString, + Computed: true, + }, + + "created_date": { + Type: schema.TypeString, + Computed: true, + }, + + "last_updated_date": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceAwsAppmeshVirtualNodeCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).appmeshconn + + req := &appmesh.CreateVirtualNodeInput{ + MeshName: aws.String(d.Get("mesh_name").(string)), + VirtualNodeName: aws.String(d.Get("name").(string)), + Spec: expandAppmeshVirtualNodeSpec(d.Get("spec").([]interface{})), + } + + log.Printf("[DEBUG] Creating App Mesh virtual node: %#v", req) + resp, err := conn.CreateVirtualNode(req) + if err != nil { + return fmt.Errorf("error creating App Mesh virtual node: %s", err) + } + + d.SetId(aws.StringValue(resp.VirtualNode.Metadata.Uid)) + + return resourceAwsAppmeshVirtualNodeRead(d, meta) +} + +func resourceAwsAppmeshVirtualNodeRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).appmeshconn + + resp, err := conn.DescribeVirtualNode(&appmesh.DescribeVirtualNodeInput{ + MeshName: aws.String(d.Get("mesh_name").(string)), + VirtualNodeName: aws.String(d.Get("name").(string)), + }) + if err != nil { + if isAWSErr(err, appmesh.ErrCodeNotFoundException, "") { + log.Printf("[WARN] App Mesh virtual node (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + return fmt.Errorf("error reading App Mesh virtual node: %s", err) + } + if aws.StringValue(resp.VirtualNode.Status.Status) == appmesh.VirtualNodeStatusCodeDeleted { + log.Printf("[WARN] App Mesh virtual node (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + d.Set("name", resp.VirtualNode.VirtualNodeName) + d.Set("mesh_name", resp.VirtualNode.MeshName) + d.Set("arn", resp.VirtualNode.Metadata.Arn) + d.Set("created_date", resp.VirtualNode.Metadata.CreatedAt.Format(time.RFC3339)) + d.Set("last_updated_date", resp.VirtualNode.Metadata.LastUpdatedAt.Format(time.RFC3339)) + if err := d.Set("spec", flattenAppmeshVirtualNodeSpec(resp.VirtualNode.Spec)); err != nil { + return err + } + + return nil +} + +func resourceAwsAppmeshVirtualNodeUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).appmeshconn + + if d.HasChange("spec") { + _, v := d.GetChange("spec") + req := &appmesh.UpdateVirtualNodeInput{ + MeshName: aws.String(d.Get("mesh_name").(string)), + VirtualNodeName: aws.String(d.Get("name").(string)), + Spec: expandAppmeshVirtualNodeSpec(v.([]interface{})), + } + + log.Printf("[DEBUG] Updating App Mesh virtual node: %#v", req) + _, err := conn.UpdateVirtualNode(req) + if err != nil { + return fmt.Errorf("error updating App Mesh virtual node: %s", err) + } + } + + return resourceAwsAppmeshVirtualNodeRead(d, meta) +} + +func resourceAwsAppmeshVirtualNodeDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).appmeshconn + + log.Printf("[DEBUG] Deleting App Mesh virtual node: %s", d.Id()) + _, err := conn.DeleteVirtualNode(&appmesh.DeleteVirtualNodeInput{ + MeshName: aws.String(d.Get("mesh_name").(string)), + VirtualNodeName: aws.String(d.Get("name").(string)), + }) + if err != nil { + if isAWSErr(err, appmesh.ErrCodeNotFoundException, "") { + return nil + } + return fmt.Errorf("error deleting App Mesh virtual node: %s", err) + } + + return nil +} + +func appmeshVirtualNodeListenerHash(vListener interface{}) int { + var buf bytes.Buffer + mListener := vListener.(map[string]interface{}) + if vPortMapping, ok := mListener["port_mapping"].([]interface{}); ok && len(vPortMapping) > 0 && vPortMapping[0] != nil { + mPortMapping := vPortMapping[0].(map[string]interface{}) + if v, ok := mPortMapping["port"].(int); ok { + buf.WriteString(fmt.Sprintf("%d-", v)) + } + if v, ok := mPortMapping["protocol"].(string); ok { + buf.WriteString(fmt.Sprintf("%s-", v)) + } + } + return hashcode.String(buf.String()) +} diff --git a/aws/resource_aws_virtual_node_test.go b/aws/resource_aws_virtual_node_test.go new file mode 100644 index 00000000000..388ce5e96b9 --- /dev/null +++ b/aws/resource_aws_virtual_node_test.go @@ -0,0 +1,269 @@ +package aws + +import ( + "fmt" + "regexp" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/appmesh" + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func testAccAwsAppmeshVirtualNode_basic(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_basic(meshName, vnName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAppmeshVirtualNodeExists( + resourceName, &vn), + resource.TestCheckResourceAttr( + resourceName, "name", vnName), + resource.TestCheckResourceAttr( + resourceName, "mesh_name", meshName), + resource.TestCheckResourceAttr( + resourceName, "spec.#", "1"), + resource.TestCheckResourceAttr( + resourceName, "spec.0.backends.#", "0"), + resource.TestCheckResourceAttr( + resourceName, "spec.0.listener.#", "0"), + resource.TestCheckResourceAttr( + resourceName, "spec.0.service_discovery.#", "0"), + resource.TestCheckResourceAttrSet( + resourceName, "created_date"), + resource.TestCheckResourceAttrSet( + resourceName, "last_updated_date"), + resource.TestMatchResourceAttr( + resourceName, "arn", regexp.MustCompile(fmt.Sprintf("^arn:[^:]+:appmesh:[^:]+:\\d{12}:mesh/%s/virtualNode/%s", meshName, vnName))), + ), + }, + }, + }) +} + +func testAccAwsAppmeshVirtualNode_allAttributes(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_allAttributes(meshName, vnName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAppmeshVirtualNodeExists( + resourceName, &vn), + resource.TestCheckResourceAttr( + resourceName, "name", vnName), + resource.TestCheckResourceAttr( + resourceName, "mesh_name", meshName), + resource.TestCheckResourceAttr( + resourceName, "spec.#", "1"), + resource.TestCheckResourceAttr( + resourceName, "spec.0.backends.#", "1"), + resource.TestCheckResourceAttr( + resourceName, "spec.0.backends.1255689679", "servicea.simpleapp.local"), + resource.TestCheckResourceAttr( + resourceName, "spec.0.listener.#", "1"), + resource.TestCheckResourceAttr( + resourceName, "spec.0.listener.2279702354.port_mapping.#", "1"), + resource.TestCheckResourceAttr( + resourceName, "spec.0.listener.2279702354.port_mapping.0.port", "8080"), + resource.TestCheckResourceAttr( + resourceName, "spec.0.listener.2279702354.port_mapping.0.protocol", "http"), + resource.TestCheckResourceAttr( + resourceName, "spec.0.service_discovery.#", "1"), + resource.TestCheckResourceAttr( + resourceName, "spec.0.service_discovery.0.dns.#", "1"), + resource.TestCheckResourceAttr( + resourceName, "spec.0.service_discovery.0.dns.0.service_name", "serviceb.simpleapp.local"), + resource.TestCheckResourceAttrSet( + resourceName, "created_date"), + resource.TestCheckResourceAttrSet( + resourceName, "last_updated_date"), + resource.TestMatchResourceAttr( + resourceName, "arn", regexp.MustCompile(fmt.Sprintf("^arn:[^:]+:appmesh:[^:]+:\\d{12}:mesh/%s/virtualNode/%s", meshName, vnName))), + ), + }, + { + Config: testAccAppmeshVirtualNodeConfig_allAttributesUpdated(meshName, vnName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAppmeshVirtualNodeExists( + resourceName, &vn), + resource.TestCheckResourceAttr( + resourceName, "name", vnName), + resource.TestCheckResourceAttr( + resourceName, "mesh_name", meshName), + resource.TestCheckResourceAttr( + resourceName, "spec.#", "1"), + resource.TestCheckResourceAttr( + resourceName, "spec.0.backends.#", "2"), + resource.TestCheckResourceAttr( + resourceName, "spec.0.backends.2665798920", "servicec.simpleapp.local"), + resource.TestCheckResourceAttr( + resourceName, "spec.0.backends.3195445571", "serviced.simpleapp.local"), + resource.TestCheckResourceAttr( + resourceName, "spec.0.listener.#", "1"), + resource.TestCheckResourceAttr( + resourceName, "spec.0.listener.563508454.port_mapping.#", "1"), + resource.TestCheckResourceAttr( + resourceName, "spec.0.listener.563508454.port_mapping.0.port", "8081"), + resource.TestCheckResourceAttr( + resourceName, "spec.0.listener.563508454.port_mapping.0.protocol", "http"), + resource.TestCheckResourceAttr( + resourceName, "spec.0.service_discovery.#", "1"), + resource.TestCheckResourceAttr( + resourceName, "spec.0.service_discovery.0.dns.#", "1"), + resource.TestCheckResourceAttr( + resourceName, "spec.0.service_discovery.0.dns.0.service_name", "serviceb1.simpleapp.local"), + resource.TestCheckResourceAttrSet( + resourceName, "created_date"), + resource.TestCheckResourceAttrSet( + resourceName, "last_updated_date"), + resource.TestMatchResourceAttr( + resourceName, "arn", regexp.MustCompile(fmt.Sprintf("^arn:[^:]+:appmesh:[^:]+:\\d{12}:mesh/%s/virtualNode/%s", meshName, vnName))), + ), + }, + }, + }) +} + +func testAccCheckAppmeshVirtualNodeDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).appmeshconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_appmesh_virtual_node" { + continue + } + + _, err := conn.DescribeVirtualNode(&appmesh.DescribeVirtualNodeInput{ + MeshName: aws.String(rs.Primary.Attributes["mesh_name"]), + VirtualNodeName: aws.String(rs.Primary.Attributes["name"]), + }) + if err != nil { + if isAWSErr(err, appmesh.ErrCodeNotFoundException, "") { + return nil + } + return err + } + return fmt.Errorf("still exist.") + } + + return nil +} + +func testAccCheckAppmeshVirtualNodeExists(name string, v *appmesh.VirtualNodeData) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).appmeshconn + + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + resp, err := conn.DescribeVirtualNode(&appmesh.DescribeVirtualNodeInput{ + MeshName: aws.String(rs.Primary.Attributes["mesh_name"]), + VirtualNodeName: aws.String(rs.Primary.Attributes["name"]), + }) + if err != nil { + return err + } + + *v = *resp.VirtualNode + + return nil + } +} + +func testAccAppmeshVirtualNodeConfig_basic(meshName, vnName string) string { + return fmt.Sprintf(` +resource "aws_appmesh_mesh" "foo" { + name = "%s" +} + +resource "aws_appmesh_virtual_node" "foo" { + name = "%s" + mesh_name = "${aws_appmesh_mesh.foo.id}" + + spec {} +} +`, meshName, vnName) +} + +func testAccAppmeshVirtualNodeConfig_allAttributes(meshName, vnName string) string { + return fmt.Sprintf(` +resource "aws_appmesh_mesh" "foo" { + name = "%s" +} + +resource "aws_appmesh_virtual_node" "foo" { + name = "%s" + mesh_name = "${aws_appmesh_mesh.foo.id}" + + spec { + backends = ["servicea.simpleapp.local"] + + listener { + port_mapping { + port = 8080 + protocol = "http" + } + } + + service_discovery { + dns { + service_name = "serviceb.simpleapp.local" + } + } + } +} +`, meshName, vnName) +} + +func testAccAppmeshVirtualNodeConfig_allAttributesUpdated(meshName, vnName string) string { + return fmt.Sprintf(` +resource "aws_appmesh_mesh" "foo" { + name = "%s" +} + +resource "aws_appmesh_virtual_node" "foo" { + name = "%s" + mesh_name = "${aws_appmesh_mesh.foo.id}" + + spec { + backends = ["servicec.simpleapp.local", "serviced.simpleapp.local"] + + listener { + port_mapping { + port = 8081 + protocol = "http" + } + } + + service_discovery { + dns { + service_name = "serviceb1.simpleapp.local" + } + } + } +} +`, meshName, vnName) +} diff --git a/aws/structure.go b/aws/structure.go index 2c55712f3d7..0702d989726 100644 --- a/aws/structure.go +++ b/aws/structure.go @@ -880,6 +880,10 @@ func flattenStringList(list []*string) []interface{} { return vs } +func flattenStringSet(list []*string) *schema.Set { + return schema.NewSet(schema.HashString, flattenStringList(list)) +} + //Flattens an array of private ip addresses into a []string, where the elements returned are the IP strings e.g. "192.168.0.0" func flattenNetworkInterfacesPrivateIPAddresses(dtos []*ec2.NetworkInterfacePrivateIpAddress) []string { ips := make([]string, 0, len(dtos)) @@ -4684,33 +4688,134 @@ func flattenRdsScalingConfigurationInfo(scalingConfigurationInfo *rds.ScalingCon return []interface{}{m} } -func expandAppmeshVirtualRouterSpec(v []interface{}) *appmesh.VirtualRouterSpec { - if len(v) == 0 || v[0] == nil { +func expandAppmeshVirtualRouterSpec(vSpec []interface{}) *appmesh.VirtualRouterSpec { + if len(vSpec) == 0 || vSpec[0] == nil { return nil } + mSpec := vSpec[0].(map[string]interface{}) - m := v[0].(map[string]interface{}) spec := &appmesh.VirtualRouterSpec{} - if s, ok := m["service_names"].(*schema.Set); ok && s.Len() > 0 { - serviceNames := []string{} - for _, n := range s.List() { - serviceNames = append(serviceNames, n.(string)) - } - spec.ServiceNames = aws.StringSlice(serviceNames) + if vServiceNames, ok := mSpec["service_names"].(*schema.Set); ok && vServiceNames.Len() > 0 { + spec.ServiceNames = expandStringSet(vServiceNames) } return spec } func flattenAppmeshVirtualRouterSpec(spec *appmesh.VirtualRouterSpec) []interface{} { - if spec == nil || len(spec.ServiceNames) == 0 { + if spec == nil { return []interface{}{} } - m := map[string]interface{}{ - "service_names": schema.NewSet(schema.HashString, flattenStringList(spec.ServiceNames)), + mSpec := map[string]interface{}{ + "service_names": flattenStringSet(spec.ServiceNames), } - return []interface{}{m} + return []interface{}{mSpec} +} + +func expandAppmeshVirtualNodeSpec(vSpec []interface{}) *appmesh.VirtualNodeSpec { + spec := &appmesh.VirtualNodeSpec{} + + if len(vSpec) == 0 || vSpec[0] == nil { + // Empty Spec is allowed. + return spec + } + mSpec := vSpec[0].(map[string]interface{}) + + if vBackends, ok := mSpec["backends"].(*schema.Set); ok && vBackends.Len() > 0 { + spec.Backends = expandStringSet(vBackends) + } + + if vListeners, ok := mSpec["listener"].(*schema.Set); ok && vListeners.Len() > 0 { + listeners := []*appmesh.Listener{} + + for _, vListener := range vListeners.List() { + listener := &appmesh.Listener{} + + mListener := vListener.(map[string]interface{}) + + if vPortMapping, ok := mListener["port_mapping"].([]interface{}); ok && len(vPortMapping) > 0 && vPortMapping[0] != nil { + mPortMapping := vPortMapping[0].(map[string]interface{}) + + listener.PortMapping = &appmesh.PortMapping{} + + if vPort, ok := mPortMapping["port"].(int); ok && vPort > 0 { + listener.PortMapping.Port = aws.Int64(int64(vPort)) + } + if vProtocol, ok := mPortMapping["protocol"].(string); ok && vProtocol != "" { + listener.PortMapping.Protocol = aws.String(vProtocol) + } + } + + listeners = append(listeners, listener) + } + + spec.Listeners = listeners + } + + if vServiceDiscovery, ok := mSpec["service_discovery"].([]interface{}); ok && len(vServiceDiscovery) > 0 && vServiceDiscovery[0] != nil { + mServiceDiscovery := vServiceDiscovery[0].(map[string]interface{}) + + if vDns, ok := mServiceDiscovery["dns"].([]interface{}); ok && len(vDns) > 0 && vDns[0] != nil { + mDns := vDns[0].(map[string]interface{}) + + if vServiceName, ok := mDns["service_name"].(string); ok && vServiceName != "" { + spec.ServiceDiscovery = &appmesh.ServiceDiscovery{ + Dns: &appmesh.DnsServiceDiscovery{ + ServiceName: aws.String(vServiceName), + }, + } + } + } + } + + return spec +} + +func flattenAppmeshVirtualNodeSpec(spec *appmesh.VirtualNodeSpec) []interface{} { + if spec == nil { + return []interface{}{} + } + + mSpec := map[string]interface{}{} + + if spec.Backends != nil { + mSpec["backends"] = flattenStringSet(spec.Backends) + } + + if spec.Listeners != nil { + vListeners := []interface{}{} + + for _, listener := range spec.Listeners { + mListener := map[string]interface{}{} + + if listener.PortMapping != nil { + mPortMapping := map[string]interface{}{ + "port": int(aws.Int64Value(listener.PortMapping.Port)), + "protocol": aws.StringValue(listener.PortMapping.Protocol), + } + mListener["port_mapping"] = []interface{}{mPortMapping} + } + + vListeners = append(vListeners, mListener) + } + + mSpec["listener"] = schema.NewSet(appmeshVirtualNodeListenerHash, vListeners) + } + + if spec.ServiceDiscovery != nil && spec.ServiceDiscovery.Dns != nil { + mSpec["service_discovery"] = []interface{}{ + map[string]interface{}{ + "dns": []interface{}{ + map[string]interface{}{ + "service_name": aws.StringValue(spec.ServiceDiscovery.Dns.ServiceName), + }, + }, + }, + } + } + + return []interface{}{mSpec} } diff --git a/website/aws.erb b/website/aws.erb index 62c01613440..813269f1701 100644 --- a/website/aws.erb +++ b/website/aws.erb @@ -521,6 +521,9 @@ > aws_appmesh_mesh + > + aws_appmesh_virtual_node + > aws_appmesh_virtual_router diff --git a/website/docs/r/appmesh_virtual_node.html.markdown b/website/docs/r/appmesh_virtual_node.html.markdown new file mode 100644 index 00000000000..c2db99263af --- /dev/null +++ b/website/docs/r/appmesh_virtual_node.html.markdown @@ -0,0 +1,77 @@ +--- +layout: "aws" +page_title: "AWS: aws_appmesh_virtual_node" +sidebar_current: "docs-aws-resource-appmesh-virtual-node" +description: |- + Provides an AWS App Mesh virtual node resource. +--- + +# aws_appmesh_route + +Provides an AWS App Mesh virtual node resource. + +## Example Usage + +```hcl +resource "aws_appmesh_virtual_node" "serviceb1" { + name = "serviceBv1" + mesh_name = "simpleapp" + + spec { + backends = ["servicea.simpleapp.local"] + + listener { + port_mapping { + port = 8080 + protocol = "http" + } + } + + service_discovery { + dns { + service_name = "serviceb.simpleapp.local" + } + } + } +} +``` + +## Argument Reference + +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. + +The `spec` object supports the following: + +* `backends` - (Optional) The backends to which the virtual node is expected to send outbound traffic. +* `listener` - (Optional) The listeners from which the virtual node is expected to receive inbound traffic. +* `service_discovery`- (Optional) The service discovery information for the virtual node. + +The `listener` object supports the following: + +* `port_mapping` - (Required) The port mapping information for the listener. + +The `service_discovery` object supports the following: + +* `dns` - (Required) Specifies the DNS service name for the virtual node. + +The `dns` object supports the following: + +* `service_name` - (Required) The DNS service name for your virtual node. + +The `port_mapping` object supports the following: + +* `port` - (Required) The port used for the port mapping. +* `protocol` - (Required) The protocol used for the port mapping. Valid values are `http` and `tcp`. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The ID of the virtual node. +* `arn` - The ARN of the virtual node. +* `created_date` - The creation date of the virtual node. +* `last_updated_date` - The last update date of the virtual node.