diff --git a/aws/diff_suppress_funcs.go b/aws/diff_suppress_funcs.go index 0e340e4351e..b1bb9f28e71 100644 --- a/aws/diff_suppress_funcs.go +++ b/aws/diff_suppress_funcs.go @@ -132,3 +132,10 @@ func suppressRoute53ZoneNameWithTrailingDot(k, old, new string, d *schema.Resour } return strings.TrimSuffix(old, ".") == strings.TrimSuffix(new, ".") } + +/* +// suppressStringsEqualFold suppresses any difference between two strings that are equal under Unicode case-folding. +func suppressStringsEqualFold(k, old, new string, d *schema.ResourceData) bool { + return strings.EqualFold(old, new) +} +*/ diff --git a/aws/resource_aws_appmesh_mesh.go b/aws/resource_aws_appmesh_mesh.go index 7ab40e45c40..2fe5c7e497e 100644 --- a/aws/resource_aws_appmesh_mesh.go +++ b/aws/resource_aws_appmesh_mesh.go @@ -189,3 +189,43 @@ func resourceAwsAppmeshMeshDelete(d *schema.ResourceData, meta interface{}) erro return nil } + +func expandAppmeshMeshSpec(vSpec []interface{}) *appmesh.MeshSpec { + spec := &appmesh.MeshSpec{} + + if len(vSpec) == 0 || vSpec[0] == nil { + // Empty Spec is allowed. + return spec + } + mSpec := vSpec[0].(map[string]interface{}) + + if vEgressFilter, ok := mSpec["egress_filter"].([]interface{}); ok && len(vEgressFilter) > 0 && vEgressFilter[0] != nil { + mEgressFilter := vEgressFilter[0].(map[string]interface{}) + + if vType, ok := mEgressFilter["type"].(string); ok && vType != "" { + spec.EgressFilter = &appmesh.EgressFilter{ + Type: aws.String(vType), + } + } + } + + return spec +} + +func flattenAppmeshMeshSpec(spec *appmesh.MeshSpec) []interface{} { + if spec == nil { + return []interface{}{} + } + + mSpec := map[string]interface{}{} + + if spec.EgressFilter != nil { + mSpec["egress_filter"] = []interface{}{ + map[string]interface{}{ + "type": aws.StringValue(spec.EgressFilter.Type), + }, + } + } + + return []interface{}{mSpec} +} diff --git a/aws/resource_aws_appmesh_route.go b/aws/resource_aws_appmesh_route.go index 128f1c10040..bb16df365c8 100644 --- a/aws/resource_aws_appmesh_route.go +++ b/aws/resource_aws_appmesh_route.go @@ -26,11 +26,19 @@ func resourceAwsAppmeshRoute() *schema.Resource { }, Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringLenBetween(1, 255), + "arn": { + Type: schema.TypeString, + Computed: true, + }, + + "created_date": { + Type: schema.TypeString, + Computed: true, + }, + + "last_updated_date": { + Type: schema.TypeString, + Computed: true, }, "mesh_name": { @@ -40,7 +48,7 @@ func resourceAwsAppmeshRoute() *schema.Resource { ValidateFunc: validation.StringLenBetween(1, 255), }, - "virtual_router_name": { + "name": { Type: schema.TypeString, Required: true, ForceNew: true, @@ -89,7 +97,7 @@ func resourceAwsAppmeshRoute() *schema.Resource { }, }, }, - Set: appmeshRouteWeightedTargetHash, + Set: appmeshWeightedTargetHash, }, }, }, @@ -102,11 +110,113 @@ func resourceAwsAppmeshRoute() *schema.Resource { MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ + "header": { + Type: schema.TypeSet, + Optional: true, + MinItems: 0, + MaxItems: 10, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "invert": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + + "match": { + Type: schema.TypeList, + Optional: true, + MinItems: 0, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "exact": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(1, 255), + }, + + "prefix": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(1, 255), + }, + + "range": { + Type: schema.TypeList, + Optional: true, + MinItems: 0, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "end": { + Type: schema.TypeInt, + Required: true, + }, + + "start": { + Type: schema.TypeInt, + Required: true, + }, + }, + }, + }, + + "regex": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(1, 255), + }, + + "suffix": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(1, 255), + }, + }, + }, + }, + + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 50), + }, + }, + }, + Set: appmeshHttpRouteHeaderHash, + }, + + "method": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + appmesh.HttpMethodConnect, + appmesh.HttpMethodDelete, + appmesh.HttpMethodGet, + appmesh.HttpMethodHead, + appmesh.HttpMethodOptions, + appmesh.HttpMethodPatch, + appmesh.HttpMethodPost, + appmesh.HttpMethodPut, + appmesh.HttpMethodTrace, + }, false), + }, + "prefix": { Type: schema.TypeString, Required: true, ValidateFunc: validation.StringMatch(regexp.MustCompile(`^/`), "must start with /"), }, + + "scheme": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + appmesh.HttpSchemeHttp, + appmesh.HttpSchemeHttps, + }, false), + }, }, }, }, @@ -114,6 +224,12 @@ func resourceAwsAppmeshRoute() *schema.Resource { }, }, + "priority": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(0, 1000), + }, + "tcp_route": { Type: schema.TypeList, Optional: true, @@ -149,7 +265,7 @@ func resourceAwsAppmeshRoute() *schema.Resource { }, }, }, - Set: appmeshRouteWeightedTargetHash, + Set: appmeshWeightedTargetHash, }, }, }, @@ -161,22 +277,14 @@ func resourceAwsAppmeshRoute() *schema.Resource { }, }, - "arn": { - Type: schema.TypeString, - Computed: true, - }, - - "created_date": { - Type: schema.TypeString, - Computed: true, - }, + "tags": tagsSchema(), - "last_updated_date": { - Type: schema.TypeString, - Computed: true, + "virtual_router_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(1, 255), }, - - "tags": tagsSchema(), }, } } @@ -187,12 +295,12 @@ func resourceAwsAppmeshRouteCreate(d *schema.ResourceData, meta interface{}) err req := &appmesh.CreateRouteInput{ MeshName: aws.String(d.Get("mesh_name").(string)), RouteName: aws.String(d.Get("name").(string)), - VirtualRouterName: aws.String(d.Get("virtual_router_name").(string)), Spec: expandAppmeshRouteSpec(d.Get("spec").([]interface{})), Tags: tagsFromMapAppmesh(d.Get("tags").(map[string]interface{})), + VirtualRouterName: aws.String(d.Get("virtual_router_name").(string)), } - log.Printf("[DEBUG] Creating App Mesh route: %#v", req) + log.Printf("[DEBUG] Creating App Mesh route: %s", req) resp, err := conn.CreateRoute(req) if err != nil { return fmt.Errorf("error creating App Mesh route: %s", err) @@ -217,7 +325,7 @@ func resourceAwsAppmeshRouteRead(d *schema.ResourceData, meta interface{}) error return nil } if err != nil { - return fmt.Errorf("error reading App Mesh route: %s", err) + return fmt.Errorf("error reading App Mesh route (%s): %s", d.Id(), err) } if aws.StringValue(resp.Route.Status.Status) == appmesh.RouteStatusCodeDeleted { log.Printf("[WARN] App Mesh route (%s) not found, removing from state", d.Id()) @@ -225,16 +333,16 @@ func resourceAwsAppmeshRouteRead(d *schema.ResourceData, meta interface{}) error return nil } - d.Set("name", resp.Route.RouteName) - d.Set("mesh_name", resp.Route.MeshName) - d.Set("virtual_router_name", resp.Route.VirtualRouterName) d.Set("arn", resp.Route.Metadata.Arn) d.Set("created_date", resp.Route.Metadata.CreatedAt.Format(time.RFC3339)) d.Set("last_updated_date", resp.Route.Metadata.LastUpdatedAt.Format(time.RFC3339)) + d.Set("mesh_name", resp.Route.MeshName) + d.Set("name", resp.Route.RouteName) err = d.Set("spec", flattenAppmeshRouteSpec(resp.Route.Spec)) if err != nil { return fmt.Errorf("error setting spec: %s", err) } + d.Set("virtual_router_name", resp.Route.VirtualRouterName) err = saveTagsAppmesh(conn, d, aws.StringValue(resp.Route.Metadata.Arn)) if isAWSErr(err, appmesh.ErrCodeNotFoundException, "") { @@ -257,14 +365,14 @@ func resourceAwsAppmeshRouteUpdate(d *schema.ResourceData, meta interface{}) err req := &appmesh.UpdateRouteInput{ MeshName: aws.String(d.Get("mesh_name").(string)), RouteName: aws.String(d.Get("name").(string)), - VirtualRouterName: aws.String(d.Get("virtual_router_name").(string)), Spec: expandAppmeshRouteSpec(v.([]interface{})), + VirtualRouterName: aws.String(d.Get("virtual_router_name").(string)), } - log.Printf("[DEBUG] Updating App Mesh route: %#v", req) + log.Printf("[DEBUG] Updating App Mesh route: %s", req) _, err := conn.UpdateRoute(req) if err != nil { - return fmt.Errorf("error updating App Mesh route: %s", err) + return fmt.Errorf("error updating App Mesh route (%s): %s", d.Id(), err) } } @@ -294,7 +402,7 @@ func resourceAwsAppmeshRouteDelete(d *schema.ResourceData, meta interface{}) err return nil } if err != nil { - return fmt.Errorf("error deleting App Mesh route: %s", err) + return fmt.Errorf("error deleting App Mesh route (%s): %s", d.Id(), err) } return nil @@ -323,14 +431,311 @@ func resourceAwsAppmeshRouteImport(d *schema.ResourceData, meta interface{}) ([] } d.SetId(aws.StringValue(resp.Route.Metadata.Uid)) - d.Set("name", resp.Route.RouteName) d.Set("mesh_name", resp.Route.MeshName) + d.Set("name", resp.Route.RouteName) d.Set("virtual_router_name", resp.Route.VirtualRouterName) return []*schema.ResourceData{d}, nil } -func appmeshRouteWeightedTargetHash(v interface{}) int { +func expandAppmeshRouteSpec(vSpec []interface{}) *appmesh.RouteSpec { + spec := &appmesh.RouteSpec{} + + if len(vSpec) == 0 || vSpec[0] == nil { + // Empty Spec is allowed. + return spec + } + mSpec := vSpec[0].(map[string]interface{}) + + if vPriority, ok := mSpec["priority"].(int); ok && vPriority > 0 { + spec.Priority = aws.Int64(int64(vPriority)) + } + + if vHttpRoute, ok := mSpec["http_route"].([]interface{}); ok && len(vHttpRoute) > 0 && vHttpRoute[0] != nil { + mHttpRoute := vHttpRoute[0].(map[string]interface{}) + + spec.HttpRoute = &appmesh.HttpRoute{} + + if vHttpRouteAction, ok := mHttpRoute["action"].([]interface{}); ok && len(vHttpRouteAction) > 0 && vHttpRouteAction[0] != nil { + mHttpRouteAction := vHttpRouteAction[0].(map[string]interface{}) + + if vWeightedTargets, ok := mHttpRouteAction["weighted_target"].(*schema.Set); ok && vWeightedTargets.Len() > 0 { + weightedTargets := []*appmesh.WeightedTarget{} + + for _, vWeightedTarget := range vWeightedTargets.List() { + weightedTarget := &appmesh.WeightedTarget{} + + mWeightedTarget := vWeightedTarget.(map[string]interface{}) + + if vVirtualNode, ok := mWeightedTarget["virtual_node"].(string); ok && vVirtualNode != "" { + weightedTarget.VirtualNode = aws.String(vVirtualNode) + } + if vWeight, ok := mWeightedTarget["weight"].(int); ok && vWeight > 0 { + weightedTarget.Weight = aws.Int64(int64(vWeight)) + } + + weightedTargets = append(weightedTargets, weightedTarget) + } + + spec.HttpRoute.Action = &appmesh.HttpRouteAction{ + WeightedTargets: weightedTargets, + } + } + } + + if vHttpRouteMatch, ok := mHttpRoute["match"].([]interface{}); ok && len(vHttpRouteMatch) > 0 && vHttpRouteMatch[0] != nil { + httpRouteMatch := &appmesh.HttpRouteMatch{} + + mHttpRouteMatch := vHttpRouteMatch[0].(map[string]interface{}) + + if vMethod, ok := mHttpRouteMatch["method"].(string); ok && vMethod != "" { + httpRouteMatch.Method = aws.String(vMethod) + } + if vPrefix, ok := mHttpRouteMatch["prefix"].(string); ok && vPrefix != "" { + httpRouteMatch.Prefix = aws.String(vPrefix) + } + if vScheme, ok := mHttpRouteMatch["scheme"].(string); ok && vScheme != "" { + httpRouteMatch.Scheme = aws.String(vScheme) + } + + if vHttpRouteHeaders, ok := mHttpRouteMatch["header"].(*schema.Set); ok && vHttpRouteHeaders.Len() > 0 { + httpRouteHeaders := []*appmesh.HttpRouteHeader{} + + for _, vHttpRouteHeader := range vHttpRouteHeaders.List() { + httpRouteHeader := &appmesh.HttpRouteHeader{} + + mHttpRouteHeader := vHttpRouteHeader.(map[string]interface{}) + + if vInvert, ok := mHttpRouteHeader["invert"].(bool); ok { + httpRouteHeader.Invert = aws.Bool(vInvert) + } + if vName, ok := mHttpRouteHeader["name"].(string); ok && vName != "" { + httpRouteHeader.Name = aws.String(vName) + } + + if vMatch, ok := mHttpRouteHeader["match"].([]interface{}); ok && len(vMatch) > 0 && vMatch[0] != nil { + httpRouteHeader.Match = &appmesh.HeaderMatchMethod{} + + mMatch := vMatch[0].(map[string]interface{}) + + if vExact, ok := mMatch["exact"].(string); ok && vExact != "" { + httpRouteHeader.Match.Exact = aws.String(vExact) + } + if vPrefix, ok := mMatch["prefix"].(string); ok && vPrefix != "" { + httpRouteHeader.Match.Prefix = aws.String(vPrefix) + } + if vRegex, ok := mMatch["regex"].(string); ok && vRegex != "" { + httpRouteHeader.Match.Regex = aws.String(vRegex) + } + if vSuffix, ok := mMatch["suffix"].(string); ok && vSuffix != "" { + httpRouteHeader.Match.Suffix = aws.String(vSuffix) + } + + if vRange, ok := mMatch["range"].([]interface{}); ok && len(vRange) > 0 && vRange[0] != nil { + httpRouteHeader.Match.Range = &appmesh.MatchRange{} + + mRange := vRange[0].(map[string]interface{}) + + if vEnd, ok := mRange["end"].(int); ok && vEnd > 0 { + httpRouteHeader.Match.Range.End = aws.Int64(int64(vEnd)) + } + if vStart, ok := mRange["start"].(int); ok && vStart > 0 { + httpRouteHeader.Match.Range.Start = aws.Int64(int64(vStart)) + } + } + } + + httpRouteHeaders = append(httpRouteHeaders, httpRouteHeader) + } + + httpRouteMatch.Headers = httpRouteHeaders + } + + spec.HttpRoute.Match = httpRouteMatch + } + } + + if vTcpRoute, ok := mSpec["tcp_route"].([]interface{}); ok && len(vTcpRoute) > 0 && vTcpRoute[0] != nil { + mTcpRoute := vTcpRoute[0].(map[string]interface{}) + + spec.TcpRoute = &appmesh.TcpRoute{} + + if vTcpRouteAction, ok := mTcpRoute["action"].([]interface{}); ok && len(vTcpRouteAction) > 0 && vTcpRouteAction[0] != nil { + mTcpRouteAction := vTcpRouteAction[0].(map[string]interface{}) + + if vWeightedTargets, ok := mTcpRouteAction["weighted_target"].(*schema.Set); ok && vWeightedTargets.Len() > 0 { + weightedTargets := []*appmesh.WeightedTarget{} + + for _, vWeightedTarget := range vWeightedTargets.List() { + weightedTarget := &appmesh.WeightedTarget{} + + mWeightedTarget := vWeightedTarget.(map[string]interface{}) + + if vVirtualNode, ok := mWeightedTarget["virtual_node"].(string); ok && vVirtualNode != "" { + weightedTarget.VirtualNode = aws.String(vVirtualNode) + } + if vWeight, ok := mWeightedTarget["weight"].(int); ok && vWeight > 0 { + weightedTarget.Weight = aws.Int64(int64(vWeight)) + } + + weightedTargets = append(weightedTargets, weightedTarget) + } + + spec.TcpRoute.Action = &appmesh.TcpRouteAction{ + WeightedTargets: weightedTargets, + } + } + } + } + + return spec +} + +func flattenAppmeshRouteSpec(spec *appmesh.RouteSpec) []interface{} { + if spec == nil { + return []interface{}{} + } + + mSpec := map[string]interface{}{ + "priority": int(aws.Int64Value(spec.Priority)), + } + + if httpRoute := spec.HttpRoute; httpRoute != nil { + mHttpRoute := map[string]interface{}{} + + if action := httpRoute.Action; action != nil { + if weightedTargets := action.WeightedTargets; weightedTargets != nil { + vWeightedTargets := []interface{}{} + + for _, weightedTarget := range weightedTargets { + mWeightedTarget := map[string]interface{}{ + "virtual_node": aws.StringValue(weightedTarget.VirtualNode), + "weight": int(aws.Int64Value(weightedTarget.Weight)), + } + + vWeightedTargets = append(vWeightedTargets, mWeightedTarget) + } + + mHttpRoute["action"] = []interface{}{ + map[string]interface{}{ + "weighted_target": schema.NewSet(appmeshWeightedTargetHash, vWeightedTargets), + }, + } + } + } + + if httpRouteMatch := httpRoute.Match; httpRouteMatch != nil { + vHttpRouteHeaders := []interface{}{} + + for _, httpRouteHeader := range httpRouteMatch.Headers { + mHttpRouteHeader := map[string]interface{}{ + "invert": aws.BoolValue(httpRouteHeader.Invert), + "name": aws.StringValue(httpRouteHeader.Name), + } + + if match := httpRouteHeader.Match; match != nil { + mMatch := map[string]interface{}{ + "exact": aws.StringValue(match.Exact), + "prefix": aws.StringValue(match.Prefix), + "regex": aws.StringValue(match.Regex), + "suffix": aws.StringValue(match.Suffix), + } + + if r := match.Range; r != nil { + mRange := map[string]interface{}{ + "end": int(aws.Int64Value(r.End)), + "start": int(aws.Int64Value(r.Start)), + } + + mMatch["range"] = []interface{}{mRange} + } + + mHttpRouteHeader["match"] = []interface{}{mMatch} + } + + vHttpRouteHeaders = append(vHttpRouteHeaders, mHttpRouteHeader) + } + + mHttpRoute["match"] = []interface{}{ + map[string]interface{}{ + "header": schema.NewSet(appmeshHttpRouteHeaderHash, vHttpRouteHeaders), + "method": aws.StringValue(httpRouteMatch.Method), + "prefix": aws.StringValue(httpRouteMatch.Prefix), + "scheme": aws.StringValue(httpRouteMatch.Scheme), + }, + } + } + + mSpec["http_route"] = []interface{}{mHttpRoute} + } + + if tcpRoute := spec.TcpRoute; tcpRoute != nil { + mTcpRoute := map[string]interface{}{} + + if action := tcpRoute.Action; action != nil { + if weightedTargets := action.WeightedTargets; weightedTargets != nil { + vWeightedTargets := []interface{}{} + + for _, weightedTarget := range weightedTargets { + mWeightedTarget := map[string]interface{}{ + "virtual_node": aws.StringValue(weightedTarget.VirtualNode), + "weight": int(aws.Int64Value(weightedTarget.Weight)), + } + + vWeightedTargets = append(vWeightedTargets, mWeightedTarget) + } + + mTcpRoute["action"] = []interface{}{ + map[string]interface{}{ + "weighted_target": schema.NewSet(appmeshWeightedTargetHash, vWeightedTargets), + }, + } + } + } + + mSpec["tcp_route"] = []interface{}{mTcpRoute} + } + + return []interface{}{mSpec} +} + +func appmeshHttpRouteHeaderHash(vHttpRouteHeader interface{}) int { + var buf bytes.Buffer + mHttpRouteHeader := vHttpRouteHeader.(map[string]interface{}) + if v, ok := mHttpRouteHeader["invert"].(bool); ok { + buf.WriteString(fmt.Sprintf("%t-", v)) + } + if vMatch, ok := mHttpRouteHeader["match"].([]interface{}); ok && len(vMatch) > 0 && vMatch[0] != nil { + mMatch := vMatch[0].(map[string]interface{}) + if v, ok := mMatch["exact"].(string); ok { + buf.WriteString(fmt.Sprintf("%s-", v)) + } + if v, ok := mMatch["prefix"].(string); ok { + buf.WriteString(fmt.Sprintf("%s-", v)) + } + if vRange, ok := mMatch["range"].([]interface{}); ok && len(vRange) > 0 && vRange[0] != nil { + mRange := vRange[0].(map[string]interface{}) + if v, ok := mRange["end"].(int); ok { + buf.WriteString(fmt.Sprintf("%d-", v)) + } + if v, ok := mRange["start"].(int); ok { + buf.WriteString(fmt.Sprintf("%d-", v)) + } + } + if v, ok := mMatch["regex"].(string); ok { + buf.WriteString(fmt.Sprintf("%s-", v)) + } + if v, ok := mMatch["suffix"].(string); ok { + buf.WriteString(fmt.Sprintf("%s-", v)) + } + } + if v, ok := mHttpRouteHeader["name"].(string); ok { + buf.WriteString(fmt.Sprintf("%s-", v)) + } + return hashcode.String(buf.String()) +} + +func appmeshWeightedTargetHash(v interface{}) int { var buf bytes.Buffer m := v.(map[string]interface{}) if v, ok := m["virtual_node"].(string); ok { diff --git a/aws/resource_aws_appmesh_route_test.go b/aws/resource_aws_appmesh_route_test.go index d0019d29f6c..5e7ba1004ba 100644 --- a/aws/resource_aws_appmesh_route_test.go +++ b/aws/resource_aws_appmesh_route_test.go @@ -3,7 +3,6 @@ package aws import ( "fmt" "log" - "regexp" "testing" "github.com/aws/aws-sdk-go/aws" @@ -16,11 +15,11 @@ import ( func init() { resource.AddTestSweepers("aws_appmesh_route", &resource.Sweeper{ Name: "aws_appmesh_route", - F: testSweepAppmeshRoutes, + F: testSweepAwsAppmeshRoutes, }) } -func testSweepAppmeshRoutes(region string) error { +func testSweepAwsAppmeshRoutes(region string) error { client, err := sharedClientForRegion(region) if err != nil { return fmt.Errorf("error getting client: %s", err) @@ -102,57 +101,149 @@ func testSweepAppmeshRoutes(region string) error { func testAccAwsAppmeshRoute_httpRoute(t *testing.T) { var r appmesh.RouteData - resourceName := "aws_appmesh_route.foo" - meshName := fmt.Sprintf("tf-test-mesh-%d", acctest.RandInt()) - vrName := fmt.Sprintf("tf-test-router-%d", acctest.RandInt()) - vn1Name := fmt.Sprintf("tf-test-node-%d", acctest.RandInt()) - vn2Name := fmt.Sprintf("tf-test-node-%d", acctest.RandInt()) - rName := fmt.Sprintf("tf-test-route-%d", acctest.RandInt()) + resourceName := "aws_appmesh_route.test" + rName := fmt.Sprintf("tf-testacc-appmesh-%s", acctest.RandStringFromCharSet(13, acctest.CharSetAlphaNum)) resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, - CheckDestroy: testAccCheckAppmeshRouteDestroy, + CheckDestroy: testAccCheckAwsAppmeshRouteDestroy, Steps: []resource.TestStep{ { - Config: testAccAppmeshRouteConfig_httpRoute(meshName, vrName, vn1Name, vn2Name, rName), + Config: testAccAwsAppmeshRouteConfig_httpRoute(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAppmeshRouteExists(resourceName, &r), + testAccCheckAwsAppmeshRouteExists(resourceName, &r), + testAccCheckResourceAttrRegionalARN(resourceName, "arn", "appmesh", fmt.Sprintf("mesh/%s/virtualRouter/%s/route/%s", rName, rName, rName)), + resource.TestCheckResourceAttrSet(resourceName, "created_date"), + resource.TestCheckResourceAttrSet(resourceName, "last_updated_date"), + resource.TestCheckResourceAttr(resourceName, "mesh_name", rName), resource.TestCheckResourceAttr(resourceName, "name", rName), - resource.TestCheckResourceAttr(resourceName, "mesh_name", meshName), - resource.TestCheckResourceAttr(resourceName, "virtual_router_name", vrName), resource.TestCheckResourceAttr(resourceName, "spec.#", "1"), resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.#", "1"), resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.action.#", "1"), resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.action.0.weighted_target.#", "1"), resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.header.#", "0"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.method", ""), resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.prefix", "/"), - 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/virtualRouter/%s/route/%s", meshName, vrName, rName))), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.scheme", ""), + resource.TestCheckResourceAttr(resourceName, "spec.0.priority", "0"), + resource.TestCheckResourceAttr(resourceName, "spec.0.tcp_route.#", "0"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "virtual_router_name", rName), ), }, { - Config: testAccAppmeshRouteConfig_httpRouteUpdated(meshName, vrName, vn1Name, vn2Name, rName), + Config: testAccAwsAppmeshRouteConfig_httpRouteUpdated(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAppmeshRouteExists(resourceName, &r), + testAccCheckAwsAppmeshRouteExists(resourceName, &r), + testAccCheckResourceAttrRegionalARN(resourceName, "arn", "appmesh", fmt.Sprintf("mesh/%s/virtualRouter/%s/route/%s", rName, rName, rName)), + resource.TestCheckResourceAttrSet(resourceName, "created_date"), + resource.TestCheckResourceAttrSet(resourceName, "last_updated_date"), + resource.TestCheckResourceAttr(resourceName, "mesh_name", rName), resource.TestCheckResourceAttr(resourceName, "name", rName), - resource.TestCheckResourceAttr(resourceName, "mesh_name", meshName), - resource.TestCheckResourceAttr(resourceName, "virtual_router_name", vrName), resource.TestCheckResourceAttr(resourceName, "spec.#", "1"), resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.#", "1"), resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.action.#", "1"), resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.action.0.weighted_target.#", "2"), resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.header.#", "0"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.method", ""), resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.prefix", "/path"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.scheme", ""), + resource.TestCheckResourceAttr(resourceName, "spec.0.priority", "0"), + resource.TestCheckResourceAttr(resourceName, "spec.0.tcp_route.#", "0"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "virtual_router_name", rName), + ), + }, + { + ResourceName: resourceName, + ImportStateIdFunc: testAccAwsAppmeshRouteImportStateIdFunc(resourceName), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAwsAppmeshRoute_httpHeader(t *testing.T) { + var r appmesh.RouteData + resourceName := "aws_appmesh_route.test" + rName := fmt.Sprintf("tf-testacc-appmesh-%s", acctest.RandStringFromCharSet(13, acctest.CharSetAlphaNum)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsAppmeshRouteDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsAppmeshRouteConfig_httpHeader(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsAppmeshRouteExists(resourceName, &r), + testAccCheckResourceAttrRegionalARN(resourceName, "arn", "appmesh", fmt.Sprintf("mesh/%s/virtualRouter/%s/route/%s", rName, rName, rName)), 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/virtualRouter/%s/route/%s", meshName, vrName, rName))), + resource.TestCheckResourceAttr(resourceName, "mesh_name", rName), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "spec.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.action.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.action.0.weighted_target.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.header.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.header.2366200004.invert", "false"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.header.2366200004.match.#", "0"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.header.2366200004.name", "X-Testing1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.method", "POST"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.prefix", "/"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.scheme", "http"), + resource.TestCheckResourceAttr(resourceName, "spec.0.priority", "0"), + resource.TestCheckResourceAttr(resourceName, "spec.0.tcp_route.#", "0"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "virtual_router_name", rName), + ), + }, + { + Config: testAccAwsAppmeshRouteConfig_httpHeaderUpdated(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsAppmeshRouteExists(resourceName, &r), + testAccCheckResourceAttrRegionalARN(resourceName, "arn", "appmesh", fmt.Sprintf("mesh/%s/virtualRouter/%s/route/%s", rName, rName, rName)), + resource.TestCheckResourceAttrSet(resourceName, "created_date"), + resource.TestCheckResourceAttrSet(resourceName, "last_updated_date"), + resource.TestCheckResourceAttr(resourceName, "mesh_name", rName), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "spec.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.action.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.action.0.weighted_target.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.header.#", "2"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.header.2971741486.invert", "true"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.header.2971741486.match.#", "0"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.header.2971741486.name", "X-Testing1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.header.3147536248.invert", "false"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.header.3147536248.match.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.header.3147536248.match.0.exact", ""), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.header.3147536248.match.0.prefix", ""), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.header.3147536248.match.0.range.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.header.3147536248.match.0.range.0.end", "7"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.header.3147536248.match.0.range.0.start", "2"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.header.3147536248.match.0.regex", ""), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.header.3147536248.match.0.suffix", ""), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.header.3147536248.name", "X-Testing2"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.method", "PUT"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.prefix", "/path"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.scheme", "https"), + resource.TestCheckResourceAttr(resourceName, "spec.0.priority", "0"), + resource.TestCheckResourceAttr(resourceName, "spec.0.tcp_route.#", "0"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "virtual_router_name", rName), ), }, { ResourceName: resourceName, - ImportStateId: fmt.Sprintf("%s/%s/%s", meshName, vrName, rName), + ImportStateIdFunc: testAccAwsAppmeshRouteImportStateIdFunc(resourceName), ImportState: true, ImportStateVerify: true, }, @@ -162,53 +253,55 @@ func testAccAwsAppmeshRoute_httpRoute(t *testing.T) { func testAccAwsAppmeshRoute_tcpRoute(t *testing.T) { var r appmesh.RouteData - resourceName := "aws_appmesh_route.foo" - meshName := fmt.Sprintf("tf-test-mesh-%d", acctest.RandInt()) - vrName := fmt.Sprintf("tf-test-router-%d", acctest.RandInt()) - vn1Name := fmt.Sprintf("tf-test-node-%d", acctest.RandInt()) - vn2Name := fmt.Sprintf("tf-test-node-%d", acctest.RandInt()) - rName := fmt.Sprintf("tf-test-route-%d", acctest.RandInt()) + resourceName := "aws_appmesh_route.test" + rName := fmt.Sprintf("tf-testacc-appmesh-%s", acctest.RandStringFromCharSet(13, acctest.CharSetAlphaNum)) resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, - CheckDestroy: testAccCheckAppmeshRouteDestroy, + CheckDestroy: testAccCheckAwsAppmeshRouteDestroy, Steps: []resource.TestStep{ { - Config: testAccAppmeshRouteConfig_tcpRoute(meshName, vrName, vn1Name, vn2Name, rName), + Config: testAccAwsAppmeshRouteConfig_tcpRoute(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAppmeshRouteExists(resourceName, &r), + testAccCheckAwsAppmeshRouteExists(resourceName, &r), + testAccCheckResourceAttrRegionalARN(resourceName, "arn", "appmesh", fmt.Sprintf("mesh/%s/virtualRouter/%s/route/%s", rName, rName, rName)), + resource.TestCheckResourceAttrSet(resourceName, "created_date"), + resource.TestCheckResourceAttrSet(resourceName, "last_updated_date"), + resource.TestCheckResourceAttr(resourceName, "mesh_name", rName), resource.TestCheckResourceAttr(resourceName, "name", rName), - resource.TestCheckResourceAttr(resourceName, "mesh_name", meshName), - resource.TestCheckResourceAttr(resourceName, "virtual_router_name", vrName), resource.TestCheckResourceAttr(resourceName, "spec.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.#", "0"), + resource.TestCheckResourceAttr(resourceName, "spec.0.priority", "0"), resource.TestCheckResourceAttr(resourceName, "spec.0.tcp_route.#", "1"), resource.TestCheckResourceAttr(resourceName, "spec.0.tcp_route.0.action.#", "1"), resource.TestCheckResourceAttr(resourceName, "spec.0.tcp_route.0.action.0.weighted_target.#", "1"), - 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/virtualRouter/%s/route/%s", meshName, vrName, rName))), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "virtual_router_name", rName), ), }, { - Config: testAccAppmeshRouteConfig_tcpRouteUpdated(meshName, vrName, vn1Name, vn2Name, rName), + Config: testAccAwsAppmeshRouteConfig_tcpRouteUpdated(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAppmeshRouteExists(resourceName, &r), + testAccCheckAwsAppmeshRouteExists(resourceName, &r), + testAccCheckResourceAttrRegionalARN(resourceName, "arn", "appmesh", fmt.Sprintf("mesh/%s/virtualRouter/%s/route/%s", rName, rName, rName)), + resource.TestCheckResourceAttrSet(resourceName, "created_date"), + resource.TestCheckResourceAttrSet(resourceName, "last_updated_date"), + resource.TestCheckResourceAttr(resourceName, "mesh_name", rName), resource.TestCheckResourceAttr(resourceName, "name", rName), - resource.TestCheckResourceAttr(resourceName, "mesh_name", meshName), - resource.TestCheckResourceAttr(resourceName, "virtual_router_name", vrName), resource.TestCheckResourceAttr(resourceName, "spec.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.#", "0"), + resource.TestCheckResourceAttr(resourceName, "spec.0.priority", "0"), resource.TestCheckResourceAttr(resourceName, "spec.0.tcp_route.#", "1"), resource.TestCheckResourceAttr(resourceName, "spec.0.tcp_route.0.action.#", "1"), resource.TestCheckResourceAttr(resourceName, "spec.0.tcp_route.0.action.0.weighted_target.#", "2"), - 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/virtualRouter/%s/route/%s", meshName, vrName, rName))), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "virtual_router_name", rName), ), }, { ResourceName: resourceName, - ImportStateId: fmt.Sprintf("%s/%s/%s", meshName, vrName, rName), + ImportStateIdFunc: testAccAwsAppmeshRouteImportStateIdFunc(resourceName), ImportState: true, ImportStateVerify: true, }, @@ -216,55 +309,141 @@ func testAccAwsAppmeshRoute_tcpRoute(t *testing.T) { }) } -func testAccAwsAppmeshRoute_tags(t *testing.T) { +func testAccAwsAppmeshRoute_routePriority(t *testing.T) { var r appmesh.RouteData - resourceName := "aws_appmesh_route.foo" - meshName := fmt.Sprintf("tf-test-mesh-%d", acctest.RandInt()) - vrName := fmt.Sprintf("tf-test-router-%d", acctest.RandInt()) - vn1Name := fmt.Sprintf("tf-test-node-%d", acctest.RandInt()) - vn2Name := fmt.Sprintf("tf-test-node-%d", acctest.RandInt()) - rName := fmt.Sprintf("tf-test-route-%d", acctest.RandInt()) + resourceName := "aws_appmesh_route.test" + rName := fmt.Sprintf("tf-testacc-appmesh-%s", acctest.RandStringFromCharSet(13, acctest.CharSetAlphaNum)) resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, - CheckDestroy: testAccCheckAppmeshRouteDestroy, + CheckDestroy: testAccCheckAwsAppmeshRouteDestroy, Steps: []resource.TestStep{ { - Config: testAccAppmeshRouteConfigWithTags(meshName, vrName, vn1Name, vn2Name, rName, "foo", "bar", "good", "bad"), + Config: testAccAwsAppmeshRouteConfig_routePriority(rName, 42), Check: resource.ComposeTestCheckFunc( - testAccCheckAppmeshRouteExists(resourceName, &r), - resource.TestCheckResourceAttr( - resourceName, "tags.%", "2"), - resource.TestCheckResourceAttr( - resourceName, "tags.foo", "bar"), - resource.TestCheckResourceAttr( - resourceName, "tags.good", "bad"), + testAccCheckAwsAppmeshRouteExists(resourceName, &r), + testAccCheckResourceAttrRegionalARN(resourceName, "arn", "appmesh", fmt.Sprintf("mesh/%s/virtualRouter/%s/route/%s", rName, rName, rName)), + resource.TestCheckResourceAttrSet(resourceName, "created_date"), + resource.TestCheckResourceAttrSet(resourceName, "last_updated_date"), + resource.TestCheckResourceAttr(resourceName, "mesh_name", rName), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "spec.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.action.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.action.0.weighted_target.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.header.#", "0"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.method", ""), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.prefix", "/"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.scheme", ""), + resource.TestCheckResourceAttr(resourceName, "spec.0.priority", "42"), + resource.TestCheckResourceAttr(resourceName, "spec.0.tcp_route.#", "0"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "virtual_router_name", rName), ), }, { - Config: testAccAppmeshRouteConfigWithTags(meshName, vrName, vn1Name, vn2Name, rName, "foo2", "bar", "good", "bad2"), + Config: testAccAwsAppmeshRouteConfig_routePriority(rName, 1000), Check: resource.ComposeTestCheckFunc( - testAccCheckAppmeshRouteExists(resourceName, &r), - resource.TestCheckResourceAttr( - resourceName, "tags.%", "2"), - resource.TestCheckResourceAttr( - resourceName, "tags.foo2", "bar"), - resource.TestCheckResourceAttr( - resourceName, "tags.good", "bad2"), + testAccCheckAwsAppmeshRouteExists(resourceName, &r), + testAccCheckResourceAttrRegionalARN(resourceName, "arn", "appmesh", fmt.Sprintf("mesh/%s/virtualRouter/%s/route/%s", rName, rName, rName)), + resource.TestCheckResourceAttrSet(resourceName, "created_date"), + resource.TestCheckResourceAttrSet(resourceName, "last_updated_date"), + resource.TestCheckResourceAttr(resourceName, "mesh_name", rName), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "spec.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.action.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.action.0.weighted_target.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.header.#", "0"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.method", ""), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.prefix", "/"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.scheme", ""), + resource.TestCheckResourceAttr(resourceName, "spec.0.priority", "1000"), + resource.TestCheckResourceAttr(resourceName, "spec.0.tcp_route.#", "0"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "virtual_router_name", rName), ), }, { - Config: testAccAppmeshRouteConfig_httpRoute(meshName, vrName, vn1Name, vn2Name, rName), + ResourceName: resourceName, + ImportStateIdFunc: testAccAwsAppmeshRouteImportStateIdFunc(resourceName), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAwsAppmeshRoute_tags(t *testing.T) { + var r appmesh.RouteData + resourceName := "aws_appmesh_route.test" + rName := fmt.Sprintf("tf-testacc-appmesh-%s", acctest.RandStringFromCharSet(13, acctest.CharSetAlphaNum)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsAppmeshRouteDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsAppmeshRouteConfig_tags(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsAppmeshRouteExists(resourceName, &r), + testAccCheckResourceAttrRegionalARN(resourceName, "arn", "appmesh", fmt.Sprintf("mesh/%s/virtualRouter/%s/route/%s", rName, rName, rName)), + resource.TestCheckResourceAttrSet(resourceName, "created_date"), + resource.TestCheckResourceAttrSet(resourceName, "last_updated_date"), + resource.TestCheckResourceAttr(resourceName, "mesh_name", rName), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "spec.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.action.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.action.0.weighted_target.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.header.#", "0"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.method", ""), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.prefix", "/"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.scheme", ""), + resource.TestCheckResourceAttr(resourceName, "spec.0.priority", "0"), + resource.TestCheckResourceAttr(resourceName, "spec.0.tcp_route.#", "0"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "3"), + resource.TestCheckResourceAttr(resourceName, "tags.Name", rName), + resource.TestCheckResourceAttr(resourceName, "tags.Key1", "Value1"), + resource.TestCheckResourceAttr(resourceName, "tags.Key2", "Value2a"), + resource.TestCheckResourceAttr(resourceName, "virtual_router_name", rName), + ), + }, + { + Config: testAccAwsAppmeshRouteConfig_tagsUpdated(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAppmeshRouteExists(resourceName, &r), - resource.TestCheckResourceAttr( - resourceName, "tags.%", "0"), + testAccCheckAwsAppmeshRouteExists(resourceName, &r), + testAccCheckResourceAttrRegionalARN(resourceName, "arn", "appmesh", fmt.Sprintf("mesh/%s/virtualRouter/%s/route/%s", rName, rName, rName)), + resource.TestCheckResourceAttrSet(resourceName, "created_date"), + resource.TestCheckResourceAttrSet(resourceName, "last_updated_date"), + resource.TestCheckResourceAttr(resourceName, "mesh_name", rName), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "spec.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.action.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.action.0.weighted_target.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.header.#", "0"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.method", ""), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.prefix", "/"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.0.match.0.scheme", ""), + resource.TestCheckResourceAttr(resourceName, "spec.0.priority", "0"), + resource.TestCheckResourceAttr(resourceName, "spec.0.tcp_route.#", "0"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "3"), + resource.TestCheckResourceAttr(resourceName, "tags.Name", rName), + resource.TestCheckResourceAttr(resourceName, "tags.Key2", "Value2b"), + resource.TestCheckResourceAttr(resourceName, "tags.Key3", "Value3"), + resource.TestCheckResourceAttr(resourceName, "virtual_router_name", rName), ), }, { ResourceName: resourceName, - ImportStateId: fmt.Sprintf("%s/%s/%s", meshName, vrName, rName), + ImportStateIdFunc: testAccAwsAppmeshRouteImportStateIdFunc(resourceName), ImportState: true, ImportStateVerify: true, }, @@ -272,7 +451,7 @@ func testAccAwsAppmeshRoute_tags(t *testing.T) { }) } -func testAccCheckAppmeshRouteDestroy(s *terraform.State) error { +func testAccCheckAwsAppmeshRouteDestroy(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).appmeshconn for _, rs := range s.RootModule().Resources { @@ -297,7 +476,7 @@ func testAccCheckAppmeshRouteDestroy(s *terraform.State) error { return nil } -func testAccCheckAppmeshRouteExists(name string, v *appmesh.RouteData) resource.TestCheckFunc { +func testAccCheckAwsAppmeshRouteExists(name string, v *appmesh.RouteData) resource.TestCheckFunc { return func(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).appmeshconn @@ -324,49 +503,54 @@ func testAccCheckAppmeshRouteExists(name string, v *appmesh.RouteData) resource. } } -func testAccAppmeshRouteConfigBase(meshName, vrName, vn1Name, vn2Name string) string { - return fmt.Sprintf(` -resource "aws_appmesh_mesh" "foo" { - name = %[1]q -} - -resource "aws_appmesh_virtual_router" "foo" { - name = %[2]q - mesh_name = "${aws_appmesh_mesh.foo.id}" - - spec { - listener { - port_mapping { - port = 8080 - protocol = "http" - } +func testAccAwsAppmeshRouteImportStateIdFunc(resourceName string) resource.ImportStateIdFunc { + return func(s *terraform.State) (string, error) { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return "", fmt.Errorf("Not Found: %s", resourceName) } + + return fmt.Sprintf("%s/%s/%s", rs.Primary.Attributes["mesh_name"], rs.Primary.Attributes["virtual_router_name"], rs.Primary.Attributes["name"]), nil } } -resource "aws_appmesh_virtual_node" "foo" { - name = %[3]q - mesh_name = "${aws_appmesh_mesh.foo.id}" +func testAccAwsAppmeshRouteConfig_base(rName string) string { + return fmt.Sprintf(` +resource "aws_appmesh_mesh" "test" { + name = %[1]q +} + +resource "aws_appmesh_virtual_router" "test" { + name = %[1]q + mesh_name = "${aws_appmesh_mesh.test.id}" - spec {} + spec { + listener { + port_mapping { + port = 8080 + protocol = "http" + } + } + } } -resource "aws_appmesh_virtual_node" "bar" { - name = %[4]q - mesh_name = "${aws_appmesh_mesh.foo.id}" +resource "aws_appmesh_virtual_node" "test" { + count = 2 - spec {} + name = "%[1]s-${count.index}" + mesh_name = "${aws_appmesh_mesh.test.id}" + + spec {} } -`, meshName, vrName, vn1Name, vn2Name) +`, rName) } -func testAccAppmeshRouteConfig_httpRoute(meshName, vrName, vn1Name, vn2Name, rName string) string { - return testAccAppmeshRouteConfigBase(meshName, vrName, vn1Name, vn2Name) + fmt.Sprintf(` - -resource "aws_appmesh_route" "foo" { +func testAccAwsAppmeshRouteConfig_httpRoute(rName string) string { + return testAccAwsAppmeshRouteConfig_base(rName) + fmt.Sprintf(` +resource "aws_appmesh_route" "test" { name = %[1]q - mesh_name = "${aws_appmesh_mesh.foo.id}" - virtual_router_name = "${aws_appmesh_virtual_router.foo.name}" + mesh_name = "${aws_appmesh_mesh.test.id}" + virtual_router_name = "${aws_appmesh_virtual_router.test.name}" spec { http_route { @@ -376,7 +560,7 @@ resource "aws_appmesh_route" "foo" { action { weighted_target { - virtual_node = "${aws_appmesh_virtual_node.foo.name}" + virtual_node = "${aws_appmesh_virtual_node.test.*.name[0]}" weight = 100 } } @@ -386,13 +570,12 @@ resource "aws_appmesh_route" "foo" { `, rName) } -func testAccAppmeshRouteConfig_httpRouteUpdated(meshName, vrName, vn1Name, vn2Name, rName string) string { - return testAccAppmeshRouteConfigBase(meshName, vrName, vn1Name, vn2Name) + fmt.Sprintf(` - -resource "aws_appmesh_route" "foo" { +func testAccAwsAppmeshRouteConfig_httpRouteUpdated(rName string) string { + return testAccAwsAppmeshRouteConfig_base(rName) + fmt.Sprintf(` +resource "aws_appmesh_route" "test" { name = %[1]q - mesh_name = "${aws_appmesh_mesh.foo.id}" - virtual_router_name = "${aws_appmesh_virtual_router.foo.name}" + mesh_name = "${aws_appmesh_mesh.test.id}" + virtual_router_name = "${aws_appmesh_virtual_router.test.name}" spec { http_route { @@ -402,12 +585,12 @@ resource "aws_appmesh_route" "foo" { action { weighted_target { - virtual_node = "${aws_appmesh_virtual_node.foo.name}" + virtual_node = "${aws_appmesh_virtual_node.test.*.name[0]}" weight = 90 } weighted_target { - virtual_node = "${aws_appmesh_virtual_node.bar.name}" + virtual_node = "${aws_appmesh_virtual_node.test.*.name[1]}" weight = 10 } } @@ -417,19 +600,71 @@ resource "aws_appmesh_route" "foo" { `, rName) } -func testAccAppmeshRouteConfig_tcpRoute(meshName, vrName, vn1Name, vn2Name, rName string) string { - return testAccAppmeshRouteConfigBase(meshName, vrName, vn1Name, vn2Name) + fmt.Sprintf(` +func testAccAwsAppmeshRouteConfig_httpHeader(rName string) string { + return testAccAwsAppmeshRouteConfig_base(rName) + fmt.Sprintf(` +resource "aws_appmesh_route" "test" { + name = %[1]q + mesh_name = "${aws_appmesh_mesh.test.id}" + virtual_router_name = "${aws_appmesh_virtual_router.test.name}" -resource "aws_appmesh_route" "foo" { + spec { + http_route { + match { + prefix = "/" + method = "POST" + scheme = "http" + + header { + name = "X-Testing1" + } + } + + action { + weighted_target { + virtual_node = "${aws_appmesh_virtual_node.test.*.name[0]}" + weight = 100 + } + } + } + } +} +`, rName) +} + +func testAccAwsAppmeshRouteConfig_httpHeaderUpdated(rName string) string { + return testAccAwsAppmeshRouteConfig_base(rName) + fmt.Sprintf(` +resource "aws_appmesh_route" "test" { name = %[1]q - mesh_name = "${aws_appmesh_mesh.foo.id}" - virtual_router_name = "${aws_appmesh_virtual_router.foo.name}" + mesh_name = "${aws_appmesh_mesh.test.id}" + virtual_router_name = "${aws_appmesh_virtual_router.test.name}" spec { - tcp_route { + http_route { + match { + prefix = "/path" + method = "PUT" + scheme = "https" + + header { + name = "X-Testing1" + invert = true + } + header { + name = "X-Testing2" + invert = false + + match { + range { + start = 2 + end = 7 + } + } + } + } + action { weighted_target { - virtual_node = "${aws_appmesh_virtual_node.foo.name}" + virtual_node = "${aws_appmesh_virtual_node.test.*.name[0]}" weight = 100 } } @@ -439,24 +674,44 @@ resource "aws_appmesh_route" "foo" { `, rName) } -func testAccAppmeshRouteConfig_tcpRouteUpdated(meshName, vrName, vn1Name, vn2Name, rName string) string { - return testAccAppmeshRouteConfigBase(meshName, vrName, vn1Name, vn2Name) + fmt.Sprintf(` +func testAccAwsAppmeshRouteConfig_tcpRoute(rName string) string { + return testAccAwsAppmeshRouteConfig_base(rName) + fmt.Sprintf(` +resource "aws_appmesh_route" "test" { + name = %[1]q + mesh_name = "${aws_appmesh_mesh.test.id}" + virtual_router_name = "${aws_appmesh_virtual_router.test.name}" + + spec { + tcp_route { + action { + weighted_target { + virtual_node = "${aws_appmesh_virtual_node.test.*.name[0]}" + weight = 100 + } + } + } + } +} +`, rName) +} -resource "aws_appmesh_route" "foo" { +func testAccAwsAppmeshRouteConfig_tcpRouteUpdated(rName string) string { + return testAccAwsAppmeshRouteConfig_base(rName) + fmt.Sprintf(` +resource "aws_appmesh_route" "test" { name = %[1]q - mesh_name = "${aws_appmesh_mesh.foo.id}" - virtual_router_name = "${aws_appmesh_virtual_router.foo.name}" + mesh_name = "${aws_appmesh_mesh.test.id}" + virtual_router_name = "${aws_appmesh_virtual_router.test.name}" spec { tcp_route { action { weighted_target { - virtual_node = "${aws_appmesh_virtual_node.foo.name}" + virtual_node = "${aws_appmesh_virtual_node.test.*.name[0]}" weight = 90 } weighted_target { - virtual_node = "${aws_appmesh_virtual_node.bar.name}" + virtual_node = "${aws_appmesh_virtual_node.test.*.name[1]}" weight = 10 } } @@ -466,13 +721,70 @@ resource "aws_appmesh_route" "foo" { `, rName) } -func testAccAppmeshRouteConfigWithTags(meshName, vrName, vn1Name, vn2Name, rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { - return testAccAppmeshRouteConfigBase(meshName, vrName, vn1Name, vn2Name) + fmt.Sprintf(` +func testAccAwsAppmeshRouteConfig_routePriority(rName string, priority int) string { + return testAccAwsAppmeshRouteConfig_base(rName) + fmt.Sprintf(` +resource "aws_appmesh_route" "test" { + name = %[1]q + mesh_name = "${aws_appmesh_mesh.test.id}" + virtual_router_name = "${aws_appmesh_virtual_router.test.name}" -resource "aws_appmesh_route" "foo" { + spec { + http_route { + match { + prefix = "/" + } + + action { + weighted_target { + virtual_node = "${aws_appmesh_virtual_node.test.*.name[0]}" + weight = 100 + } + } + } + + priority = %d + } +} +`, rName, priority) +} + +func testAccAwsAppmeshRouteConfig_tags(rName string) string { + return testAccAwsAppmeshRouteConfig_base(rName) + fmt.Sprintf(` +resource "aws_appmesh_route" "test" { + name = %[1]q + mesh_name = "${aws_appmesh_mesh.test.id}" + virtual_router_name = "${aws_appmesh_virtual_router.test.name}" + + spec { + http_route { + match { + prefix = "/" + } + + action { + weighted_target { + virtual_node = "${aws_appmesh_virtual_node.test.*.name[0]}" + weight = 100 + } + } + } + } + + tags = { + Name = %[1]q + Key1 = "Value1" + Key2 = "Value2a" + } +} +`, rName) +} + +func testAccAwsAppmeshRouteConfig_tagsUpdated(rName string) string { + return testAccAwsAppmeshRouteConfig_base(rName) + fmt.Sprintf(` +resource "aws_appmesh_route" "test" { name = %[1]q - mesh_name = "${aws_appmesh_mesh.foo.id}" - virtual_router_name = "${aws_appmesh_virtual_router.foo.name}" + mesh_name = "${aws_appmesh_mesh.test.id}" + virtual_router_name = "${aws_appmesh_virtual_router.test.name}" spec { http_route { @@ -482,7 +794,7 @@ resource "aws_appmesh_route" "foo" { action { weighted_target { - virtual_node = "${aws_appmesh_virtual_node.foo.name}" + virtual_node = "${aws_appmesh_virtual_node.test.*.name[0]}" weight = 100 } } @@ -490,9 +802,10 @@ resource "aws_appmesh_route" "foo" { } tags = { - %[2]s = %[3]q - %[4]s = %[5]q + Name = %[1]q + Key2 = "Value2b" + Key3 = "Value3" } } -`, rName, tagKey1, tagValue1, tagKey2, tagValue2) +`, rName) } diff --git a/aws/resource_aws_appmesh_test.go b/aws/resource_aws_appmesh_test.go index bc26eadbc17..86bb8a44f76 100644 --- a/aws/resource_aws_appmesh_test.go +++ b/aws/resource_aws_appmesh_test.go @@ -12,9 +12,11 @@ func TestAccAWSAppmesh(t *testing.T) { "tags": testAccAwsAppmeshMesh_tags, }, "Route": { - "httpRoute": testAccAwsAppmeshRoute_httpRoute, - "tcpRoute": testAccAwsAppmeshRoute_tcpRoute, - "tags": testAccAwsAppmeshRoute_tags, + "httpHeader": testAccAwsAppmeshRoute_httpHeader, + "httpRoute": testAccAwsAppmeshRoute_httpRoute, + "tcpRoute": testAccAwsAppmeshRoute_tcpRoute, + "routePriority": testAccAwsAppmeshRoute_routePriority, + "tags": testAccAwsAppmeshRoute_tags, }, "VirtualNode": { "basic": testAccAwsAppmeshVirtualNode_basic, diff --git a/aws/resource_aws_appmesh_virtual_node.go b/aws/resource_aws_appmesh_virtual_node.go index 6ff75998551..ab8b3753adf 100644 --- a/aws/resource_aws_appmesh_virtual_node.go +++ b/aws/resource_aws_appmesh_virtual_node.go @@ -28,14 +28,29 @@ func resourceAwsAppmeshVirtualNode() *schema.Resource { MigrateState: resourceAwsAppmeshVirtualNodeMigrateState, Schema: map[string]*schema.Schema{ - "name": { + "arn": { + Type: schema.TypeString, + Computed: true, + }, + + "created_date": { + Type: schema.TypeString, + Computed: true, + }, + + "last_updated_date": { + Type: schema.TypeString, + Computed: true, + }, + + "mesh_name": { Type: schema.TypeString, Required: true, ForceNew: true, ValidateFunc: validation.StringLenBetween(1, 255), }, - "mesh_name": { + "name": { Type: schema.TypeString, Required: true, ForceNew: true, @@ -49,14 +64,6 @@ func resourceAwsAppmeshVirtualNode() *schema.Resource { MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "backends": { - Type: schema.TypeSet, - Removed: "Use `backend` configuration blocks instead", - Optional: true, - Computed: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - "backend": { Type: schema.TypeSet, Optional: true, @@ -81,7 +88,15 @@ func resourceAwsAppmeshVirtualNode() *schema.Resource { }, }, }, - Set: appmeshVirtualNodeBackendHash, + Set: appmeshBackendHash, + }, + + "backends": { + Type: schema.TypeSet, + Removed: "Use `backend` configuration blocks instead", + Optional: true, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, }, "listener": { @@ -172,7 +187,7 @@ func resourceAwsAppmeshVirtualNode() *schema.Resource { }, }, }, - Set: appmeshVirtualNodeListenerHash, + Set: appmeshListenerHash, }, "logging": { @@ -254,18 +269,18 @@ func resourceAwsAppmeshVirtualNode() *schema.Resource { ConflictsWith: []string{"spec.0.service_discovery.0.aws_cloud_map"}, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ + "hostname": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.NoZeroValues, + }, + "service_name": { Type: schema.TypeString, Removed: "Use `hostname` argument instead", Optional: true, Computed: true, }, - - "hostname": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.NoZeroValues, - }, }, }, }, @@ -276,21 +291,6 @@ func resourceAwsAppmeshVirtualNode() *schema.Resource { }, }, - "arn": { - Type: schema.TypeString, - Computed: true, - }, - - "created_date": { - Type: schema.TypeString, - Computed: true, - }, - - "last_updated_date": { - Type: schema.TypeString, - Computed: true, - }, - "tags": tagsSchema(), }, } @@ -301,12 +301,12 @@ func resourceAwsAppmeshVirtualNodeCreate(d *schema.ResourceData, meta interface{ req := &appmesh.CreateVirtualNodeInput{ 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{})), + VirtualNodeName: aws.String(d.Get("name").(string)), } - log.Printf("[DEBUG] Creating App Mesh virtual node: %#v", req) + log.Printf("[DEBUG] Creating App Mesh virtual node: %s", req) resp, err := conn.CreateVirtualNode(req) if err != nil { return fmt.Errorf("error creating App Mesh virtual node: %s", err) @@ -330,7 +330,7 @@ func resourceAwsAppmeshVirtualNodeRead(d *schema.ResourceData, meta interface{}) return nil } if err != nil { - return fmt.Errorf("error reading App Mesh virtual node: %s", err) + return fmt.Errorf("error reading App Mesh virtual node (%s): %s", d.Id(), 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()) @@ -338,11 +338,11 @@ func resourceAwsAppmeshVirtualNodeRead(d *schema.ResourceData, meta interface{}) 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)) + d.Set("mesh_name", resp.VirtualNode.MeshName) + d.Set("name", resp.VirtualNode.VirtualNodeName) err = d.Set("spec", flattenAppmeshVirtualNodeSpec(resp.VirtualNode.Spec)) if err != nil { return fmt.Errorf("error setting spec: %s", err) @@ -368,14 +368,14 @@ func resourceAwsAppmeshVirtualNodeUpdate(d *schema.ResourceData, meta interface{ _, 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{})), + VirtualNodeName: aws.String(d.Get("name").(string)), } - log.Printf("[DEBUG] Updating App Mesh virtual node: %#v", req) + log.Printf("[DEBUG] Updating App Mesh virtual node: %s", req) _, err := conn.UpdateVirtualNode(req) if err != nil { - return fmt.Errorf("error updating App Mesh virtual node: %s", err) + return fmt.Errorf("error updating App Mesh virtual node (%s): %s", d.Id(), err) } } @@ -404,7 +404,7 @@ func resourceAwsAppmeshVirtualNodeDelete(d *schema.ResourceData, meta interface{ return nil } if err != nil { - return fmt.Errorf("error deleting App Mesh virtual node: %s", err) + return fmt.Errorf("error deleting App Mesh virtual node (%s): %s", d.Id(), err) } return nil @@ -431,13 +431,274 @@ func resourceAwsAppmeshVirtualNodeImport(d *schema.ResourceData, meta interface{ } d.SetId(aws.StringValue(resp.VirtualNode.Metadata.Uid)) - d.Set("name", resp.VirtualNode.VirtualNodeName) d.Set("mesh_name", resp.VirtualNode.MeshName) + d.Set("name", resp.VirtualNode.VirtualNodeName) return []*schema.ResourceData{d}, nil } -func appmeshVirtualNodeBackendHash(vBackend interface{}) int { +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["backend"].(*schema.Set); ok && vBackends.Len() > 0 { + backends := []*appmesh.Backend{} + + for _, vBackend := range vBackends.List() { + backend := &appmesh.Backend{} + + mBackend := vBackend.(map[string]interface{}) + + if vVirtualService, ok := mBackend["virtual_service"].([]interface{}); ok && len(vVirtualService) > 0 && vVirtualService[0] != nil { + mVirtualService := vVirtualService[0].(map[string]interface{}) + + backend.VirtualService = &appmesh.VirtualServiceBackend{} + + if vVirtualServiceName, ok := mVirtualService["virtual_service_name"].(string); ok { + backend.VirtualService.VirtualServiceName = aws.String(vVirtualServiceName) + } + } + backends = append(backends, backend) + } + + spec.Backends = backends + } + + 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 vHealthCheck, ok := mListener["health_check"].([]interface{}); ok && len(vHealthCheck) > 0 && vHealthCheck[0] != nil { + mHealthCheck := vHealthCheck[0].(map[string]interface{}) + + listener.HealthCheck = &appmesh.HealthCheckPolicy{} + + if vHealthyThreshold, ok := mHealthCheck["healthy_threshold"].(int); ok && vHealthyThreshold > 0 { + listener.HealthCheck.HealthyThreshold = aws.Int64(int64(vHealthyThreshold)) + } + if vIntervalMillis, ok := mHealthCheck["interval_millis"].(int); ok && vIntervalMillis > 0 { + listener.HealthCheck.IntervalMillis = aws.Int64(int64(vIntervalMillis)) + } + if vPath, ok := mHealthCheck["path"].(string); ok && vPath != "" { + listener.HealthCheck.Path = aws.String(vPath) + } + if vPort, ok := mHealthCheck["port"].(int); ok && vPort > 0 { + listener.HealthCheck.Port = aws.Int64(int64(vPort)) + } + if vProtocol, ok := mHealthCheck["protocol"].(string); ok && vProtocol != "" { + listener.HealthCheck.Protocol = aws.String(vProtocol) + } + if vTimeoutMillis, ok := mHealthCheck["timeout_millis"].(int); ok && vTimeoutMillis > 0 { + listener.HealthCheck.TimeoutMillis = aws.Int64(int64(vTimeoutMillis)) + } + if vUnhealthyThreshold, ok := mHealthCheck["unhealthy_threshold"].(int); ok && vUnhealthyThreshold > 0 { + listener.HealthCheck.UnhealthyThreshold = aws.Int64(int64(vUnhealthyThreshold)) + } + } + + 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 vLogging, ok := mSpec["logging"].([]interface{}); ok && len(vLogging) > 0 && vLogging[0] != nil { + mLogging := vLogging[0].(map[string]interface{}) + + if vAccessLog, ok := mLogging["access_log"].([]interface{}); ok && len(vAccessLog) > 0 && vAccessLog[0] != nil { + mAccessLog := vAccessLog[0].(map[string]interface{}) + + if vFile, ok := mAccessLog["file"].([]interface{}); ok && len(vFile) > 0 && vFile[0] != nil { + mFile := vFile[0].(map[string]interface{}) + + if vPath, ok := mFile["path"].(string); ok && vPath != "" { + spec.Logging = &appmesh.Logging{ + AccessLog: &appmesh.AccessLog{ + File: &appmesh.FileAccessLog{ + Path: aws.String(vPath), + }, + }, + } + } + } + } + } + + if vServiceDiscovery, ok := mSpec["service_discovery"].([]interface{}); ok && len(vServiceDiscovery) > 0 && vServiceDiscovery[0] != nil { + spec.ServiceDiscovery = &appmesh.ServiceDiscovery{} + + mServiceDiscovery := vServiceDiscovery[0].(map[string]interface{}) + + if vAwsCloudMap, ok := mServiceDiscovery["aws_cloud_map"].([]interface{}); ok && len(vAwsCloudMap) > 0 && vAwsCloudMap[0] != nil { + spec.ServiceDiscovery.AwsCloudMap = &appmesh.AwsCloudMapServiceDiscovery{} + + mAwsCloudMap := vAwsCloudMap[0].(map[string]interface{}) + + if vAttributes, ok := mAwsCloudMap["attributes"].(map[string]interface{}); ok && len(vAttributes) > 0 { + attributes := []*appmesh.AwsCloudMapInstanceAttribute{} + + for k, v := range vAttributes { + attributes = append(attributes, &appmesh.AwsCloudMapInstanceAttribute{ + Key: aws.String(k), + Value: aws.String(v.(string)), + }) + } + + spec.ServiceDiscovery.AwsCloudMap.Attributes = attributes + } + if vNamespaceName, ok := mAwsCloudMap["namespace_name"].(string); ok && vNamespaceName != "" { + spec.ServiceDiscovery.AwsCloudMap.NamespaceName = aws.String(vNamespaceName) + } + if vServiceName, ok := mAwsCloudMap["service_name"].(string); ok && vServiceName != "" { + spec.ServiceDiscovery.AwsCloudMap.ServiceName = aws.String(vServiceName) + } + } + + if vDns, ok := mServiceDiscovery["dns"].([]interface{}); ok && len(vDns) > 0 && vDns[0] != nil { + mDns := vDns[0].(map[string]interface{}) + + if vHostname, ok := mDns["hostname"].(string); ok && vHostname != "" { + spec.ServiceDiscovery.Dns = &appmesh.DnsServiceDiscovery{ + Hostname: aws.String(vHostname), + } + } + } + } + + return spec +} + +func flattenAppmeshVirtualNodeSpec(spec *appmesh.VirtualNodeSpec) []interface{} { + if spec == nil { + return []interface{}{} + } + + mSpec := map[string]interface{}{} + + if spec.Backends != nil { + vBackends := []interface{}{} + + for _, backend := range spec.Backends { + mBackend := map[string]interface{}{} + + if backend.VirtualService != nil { + mVirtualService := map[string]interface{}{ + "virtual_service_name": aws.StringValue(backend.VirtualService.VirtualServiceName), + } + mBackend["virtual_service"] = []interface{}{mVirtualService} + } + + vBackends = append(vBackends, mBackend) + } + + mSpec["backend"] = schema.NewSet(appmeshBackendHash, vBackends) + } + + if spec.Listeners != nil { + vListeners := []interface{}{} + + for _, listener := range spec.Listeners { + mListener := map[string]interface{}{} + + if listener.HealthCheck != nil { + mHealthCheck := map[string]interface{}{ + "healthy_threshold": int(aws.Int64Value(listener.HealthCheck.HealthyThreshold)), + "interval_millis": int(aws.Int64Value(listener.HealthCheck.IntervalMillis)), + "path": aws.StringValue(listener.HealthCheck.Path), + "port": int(aws.Int64Value(listener.HealthCheck.Port)), + "protocol": aws.StringValue(listener.HealthCheck.Protocol), + "timeout_millis": int(aws.Int64Value(listener.HealthCheck.TimeoutMillis)), + "unhealthy_threshold": int(aws.Int64Value(listener.HealthCheck.UnhealthyThreshold)), + } + mListener["health_check"] = []interface{}{mHealthCheck} + } + + 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(appmeshListenerHash, vListeners) + } + + if spec.Logging != nil && spec.Logging.AccessLog != nil && spec.Logging.AccessLog.File != nil { + mSpec["logging"] = []interface{}{ + map[string]interface{}{ + "access_log": []interface{}{ + map[string]interface{}{ + "file": []interface{}{ + map[string]interface{}{ + "path": aws.StringValue(spec.Logging.AccessLog.File.Path), + }, + }, + }, + }, + }, + } + } + + if spec.ServiceDiscovery != nil { + mServiceDiscovery := map[string]interface{}{} + + if spec.ServiceDiscovery.AwsCloudMap != nil { + vAttributes := map[string]interface{}{} + + for _, attribute := range spec.ServiceDiscovery.AwsCloudMap.Attributes { + vAttributes[aws.StringValue(attribute.Key)] = aws.StringValue(attribute.Value) + } + + mServiceDiscovery["aws_cloud_map"] = []interface{}{ + map[string]interface{}{ + "attributes": vAttributes, + "namespace_name": aws.StringValue(spec.ServiceDiscovery.AwsCloudMap.NamespaceName), + "service_name": aws.StringValue(spec.ServiceDiscovery.AwsCloudMap.ServiceName), + }, + } + } + + if spec.ServiceDiscovery.Dns != nil { + mServiceDiscovery["dns"] = []interface{}{ + map[string]interface{}{ + "hostname": aws.StringValue(spec.ServiceDiscovery.Dns.Hostname), + }, + } + } + + mSpec["service_discovery"] = []interface{}{mServiceDiscovery} + } + + return []interface{}{mSpec} +} + +func appmeshBackendHash(vBackend interface{}) int { var buf bytes.Buffer mBackend := vBackend.(map[string]interface{}) if vVirtualService, ok := mBackend["virtual_service"].([]interface{}); ok && len(vVirtualService) > 0 && vVirtualService[0] != nil { @@ -449,7 +710,7 @@ func appmeshVirtualNodeBackendHash(vBackend interface{}) int { return hashcode.String(buf.String()) } -func appmeshVirtualNodeListenerHash(vListener interface{}) int { +func appmeshListenerHash(vListener interface{}) int { var buf bytes.Buffer mListener := vListener.(map[string]interface{}) if vHealthCheck, ok := mListener["health_check"].([]interface{}); ok && len(vHealthCheck) > 0 && vHealthCheck[0] != nil { diff --git a/aws/resource_aws_appmesh_virtual_node_migrate.go b/aws/resource_aws_appmesh_virtual_node_migrate.go index eb71da9efdd..312f2352c35 100644 --- a/aws/resource_aws_appmesh_virtual_node_migrate.go +++ b/aws/resource_aws_appmesh_virtual_node_migrate.go @@ -29,7 +29,7 @@ func migrateAppmeshVirtualNodeStateV0toV1(is *terraform.InstanceState) (*terrafo delete(is.Attributes, "spec.0.backends.#") for k, v := range is.Attributes { if strings.HasPrefix(k, "spec.0.backends.") { - hash := appmeshVirtualNodeBackendHash(map[string]interface{}{ + hash := appmeshBackendHash(map[string]interface{}{ "virtual_service": []interface{}{map[string]interface{}{ "virtual_service_name": v, }}, diff --git a/aws/resource_aws_appmesh_virtual_router.go b/aws/resource_aws_appmesh_virtual_router.go index c4a4f6188cc..131cf0d3573 100644 --- a/aws/resource_aws_appmesh_virtual_router.go +++ b/aws/resource_aws_appmesh_virtual_router.go @@ -257,6 +257,75 @@ func resourceAwsAppmeshVirtualRouterImport(d *schema.ResourceData, meta interfac return []*schema.ResourceData{d}, nil } +func expandAppmeshVirtualRouterSpec(vSpec []interface{}) *appmesh.VirtualRouterSpec { + spec := &appmesh.VirtualRouterSpec{} + + if len(vSpec) == 0 || vSpec[0] == nil { + // Empty Spec is allowed. + return spec + } + mSpec := vSpec[0].(map[string]interface{}) + + if vListeners, ok := mSpec["listener"].(*schema.Set); ok && vListeners.Len() > 0 { + listeners := []*appmesh.VirtualRouterListener{} + + for _, vListener := range vListeners.List() { + listener := &appmesh.VirtualRouterListener{} + + 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 + } + + return spec +} + +func flattenAppmeshVirtualRouterSpec(spec *appmesh.VirtualRouterSpec) []interface{} { + if spec == nil { + return []interface{}{} + } + + mSpec := map[string]interface{}{} + + 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(appmeshVirtualRouterListenerHash, vListeners) + } + + return []interface{}{mSpec} +} + func appmeshVirtualRouterListenerHash(vListener interface{}) int { var buf bytes.Buffer mListener := vListener.(map[string]interface{}) diff --git a/aws/resource_aws_appmesh_virtual_service.go b/aws/resource_aws_appmesh_virtual_service.go index 9ac2fbba396..c49350e4a34 100644 --- a/aws/resource_aws_appmesh_virtual_service.go +++ b/aws/resource_aws_appmesh_virtual_service.go @@ -251,3 +251,73 @@ func resourceAwsAppmeshVirtualServiceImport(d *schema.ResourceData, meta interfa return []*schema.ResourceData{d}, nil } + +func expandAppmeshVirtualServiceSpec(vSpec []interface{}) *appmesh.VirtualServiceSpec { + spec := &appmesh.VirtualServiceSpec{} + + if len(vSpec) == 0 || vSpec[0] == nil { + // Empty Spec is allowed. + return spec + } + mSpec := vSpec[0].(map[string]interface{}) + + if vProvider, ok := mSpec["provider"].([]interface{}); ok && len(vProvider) > 0 && vProvider[0] != nil { + mProvider := vProvider[0].(map[string]interface{}) + + spec.Provider = &appmesh.VirtualServiceProvider{} + + if vVirtualNode, ok := mProvider["virtual_node"].([]interface{}); ok && len(vVirtualNode) > 0 && vVirtualNode[0] != nil { + mVirtualNode := vVirtualNode[0].(map[string]interface{}) + + if vVirtualNodeName, ok := mVirtualNode["virtual_node_name"].(string); ok && vVirtualNodeName != "" { + spec.Provider.VirtualNode = &appmesh.VirtualNodeServiceProvider{ + VirtualNodeName: aws.String(vVirtualNodeName), + } + } + } + + if vVirtualRouter, ok := mProvider["virtual_router"].([]interface{}); ok && len(vVirtualRouter) > 0 && vVirtualRouter[0] != nil { + mVirtualRouter := vVirtualRouter[0].(map[string]interface{}) + + if vVirtualRouterName, ok := mVirtualRouter["virtual_router_name"].(string); ok && vVirtualRouterName != "" { + spec.Provider.VirtualRouter = &appmesh.VirtualRouterServiceProvider{ + VirtualRouterName: aws.String(vVirtualRouterName), + } + } + } + } + + return spec +} + +func flattenAppmeshVirtualServiceSpec(spec *appmesh.VirtualServiceSpec) []interface{} { + if spec == nil { + return []interface{}{} + } + + mSpec := map[string]interface{}{} + + if spec.Provider != nil { + mProvider := map[string]interface{}{} + + if spec.Provider.VirtualNode != nil { + mProvider["virtual_node"] = []interface{}{ + map[string]interface{}{ + "virtual_node_name": aws.StringValue(spec.Provider.VirtualNode.VirtualNodeName), + }, + } + } + + if spec.Provider.VirtualRouter != nil { + mProvider["virtual_router"] = []interface{}{ + map[string]interface{}{ + "virtual_router_name": aws.StringValue(spec.Provider.VirtualRouter.VirtualRouterName), + }, + } + } + + mSpec["provider"] = []interface{}{mProvider} + } + + return []interface{}{mSpec} +} diff --git a/aws/structure.go b/aws/structure.go index db19625ed74..080046bf8d3 100644 --- a/aws/structure.go +++ b/aws/structure.go @@ -13,7 +13,6 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/private/protocol/json/jsonutil" "github.com/aws/aws-sdk-go/service/apigateway" - "github.com/aws/aws-sdk-go/service/appmesh" "github.com/aws/aws-sdk-go/service/appsync" "github.com/aws/aws-sdk-go/service/autoscaling" "github.com/aws/aws-sdk-go/service/cloudformation" @@ -4828,602 +4827,6 @@ func flattenRdsScalingConfigurationInfo(scalingConfigurationInfo *rds.ScalingCon return []interface{}{m} } -func expandAppmeshMeshSpec(vSpec []interface{}) *appmesh.MeshSpec { - spec := &appmesh.MeshSpec{} - - if len(vSpec) == 0 || vSpec[0] == nil { - // Empty Spec is allowed. - return spec - } - mSpec := vSpec[0].(map[string]interface{}) - - if vEgressFilter, ok := mSpec["egress_filter"].([]interface{}); ok && len(vEgressFilter) > 0 && vEgressFilter[0] != nil { - mEgressFilter := vEgressFilter[0].(map[string]interface{}) - - if vType, ok := mEgressFilter["type"].(string); ok && vType != "" { - spec.EgressFilter = &appmesh.EgressFilter{ - Type: aws.String(vType), - } - } - } - - return spec -} - -func flattenAppmeshMeshSpec(spec *appmesh.MeshSpec) []interface{} { - if spec == nil { - return []interface{}{} - } - - mSpec := map[string]interface{}{} - - if spec.EgressFilter != nil { - mSpec["egress_filter"] = []interface{}{ - map[string]interface{}{ - "type": aws.StringValue(spec.EgressFilter.Type), - }, - } - } - - return []interface{}{mSpec} -} - -func expandAppmeshVirtualRouterSpec(vSpec []interface{}) *appmesh.VirtualRouterSpec { - spec := &appmesh.VirtualRouterSpec{} - - if len(vSpec) == 0 || vSpec[0] == nil { - // Empty Spec is allowed. - return spec - } - mSpec := vSpec[0].(map[string]interface{}) - - if vListeners, ok := mSpec["listener"].(*schema.Set); ok && vListeners.Len() > 0 { - listeners := []*appmesh.VirtualRouterListener{} - - for _, vListener := range vListeners.List() { - listener := &appmesh.VirtualRouterListener{} - - 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 - } - - return spec -} - -func flattenAppmeshVirtualRouterSpec(spec *appmesh.VirtualRouterSpec) []interface{} { - if spec == nil { - return []interface{}{} - } - - mSpec := map[string]interface{}{} - - 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) - } - - 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["backend"].(*schema.Set); ok && vBackends.Len() > 0 { - backends := []*appmesh.Backend{} - - for _, vBackend := range vBackends.List() { - backend := &appmesh.Backend{} - - mBackend := vBackend.(map[string]interface{}) - - if vVirtualService, ok := mBackend["virtual_service"].([]interface{}); ok && len(vVirtualService) > 0 && vVirtualService[0] != nil { - mVirtualService := vVirtualService[0].(map[string]interface{}) - - backend.VirtualService = &appmesh.VirtualServiceBackend{} - - if vVirtualServiceName, ok := mVirtualService["virtual_service_name"].(string); ok { - backend.VirtualService.VirtualServiceName = aws.String(vVirtualServiceName) - } - } - backends = append(backends, backend) - } - - spec.Backends = backends - } - - 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 vHealthCheck, ok := mListener["health_check"].([]interface{}); ok && len(vHealthCheck) > 0 && vHealthCheck[0] != nil { - mHealthCheck := vHealthCheck[0].(map[string]interface{}) - - listener.HealthCheck = &appmesh.HealthCheckPolicy{} - - if vHealthyThreshold, ok := mHealthCheck["healthy_threshold"].(int); ok && vHealthyThreshold > 0 { - listener.HealthCheck.HealthyThreshold = aws.Int64(int64(vHealthyThreshold)) - } - if vIntervalMillis, ok := mHealthCheck["interval_millis"].(int); ok && vIntervalMillis > 0 { - listener.HealthCheck.IntervalMillis = aws.Int64(int64(vIntervalMillis)) - } - if vPath, ok := mHealthCheck["path"].(string); ok && vPath != "" { - listener.HealthCheck.Path = aws.String(vPath) - } - if vPort, ok := mHealthCheck["port"].(int); ok && vPort > 0 { - listener.HealthCheck.Port = aws.Int64(int64(vPort)) - } - if vProtocol, ok := mHealthCheck["protocol"].(string); ok && vProtocol != "" { - listener.HealthCheck.Protocol = aws.String(vProtocol) - } - if vTimeoutMillis, ok := mHealthCheck["timeout_millis"].(int); ok && vTimeoutMillis > 0 { - listener.HealthCheck.TimeoutMillis = aws.Int64(int64(vTimeoutMillis)) - } - if vUnhealthyThreshold, ok := mHealthCheck["unhealthy_threshold"].(int); ok && vUnhealthyThreshold > 0 { - listener.HealthCheck.UnhealthyThreshold = aws.Int64(int64(vUnhealthyThreshold)) - } - } - - 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 vLogging, ok := mSpec["logging"].([]interface{}); ok && len(vLogging) > 0 && vLogging[0] != nil { - mLogging := vLogging[0].(map[string]interface{}) - - if vAccessLog, ok := mLogging["access_log"].([]interface{}); ok && len(vAccessLog) > 0 && vAccessLog[0] != nil { - mAccessLog := vAccessLog[0].(map[string]interface{}) - - if vFile, ok := mAccessLog["file"].([]interface{}); ok && len(vFile) > 0 && vFile[0] != nil { - mFile := vFile[0].(map[string]interface{}) - - if vPath, ok := mFile["path"].(string); ok && vPath != "" { - spec.Logging = &appmesh.Logging{ - AccessLog: &appmesh.AccessLog{ - File: &appmesh.FileAccessLog{ - Path: aws.String(vPath), - }, - }, - } - } - } - } - } - - if vServiceDiscovery, ok := mSpec["service_discovery"].([]interface{}); ok && len(vServiceDiscovery) > 0 && vServiceDiscovery[0] != nil { - spec.ServiceDiscovery = &appmesh.ServiceDiscovery{} - - mServiceDiscovery := vServiceDiscovery[0].(map[string]interface{}) - - if vAwsCloudMap, ok := mServiceDiscovery["aws_cloud_map"].([]interface{}); ok && len(vAwsCloudMap) > 0 && vAwsCloudMap[0] != nil { - spec.ServiceDiscovery.AwsCloudMap = &appmesh.AwsCloudMapServiceDiscovery{} - - mAwsCloudMap := vAwsCloudMap[0].(map[string]interface{}) - - if vAttributes, ok := mAwsCloudMap["attributes"].(map[string]interface{}); ok && len(vAttributes) > 0 { - attributes := []*appmesh.AwsCloudMapInstanceAttribute{} - - for k, v := range vAttributes { - attributes = append(attributes, &appmesh.AwsCloudMapInstanceAttribute{ - Key: aws.String(k), - Value: aws.String(v.(string)), - }) - } - - spec.ServiceDiscovery.AwsCloudMap.Attributes = attributes - } - if vNamespaceName, ok := mAwsCloudMap["namespace_name"].(string); ok && vNamespaceName != "" { - spec.ServiceDiscovery.AwsCloudMap.NamespaceName = aws.String(vNamespaceName) - } - if vServiceName, ok := mAwsCloudMap["service_name"].(string); ok && vServiceName != "" { - spec.ServiceDiscovery.AwsCloudMap.ServiceName = aws.String(vServiceName) - } - } - - if vDns, ok := mServiceDiscovery["dns"].([]interface{}); ok && len(vDns) > 0 && vDns[0] != nil { - mDns := vDns[0].(map[string]interface{}) - - if vHostname, ok := mDns["hostname"].(string); ok && vHostname != "" { - spec.ServiceDiscovery.Dns = &appmesh.DnsServiceDiscovery{ - Hostname: aws.String(vHostname), - } - } - } - } - - return spec -} - -func flattenAppmeshVirtualNodeSpec(spec *appmesh.VirtualNodeSpec) []interface{} { - if spec == nil { - return []interface{}{} - } - - mSpec := map[string]interface{}{} - - if spec.Backends != nil { - vBackends := []interface{}{} - - for _, backend := range spec.Backends { - mBackend := map[string]interface{}{} - - if backend.VirtualService != nil { - mVirtualService := map[string]interface{}{ - "virtual_service_name": aws.StringValue(backend.VirtualService.VirtualServiceName), - } - mBackend["virtual_service"] = []interface{}{mVirtualService} - } - - vBackends = append(vBackends, mBackend) - } - - mSpec["backend"] = schema.NewSet(appmeshVirtualNodeBackendHash, vBackends) - } - - if spec.Listeners != nil { - vListeners := []interface{}{} - - for _, listener := range spec.Listeners { - mListener := map[string]interface{}{} - - if listener.HealthCheck != nil { - mHealthCheck := map[string]interface{}{ - "healthy_threshold": int(aws.Int64Value(listener.HealthCheck.HealthyThreshold)), - "interval_millis": int(aws.Int64Value(listener.HealthCheck.IntervalMillis)), - "path": aws.StringValue(listener.HealthCheck.Path), - "port": int(aws.Int64Value(listener.HealthCheck.Port)), - "protocol": aws.StringValue(listener.HealthCheck.Protocol), - "timeout_millis": int(aws.Int64Value(listener.HealthCheck.TimeoutMillis)), - "unhealthy_threshold": int(aws.Int64Value(listener.HealthCheck.UnhealthyThreshold)), - } - mListener["health_check"] = []interface{}{mHealthCheck} - } - - 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.Logging != nil && spec.Logging.AccessLog != nil && spec.Logging.AccessLog.File != nil { - mSpec["logging"] = []interface{}{ - map[string]interface{}{ - "access_log": []interface{}{ - map[string]interface{}{ - "file": []interface{}{ - map[string]interface{}{ - "path": aws.StringValue(spec.Logging.AccessLog.File.Path), - }, - }, - }, - }, - }, - } - } - - if spec.ServiceDiscovery != nil { - mServiceDiscovery := map[string]interface{}{} - - if spec.ServiceDiscovery.AwsCloudMap != nil { - vAttributes := map[string]interface{}{} - - for _, attribute := range spec.ServiceDiscovery.AwsCloudMap.Attributes { - vAttributes[aws.StringValue(attribute.Key)] = aws.StringValue(attribute.Value) - } - - mServiceDiscovery["aws_cloud_map"] = []interface{}{ - map[string]interface{}{ - "attributes": vAttributes, - "namespace_name": aws.StringValue(spec.ServiceDiscovery.AwsCloudMap.NamespaceName), - "service_name": aws.StringValue(spec.ServiceDiscovery.AwsCloudMap.ServiceName), - }, - } - } - - if spec.ServiceDiscovery.Dns != nil { - mServiceDiscovery["dns"] = []interface{}{ - map[string]interface{}{ - "hostname": aws.StringValue(spec.ServiceDiscovery.Dns.Hostname), - }, - } - } - - mSpec["service_discovery"] = []interface{}{mServiceDiscovery} - } - - return []interface{}{mSpec} -} - -func expandAppmeshVirtualServiceSpec(vSpec []interface{}) *appmesh.VirtualServiceSpec { - spec := &appmesh.VirtualServiceSpec{} - - if len(vSpec) == 0 || vSpec[0] == nil { - // Empty Spec is allowed. - return spec - } - mSpec := vSpec[0].(map[string]interface{}) - - if vProvider, ok := mSpec["provider"].([]interface{}); ok && len(vProvider) > 0 && vProvider[0] != nil { - mProvider := vProvider[0].(map[string]interface{}) - - spec.Provider = &appmesh.VirtualServiceProvider{} - - if vVirtualNode, ok := mProvider["virtual_node"].([]interface{}); ok && len(vVirtualNode) > 0 && vVirtualNode[0] != nil { - mVirtualNode := vVirtualNode[0].(map[string]interface{}) - - if vVirtualNodeName, ok := mVirtualNode["virtual_node_name"].(string); ok && vVirtualNodeName != "" { - spec.Provider.VirtualNode = &appmesh.VirtualNodeServiceProvider{ - VirtualNodeName: aws.String(vVirtualNodeName), - } - } - } - - if vVirtualRouter, ok := mProvider["virtual_router"].([]interface{}); ok && len(vVirtualRouter) > 0 && vVirtualRouter[0] != nil { - mVirtualRouter := vVirtualRouter[0].(map[string]interface{}) - - if vVirtualRouterName, ok := mVirtualRouter["virtual_router_name"].(string); ok && vVirtualRouterName != "" { - spec.Provider.VirtualRouter = &appmesh.VirtualRouterServiceProvider{ - VirtualRouterName: aws.String(vVirtualRouterName), - } - } - } - } - - return spec -} - -func flattenAppmeshVirtualServiceSpec(spec *appmesh.VirtualServiceSpec) []interface{} { - if spec == nil { - return []interface{}{} - } - - mSpec := map[string]interface{}{} - - if spec.Provider != nil { - mProvider := map[string]interface{}{} - - if spec.Provider.VirtualNode != nil { - mProvider["virtual_node"] = []interface{}{ - map[string]interface{}{ - "virtual_node_name": aws.StringValue(spec.Provider.VirtualNode.VirtualNodeName), - }, - } - } - - if spec.Provider.VirtualRouter != nil { - mProvider["virtual_router"] = []interface{}{ - map[string]interface{}{ - "virtual_router_name": aws.StringValue(spec.Provider.VirtualRouter.VirtualRouterName), - }, - } - } - - mSpec["provider"] = []interface{}{mProvider} - } - - return []interface{}{mSpec} -} - -func expandAppmeshRouteSpec(vSpec []interface{}) *appmesh.RouteSpec { - spec := &appmesh.RouteSpec{} - - if len(vSpec) == 0 || vSpec[0] == nil { - // Empty Spec is allowed. - return spec - } - mSpec := vSpec[0].(map[string]interface{}) - - if vHttpRoute, ok := mSpec["http_route"].([]interface{}); ok && len(vHttpRoute) > 0 && vHttpRoute[0] != nil { - mHttpRoute := vHttpRoute[0].(map[string]interface{}) - - spec.HttpRoute = &appmesh.HttpRoute{} - - if vHttpRouteAction, ok := mHttpRoute["action"].([]interface{}); ok && len(vHttpRouteAction) > 0 && vHttpRouteAction[0] != nil { - mHttpRouteAction := vHttpRouteAction[0].(map[string]interface{}) - - if vWeightedTargets, ok := mHttpRouteAction["weighted_target"].(*schema.Set); ok && vWeightedTargets.Len() > 0 { - weightedTargets := []*appmesh.WeightedTarget{} - - for _, vWeightedTarget := range vWeightedTargets.List() { - weightedTarget := &appmesh.WeightedTarget{} - - mWeightedTarget := vWeightedTarget.(map[string]interface{}) - - if vVirtualNode, ok := mWeightedTarget["virtual_node"].(string); ok && vVirtualNode != "" { - weightedTarget.VirtualNode = aws.String(vVirtualNode) - } - if vWeight, ok := mWeightedTarget["weight"].(int); ok { - weightedTarget.Weight = aws.Int64(int64(vWeight)) - } - - weightedTargets = append(weightedTargets, weightedTarget) - } - - spec.HttpRoute.Action = &appmesh.HttpRouteAction{ - WeightedTargets: weightedTargets, - } - } - } - - if vHttpRouteMatch, ok := mHttpRoute["match"].([]interface{}); ok && len(vHttpRouteMatch) > 0 && vHttpRouteMatch[0] != nil { - mHttpRouteMatch := vHttpRouteMatch[0].(map[string]interface{}) - - if vPrefix, ok := mHttpRouteMatch["prefix"].(string); ok && vPrefix != "" { - spec.HttpRoute.Match = &appmesh.HttpRouteMatch{ - Prefix: aws.String(vPrefix), - } - } - } - } - - if vTcpRoute, ok := mSpec["tcp_route"].([]interface{}); ok && len(vTcpRoute) > 0 && vTcpRoute[0] != nil { - mTcpRoute := vTcpRoute[0].(map[string]interface{}) - - spec.TcpRoute = &appmesh.TcpRoute{} - - if vTcpRouteAction, ok := mTcpRoute["action"].([]interface{}); ok && len(vTcpRouteAction) > 0 && vTcpRouteAction[0] != nil { - mTcpRouteAction := vTcpRouteAction[0].(map[string]interface{}) - - if vWeightedTargets, ok := mTcpRouteAction["weighted_target"].(*schema.Set); ok && vWeightedTargets.Len() > 0 { - weightedTargets := []*appmesh.WeightedTarget{} - - for _, vWeightedTarget := range vWeightedTargets.List() { - weightedTarget := &appmesh.WeightedTarget{} - - mWeightedTarget := vWeightedTarget.(map[string]interface{}) - - if vVirtualNode, ok := mWeightedTarget["virtual_node"].(string); ok && vVirtualNode != "" { - weightedTarget.VirtualNode = aws.String(vVirtualNode) - } - if vWeight, ok := mWeightedTarget["weight"].(int); ok { - weightedTarget.Weight = aws.Int64(int64(vWeight)) - } - - weightedTargets = append(weightedTargets, weightedTarget) - } - - spec.TcpRoute.Action = &appmesh.TcpRouteAction{ - WeightedTargets: weightedTargets, - } - } - } - } - - return spec -} - -func flattenAppmeshRouteSpec(spec *appmesh.RouteSpec) []interface{} { - if spec == nil { - return []interface{}{} - } - - mSpec := map[string]interface{}{} - - if spec.HttpRoute != nil { - mHttpRoute := map[string]interface{}{} - - if spec.HttpRoute.Action != nil && spec.HttpRoute.Action.WeightedTargets != nil { - vWeightedTargets := []interface{}{} - - for _, weightedTarget := range spec.HttpRoute.Action.WeightedTargets { - mWeightedTarget := map[string]interface{}{ - "virtual_node": aws.StringValue(weightedTarget.VirtualNode), - "weight": int(aws.Int64Value(weightedTarget.Weight)), - } - - vWeightedTargets = append(vWeightedTargets, mWeightedTarget) - } - - mHttpRoute["action"] = []interface{}{ - map[string]interface{}{ - "weighted_target": schema.NewSet(appmeshRouteWeightedTargetHash, vWeightedTargets), - }, - } - } - - if spec.HttpRoute.Match != nil { - mHttpRoute["match"] = []interface{}{ - map[string]interface{}{ - "prefix": aws.StringValue(spec.HttpRoute.Match.Prefix), - }, - } - } - - mSpec["http_route"] = []interface{}{mHttpRoute} - } - - if spec.TcpRoute != nil { - mTcpRoute := map[string]interface{}{} - - if spec.TcpRoute.Action != nil && spec.TcpRoute.Action.WeightedTargets != nil { - vWeightedTargets := []interface{}{} - - for _, weightedTarget := range spec.TcpRoute.Action.WeightedTargets { - mWeightedTarget := map[string]interface{}{ - "virtual_node": aws.StringValue(weightedTarget.VirtualNode), - "weight": int(aws.Int64Value(weightedTarget.Weight)), - } - - vWeightedTargets = append(vWeightedTargets, mWeightedTarget) - } - - mTcpRoute["action"] = []interface{}{ - map[string]interface{}{ - "weighted_target": schema.NewSet(appmeshRouteWeightedTargetHash, vWeightedTargets), - }, - } - } - - mSpec["tcp_route"] = []interface{}{mTcpRoute} - } - - return []interface{}{mSpec} -} - func flattenAppsyncPipelineConfig(c *appsync.PipelineConfig) []interface{} { if c == nil { return nil diff --git a/website/docs/r/appmesh_route.html.markdown b/website/docs/r/appmesh_route.html.markdown index f011866f53b..eee4de43a75 100644 --- a/website/docs/r/appmesh_route.html.markdown +++ b/website/docs/r/appmesh_route.html.markdown @@ -42,6 +42,41 @@ resource "aws_appmesh_route" "serviceb" { } ``` +### HTTP Header Routing + +```hcl +resource "aws_appmesh_route" "serviceb" { + name = "serviceB-route" + mesh_name = "${aws_appmesh_mesh.simple.id}" + virtual_router_name = "${aws_appmesh_virtual_router.serviceb.name}" + + spec { + http_route { + match { + method = "POST" + prefix = "/" + scheme = "https" + + header { + name = "clientRequestId" + + match { + prefix = "123" + } + } + } + + action { + weighted_target { + virtual_node = "${aws_appmesh_virtual_node.serviceb.name}" + weight = 100 + } + } + } + } +} +``` + ### TCP Routing ```hcl @@ -76,6 +111,8 @@ The following arguments are supported: The `spec` object supports the following: * `http_route` - (Optional) The HTTP routing information for the route. +* `priority` - (Optional) The priority for the route, between `0` and `1000`. +Routes are matched based on the specified value, where `0` is the highest priority. * `tcp_route` - (Optional) The TCP routing information for the route. The `http_route` object supports the following: @@ -92,16 +129,38 @@ The `action` object supports the following: * `weighted_target` - (Required) The targets that traffic is routed to when a request matches the route. You can specify one or more targets and their relative weights with which to distribute traffic. -The `match` object supports the following: +The `http_route`'s `match` object supports the following: * `prefix` - (Required) Specifies the path with which to match requests. This parameter must always start with /, which by itself matches all requests to the virtual router service name. +* `header` - (Optional) The client request headers to match on. +* `method` - (Optional) The client request header method to match on. Valid values: `GET`, `HEAD`, `POST`, `PUT`, `DELETE`, `CONNECT`, `OPTIONS`, `TRACE`, `PATCH`. +* `scheme` - (Optional) The client request header scheme to match on. Valid values: `http`, `https`. The `weighted_target` object supports the following: * `virtual_node` - (Required) The virtual node to associate with the weighted target. * `weight` - (Required) The relative weight of the weighted target. An integer between 0 and 100. +The `header` object supports the following: + +* `name` - (Required) A name for the HTTP header in the client request that will be matched on. +* `invert` - (Optional) If `true`, the match is on the opposite of the `match` method and value. Default is `false`. +* `match` - (Optional) The method and value to match the header value sent with a request. Specify one match method. + +The `header`'s `match` object supports the following: + +* `exact` - (Optional) The header value sent by the client must match the specified value exactly. +* `prefix` - (Optional) The header value sent by the client must begin with the specified characters. +* `range`- (Optional) The object that specifies the range of numbers that the header value sent by the client must be included in. +* `regex` - (Optional) The header value sent by the client must include the specified characters. +* `suffix` - (Optional) The header value sent by the client must end with the specified characters. + +The `range` object supports the following: + +* `end` - (Required) The end of the range. +* `start` - (Requited) The start of the range. + ## Attributes Reference In addition to all arguments above, the following attributes are exported: