From 4606090c32dd69da5b7bcd5be5b1165f96344f3e Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Wed, 12 Apr 2017 16:13:33 +0100 Subject: [PATCH] provider/aws: Add support for api_gateway_method_settings (#13542) --- builtin/providers/aws/provider.go | 1 + ...esource_aws_api_gateway_method_settings.go | 248 ++++++++++++++++ ...ce_aws_api_gateway_method_settings_test.go | 265 ++++++++++++++++++ .../api_gateway_method_settings.html.markdown | 93 ++++++ website/source/layouts/aws.erb | 3 + 5 files changed, 610 insertions(+) create mode 100644 builtin/providers/aws/resource_aws_api_gateway_method_settings.go create mode 100644 builtin/providers/aws/resource_aws_api_gateway_method_settings_test.go create mode 100644 website/source/docs/providers/aws/r/api_gateway_method_settings.html.markdown diff --git a/builtin/providers/aws/provider.go b/builtin/providers/aws/provider.go index 9a2f7e5ff0..d086dedb3b 100644 --- a/builtin/providers/aws/provider.go +++ b/builtin/providers/aws/provider.go @@ -218,6 +218,7 @@ func Provider() terraform.ResourceProvider { "aws_api_gateway_integration_response": resourceAwsApiGatewayIntegrationResponse(), "aws_api_gateway_method": resourceAwsApiGatewayMethod(), "aws_api_gateway_method_response": resourceAwsApiGatewayMethodResponse(), + "aws_api_gateway_method_settings": resourceAwsApiGatewayMethodSettings(), "aws_api_gateway_model": resourceAwsApiGatewayModel(), "aws_api_gateway_resource": resourceAwsApiGatewayResource(), "aws_api_gateway_rest_api": resourceAwsApiGatewayRestApi(), diff --git a/builtin/providers/aws/resource_aws_api_gateway_method_settings.go b/builtin/providers/aws/resource_aws_api_gateway_method_settings.go new file mode 100644 index 0000000000..06d5efd014 --- /dev/null +++ b/builtin/providers/aws/resource_aws_api_gateway_method_settings.go @@ -0,0 +1,248 @@ +package aws + +import ( + "fmt" + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/apigateway" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAwsApiGatewayMethodSettings() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsApiGatewayMethodSettingsUpdate, + Read: resourceAwsApiGatewayMethodSettingsRead, + Update: resourceAwsApiGatewayMethodSettingsUpdate, + Delete: resourceAwsApiGatewayMethodSettingsDelete, + + Schema: map[string]*schema.Schema{ + "rest_api_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "stage_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "method_path": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "settings": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "metrics_enabled": { + Type: schema.TypeBool, + Optional: true, + }, + "logging_level": { + Type: schema.TypeString, + Optional: true, + }, + "data_trace_enabled": { + Type: schema.TypeBool, + Optional: true, + }, + "throttling_burst_limit": { + Type: schema.TypeInt, + Optional: true, + }, + "throttling_rate_limit": { + Type: schema.TypeFloat, + Optional: true, + }, + "caching_enabled": { + Type: schema.TypeBool, + Optional: true, + }, + "cache_ttl_in_seconds": { + Type: schema.TypeInt, + Optional: true, + }, + "cache_data_encrypted": { + Type: schema.TypeBool, + Optional: true, + }, + "require_authorization_for_cache_control": { + Type: schema.TypeBool, + Optional: true, + }, + "unauthorized_cache_control_header_strategy": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + }, + } +} + +func resourceAwsApiGatewayMethodSettingsRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).apigateway + + log.Printf("[DEBUG] Reading API Gateway Method Settings %s", d.Id()) + input := apigateway.GetStageInput{ + RestApiId: aws.String(d.Get("rest_api_id").(string)), + StageName: aws.String(d.Get("stage_name").(string)), + } + stage, err := conn.GetStage(&input) + if err != nil { + if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NotFoundException" { + log.Printf("[WARN] API Gateway Stage %s not found, removing method settings", d.Id()) + d.SetId("") + return nil + } + return err + } + log.Printf("[DEBUG] Received API Gateway Stage: %s", stage) + + methodPath := d.Get("method_path").(string) + settings, ok := stage.MethodSettings[methodPath] + if !ok { + log.Printf("[WARN] API Gateway Method Settings for %q not found, removing", methodPath) + d.SetId("") + return nil + } + + d.Set("settings.0.metrics_enabled", settings.MetricsEnabled) + d.Set("settings.0.logging_level", settings.LoggingLevel) + d.Set("settings.0.data_trace_enabled", settings.DataTraceEnabled) + d.Set("settings.0.throttling_burst_limit", settings.ThrottlingBurstLimit) + d.Set("settings.0.throttling_rate_limit", settings.ThrottlingRateLimit) + d.Set("settings.0.caching_enabled", settings.CachingEnabled) + d.Set("settings.0.cache_ttl_in_seconds", settings.CacheTtlInSeconds) + d.Set("settings.0.cache_data_encrypted", settings.CacheDataEncrypted) + d.Set("settings.0.require_authorization_for_cache_control", settings.RequireAuthorizationForCacheControl) + d.Set("settings.0.unauthorized_cache_control_header_strategy", settings.UnauthorizedCacheControlHeaderStrategy) + + return nil +} + +func resourceAwsApiGatewayMethodSettingsUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).apigateway + + methodPath := d.Get("method_path").(string) + prefix := fmt.Sprintf("/%s/", methodPath) + + ops := make([]*apigateway.PatchOperation, 0) + if d.HasChange("settings.0.metrics_enabled") { + ops = append(ops, &apigateway.PatchOperation{ + Op: aws.String("replace"), + Path: aws.String(prefix + "metrics/enabled"), + Value: aws.String(fmt.Sprintf("%t", d.Get("settings.0.metrics_enabled").(bool))), + }) + } + if d.HasChange("settings.0.logging_level") { + ops = append(ops, &apigateway.PatchOperation{ + Op: aws.String("replace"), + Path: aws.String(prefix + "logging/loglevel"), + Value: aws.String(d.Get("settings.0.logging_level").(string)), + }) + } + if d.HasChange("settings.0.data_trace_enabled") { + ops = append(ops, &apigateway.PatchOperation{ + Op: aws.String("replace"), + Path: aws.String(prefix + "logging/dataTrace"), + Value: aws.String(fmt.Sprintf("%t", d.Get("settings.0.data_trace_enabled").(bool))), + }) + } + + if d.HasChange("settings.0.throttling_burst_limit") { + ops = append(ops, &apigateway.PatchOperation{ + Op: aws.String("replace"), + Path: aws.String(prefix + "throttling/burstLimit"), + Value: aws.String(fmt.Sprintf("%d", d.Get("settings.0.throttling_burst_limit").(int))), + }) + } + if d.HasChange("settings.0.throttling_rate_limit") { + ops = append(ops, &apigateway.PatchOperation{ + Op: aws.String("replace"), + Path: aws.String(prefix + "throttling/rateLimit"), + Value: aws.String(fmt.Sprintf("%f", d.Get("settings.0.throttling_rate_limit").(float64))), + }) + } + if d.HasChange("settings.0.caching_enabled") { + ops = append(ops, &apigateway.PatchOperation{ + Op: aws.String("replace"), + Path: aws.String(prefix + "caching/enabled"), + Value: aws.String(fmt.Sprintf("%t", d.Get("settings.0.caching_enabled").(bool))), + }) + } + if d.HasChange("settings.0.cache_ttl_in_seconds") { + ops = append(ops, &apigateway.PatchOperation{ + Op: aws.String("replace"), + Path: aws.String(prefix + "caching/ttlInSeconds"), + Value: aws.String(fmt.Sprintf("%d", d.Get("settings.0.cache_ttl_in_seconds").(int))), + }) + } + if d.HasChange("settings.0.cache_data_encrypted") { + ops = append(ops, &apigateway.PatchOperation{ + Op: aws.String("replace"), + Path: aws.String(prefix + "caching/dataEncrypted"), + Value: aws.String(fmt.Sprintf("%d", d.Get("settings.0.cache_data_encrypted").(int))), + }) + } + if d.HasChange("settings.0.require_authorization_for_cache_control") { + ops = append(ops, &apigateway.PatchOperation{ + Op: aws.String("replace"), + Path: aws.String(prefix + "caching/requireAuthorizationForCacheControl"), + Value: aws.String(fmt.Sprintf("%t", d.Get("settings.0.require_authorization_for_cache_control").(bool))), + }) + } + if d.HasChange("settings.0.unauthorized_cache_control_header_strategy") { + ops = append(ops, &apigateway.PatchOperation{ + Op: aws.String("replace"), + Path: aws.String(prefix + "caching/unauthorizedCacheControlHeaderStrategy"), + Value: aws.String(d.Get("settings.0.unauthorized_cache_control_header_strategy").(string)), + }) + } + + restApiId := d.Get("rest_api_id").(string) + stageName := d.Get("stage_name").(string) + input := apigateway.UpdateStageInput{ + RestApiId: aws.String(restApiId), + StageName: aws.String(stageName), + PatchOperations: ops, + } + log.Printf("[DEBUG] Updating API Gateway Stage: %s", input) + _, err := conn.UpdateStage(&input) + if err != nil { + return fmt.Errorf("Updating API Gateway Stage failed: %s", err) + } + + d.SetId(restApiId + "-" + stageName + "-" + methodPath) + + return resourceAwsApiGatewayMethodSettingsRead(d, meta) +} + +func resourceAwsApiGatewayMethodSettingsDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).apigateway + log.Printf("[DEBUG] Deleting API Gateway Method Settings: %s", d.Id()) + + input := apigateway.UpdateStageInput{ + RestApiId: aws.String(d.Get("rest_api_id").(string)), + StageName: aws.String(d.Get("stage_name").(string)), + PatchOperations: []*apigateway.PatchOperation{ + { + Op: aws.String("remove"), + Path: aws.String(fmt.Sprintf("/%s", d.Get("method_path").(string))), + }, + }, + } + log.Printf("[DEBUG] Updating API Gateway Stage: %s", input) + _, err := conn.UpdateStage(&input) + if err != nil { + return fmt.Errorf("Updating API Gateway Stage failed: %s", err) + } + + return nil +} diff --git a/builtin/providers/aws/resource_aws_api_gateway_method_settings_test.go b/builtin/providers/aws/resource_aws_api_gateway_method_settings_test.go new file mode 100644 index 0000000000..9372a6748c --- /dev/null +++ b/builtin/providers/aws/resource_aws_api_gateway_method_settings_test.go @@ -0,0 +1,265 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/apigateway" + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSAPIGatewayMethodSettings_basic(t *testing.T) { + var stage apigateway.Stage + rInt := acctest.RandInt() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSAPIGatewayMethodSettingsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAPIGatewayMethodSettingsConfig(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAPIGatewayMethodSettingsExists("aws_api_gateway_method_settings.test", &stage), + testAccCheckAWSAPIGatewayMethodSettings_metricsEnabled(&stage, "test/GET", true), + testAccCheckAWSAPIGatewayMethodSettings_loggingLevel(&stage, "test/GET", "INFO"), + resource.TestCheckResourceAttr("aws_api_gateway_method_settings.test", "settings.#", "1"), + resource.TestCheckResourceAttr("aws_api_gateway_method_settings.test", "settings.0.metrics_enabled", "true"), + resource.TestCheckResourceAttr("aws_api_gateway_method_settings.test", "settings.0.logging_level", "INFO"), + ), + }, + + { + Config: testAccAWSAPIGatewayMethodSettingsConfigUpdate(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAPIGatewayMethodSettingsExists("aws_api_gateway_method_settings.test", &stage), + testAccCheckAWSAPIGatewayMethodSettings_metricsEnabled(&stage, "test/GET", false), + testAccCheckAWSAPIGatewayMethodSettings_loggingLevel(&stage, "test/GET", "OFF"), + resource.TestCheckResourceAttr("aws_api_gateway_method_settings.test", "settings.#", "1"), + resource.TestCheckResourceAttr("aws_api_gateway_method_settings.test", "settings.0.metrics_enabled", "false"), + resource.TestCheckResourceAttr("aws_api_gateway_method_settings.test", "settings.0.logging_level", "OFF"), + ), + }, + }, + }) +} + +func testAccCheckAWSAPIGatewayMethodSettings_metricsEnabled(conf *apigateway.Stage, path string, expected bool) resource.TestCheckFunc { + return func(s *terraform.State) error { + settings, ok := conf.MethodSettings[path] + if !ok { + return fmt.Errorf("Expected to find method settings for %q", path) + } + + if expected && *settings.MetricsEnabled != expected { + return fmt.Errorf("Expected metrics to be enabled, got %t", *settings.MetricsEnabled) + } + if !expected && *settings.MetricsEnabled != expected { + return fmt.Errorf("Expected metrics to be disabled, got %t", *settings.MetricsEnabled) + } + + return nil + } +} + +func testAccCheckAWSAPIGatewayMethodSettings_loggingLevel(conf *apigateway.Stage, path string, expectedLevel string) resource.TestCheckFunc { + return func(s *terraform.State) error { + settings, ok := conf.MethodSettings[path] + if !ok { + return fmt.Errorf("Expected to find method settings for %q", path) + } + + if *settings.LoggingLevel != expectedLevel { + return fmt.Errorf("Expected logging level to match %q, got %q", expectedLevel, *settings.LoggingLevel) + } + + return nil + } +} + +func testAccCheckAWSAPIGatewayMethodSettingsExists(n string, res *apigateway.Stage) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No API Gateway Stage ID is set") + } + + conn := testAccProvider.Meta().(*AWSClient).apigateway + + req := &apigateway.GetStageInput{ + StageName: aws.String(s.RootModule().Resources["aws_api_gateway_deployment.test"].Primary.Attributes["stage_name"]), + RestApiId: aws.String(s.RootModule().Resources["aws_api_gateway_rest_api.test"].Primary.ID), + } + out, err := conn.GetStage(req) + if err != nil { + return err + } + + *res = *out + + return nil + } +} + +func testAccCheckAWSAPIGatewayMethodSettingsDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).apigateway + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_api_gateway_method_settings" { + continue + } + + req := &apigateway.GetStageInput{ + StageName: aws.String(s.RootModule().Resources["aws_api_gateway_deployment.test"].Primary.Attributes["stage_name"]), + RestApiId: aws.String(s.RootModule().Resources["aws_api_gateway_rest_api.test"].Primary.ID), + } + out, err := conn.GetStage(req) + if err == nil { + return fmt.Errorf("API Gateway Stage still exists: %s", out) + } + + awsErr, ok := err.(awserr.Error) + if !ok { + return err + } + if awsErr.Code() != "NotFoundException" { + return err + } + + return nil + } + + return nil +} + +func testAccAWSAPIGatewayMethodSettingsConfig(rInt int) string { + return fmt.Sprintf(` +resource "aws_api_gateway_rest_api" "test" { + name = "tf-acc-test-apig-method-%d" +} + +resource "aws_api_gateway_resource" "test" { + rest_api_id = "${aws_api_gateway_rest_api.test.id}" + parent_id = "${aws_api_gateway_rest_api.test.root_resource_id}" + path_part = "test" +} + +resource "aws_api_gateway_method" "test" { + rest_api_id = "${aws_api_gateway_rest_api.test.id}" + resource_id = "${aws_api_gateway_resource.test.id}" + http_method = "GET" + authorization = "NONE" + + request_models = { + "application/json" = "Error" + } + + request_parameters = { + "method.request.header.Content-Type" = false, + "method.request.querystring.page" = true + } +} + +resource "aws_api_gateway_integration" "test" { + rest_api_id = "${aws_api_gateway_rest_api.test.id}" + resource_id = "${aws_api_gateway_resource.test.id}" + http_method = "${aws_api_gateway_method.test.http_method}" + type = "MOCK" + + request_templates { + "application/xml" = <> aws_api_gateway_method_response + > + aws_api_gateway_method_settings + > aws_api_gateway_model