From f8ba388f7044d0e66a3e3f85f11b6d106f675ec0 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 21 Dec 2020 17:49:45 -0500 Subject: [PATCH] r/aws_appmesh_route: Allow empty 'match' for 'grpc_route'. Acceptance test output: $ make testacc TEST=./aws TESTARGS='-run=TestAccAWSAppmesh/Route/grpcRoute' ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./aws -v -count 1 -parallel 20 -run=TestAccAWSAppmesh/Route/grpcRoute -timeout 120m === RUN TestAccAWSAppmesh_serial === RUN TestAccAWSAppmesh_serial/VirtualRouter === RUN TestAccAWSAppmesh_serial/GatewayRoute === RUN TestAccAWSAppmesh_serial/GatewayRoute/grpcRoute === RUN TestAccAWSAppmesh_serial/Route === RUN TestAccAWSAppmesh_serial/Route/grpcRoute === RUN TestAccAWSAppmesh_serial/Route/grpcRouteEmptyMatch === RUN TestAccAWSAppmesh_serial/Route/grpcRouteTimeout --- PASS: TestAccAWSAppmesh_serial (131.91s) --- PASS: TestAccAWSAppmesh_serial/VirtualRouter (0.00s) --- PASS: TestAccAWSAppmesh_serial/GatewayRoute (32.18s) --- PASS: TestAccAWSAppmesh_serial/GatewayRoute/grpcRoute (32.18s) --- PASS: TestAccAWSAppmesh_serial/Route (99.73s) --- PASS: TestAccAWSAppmesh_serial/Route/grpcRoute (47.54s) --- PASS: TestAccAWSAppmesh_serial/Route/grpcRouteEmptyMatch (18.53s) --- PASS: TestAccAWSAppmesh_serial/Route/grpcRouteTimeout (33.66s) PASS ok github.com/terraform-providers/terraform-provider-aws/aws 132.036s --- aws/resource_aws_appmesh_route.go | 9 +-- aws/resource_aws_appmesh_route_test.go | 75 +++++++++++++++++++++ aws/resource_aws_appmesh_test.go | 25 +++---- aws/structure.go | 93 ++++++++++++++------------ 4 files changed, 142 insertions(+), 60 deletions(-) diff --git a/aws/resource_aws_appmesh_route.go b/aws/resource_aws_appmesh_route.go index 5e02abfa7a4..77bf73c9ef5 100644 --- a/aws/resource_aws_appmesh_route.go +++ b/aws/resource_aws_appmesh_route.go @@ -103,8 +103,8 @@ func resourceAwsAppmeshRoute() *schema.Resource { "match": { Type: schema.TypeList, - Required: true, - MinItems: 1, + Optional: true, + MinItems: 0, MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ @@ -196,8 +196,9 @@ func resourceAwsAppmeshRoute() *schema.Resource { }, "service_name": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + RequiredWith: []string{"spec.0.grpc_route.0.match.0.method_name"}, }, }, }, diff --git a/aws/resource_aws_appmesh_route_test.go b/aws/resource_aws_appmesh_route_test.go index 2aa4180ec38..19750749a3a 100644 --- a/aws/resource_aws_appmesh_route_test.go +++ b/aws/resource_aws_appmesh_route_test.go @@ -368,6 +368,58 @@ func testAccAwsAppmeshRoute_grpcRouteTimeout(t *testing.T) { }) } +func testAccAwsAppmeshRoute_grpcRouteEmptyMatch(t *testing.T) { + var r appmesh.RouteData + resourceName := "aws_appmesh_route.test" + meshName := acctest.RandomWithPrefix("tf-acc-test") + vrName := acctest.RandomWithPrefix("tf-acc-test") + vn1Name := acctest.RandomWithPrefix("tf-acc-test") + vn2Name := acctest.RandomWithPrefix("tf-acc-test") + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(appmesh.EndpointsID, t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAppmeshRouteDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsAppmeshRouteConfig_grpcRouteWithEmptyMatch(meshName, vrName, vn1Name, vn2Name, rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAppmeshRouteExists(resourceName, &r), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "mesh_name", meshName), + testAccCheckResourceAttrAccountID(resourceName, "mesh_owner"), + resource.TestCheckResourceAttr(resourceName, "virtual_router_name", vrName), + resource.TestCheckResourceAttr(resourceName, "spec.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.grpc_route.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.grpc_route.0.action.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.grpc_route.0.action.0.weighted_target.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.grpc_route.0.match.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.grpc_route.0.match.0.metadata.#", "0"), + resource.TestCheckResourceAttr(resourceName, "spec.0.grpc_route.0.match.0.method_name", ""), + resource.TestCheckResourceAttr(resourceName, "spec.0.grpc_route.0.match.0.service_name", ""), + resource.TestCheckResourceAttr(resourceName, "spec.0.grpc_route.0.retry_policy.#", "0"), + resource.TestCheckResourceAttr(resourceName, "spec.0.grpc_route.0.timeout.#", "0"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http2_route.#", "0"), + resource.TestCheckResourceAttr(resourceName, "spec.0.http_route.#", "0"), + resource.TestCheckResourceAttr(resourceName, "spec.0.tcp_route.#", "0"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttrSet(resourceName, "created_date"), + resource.TestCheckResourceAttrSet(resourceName, "last_updated_date"), + testAccCheckResourceAttrAccountID(resourceName, "resource_owner"), + testAccCheckResourceAttrRegionalARN(resourceName, "arn", "appmesh", fmt.Sprintf("mesh/%s/virtualRouter/%s/route/%s", meshName, vrName, rName)), + ), + }, + { + ResourceName: resourceName, + ImportStateIdFunc: testAccAwsAppmeshRouteImportStateIdFunc(resourceName), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func testAccAwsAppmeshRoute_http2Route(t *testing.T) { var r appmesh.RouteData resourceName := "aws_appmesh_route.test" @@ -1645,6 +1697,29 @@ resource "aws_appmesh_route" "test" { `, rName)) } +func testAccAwsAppmeshRouteConfig_grpcRouteWithEmptyMatch(meshName, vrName, vn1Name, vn2Name, rName string) string { + return composeConfig(testAccAppmeshRouteConfigBase(meshName, vrName, "grpc", vn1Name, vn2Name), 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 { + grpc_route { + match {} + + action { + weighted_target { + virtual_node = aws_appmesh_virtual_node.foo.name + weight = 100 + } + } + } + } +} +`, rName)) +} + func testAccAwsAppmeshRouteConfig_http2Route(meshName, vrName, vn1Name, vn2Name, rName string) string { return composeConfig(testAccAppmeshRouteConfigBase(meshName, vrName, "http2", vn1Name, vn2Name), fmt.Sprintf(` resource "aws_appmesh_route" "test" { diff --git a/aws/resource_aws_appmesh_test.go b/aws/resource_aws_appmesh_test.go index db9afbaa07f..5fd7be34fee 100644 --- a/aws/resource_aws_appmesh_test.go +++ b/aws/resource_aws_appmesh_test.go @@ -20,18 +20,19 @@ func TestAccAWSAppmesh_serial(t *testing.T) { "tags": testAccAwsAppmeshMesh_tags, }, "Route": { - "grpcRoute": testAccAwsAppmeshRoute_grpcRoute, - "grpcRouteTimeout": testAccAwsAppmeshRoute_grpcRouteTimeout, - "http2Route": testAccAwsAppmeshRoute_http2Route, - "http2RouteTimeout": testAccAwsAppmeshRoute_http2RouteTimeout, - "httpHeader": testAccAwsAppmeshRoute_httpHeader, - "httpRetryPolicy": testAccAwsAppmeshRoute_httpRetryPolicy, - "httpRoute": testAccAwsAppmeshRoute_httpRoute, - "httpRouteTimeout": testAccAwsAppmeshRoute_httpRouteTimeout, - "routePriority": testAccAwsAppmeshRoute_routePriority, - "tcpRoute": testAccAwsAppmeshRoute_tcpRoute, - "tcpRouteTimeout": testAccAwsAppmeshRoute_tcpRouteTimeout, - "tags": testAccAwsAppmeshRoute_tags, + "grpcRoute": testAccAwsAppmeshRoute_grpcRoute, + "grpcRouteEmptyMatch": testAccAwsAppmeshRoute_grpcRouteEmptyMatch, + "grpcRouteTimeout": testAccAwsAppmeshRoute_grpcRouteTimeout, + "http2Route": testAccAwsAppmeshRoute_http2Route, + "http2RouteTimeout": testAccAwsAppmeshRoute_http2RouteTimeout, + "httpHeader": testAccAwsAppmeshRoute_httpHeader, + "httpRetryPolicy": testAccAwsAppmeshRoute_httpRetryPolicy, + "httpRoute": testAccAwsAppmeshRoute_httpRoute, + "httpRouteTimeout": testAccAwsAppmeshRoute_httpRouteTimeout, + "routePriority": testAccAwsAppmeshRoute_routePriority, + "tcpRoute": testAccAwsAppmeshRoute_tcpRoute, + "tcpRouteTimeout": testAccAwsAppmeshRoute_tcpRouteTimeout, + "tags": testAccAwsAppmeshRoute_tags, }, "VirtualGateway": { "basic": testAccAwsAppmeshVirtualGateway_basic, diff --git a/aws/structure.go b/aws/structure.go index 10a7f881773..8eed4b36e95 100644 --- a/aws/structure.go +++ b/aws/structure.go @@ -5547,69 +5547,74 @@ func expandAppmeshGrpcRoute(vGrpcRoute []interface{}) *appmesh.GrpcRoute { } } - if vGrpcRouteMatch, ok := mGrpcRoute["match"].([]interface{}); ok && len(vGrpcRouteMatch) > 0 && vGrpcRouteMatch[0] != nil { + if vGrpcRouteMatch, ok := mGrpcRoute["match"].([]interface{}); ok { grpcRouteMatch := &appmesh.GrpcRouteMatch{} - mGrpcRouteMatch := vGrpcRouteMatch[0].(map[string]interface{}) + // Empty match is allowed. + // https://github.com/hashicorp/terraform-provider-aws/issues/16816. - if vMethodName, ok := mGrpcRouteMatch["method_name"].(string); ok && vMethodName != "" { - grpcRouteMatch.MethodName = aws.String(vMethodName) - } - if vServiceName, ok := mGrpcRouteMatch["service_name"].(string); ok && vServiceName != "" { - grpcRouteMatch.ServiceName = aws.String(vServiceName) - } - - if vGrpcRouteMetadatas, ok := mGrpcRouteMatch["metadata"].(*schema.Set); ok && vGrpcRouteMetadatas.Len() > 0 { - grpcRouteMetadatas := []*appmesh.GrpcRouteMetadata{} - - for _, vGrpcRouteMetadata := range vGrpcRouteMetadatas.List() { - grpcRouteMetadata := &appmesh.GrpcRouteMetadata{} + if len(vGrpcRouteMatch) > 0 && vGrpcRouteMatch[0] != nil { + mGrpcRouteMatch := vGrpcRouteMatch[0].(map[string]interface{}) - mGrpcRouteMetadata := vGrpcRouteMetadata.(map[string]interface{}) + if vMethodName, ok := mGrpcRouteMatch["method_name"].(string); ok && vMethodName != "" { + grpcRouteMatch.MethodName = aws.String(vMethodName) + } + if vServiceName, ok := mGrpcRouteMatch["service_name"].(string); ok && vServiceName != "" { + grpcRouteMatch.ServiceName = aws.String(vServiceName) + } - if vInvert, ok := mGrpcRouteMetadata["invert"].(bool); ok { - grpcRouteMetadata.Invert = aws.Bool(vInvert) - } - if vName, ok := mGrpcRouteMetadata["name"].(string); ok && vName != "" { - grpcRouteMetadata.Name = aws.String(vName) - } + if vGrpcRouteMetadatas, ok := mGrpcRouteMatch["metadata"].(*schema.Set); ok && vGrpcRouteMetadatas.Len() > 0 { + grpcRouteMetadatas := []*appmesh.GrpcRouteMetadata{} - if vMatch, ok := mGrpcRouteMetadata["match"].([]interface{}); ok && len(vMatch) > 0 && vMatch[0] != nil { - grpcRouteMetadata.Match = &appmesh.GrpcRouteMetadataMatchMethod{} + for _, vGrpcRouteMetadata := range vGrpcRouteMetadatas.List() { + grpcRouteMetadata := &appmesh.GrpcRouteMetadata{} - mMatch := vMatch[0].(map[string]interface{}) + mGrpcRouteMetadata := vGrpcRouteMetadata.(map[string]interface{}) - if vExact, ok := mMatch["exact"].(string); ok && vExact != "" { - grpcRouteMetadata.Match.Exact = aws.String(vExact) + if vInvert, ok := mGrpcRouteMetadata["invert"].(bool); ok { + grpcRouteMetadata.Invert = aws.Bool(vInvert) } - if vPrefix, ok := mMatch["prefix"].(string); ok && vPrefix != "" { - grpcRouteMetadata.Match.Prefix = aws.String(vPrefix) - } - if vRegex, ok := mMatch["regex"].(string); ok && vRegex != "" { - grpcRouteMetadata.Match.Regex = aws.String(vRegex) - } - if vSuffix, ok := mMatch["suffix"].(string); ok && vSuffix != "" { - grpcRouteMetadata.Match.Suffix = aws.String(vSuffix) + if vName, ok := mGrpcRouteMetadata["name"].(string); ok && vName != "" { + grpcRouteMetadata.Name = aws.String(vName) } - if vRange, ok := mMatch["range"].([]interface{}); ok && len(vRange) > 0 && vRange[0] != nil { - grpcRouteMetadata.Match.Range = &appmesh.MatchRange{} + if vMatch, ok := mGrpcRouteMetadata["match"].([]interface{}); ok && len(vMatch) > 0 && vMatch[0] != nil { + grpcRouteMetadata.Match = &appmesh.GrpcRouteMetadataMatchMethod{} - mRange := vRange[0].(map[string]interface{}) + mMatch := vMatch[0].(map[string]interface{}) - if vEnd, ok := mRange["end"].(int); ok && vEnd > 0 { - grpcRouteMetadata.Match.Range.End = aws.Int64(int64(vEnd)) + if vExact, ok := mMatch["exact"].(string); ok && vExact != "" { + grpcRouteMetadata.Match.Exact = aws.String(vExact) } - if vStart, ok := mRange["start"].(int); ok && vStart > 0 { - grpcRouteMetadata.Match.Range.Start = aws.Int64(int64(vStart)) + if vPrefix, ok := mMatch["prefix"].(string); ok && vPrefix != "" { + grpcRouteMetadata.Match.Prefix = aws.String(vPrefix) + } + if vRegex, ok := mMatch["regex"].(string); ok && vRegex != "" { + grpcRouteMetadata.Match.Regex = aws.String(vRegex) + } + if vSuffix, ok := mMatch["suffix"].(string); ok && vSuffix != "" { + grpcRouteMetadata.Match.Suffix = aws.String(vSuffix) + } + + if vRange, ok := mMatch["range"].([]interface{}); ok && len(vRange) > 0 && vRange[0] != nil { + grpcRouteMetadata.Match.Range = &appmesh.MatchRange{} + + mRange := vRange[0].(map[string]interface{}) + + if vEnd, ok := mRange["end"].(int); ok && vEnd > 0 { + grpcRouteMetadata.Match.Range.End = aws.Int64(int64(vEnd)) + } + if vStart, ok := mRange["start"].(int); ok && vStart > 0 { + grpcRouteMetadata.Match.Range.Start = aws.Int64(int64(vStart)) + } } } + + grpcRouteMetadatas = append(grpcRouteMetadatas, grpcRouteMetadata) } - grpcRouteMetadatas = append(grpcRouteMetadatas, grpcRouteMetadata) + grpcRouteMatch.Metadata = grpcRouteMetadatas } - - grpcRouteMatch.Metadata = grpcRouteMetadatas } grpcRoute.Match = grpcRouteMatch