diff --git a/aws/resource_aws_api_gateway_stage.go b/aws/resource_aws_api_gateway_stage.go index 2735e736f5c..8ab4f5c70d2 100644 --- a/aws/resource_aws_api_gateway_stage.go +++ b/aws/resource_aws_api_gateway_stage.go @@ -12,6 +12,8 @@ import ( "github.com/aws/aws-sdk-go/service/apigateway" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" ) func resourceAwsApiGatewayStage() *schema.Resource { @@ -64,6 +66,16 @@ func resourceAwsApiGatewayStage() *schema.Resource { "cache_cluster_size": { Type: schema.TypeString, Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + apigateway.CacheClusterSize05, + apigateway.CacheClusterSize16, + apigateway.CacheClusterSize61, + apigateway.CacheClusterSize118, + apigateway.CacheClusterSize135, + apigateway.CacheClusterSize237, + apigateway.CacheClusterSize284, + apigateway.CacheClusterSize582, + }, true), }, "client_certificate_id": { Type: schema.TypeString, @@ -108,6 +120,10 @@ func resourceAwsApiGatewayStage() *schema.Resource { Type: schema.TypeBool, Optional: true, }, + "arn": { + Type: schema.TypeString, + Computed: true, + }, }, } } @@ -148,12 +164,8 @@ func resourceAwsApiGatewayStageCreate(d *schema.ResourceData, meta interface{}) } input.Variables = aws.StringMap(variables) } - if vars, ok := d.GetOk("tags"); ok { - newMap := make(map[string]string, len(vars.(map[string]interface{}))) - for k, v := range vars.(map[string]interface{}) { - newMap[k] = v.(string) - } - input.Tags = aws.StringMap(newMap) + if v, ok := d.GetOk("tags"); ok { + input.Tags = keyvaluetags.New(v.(map[string]interface{})).IgnoreAws().ApigatewayTags() } out, err := conn.CreateStage(&input) @@ -170,14 +182,14 @@ func resourceAwsApiGatewayStageCreate(d *schema.ResourceData, meta interface{}) d.SetPartial("variables") d.SetPartial("xray_tracing_enabled") - if waitForCache && *out.CacheClusterStatus != "NOT_AVAILABLE" { + if waitForCache && *out.CacheClusterStatus != apigateway.CacheClusterStatusNotAvailable { stateConf := &resource.StateChangeConf{ Pending: []string{ - "CREATE_IN_PROGRESS", - "DELETE_IN_PROGRESS", - "FLUSH_IN_PROGRESS", + apigateway.CacheClusterStatusCreateInProgress, + apigateway.CacheClusterStatusDeleteInProgress, + apigateway.CacheClusterStatusFlushInProgress, }, - Target: []string{"AVAILABLE"}, + Target: []string{apigateway.CacheClusterStatusAvailable}, Refresh: apiGatewayStageCacheRefreshFunc(conn, d.Get("rest_api_id").(string), d.Get("stage_name").(string)), @@ -215,7 +227,7 @@ func resourceAwsApiGatewayStageRead(d *schema.ResourceData, meta interface{}) er } stage, err := conn.GetStage(&input) if err != nil { - if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NotFoundException" { + if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == apigateway.ErrCodeNotFoundException { log.Printf("[WARN] API Gateway Stage (%s) not found, removing from state", d.Id()) d.SetId("") return nil @@ -230,7 +242,7 @@ func resourceAwsApiGatewayStageRead(d *schema.ResourceData, meta interface{}) er d.Set("client_certificate_id", stage.ClientCertificateId) - if stage.CacheClusterStatus != nil && *stage.CacheClusterStatus == "DELETE_IN_PROGRESS" { + if stage.CacheClusterStatus != nil && *stage.CacheClusterStatus == apigateway.CacheClusterStatusDeleteInProgress { d.Set("cache_cluster_enabled", false) d.Set("cache_cluster_size", nil) } else { @@ -243,10 +255,18 @@ func resourceAwsApiGatewayStageRead(d *schema.ResourceData, meta interface{}) er d.Set("documentation_version", stage.DocumentationVersion) d.Set("xray_tracing_enabled", stage.TracingEnabled) - if err := d.Set("tags", aws.StringValueMap(stage.Tags)); err != nil { + if err := d.Set("tags", keyvaluetags.ApigatewayKeyValueTags(stage.Tags).IgnoreAws().Map()); err != nil { return fmt.Errorf("error setting tags: %s", err) } + stageArn := arn.ARN{ + Partition: meta.(*AWSClient).partition, + Region: meta.(*AWSClient).region, + Service: "apigateway", + Resource: fmt.Sprintf("/restapis/%s/stages/%s", d.Get("rest_api_id").(string), d.Get("stage_name").(string)), + }.String() + d.Set("arn", stageArn) + if err := d.Set("variables", aws.StringValueMap(stage.Variables)); err != nil { return fmt.Errorf("error setting variables: %s", err) } @@ -277,10 +297,12 @@ func resourceAwsApiGatewayStageUpdate(d *schema.ResourceData, meta interface{}) Service: "apigateway", Resource: fmt.Sprintf("/restapis/%s/stages/%s", d.Get("rest_api_id").(string), d.Get("stage_name").(string)), }.String() - if tagErr := setTagsAPIGatewayStage(conn, d, stageArn); tagErr != nil { - return tagErr + if d.HasChange("tags") { + o, n := d.GetChange("tags") + if err := keyvaluetags.ApigatewayUpdateTags(conn, stageArn, o, n); err != nil { + return fmt.Errorf("error updating tags: %s", err) + } } - d.SetPartial("tags") operations := make([]*apigateway.PatchOperation, 0) waitForCache := false @@ -380,17 +402,17 @@ func resourceAwsApiGatewayStageUpdate(d *schema.ResourceData, meta interface{}) d.SetPartial("xray_tracing_enabled") d.SetPartial("variables") - if waitForCache && *out.CacheClusterStatus != "NOT_AVAILABLE" { + if waitForCache && *out.CacheClusterStatus != apigateway.CacheClusterStatusNotAvailable { stateConf := &resource.StateChangeConf{ Pending: []string{ - "CREATE_IN_PROGRESS", - "FLUSH_IN_PROGRESS", + apigateway.CacheClusterStatusCreateInProgress, + apigateway.CacheClusterStatusFlushInProgress, }, Target: []string{ - "AVAILABLE", + apigateway.CacheClusterStatusAvailable, // There's an AWS API bug (raised & confirmed in Sep 2016 by support) // which causes the stage to remain in deletion state forever - "DELETE_IN_PROGRESS", + apigateway.CacheClusterStatusDeleteInProgress, }, Refresh: apiGatewayStageCacheRefreshFunc(conn, d.Get("rest_api_id").(string), diff --git a/aws/resource_aws_api_gateway_stage_test.go b/aws/resource_aws_api_gateway_stage_test.go index 9ea0ba46cb6..e0510e51794 100644 --- a/aws/resource_aws_api_gateway_stage_test.go +++ b/aws/resource_aws_api_gateway_stage_test.go @@ -16,6 +16,7 @@ import ( func TestAccAWSAPIGatewayStage_basic(t *testing.T) { var conf apigateway.Stage rName := acctest.RandString(5) + resourceName := "aws_api_gateway_stage.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -25,43 +26,51 @@ func TestAccAWSAPIGatewayStage_basic(t *testing.T) { { Config: testAccAWSAPIGatewayStageConfig_basic(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAPIGatewayStageExists("aws_api_gateway_stage.test", &conf), - resource.TestCheckResourceAttr("aws_api_gateway_stage.test", "stage_name", "prod"), - resource.TestCheckResourceAttr("aws_api_gateway_stage.test", "cache_cluster_enabled", "true"), - resource.TestCheckResourceAttr("aws_api_gateway_stage.test", "cache_cluster_size", "0.5"), - resource.TestCheckResourceAttr("aws_api_gateway_stage.test", "tags.%", "1"), - resource.TestCheckResourceAttrSet("aws_api_gateway_stage.test", "execution_arn"), - resource.TestCheckResourceAttrSet("aws_api_gateway_stage.test", "invoke_url"), - resource.TestCheckResourceAttr("aws_api_gateway_stage.test", "xray_tracing_enabled", "true"), + testAccCheckAWSAPIGatewayStageExists(resourceName, &conf), + testAccMatchResourceAttrRegionalARNNoAccount(resourceName, "arn", "apigateway", regexp.MustCompile(`/restapis/.+/stages/prod`)), + resource.TestCheckResourceAttr(resourceName, "stage_name", "prod"), + resource.TestCheckResourceAttr(resourceName, "cache_cluster_enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "cache_cluster_size", "0.5"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.Name", "tf-test"), + resource.TestCheckResourceAttrSet(resourceName, "execution_arn"), + resource.TestCheckResourceAttrSet(resourceName, "invoke_url"), + resource.TestCheckResourceAttr(resourceName, "xray_tracing_enabled", "true"), ), }, { - ResourceName: "aws_api_gateway_stage.test", + ResourceName: resourceName, ImportState: true, - ImportStateIdFunc: testAccAWSAPIGatewayStageImportStateIdFunc("aws_api_gateway_stage.test"), + ImportStateIdFunc: testAccAWSAPIGatewayStageImportStateIdFunc(resourceName), ImportStateVerify: true, }, { Config: testAccAWSAPIGatewayStageConfig_updated(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAPIGatewayStageExists("aws_api_gateway_stage.test", &conf), - resource.TestCheckResourceAttr("aws_api_gateway_stage.test", "stage_name", "prod"), - resource.TestCheckResourceAttr("aws_api_gateway_stage.test", "cache_cluster_enabled", "false"), - resource.TestCheckResourceAttr("aws_api_gateway_stage.test", "tags.%", "2"), - resource.TestCheckResourceAttr("aws_api_gateway_stage.test", "xray_tracing_enabled", "false"), + testAccCheckAWSAPIGatewayStageExists(resourceName, &conf), + testAccMatchResourceAttrRegionalARNNoAccount(resourceName, "arn", "apigateway", regexp.MustCompile(`/restapis/.+/stages/prod`)), + resource.TestCheckResourceAttr(resourceName, "stage_name", "prod"), + resource.TestCheckResourceAttr(resourceName, "cache_cluster_enabled", "false"), + resource.TestCheckResourceAttr(resourceName, "tags.Name", "tf-test"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.Name", "tf-test"), + resource.TestCheckResourceAttr(resourceName, "tags.ExtraName", "tf-test"), + resource.TestCheckResourceAttr(resourceName, "xray_tracing_enabled", "false"), ), }, { Config: testAccAWSAPIGatewayStageConfig_basic(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAPIGatewayStageExists("aws_api_gateway_stage.test", &conf), - resource.TestCheckResourceAttr("aws_api_gateway_stage.test", "stage_name", "prod"), - resource.TestCheckResourceAttr("aws_api_gateway_stage.test", "cache_cluster_enabled", "true"), - resource.TestCheckResourceAttr("aws_api_gateway_stage.test", "cache_cluster_size", "0.5"), - resource.TestCheckResourceAttr("aws_api_gateway_stage.test", "tags.%", "1"), - resource.TestCheckResourceAttrSet("aws_api_gateway_stage.test", "execution_arn"), - resource.TestCheckResourceAttrSet("aws_api_gateway_stage.test", "invoke_url"), - resource.TestCheckResourceAttr("aws_api_gateway_stage.test", "xray_tracing_enabled", "true"), + testAccCheckAWSAPIGatewayStageExists(resourceName, &conf), + testAccMatchResourceAttrRegionalARNNoAccount(resourceName, "arn", "apigateway", regexp.MustCompile(`/restapis/.+/stages/prod`)), + resource.TestCheckResourceAttr(resourceName, "stage_name", "prod"), + resource.TestCheckResourceAttr(resourceName, "cache_cluster_enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "cache_cluster_size", "0.5"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.Name", "tf-test"), + resource.TestCheckResourceAttrSet(resourceName, "execution_arn"), + resource.TestCheckResourceAttrSet(resourceName, "invoke_url"), + resource.TestCheckResourceAttr(resourceName, "xray_tracing_enabled", "true"), ), }, }, @@ -71,6 +80,7 @@ func TestAccAWSAPIGatewayStage_basic(t *testing.T) { func TestAccAWSAPIGatewayStage_accessLogSettings(t *testing.T) { var conf apigateway.Stage rName := acctest.RandString(5) + resourceName := "aws_api_gateway_stage.test" logGroupArnRegex := regexp.MustCompile(fmt.Sprintf("^arn:[^:]+:logs:[^:]+:[^:]+:log-group:foo-bar-%s$", rName)) clf := `$context.identity.sourceIp $context.identity.caller $context.identity.user [$context.requestTime] "$context.httpMethod $context.resourcePath $context.protocol" $context.status $context.responseLength $context.requestId` json := `{ "requestId":"$context.requestId", "ip": "$context.identity.sourceIp", "caller":"$context.identity.caller", "user":"$context.identity.user", "requestTime":"$context.requestTime", "httpMethod":"$context.httpMethod", "resourcePath":"$context.resourcePath", "status":"$context.status", "protocol":"$context.protocol", "responseLength":"$context.responseLength" }` @@ -85,45 +95,50 @@ func TestAccAWSAPIGatewayStage_accessLogSettings(t *testing.T) { { Config: testAccAWSAPIGatewayStageConfig_accessLogSettings(rName, clf), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAPIGatewayStageExists("aws_api_gateway_stage.test", &conf), - resource.TestCheckResourceAttr("aws_api_gateway_stage.test", "access_log_settings.#", "1"), - resource.TestMatchResourceAttr("aws_api_gateway_stage.test", "access_log_settings.0.destination_arn", logGroupArnRegex), - resource.TestCheckResourceAttr("aws_api_gateway_stage.test", "access_log_settings.0.format", clf), + testAccCheckAWSAPIGatewayStageExists(resourceName, &conf), + testAccMatchResourceAttrRegionalARNNoAccount(resourceName, "arn", "apigateway", regexp.MustCompile(`/restapis/.+/stages/prod`)), + resource.TestCheckResourceAttr(resourceName, "access_log_settings.#", "1"), + resource.TestMatchResourceAttr(resourceName, "access_log_settings.0.destination_arn", logGroupArnRegex), + resource.TestCheckResourceAttr(resourceName, "access_log_settings.0.format", clf), ), }, { Config: testAccAWSAPIGatewayStageConfig_accessLogSettings(rName, json), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAPIGatewayStageExists("aws_api_gateway_stage.test", &conf), - resource.TestCheckResourceAttr("aws_api_gateway_stage.test", "access_log_settings.#", "1"), - resource.TestMatchResourceAttr("aws_api_gateway_stage.test", "access_log_settings.0.destination_arn", logGroupArnRegex), - resource.TestCheckResourceAttr("aws_api_gateway_stage.test", "access_log_settings.0.format", json), + testAccCheckAWSAPIGatewayStageExists(resourceName, &conf), + testAccMatchResourceAttrRegionalARNNoAccount(resourceName, "arn", "apigateway", regexp.MustCompile(`/restapis/.+/stages/prod`)), + resource.TestCheckResourceAttr(resourceName, "access_log_settings.#", "1"), + resource.TestMatchResourceAttr(resourceName, "access_log_settings.0.destination_arn", logGroupArnRegex), + resource.TestCheckResourceAttr(resourceName, "access_log_settings.0.format", json), ), }, { Config: testAccAWSAPIGatewayStageConfig_accessLogSettings(rName, xml), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAPIGatewayStageExists("aws_api_gateway_stage.test", &conf), - resource.TestCheckResourceAttr("aws_api_gateway_stage.test", "access_log_settings.#", "1"), - resource.TestMatchResourceAttr("aws_api_gateway_stage.test", "access_log_settings.0.destination_arn", logGroupArnRegex), - resource.TestCheckResourceAttr("aws_api_gateway_stage.test", "access_log_settings.0.format", xml), + testAccCheckAWSAPIGatewayStageExists(resourceName, &conf), + testAccMatchResourceAttrRegionalARNNoAccount(resourceName, "arn", "apigateway", regexp.MustCompile(`/restapis/.+/stages/prod`)), + resource.TestCheckResourceAttr(resourceName, "access_log_settings.#", "1"), + resource.TestMatchResourceAttr(resourceName, "access_log_settings.0.destination_arn", logGroupArnRegex), + resource.TestCheckResourceAttr(resourceName, "access_log_settings.0.format", xml), ), }, { Config: testAccAWSAPIGatewayStageConfig_accessLogSettings(rName, csv), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAPIGatewayStageExists("aws_api_gateway_stage.test", &conf), - resource.TestCheckResourceAttr("aws_api_gateway_stage.test", "access_log_settings.#", "1"), - resource.TestMatchResourceAttr("aws_api_gateway_stage.test", "access_log_settings.0.destination_arn", logGroupArnRegex), - resource.TestCheckResourceAttr("aws_api_gateway_stage.test", "access_log_settings.0.format", csv), + testAccCheckAWSAPIGatewayStageExists(resourceName, &conf), + testAccMatchResourceAttrRegionalARNNoAccount(resourceName, "arn", "apigateway", regexp.MustCompile(`/restapis/.+/stages/prod`)), + resource.TestCheckResourceAttr(resourceName, "access_log_settings.#", "1"), + resource.TestMatchResourceAttr(resourceName, "access_log_settings.0.destination_arn", logGroupArnRegex), + resource.TestCheckResourceAttr(resourceName, "access_log_settings.0.format", csv), ), }, { Config: testAccAWSAPIGatewayStageConfig_basic(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAPIGatewayStageExists("aws_api_gateway_stage.test", &conf), - resource.TestCheckResourceAttr("aws_api_gateway_stage.test", "access_log_settings.#", "0"), + testAccCheckAWSAPIGatewayStageExists(resourceName, &conf), + testAccMatchResourceAttrRegionalARNNoAccount(resourceName, "arn", "apigateway", regexp.MustCompile(`/restapis/.+/stages/prod`)), + resource.TestCheckResourceAttr(resourceName, "access_log_settings.#", "0"), ), }, }, @@ -179,7 +194,7 @@ func testAccCheckAWSAPIGatewayStageDestroy(s *terraform.State) error { if !ok { return err } - if awsErr.Code() != "NotFoundException" { + if awsErr.Code() != apigateway.ErrCodeNotFoundException { return err } diff --git a/aws/tags_apigateway.go b/aws/tags_apigateway.go deleted file mode 100644 index 56645afd45a..00000000000 --- a/aws/tags_apigateway.go +++ /dev/null @@ -1,44 +0,0 @@ -package aws - -import ( - "log" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/apigateway" - "github.com/hashicorp/terraform-plugin-sdk/helper/schema" -) - -func setTagsAPIGatewayStage(conn *apigateway.APIGateway, d *schema.ResourceData, arn string) error { - if d.HasChange("tags") { - oraw, nraw := d.GetChange("tags") - o := oraw.(map[string]interface{}) - n := nraw.(map[string]interface{}) - create, remove := diffTagsGeneric(o, n) - if len(remove) > 0 { - log.Printf("[DEBUG] Removing tags: %#v", remove) - keys := make([]*string, 0, len(remove)) - for k := range remove { - keys = append(keys, aws.String(k)) - } - - _, err := conn.UntagResource(&apigateway.UntagResourceInput{ - ResourceArn: aws.String(arn), - TagKeys: keys, - }) - if err != nil { - return err - } - } - if len(create) > 0 { - log.Printf("[DEBUG] Creating tags: %#v", create) - _, err := conn.TagResource(&apigateway.TagResourceInput{ - ResourceArn: aws.String(arn), - Tags: create, - }) - if err != nil { - return err - } - } - } - return nil -} diff --git a/website/docs/r/api_gateway_stage.html.markdown b/website/docs/r/api_gateway_stage.html.markdown index 8ca91efb931..b1fef7537fb 100644 --- a/website/docs/r/api_gateway_stage.html.markdown +++ b/website/docs/r/api_gateway_stage.html.markdown @@ -130,6 +130,7 @@ In addition to all arguments above, the following attributes are exported: * `execution_arn` - The execution ARN to be used in [`lambda_permission`](/docs/providers/aws/r/lambda_permission.html)'s `source_arn` when allowing API Gateway to invoke a Lambda function, e.g. `arn:aws:execute-api:eu-west-2:123456789012:z4675bid1j/prod` + * `arn` - Amazon Resource Name (ARN) ## Import