From 8195acf3957b1c961ad5db8ef2f508e48178300b Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Tue, 9 Jan 2018 15:03:15 +0000 Subject: [PATCH 01/88] test/aws_config_*: Add sweepers --- ..._aws_config_configuration_recorder_test.go | 47 +++++++++++++++++++ ...source_aws_config_delivery_channel_test.go | 43 +++++++++++++++++ 2 files changed, 90 insertions(+) diff --git a/aws/resource_aws_config_configuration_recorder_test.go b/aws/resource_aws_config_configuration_recorder_test.go index 8180f669408..395f2288857 100644 --- a/aws/resource_aws_config_configuration_recorder_test.go +++ b/aws/resource_aws_config_configuration_recorder_test.go @@ -2,6 +2,7 @@ package aws import ( "fmt" + "log" "regexp" "testing" @@ -12,6 +13,52 @@ import ( "github.com/hashicorp/terraform/terraform" ) +func init() { + resource.AddTestSweepers("aws_config_configuration_recorder", &resource.Sweeper{ + Name: "aws_config_configuration_recorder", + F: testSweepConfigConfigurationRecorder, + }) +} + +func testSweepConfigConfigurationRecorder(region string) error { + client, err := sharedClientForRegion(region) + if err != nil { + return fmt.Errorf("error getting client: %s", err) + } + conn := client.(*AWSClient).configconn + + req := &configservice.DescribeConfigurationRecordersInput{} + resp, err := conn.DescribeConfigurationRecorders(req) + if err != nil { + return fmt.Errorf("Error describing Configuration Recorders: %s", err) + } + + if len(resp.ConfigurationRecorders) == 0 { + log.Print("[DEBUG] No AWS Config Configuration Recorder to sweep") + return nil + } + + for _, cr := range resp.ConfigurationRecorders { + _, err := conn.StopConfigurationRecorder(&configservice.StopConfigurationRecorderInput{ + ConfigurationRecorderName: cr.Name, + }) + if err != nil { + return err + } + + _, err = conn.DeleteConfigurationRecorder(&configservice.DeleteConfigurationRecorderInput{ + ConfigurationRecorderName: cr.Name, + }) + if err != nil { + return fmt.Errorf( + "Error deleting Configuration Recorder (%s): %s", + *cr.Name, err) + } + } + + return nil +} + func testAccConfigConfigurationRecorder_basic(t *testing.T) { var cr configservice.ConfigurationRecorder rInt := acctest.RandInt() diff --git a/aws/resource_aws_config_delivery_channel_test.go b/aws/resource_aws_config_delivery_channel_test.go index 83f0da00a21..64f701c790a 100644 --- a/aws/resource_aws_config_delivery_channel_test.go +++ b/aws/resource_aws_config_delivery_channel_test.go @@ -2,6 +2,7 @@ package aws import ( "fmt" + "log" "regexp" "testing" @@ -12,6 +13,48 @@ import ( "github.com/hashicorp/terraform/terraform" ) +func init() { + resource.AddTestSweepers("aws_config_delivery_channel", &resource.Sweeper{ + Name: "aws_config_delivery_channel", + Dependencies: []string{ + "aws_config_configuration_recorder", + }, + F: testSweepConfigDeliveryChannels, + }) +} + +func testSweepConfigDeliveryChannels(region string) error { + client, err := sharedClientForRegion(region) + if err != nil { + return fmt.Errorf("error getting client: %s", err) + } + conn := client.(*AWSClient).configconn + + req := &configservice.DescribeDeliveryChannelsInput{} + resp, err := conn.DescribeDeliveryChannels(req) + if err != nil { + return fmt.Errorf("Error describing Delivery Channels: %s", err) + } + + if len(resp.DeliveryChannels) == 0 { + log.Print("[DEBUG] No AWS Config Delivery Channel to sweep") + return nil + } + + for _, dc := range resp.DeliveryChannels { + _, err := conn.DeleteDeliveryChannel(&configservice.DeleteDeliveryChannelInput{ + DeliveryChannelName: dc.Name, + }) + if err != nil { + return fmt.Errorf( + "Error deleting Delivery Channel (%s): %s", + *dc.Name, err) + } + } + + return nil +} + func testAccConfigDeliveryChannel_basic(t *testing.T) { var dc configservice.DeliveryChannel rInt := acctest.RandInt() From 99bef5d3236665e122fe078bb222f6d037c55d7a Mon Sep 17 00:00:00 2001 From: Jimmy Date: Tue, 9 Jan 2018 16:22:54 -0500 Subject: [PATCH 02/88] Ensure Elem is always either a *Schema or *Resource --- aws/data_source_aws_ecs_container_definition.go | 4 ++-- aws/data_source_aws_vpc_peering_connection.go | 4 ++-- aws/resource_aws_api_gateway_deployment.go | 2 +- aws/resource_aws_api_gateway_gateway_response.go | 4 ++-- aws/resource_aws_api_gateway_integration.go | 4 ++-- aws/resource_aws_api_gateway_integration_response.go | 4 ++-- aws/resource_aws_api_gateway_method.go | 4 ++-- aws/resource_aws_api_gateway_method_response.go | 4 ++-- aws/resource_aws_batch_job_definition.go | 2 +- aws/resource_aws_lambda_function.go | 2 +- 10 files changed, 17 insertions(+), 17 deletions(-) diff --git a/aws/data_source_aws_ecs_container_definition.go b/aws/data_source_aws_ecs_container_definition.go index 9bb99cdaeda..914d582649a 100644 --- a/aws/data_source_aws_ecs_container_definition.go +++ b/aws/data_source_aws_ecs_container_definition.go @@ -53,12 +53,12 @@ func dataSourceAwsEcsContainerDefinition() *schema.Resource { "docker_labels": { Type: schema.TypeMap, Computed: true, - Elem: schema.TypeString, + Elem: &schema.Schema{Type: schema.TypeString}, }, "environment": { Type: schema.TypeMap, Computed: true, - Elem: schema.TypeString, + Elem: &schema.Schema{Type: schema.TypeString}, }, }, } diff --git a/aws/data_source_aws_vpc_peering_connection.go b/aws/data_source_aws_vpc_peering_connection.go index a78e35f03e7..cf4dbf54d3c 100644 --- a/aws/data_source_aws_vpc_peering_connection.go +++ b/aws/data_source_aws_vpc_peering_connection.go @@ -67,12 +67,12 @@ func dataSourceAwsVpcPeeringConnection() *schema.Resource { "accepter": { Type: schema.TypeMap, Computed: true, - Elem: schema.TypeBool, + Elem: &schema.Schema{Type: schema.TypeBool}, }, "requester": { Type: schema.TypeMap, Computed: true, - Elem: schema.TypeBool, + Elem: &schema.Schema{Type: schema.TypeBool}, }, "filter": ec2CustomFiltersSchema(), "tags": tagsSchemaComputed(), diff --git a/aws/resource_aws_api_gateway_deployment.go b/aws/resource_aws_api_gateway_deployment.go index 4e291b780e8..aef612a6ed6 100644 --- a/aws/resource_aws_api_gateway_deployment.go +++ b/aws/resource_aws_api_gateway_deployment.go @@ -47,7 +47,7 @@ func resourceAwsApiGatewayDeployment() *schema.Resource { Type: schema.TypeMap, Optional: true, ForceNew: true, - Elem: schema.TypeString, + Elem: &schema.Schema{Type: schema.TypeString}, }, "created_date": { diff --git a/aws/resource_aws_api_gateway_gateway_response.go b/aws/resource_aws_api_gateway_gateway_response.go index 105d1227f24..bf9639960d1 100644 --- a/aws/resource_aws_api_gateway_gateway_response.go +++ b/aws/resource_aws_api_gateway_gateway_response.go @@ -39,13 +39,13 @@ func resourceAwsApiGatewayGatewayResponse() *schema.Resource { "response_templates": { Type: schema.TypeMap, - Elem: schema.TypeString, + Elem: &schema.Schema{Type: schema.TypeString}, Optional: true, }, "response_parameters": { Type: schema.TypeMap, - Elem: schema.TypeString, + Elem: &schema.Schema{Type: schema.TypeString}, Optional: true, }, }, diff --git a/aws/resource_aws_api_gateway_integration.go b/aws/resource_aws_api_gateway_integration.go index 6e0390362ea..e4658455f16 100644 --- a/aws/resource_aws_api_gateway_integration.go +++ b/aws/resource_aws_api_gateway_integration.go @@ -70,12 +70,12 @@ func resourceAwsApiGatewayIntegration() *schema.Resource { "request_templates": { Type: schema.TypeMap, Optional: true, - Elem: schema.TypeString, + Elem: &schema.Schema{Type: schema.TypeString}, }, "request_parameters": { Type: schema.TypeMap, - Elem: schema.TypeString, + Elem: &schema.Schema{Type: schema.TypeString}, Optional: true, ConflictsWith: []string{"request_parameters_in_json"}, }, diff --git a/aws/resource_aws_api_gateway_integration_response.go b/aws/resource_aws_api_gateway_integration_response.go index 88cf85176bb..a37e37209a7 100644 --- a/aws/resource_aws_api_gateway_integration_response.go +++ b/aws/resource_aws_api_gateway_integration_response.go @@ -53,12 +53,12 @@ func resourceAwsApiGatewayIntegrationResponse() *schema.Resource { "response_templates": { Type: schema.TypeMap, Optional: true, - Elem: schema.TypeString, + Elem: &schema.Schema{Type: schema.TypeString}, }, "response_parameters": { Type: schema.TypeMap, - Elem: schema.TypeString, + Elem: &schema.Schema{Type: schema.TypeString}, Optional: true, ConflictsWith: []string{"response_parameters_in_json"}, }, diff --git a/aws/resource_aws_api_gateway_method.go b/aws/resource_aws_api_gateway_method.go index fdbc451098a..d39e3e62f12 100644 --- a/aws/resource_aws_api_gateway_method.go +++ b/aws/resource_aws_api_gateway_method.go @@ -60,12 +60,12 @@ func resourceAwsApiGatewayMethod() *schema.Resource { "request_models": &schema.Schema{ Type: schema.TypeMap, Optional: true, - Elem: schema.TypeString, + Elem: &schema.Schema{Type: schema.TypeString}, }, "request_parameters": &schema.Schema{ Type: schema.TypeMap, - Elem: schema.TypeBool, + Elem: &schema.Schema{Type: schema.TypeBool}, Optional: true, ConflictsWith: []string{"request_parameters_in_json"}, }, diff --git a/aws/resource_aws_api_gateway_method_response.go b/aws/resource_aws_api_gateway_method_response.go index caedc77d3d8..7bb250e4768 100644 --- a/aws/resource_aws_api_gateway_method_response.go +++ b/aws/resource_aws_api_gateway_method_response.go @@ -52,12 +52,12 @@ func resourceAwsApiGatewayMethodResponse() *schema.Resource { "response_models": &schema.Schema{ Type: schema.TypeMap, Optional: true, - Elem: schema.TypeString, + Elem: &schema.Schema{Type: schema.TypeString}, }, "response_parameters": &schema.Schema{ Type: schema.TypeMap, - Elem: schema.TypeBool, + Elem: &schema.Schema{Type: schema.TypeBool}, Optional: true, ConflictsWith: []string{"response_parameters_in_json"}, }, diff --git a/aws/resource_aws_batch_job_definition.go b/aws/resource_aws_batch_job_definition.go index 0bea6ff8ebb..2fcf4ee8f88 100644 --- a/aws/resource_aws_batch_job_definition.go +++ b/aws/resource_aws_batch_job_definition.go @@ -39,7 +39,7 @@ func resourceAwsBatchJobDefinition() *schema.Resource { Type: schema.TypeMap, Optional: true, ForceNew: true, - Elem: schema.TypeString, + Elem: &schema.Schema{Type: schema.TypeString}, }, "retry_strategy": { Type: schema.TypeList, diff --git a/aws/resource_aws_lambda_function.go b/aws/resource_aws_lambda_function.go index d16988346c6..8c55a48ee8a 100644 --- a/aws/resource_aws_lambda_function.go +++ b/aws/resource_aws_lambda_function.go @@ -170,7 +170,7 @@ func resourceAwsLambdaFunction() *schema.Resource { "variables": { Type: schema.TypeMap, Optional: true, - Elem: schema.TypeString, + Elem: &schema.Schema{Type: schema.TypeString}, }, }, }, From 0df85b80208c0c77ff35c01325436d28deda02b4 Mon Sep 17 00:00:00 2001 From: eggsbenjamin Date: Sat, 10 Feb 2018 01:26:33 +0000 Subject: [PATCH 03/88] routing_config added && tested --- aws/resource_aws_lambda_alias.go | 59 ++++++++++++ aws/resource_aws_lambda_alias_test.go | 110 ++++++++++++++++++++-- aws/test-fixtures/lambdatest_modified.zip | Bin 0 -> 393 bytes website/docs/r/lambda_alias.html.markdown | 14 ++- 4 files changed, 176 insertions(+), 7 deletions(-) create mode 100644 aws/test-fixtures/lambdatest_modified.zip diff --git a/aws/resource_aws_lambda_alias.go b/aws/resource_aws_lambda_alias.go index 083225f3ad2..b622120242f 100644 --- a/aws/resource_aws_lambda_alias.go +++ b/aws/resource_aws_lambda_alias.go @@ -1,6 +1,7 @@ package aws import ( + "errors" "fmt" "log" "strings" @@ -39,6 +40,20 @@ func resourceAwsLambdaAlias() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "routing_config": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "additional_version_weights": { + Type: schema.TypeMap, + Optional: true, + Elem: schema.TypeFloat, + }, + }, + }, + }, }, } } @@ -60,6 +75,23 @@ func resourceAwsLambdaAliasCreate(d *schema.ResourceData, meta interface{}) erro Name: aws.String(aliasName), } + if v, ok := d.GetOk("routing_config"); ok { + routingConfigs := v.([]interface{}) + routingConfig, ok := routingConfigs[0].(map[string]interface{}) + + if !ok { + return errors.New("At least one field is expected inside routing_config") + } + + if additionalVersionWeights, ok := routingConfig["additional_version_weights"]; ok { + weights := readAdditionalVersionWeights(additionalVersionWeights.(map[string]interface{})) + + params.RoutingConfig = &lambda.AliasRoutingConfiguration{ + AdditionalVersionWeights: aws.Float64Map(weights), + } + } + } + aliasConfiguration, err := conn.CreateAlias(params) if err != nil { return fmt.Errorf("Error creating Lambda alias: %s", err) @@ -97,6 +129,7 @@ func resourceAwsLambdaAliasRead(d *schema.ResourceData, meta interface{}) error d.Set("function_version", aliasConfiguration.FunctionVersion) d.Set("name", aliasConfiguration.Name) d.Set("arn", aliasConfiguration.AliasArn) + d.Set("routing_config", aliasConfiguration.RoutingConfig) return nil } @@ -137,6 +170,23 @@ func resourceAwsLambdaAliasUpdate(d *schema.ResourceData, meta interface{}) erro Name: aws.String(d.Get("name").(string)), } + if v, ok := d.GetOk("routing_config"); ok { + routingConfigs := v.([]interface{}) + routingConfig, ok := routingConfigs[0].(map[string]interface{}) + + if !ok { + return errors.New("At least one field is expected inside routing_config") + } + + if additionalVersionWeights, ok := routingConfig["additional_version_weights"]; ok { + weights := readAdditionalVersionWeights(additionalVersionWeights.(map[string]interface{})) + + params.RoutingConfig = &lambda.AliasRoutingConfiguration{ + AdditionalVersionWeights: aws.Float64Map(weights), + } + } + } + _, err := conn.UpdateAlias(params) if err != nil { return fmt.Errorf("Error updating Lambda alias: %s", err) @@ -144,3 +194,12 @@ func resourceAwsLambdaAliasUpdate(d *schema.ResourceData, meta interface{}) erro return nil } + +func readAdditionalVersionWeights(avw map[string]interface{}) map[string]float64 { + weights := make(map[string]float64) + for k, v := range avw { + weights[k] = v.(float64) + } + + return weights +} diff --git a/aws/resource_aws_lambda_alias_test.go b/aws/resource_aws_lambda_alias_test.go index f359157594d..0ebfe969caf 100644 --- a/aws/resource_aws_lambda_alias_test.go +++ b/aws/resource_aws_lambda_alias_test.go @@ -36,6 +36,16 @@ func TestAccAWSLambdaAlias_basic(t *testing.T) { regexp.MustCompile(`^arn:aws:lambda:[a-z]+-[a-z]+-[0-9]+:\d{12}:function:`+funcName+`:`+aliasName+`$`)), ), }, + resource.TestStep{ + Config: testAccAwsLambdaAliasConfigWithRoutingConfig(roleName, policyName, attachmentName, funcName, aliasName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsLambdaAliasExists("aws_lambda_alias.lambda_alias_test", &conf), + testAccCheckAwsLambdaAttributes(&conf), + testAccCheckAwsLambdaAliasRoutingConfig(&conf), + resource.TestMatchResourceAttr("aws_lambda_alias.lambda_alias_test", "arn", + regexp.MustCompile(`^arn:aws:lambda:[a-z]+-[a-z]+-[0-9]+:\d{12}:function:`+funcName+`:`+aliasName+`$`)), + ), + }, }, }) } @@ -104,6 +114,20 @@ func testAccCheckAwsLambdaAttributes(mapping *lambda.AliasConfiguration) resourc } } +func testAccCheckAwsLambdaAliasRoutingConfig(mapping *lambda.AliasConfiguration) resource.TestCheckFunc { + return func(s *terraform.State) error { + routingConfig := mapping.RoutingConfig + + if routingConfig == nil { + return fmt.Errorf("Could not read Lambda alias routing config") + } + if len(routingConfig.AdditionalVersionWeights) != 1 { + return fmt.Errorf("Could not read Lambda alias additional version weights") + } + return nil + } +} + func testAccAwsLambdaAliasConfig(roleName, policyName, attachmentName, funcName, aliasName string) string { return fmt.Sprintf(` resource "aws_iam_role" "iam_for_lambda" { @@ -154,17 +178,91 @@ resource "aws_iam_policy_attachment" "policy_attachment_for_role" { } resource "aws_lambda_function" "lambda_function_test_create" { - filename = "test-fixtures/lambdatest.zip" - function_name = "%s" - role = "${aws_iam_role.iam_for_lambda.arn}" - handler = "exports.example" - runtime = "nodejs4.3" + filename = "test-fixtures/lambdatest.zip" + function_name = "%s" + role = "${aws_iam_role.iam_for_lambda.arn}" + handler = "exports.example" + runtime = "nodejs4.3" + source_code_hash = "${base64sha256(file("test-fixtures/lambdatest.zip"))}" + publish = "true" +} + +resource "aws_lambda_alias" "lambda_alias_test" { + name = "%s" + description = "a sample description" + function_name = "${aws_lambda_function.lambda_function_test_create.arn}" + function_version = "1" +}`, roleName, policyName, attachmentName, funcName, aliasName) +} + +func testAccAwsLambdaAliasConfigWithRoutingConfig(roleName, policyName, attachmentName, funcName, aliasName string) string { + return fmt.Sprintf(` +resource "aws_iam_role" "iam_for_lambda" { + name = "%s" + + assume_role_policy = <OJa?gvY~ve$s@rcjZ{i^GYGG=J_P3HE-I{;5w)*eo4bN>_!f Date: Sat, 10 Feb 2018 01:37:10 +0000 Subject: [PATCH 04/88] fmt'd --- aws/resource_aws_lambda_alias_test.go | 30 +++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/aws/resource_aws_lambda_alias_test.go b/aws/resource_aws_lambda_alias_test.go index 0ebfe969caf..7e1c02e4755 100644 --- a/aws/resource_aws_lambda_alias_test.go +++ b/aws/resource_aws_lambda_alias_test.go @@ -178,11 +178,11 @@ resource "aws_iam_policy_attachment" "policy_attachment_for_role" { } resource "aws_lambda_function" "lambda_function_test_create" { - filename = "test-fixtures/lambdatest.zip" - function_name = "%s" - role = "${aws_iam_role.iam_for_lambda.arn}" - handler = "exports.example" - runtime = "nodejs4.3" + filename = "test-fixtures/lambdatest.zip" + function_name = "%s" + role = "${aws_iam_role.iam_for_lambda.arn}" + handler = "exports.example" + runtime = "nodejs4.3" source_code_hash = "${base64sha256(file("test-fixtures/lambdatest.zip"))}" publish = "true" } @@ -245,11 +245,11 @@ resource "aws_iam_policy_attachment" "policy_attachment_for_role" { } resource "aws_lambda_function" "lambda_function_test_create" { - filename = "test-fixtures/lambdatest_modified.zip" - function_name = "%s" - role = "${aws_iam_role.iam_for_lambda.arn}" - handler = "exports.example" - runtime = "nodejs4.3" + filename = "test-fixtures/lambdatest_modified.zip" + function_name = "%s" + role = "${aws_iam_role.iam_for_lambda.arn}" + handler = "exports.example" + runtime = "nodejs4.3" source_code_hash = "${base64sha256(file("test-fixtures/lambdatest_modified.zip"))}" publish = "true" } @@ -259,10 +259,10 @@ resource "aws_lambda_alias" "lambda_alias_test" { description = "a sample description" function_name = "${aws_lambda_function.lambda_function_test_create.arn}" function_version = "1" - routing_config = { - additional_version_weights = { - "2" = 0.5 - } - } + routing_config = { + additional_version_weights = { + "2" = 0.5 + } + } }`, roleName, policyName, attachmentName, funcName, aliasName) } From 9410ea5cf97a3f44a18738fb75d8ecb45373f7ac Mon Sep 17 00:00:00 2001 From: eggsbenjamin Date: Sat, 10 Feb 2018 01:54:16 +0000 Subject: [PATCH 05/88] removed newlines --- aws/resource_aws_lambda_alias.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/aws/resource_aws_lambda_alias.go b/aws/resource_aws_lambda_alias.go index b622120242f..73785594dc3 100644 --- a/aws/resource_aws_lambda_alias.go +++ b/aws/resource_aws_lambda_alias.go @@ -78,7 +78,6 @@ func resourceAwsLambdaAliasCreate(d *schema.ResourceData, meta interface{}) erro if v, ok := d.GetOk("routing_config"); ok { routingConfigs := v.([]interface{}) routingConfig, ok := routingConfigs[0].(map[string]interface{}) - if !ok { return errors.New("At least one field is expected inside routing_config") } @@ -173,7 +172,6 @@ func resourceAwsLambdaAliasUpdate(d *schema.ResourceData, meta interface{}) erro if v, ok := d.GetOk("routing_config"); ok { routingConfigs := v.([]interface{}) routingConfig, ok := routingConfigs[0].(map[string]interface{}) - if !ok { return errors.New("At least one field is expected inside routing_config") } From 6086430ea2d13b8fcac3b7380c3834dbd48f224e Mon Sep 17 00:00:00 2001 From: eggsbenjamin Date: Sat, 10 Feb 2018 12:04:34 +0000 Subject: [PATCH 06/88] added dedicated test case, fix removal bug --- aws/resource_aws_lambda_alias.go | 12 ++---- aws/resource_aws_lambda_alias_test.go | 57 +++++++++++++++++++++++++-- 2 files changed, 57 insertions(+), 12 deletions(-) diff --git a/aws/resource_aws_lambda_alias.go b/aws/resource_aws_lambda_alias.go index 73785594dc3..7a864d8f009 100644 --- a/aws/resource_aws_lambda_alias.go +++ b/aws/resource_aws_lambda_alias.go @@ -73,6 +73,7 @@ func resourceAwsLambdaAliasCreate(d *schema.ResourceData, meta interface{}) erro FunctionName: aws.String(functionName), FunctionVersion: aws.String(d.Get("function_version").(string)), Name: aws.String(aliasName), + RoutingConfig: &lambda.AliasRoutingConfiguration{}, } if v, ok := d.GetOk("routing_config"); ok { @@ -84,10 +85,7 @@ func resourceAwsLambdaAliasCreate(d *schema.ResourceData, meta interface{}) erro if additionalVersionWeights, ok := routingConfig["additional_version_weights"]; ok { weights := readAdditionalVersionWeights(additionalVersionWeights.(map[string]interface{})) - - params.RoutingConfig = &lambda.AliasRoutingConfiguration{ - AdditionalVersionWeights: aws.Float64Map(weights), - } + params.RoutingConfig.AdditionalVersionWeights = aws.Float64Map(weights) } } @@ -167,6 +165,7 @@ func resourceAwsLambdaAliasUpdate(d *schema.ResourceData, meta interface{}) erro FunctionName: aws.String(d.Get("function_name").(string)), FunctionVersion: aws.String(d.Get("function_version").(string)), Name: aws.String(d.Get("name").(string)), + RoutingConfig: &lambda.AliasRoutingConfiguration{}, } if v, ok := d.GetOk("routing_config"); ok { @@ -178,10 +177,7 @@ func resourceAwsLambdaAliasUpdate(d *schema.ResourceData, meta interface{}) erro if additionalVersionWeights, ok := routingConfig["additional_version_weights"]; ok { weights := readAdditionalVersionWeights(additionalVersionWeights.(map[string]interface{})) - - params.RoutingConfig = &lambda.AliasRoutingConfiguration{ - AdditionalVersionWeights: aws.Float64Map(weights), - } + params.RoutingConfig.AdditionalVersionWeights = aws.Float64Map(weights) } } diff --git a/aws/resource_aws_lambda_alias_test.go b/aws/resource_aws_lambda_alias_test.go index 7e1c02e4755..0447f754f77 100644 --- a/aws/resource_aws_lambda_alias_test.go +++ b/aws/resource_aws_lambda_alias_test.go @@ -28,7 +28,35 @@ func TestAccAWSLambdaAlias_basic(t *testing.T) { CheckDestroy: testAccCheckAwsLambdaAliasDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: testAccAwsLambdaAliasConfig(roleName, policyName, attachmentName, funcName, aliasName), + Config: testAccAwsLambdaAliasConfigWithoutRoutingConfig(roleName, policyName, attachmentName, funcName, aliasName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsLambdaAliasExists("aws_lambda_alias.lambda_alias_test", &conf), + testAccCheckAwsLambdaAttributes(&conf), + resource.TestMatchResourceAttr("aws_lambda_alias.lambda_alias_test", "arn", + regexp.MustCompile(`^arn:aws:lambda:[a-z]+-[a-z]+-[0-9]+:\d{12}:function:`+funcName+`:`+aliasName+`$`)), + ), + }, + }, + }) +} + +func TestAccAWSLambdaAlias_routingConfig(t *testing.T) { + var conf lambda.AliasConfiguration + + rString := acctest.RandString(8) + roleName := fmt.Sprintf("tf_acc_role_lambda_alias_basic_%s", rString) + policyName := fmt.Sprintf("tf_acc_policy_lambda_alias_basic_%s", rString) + attachmentName := fmt.Sprintf("tf_acc_attachment_%s", rString) + funcName := fmt.Sprintf("tf_acc_lambda_func_alias_basic_%s", rString) + aliasName := fmt.Sprintf("tf_acc_lambda_alias_basic_%s", rString) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsLambdaAliasDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAwsLambdaAliasConfigWithoutRoutingConfig(roleName, policyName, attachmentName, funcName, aliasName), Check: resource.ComposeTestCheckFunc( testAccCheckAwsLambdaAliasExists("aws_lambda_alias.lambda_alias_test", &conf), testAccCheckAwsLambdaAttributes(&conf), @@ -41,7 +69,17 @@ func TestAccAWSLambdaAlias_basic(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckAwsLambdaAliasExists("aws_lambda_alias.lambda_alias_test", &conf), testAccCheckAwsLambdaAttributes(&conf), - testAccCheckAwsLambdaAliasRoutingConfig(&conf), + testAccCheckAwsLambdaAliasRoutingConfigExists(&conf), + resource.TestMatchResourceAttr("aws_lambda_alias.lambda_alias_test", "arn", + regexp.MustCompile(`^arn:aws:lambda:[a-z]+-[a-z]+-[0-9]+:\d{12}:function:`+funcName+`:`+aliasName+`$`)), + ), + }, + resource.TestStep{ + Config: testAccAwsLambdaAliasConfigWithoutRoutingConfig(roleName, policyName, attachmentName, funcName, aliasName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsLambdaAliasExists("aws_lambda_alias.lambda_alias_test", &conf), + testAccCheckAwsLambdaAttributes(&conf), + testAccCheckAwsLambdaAliasRoutingConfigDoesNotExist(&conf), resource.TestMatchResourceAttr("aws_lambda_alias.lambda_alias_test", "arn", regexp.MustCompile(`^arn:aws:lambda:[a-z]+-[a-z]+-[0-9]+:\d{12}:function:`+funcName+`:`+aliasName+`$`)), ), @@ -114,7 +152,7 @@ func testAccCheckAwsLambdaAttributes(mapping *lambda.AliasConfiguration) resourc } } -func testAccCheckAwsLambdaAliasRoutingConfig(mapping *lambda.AliasConfiguration) resource.TestCheckFunc { +func testAccCheckAwsLambdaAliasRoutingConfigExists(mapping *lambda.AliasConfiguration) resource.TestCheckFunc { return func(s *terraform.State) error { routingConfig := mapping.RoutingConfig @@ -128,7 +166,18 @@ func testAccCheckAwsLambdaAliasRoutingConfig(mapping *lambda.AliasConfiguration) } } -func testAccAwsLambdaAliasConfig(roleName, policyName, attachmentName, funcName, aliasName string) string { +func testAccCheckAwsLambdaAliasRoutingConfigDoesNotExist(mapping *lambda.AliasConfiguration) resource.TestCheckFunc { + return func(s *terraform.State) error { + routingConfig := mapping.RoutingConfig + + if routingConfig != nil { + return fmt.Errorf("Lambda alias routing config still exists after removal") + } + return nil + } +} + +func testAccAwsLambdaAliasConfigWithoutRoutingConfig(roleName, policyName, attachmentName, funcName, aliasName string) string { return fmt.Sprintf(` resource "aws_iam_role" "iam_for_lambda" { name = "%s" From ef884bdd8cd0e21f4d4e0420bf0b73227bb046ef Mon Sep 17 00:00:00 2001 From: squidfunk Date: Sun, 18 Feb 2018 11:36:35 +0100 Subject: [PATCH 07/88] Fixed invalid nested type definitions --- aws/data_source_aws_ecs_container_definition.go | 4 ++-- aws/data_source_aws_vpc_peering_connection.go | 4 ++-- aws/resource_aws_api_gateway_deployment.go | 2 +- aws/resource_aws_api_gateway_gateway_response.go | 4 ++-- aws/resource_aws_api_gateway_integration.go | 4 ++-- aws/resource_aws_api_gateway_integration_response.go | 4 ++-- aws/resource_aws_api_gateway_method.go | 4 ++-- aws/resource_aws_api_gateway_method_response.go | 4 ++-- aws/resource_aws_batch_job_definition.go | 2 +- aws/resource_aws_glue_catalog_database.go | 2 +- aws/resource_aws_lambda_function.go | 2 +- 11 files changed, 18 insertions(+), 18 deletions(-) diff --git a/aws/data_source_aws_ecs_container_definition.go b/aws/data_source_aws_ecs_container_definition.go index 9bb99cdaeda..914d582649a 100644 --- a/aws/data_source_aws_ecs_container_definition.go +++ b/aws/data_source_aws_ecs_container_definition.go @@ -53,12 +53,12 @@ func dataSourceAwsEcsContainerDefinition() *schema.Resource { "docker_labels": { Type: schema.TypeMap, Computed: true, - Elem: schema.TypeString, + Elem: &schema.Schema{Type: schema.TypeString}, }, "environment": { Type: schema.TypeMap, Computed: true, - Elem: schema.TypeString, + Elem: &schema.Schema{Type: schema.TypeString}, }, }, } diff --git a/aws/data_source_aws_vpc_peering_connection.go b/aws/data_source_aws_vpc_peering_connection.go index a78e35f03e7..cf4dbf54d3c 100644 --- a/aws/data_source_aws_vpc_peering_connection.go +++ b/aws/data_source_aws_vpc_peering_connection.go @@ -67,12 +67,12 @@ func dataSourceAwsVpcPeeringConnection() *schema.Resource { "accepter": { Type: schema.TypeMap, Computed: true, - Elem: schema.TypeBool, + Elem: &schema.Schema{Type: schema.TypeBool}, }, "requester": { Type: schema.TypeMap, Computed: true, - Elem: schema.TypeBool, + Elem: &schema.Schema{Type: schema.TypeBool}, }, "filter": ec2CustomFiltersSchema(), "tags": tagsSchemaComputed(), diff --git a/aws/resource_aws_api_gateway_deployment.go b/aws/resource_aws_api_gateway_deployment.go index 4e291b780e8..aef612a6ed6 100644 --- a/aws/resource_aws_api_gateway_deployment.go +++ b/aws/resource_aws_api_gateway_deployment.go @@ -47,7 +47,7 @@ func resourceAwsApiGatewayDeployment() *schema.Resource { Type: schema.TypeMap, Optional: true, ForceNew: true, - Elem: schema.TypeString, + Elem: &schema.Schema{Type: schema.TypeString}, }, "created_date": { diff --git a/aws/resource_aws_api_gateway_gateway_response.go b/aws/resource_aws_api_gateway_gateway_response.go index 105d1227f24..bf9639960d1 100644 --- a/aws/resource_aws_api_gateway_gateway_response.go +++ b/aws/resource_aws_api_gateway_gateway_response.go @@ -39,13 +39,13 @@ func resourceAwsApiGatewayGatewayResponse() *schema.Resource { "response_templates": { Type: schema.TypeMap, - Elem: schema.TypeString, + Elem: &schema.Schema{Type: schema.TypeString}, Optional: true, }, "response_parameters": { Type: schema.TypeMap, - Elem: schema.TypeString, + Elem: &schema.Schema{Type: schema.TypeString}, Optional: true, }, }, diff --git a/aws/resource_aws_api_gateway_integration.go b/aws/resource_aws_api_gateway_integration.go index 64622c319e0..6eb4f45885a 100644 --- a/aws/resource_aws_api_gateway_integration.go +++ b/aws/resource_aws_api_gateway_integration.go @@ -69,12 +69,12 @@ func resourceAwsApiGatewayIntegration() *schema.Resource { "request_templates": { Type: schema.TypeMap, Optional: true, - Elem: schema.TypeString, + Elem: &schema.Schema{Type: schema.TypeString}, }, "request_parameters": { Type: schema.TypeMap, - Elem: schema.TypeString, + Elem: &schema.Schema{Type: schema.TypeString}, Optional: true, ConflictsWith: []string{"request_parameters_in_json"}, }, diff --git a/aws/resource_aws_api_gateway_integration_response.go b/aws/resource_aws_api_gateway_integration_response.go index 88cf85176bb..a37e37209a7 100644 --- a/aws/resource_aws_api_gateway_integration_response.go +++ b/aws/resource_aws_api_gateway_integration_response.go @@ -53,12 +53,12 @@ func resourceAwsApiGatewayIntegrationResponse() *schema.Resource { "response_templates": { Type: schema.TypeMap, Optional: true, - Elem: schema.TypeString, + Elem: &schema.Schema{Type: schema.TypeString}, }, "response_parameters": { Type: schema.TypeMap, - Elem: schema.TypeString, + Elem: &schema.Schema{Type: schema.TypeString}, Optional: true, ConflictsWith: []string{"response_parameters_in_json"}, }, diff --git a/aws/resource_aws_api_gateway_method.go b/aws/resource_aws_api_gateway_method.go index fdbc451098a..d39e3e62f12 100644 --- a/aws/resource_aws_api_gateway_method.go +++ b/aws/resource_aws_api_gateway_method.go @@ -60,12 +60,12 @@ func resourceAwsApiGatewayMethod() *schema.Resource { "request_models": &schema.Schema{ Type: schema.TypeMap, Optional: true, - Elem: schema.TypeString, + Elem: &schema.Schema{Type: schema.TypeString}, }, "request_parameters": &schema.Schema{ Type: schema.TypeMap, - Elem: schema.TypeBool, + Elem: &schema.Schema{Type: schema.TypeBool}, Optional: true, ConflictsWith: []string{"request_parameters_in_json"}, }, diff --git a/aws/resource_aws_api_gateway_method_response.go b/aws/resource_aws_api_gateway_method_response.go index caedc77d3d8..7bb250e4768 100644 --- a/aws/resource_aws_api_gateway_method_response.go +++ b/aws/resource_aws_api_gateway_method_response.go @@ -52,12 +52,12 @@ func resourceAwsApiGatewayMethodResponse() *schema.Resource { "response_models": &schema.Schema{ Type: schema.TypeMap, Optional: true, - Elem: schema.TypeString, + Elem: &schema.Schema{Type: schema.TypeString}, }, "response_parameters": &schema.Schema{ Type: schema.TypeMap, - Elem: schema.TypeBool, + Elem: &schema.Schema{Type: schema.TypeBool}, Optional: true, ConflictsWith: []string{"response_parameters_in_json"}, }, diff --git a/aws/resource_aws_batch_job_definition.go b/aws/resource_aws_batch_job_definition.go index 1bef71db799..d82c9d2e38b 100644 --- a/aws/resource_aws_batch_job_definition.go +++ b/aws/resource_aws_batch_job_definition.go @@ -40,7 +40,7 @@ func resourceAwsBatchJobDefinition() *schema.Resource { Type: schema.TypeMap, Optional: true, ForceNew: true, - Elem: schema.TypeString, + Elem: &schema.Schema{Type: schema.TypeString}, }, "retry_strategy": { Type: schema.TypeList, diff --git a/aws/resource_aws_glue_catalog_database.go b/aws/resource_aws_glue_catalog_database.go index 1f040be1b61..9fde86994c1 100644 --- a/aws/resource_aws_glue_catalog_database.go +++ b/aws/resource_aws_glue_catalog_database.go @@ -44,7 +44,7 @@ func resourceAwsGlueCatalogDatabase() *schema.Resource { }, "parameters": { Type: schema.TypeMap, - Elem: schema.TypeString, + Elem: &schema.Schema{Type: schema.TypeString}, Optional: true, }, }, diff --git a/aws/resource_aws_lambda_function.go b/aws/resource_aws_lambda_function.go index 9d73e192b5a..0ac95d1fe71 100644 --- a/aws/resource_aws_lambda_function.go +++ b/aws/resource_aws_lambda_function.go @@ -170,7 +170,7 @@ func resourceAwsLambdaFunction() *schema.Resource { "variables": { Type: schema.TypeMap, Optional: true, - Elem: schema.TypeString, + Elem: &schema.Schema{Type: schema.TypeString}, }, }, }, From a4cb8ca5938ce0d92989a34a1463147fae20e62a Mon Sep 17 00:00:00 2001 From: "xiaowei.wang" Date: Thu, 8 Mar 2018 18:46:34 +0100 Subject: [PATCH 08/88] ValidateFunc: health_check_grace_period_seconds and network_mode for ecs_* --- aws/resource_aws_ecs_service.go | 11 ++----- aws/resource_aws_ecs_service_test.go | 4 +-- aws/resource_aws_ecs_task_definition.go | 32 +++++++------------- aws/resource_aws_ecs_task_definition_test.go | 25 --------------- 4 files changed, 15 insertions(+), 57 deletions(-) diff --git a/aws/resource_aws_ecs_service.go b/aws/resource_aws_ecs_service.go index fa72419e4a5..65d61a2a27b 100644 --- a/aws/resource_aws_ecs_service.go +++ b/aws/resource_aws_ecs_service.go @@ -13,6 +13,7 @@ import ( "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" ) var taskDefinitionRE = regexp.MustCompile("^([a-zA-Z0-9_-]+):([0-9]+)$") @@ -54,7 +55,7 @@ func resourceAwsEcsService() *schema.Resource { "health_check_grace_period_seconds": { Type: schema.TypeInt, Optional: true, - ValidateFunc: validateAwsEcsServiceHealthCheckGracePeriodSeconds, + ValidateFunc: validation.IntBetween(0, 1800), }, "launch_type": { @@ -701,11 +702,3 @@ func parseTaskDefinition(taskDefinition string) (string, string, error) { return matches[0][1], matches[0][2], nil } - -func validateAwsEcsServiceHealthCheckGracePeriodSeconds(v interface{}, k string) (ws []string, errors []error) { - value := v.(int) - if (value < 0) || (value > 1800) { - errors = append(errors, fmt.Errorf("%q must be between 0 and 1800", k)) - } - return -} diff --git a/aws/resource_aws_ecs_service_test.go b/aws/resource_aws_ecs_service_test.go index 60ec6bcf6ab..603b26225aa 100644 --- a/aws/resource_aws_ecs_service_test.go +++ b/aws/resource_aws_ecs_service_test.go @@ -254,12 +254,12 @@ func TestAccAWSEcsService_healthCheckGracePeriodSeconds(t *testing.T) { { Config: testAccAWSEcsService_healthCheckGracePeriodSeconds(vpcNameTag, clusterName, tdName, roleName, policyName, tgName, lbName, svcName, -1), - ExpectError: regexp.MustCompile(`must be between 0 and 1800`), + ExpectError: regexp.MustCompile(`expected health_check_grace_period_seconds to be in the range`), }, { Config: testAccAWSEcsService_healthCheckGracePeriodSeconds(vpcNameTag, clusterName, tdName, roleName, policyName, tgName, lbName, svcName, 1801), - ExpectError: regexp.MustCompile(`must be between 0 and 1800`), + ExpectError: regexp.MustCompile(`expected health_check_grace_period_seconds to be in the range`), }, { Config: testAccAWSEcsService_healthCheckGracePeriodSeconds(vpcNameTag, clusterName, tdName, diff --git a/aws/resource_aws_ecs_task_definition.go b/aws/resource_aws_ecs_task_definition.go index c03695fa052..7d039eae890 100644 --- a/aws/resource_aws_ecs_task_definition.go +++ b/aws/resource_aws_ecs_task_definition.go @@ -4,13 +4,13 @@ import ( "bytes" "fmt" "log" - "strings" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ecs" "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/structure" + "github.com/hashicorp/terraform/helper/validation" ) func resourceAwsEcsTaskDefinition() *schema.Resource { @@ -79,11 +79,16 @@ func resourceAwsEcsTaskDefinition() *schema.Resource { }, "network_mode": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - ValidateFunc: validateAwsEcsTaskDefinitionNetworkMode, + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + ecs.NetworkModeBridge, + ecs.NetworkModeHost, + ecs.NetworkModeAwsvpc, + ecs.NetworkModeNone, + }, false), }, "volume": { @@ -139,21 +144,6 @@ func resourceAwsEcsTaskDefinition() *schema.Resource { } } -func validateAwsEcsTaskDefinitionNetworkMode(v interface{}, k string) (ws []string, errors []error) { - value := strings.ToLower(v.(string)) - validTypes := map[string]struct{}{ - "bridge": {}, - "host": {}, - "awsvpc": {}, - "none": {}, - } - - if _, ok := validTypes[value]; !ok { - errors = append(errors, fmt.Errorf("ECS Task Definition network_mode %q is invalid, must be `bridge`, `host`, `awsvpc` or `none`", value)) - } - return -} - func validateAwsEcsTaskDefinitionContainerDefinitions(v interface{}, k string) (ws []string, errors []error) { value := v.(string) _, err := expandEcsContainerDefinitions(value) diff --git a/aws/resource_aws_ecs_task_definition_test.go b/aws/resource_aws_ecs_task_definition_test.go index f9008c49d4b..a5b91e79db4 100644 --- a/aws/resource_aws_ecs_task_definition_test.go +++ b/aws/resource_aws_ecs_task_definition_test.go @@ -279,31 +279,6 @@ func testAccCheckAWSTaskDefinitionConstraintsAttrs(def *ecs.TaskDefinition) reso return nil } } -func TestValidateAwsEcsTaskDefinitionNetworkMode(t *testing.T) { - validNames := []string{ - "bridge", - "host", - "none", - } - for _, v := range validNames { - _, errors := validateAwsEcsTaskDefinitionNetworkMode(v, "network_mode") - if len(errors) != 0 { - t.Fatalf("%q should be a valid AWS ECS Task Definition Network Mode: %q", v, errors) - } - } - - invalidNames := []string{ - "bridged", - "-docker", - } - for _, v := range invalidNames { - _, errors := validateAwsEcsTaskDefinitionNetworkMode(v, "network_mode") - if len(errors) == 0 { - t.Fatalf("%q should be an invalid AWS ECS Task Definition Network Mode", v) - } - } -} - func TestValidateAwsEcsTaskDefinitionContainerDefinitions(t *testing.T) { validDefinitions := []string{ testValidateAwsEcsTaskDefinitionValidContainerDefinitions, From ce483d504a45c687e4229c7560a274209ed66102 Mon Sep 17 00:00:00 2001 From: Dominik Date: Wed, 28 Mar 2018 17:49:51 +0200 Subject: [PATCH 09/88] network_acl_rule: `cidr_block` vs. `ipv6_cidr_block` (conflict) --- aws/resource_aws_network_acl_rule.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/aws/resource_aws_network_acl_rule.go b/aws/resource_aws_network_acl_rule.go index d3aa099fce6..6a17cdcea4a 100644 --- a/aws/resource_aws_network_acl_rule.go +++ b/aws/resource_aws_network_acl_rule.go @@ -54,14 +54,16 @@ func resourceAwsNetworkAclRule() *schema.Resource { ForceNew: true, }, "cidr_block": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ConflictsWith: []string{"ipv6_cidr_block"}, }, "ipv6_cidr_block": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ConflictsWith: []string{"cidr_block"}, }, "from_port": { Type: schema.TypeInt, From 99ce9a51bc4237dc8ea08d8f2fa0068e2ecb8a78 Mon Sep 17 00:00:00 2001 From: carinadigital <602255+carinadigital@users.noreply.github.com> Date: Mon, 2 Apr 2018 15:02:39 +0100 Subject: [PATCH 10/88] Add test for perpetual plan on kms_alias target_key_id. --- aws/resource_aws_kms_alias_test.go | 37 ++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/aws/resource_aws_kms_alias_test.go b/aws/resource_aws_kms_alias_test.go index 0bf4761e653..68293975e3f 100644 --- a/aws/resource_aws_kms_alias_test.go +++ b/aws/resource_aws_kms_alias_test.go @@ -94,6 +94,30 @@ func TestAccAWSKmsAlias_multiple(t *testing.T) { }) } +func TestAccAWSKmsAlias_ArnDiffSuppress(t *testing.T) { + rInt := acctest.RandInt() + kmsAliasTimestamp := time.Now().Format(time.RFC1123) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSKmsAliasDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSKmsArnDiffSuppress(rInt, kmsAliasTimestamp), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSKmsAliasExists("aws_kms_alias.bar"), + resource.TestCheckResourceAttrSet("aws_kms_alias.bar", "target_key_arn"), + ), + }, + { + ExpectNonEmptyPlan: false, + PlanOnly: true, + Config: testAccAWSKmsArnDiffSuppress(rInt, kmsAliasTimestamp), + }, + }, + }) +} + func testAccCheckAWSKmsAliasDestroy(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).kmsconn @@ -186,3 +210,16 @@ resource "aws_kms_alias" "two" { target_key_id = "${aws_kms_key.single.key_id}" }`, timestamp, rInt, rInt) } + +func testAccAWSKmsArnDiffSuppress(rInt int, timestamp string) string { + return fmt.Sprintf(` +resource "aws_kms_key" "foo" { + description = "Terraform acc test foo %s" + deletion_window_in_days = 7 +} + +resource "aws_kms_alias" "bar" { + name = "alias/tf-acc-key-alias-%d" + target_key_id = "${aws_kms_key.foo.arn}" +}`, timestamp, rInt) +} From 908162155ba3e56f3cf2a60398bb754d76ec68d7 Mon Sep 17 00:00:00 2001 From: carinadigital <602255+carinadigital@users.noreply.github.com> Date: Mon, 2 Apr 2018 15:48:32 +0100 Subject: [PATCH 11/88] Suppress diff when kms_alias target_key_id is ARN. --- aws/resource_aws_kms_alias.go | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/aws/resource_aws_kms_alias.go b/aws/resource_aws_kms_alias.go index 6f724d7a992..723532fb1b5 100644 --- a/aws/resource_aws_kms_alias.go +++ b/aws/resource_aws_kms_alias.go @@ -3,6 +3,7 @@ package aws import ( "fmt" "log" + "strings" "time" "github.com/hashicorp/terraform/helper/resource" @@ -43,8 +44,9 @@ func resourceAwsKmsAlias() *schema.Resource { ValidateFunc: validateAwsKmsName, }, "target_key_id": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, + DiffSuppressFunc: suppressEquivalentTargetKeyIdAndARN, }, "target_key_arn": { Type: schema.TypeString, @@ -220,3 +222,14 @@ func resourceAwsKmsAliasImport(d *schema.ResourceData, meta interface{}) ([]*sch d.Set("name", d.Id()) return []*schema.ResourceData{d}, nil } + +func suppressEquivalentTargetKeyIdAndARN(k, old, new string, d *schema.ResourceData) bool { + newARN, err := arn.Parse(new) + if err != nil { + log.Printf("[DEBUG] %q can not be parsed as an ARN: %q", new, err) + return false + } + + resource := strings.TrimPrefix(newARN.Resource, "key/") + return old == resource +} From c62e14aa80315fd1761b62af15763303258a40b1 Mon Sep 17 00:00:00 2001 From: "xiaowei.wang" Date: Thu, 8 Mar 2018 19:23:36 +0100 Subject: [PATCH 12/88] resource/elb: drop custom ValidateFunc for protocol and int --- aws/resource_aws_elb.go | 40 ++++++++++++++-------------- aws/resource_aws_elb_test.go | 51 ------------------------------------ 2 files changed, 19 insertions(+), 72 deletions(-) diff --git a/aws/resource_aws_elb.go b/aws/resource_aws_elb.go index eb26f962633..7587556a1fb 100644 --- a/aws/resource_aws_elb.go +++ b/aws/resource_aws_elb.go @@ -17,6 +17,7 @@ import ( "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" ) func resourceAwsElb() *schema.Resource { @@ -110,7 +111,7 @@ func resourceAwsElb() *schema.Resource { Type: schema.TypeInt, Optional: true, Default: 60, - ValidateFunc: validateIntegerInRange(1, 4000), + ValidateFunc: validation.IntBetween(1, 4000), }, "connection_draining": &schema.Schema{ @@ -162,25 +163,25 @@ func resourceAwsElb() *schema.Resource { "instance_port": &schema.Schema{ Type: schema.TypeInt, Required: true, - ValidateFunc: validateIntegerInRange(1, 65535), + ValidateFunc: validation.IntBetween(1, 65535), }, "instance_protocol": &schema.Schema{ Type: schema.TypeString, Required: true, - ValidateFunc: validateListenerProtocol, + ValidateFunc: validateListenerProtocol(), }, "lb_port": &schema.Schema{ Type: schema.TypeInt, Required: true, - ValidateFunc: validateIntegerInRange(1, 65535), + ValidateFunc: validation.IntBetween(1, 65535), }, "lb_protocol": &schema.Schema{ Type: schema.TypeString, Required: true, - ValidateFunc: validateListenerProtocol, + ValidateFunc: validateListenerProtocol(), }, "ssl_certificate_id": &schema.Schema{ @@ -203,13 +204,13 @@ func resourceAwsElb() *schema.Resource { "healthy_threshold": &schema.Schema{ Type: schema.TypeInt, Required: true, - ValidateFunc: validateIntegerInRange(2, 10), + ValidateFunc: validation.IntBetween(2, 10), }, "unhealthy_threshold": &schema.Schema{ Type: schema.TypeInt, Required: true, - ValidateFunc: validateIntegerInRange(2, 10), + ValidateFunc: validation.IntBetween(2, 10), }, "target": &schema.Schema{ @@ -221,13 +222,13 @@ func resourceAwsElb() *schema.Resource { "interval": &schema.Schema{ Type: schema.TypeInt, Required: true, - ValidateFunc: validateIntegerInRange(5, 300), + ValidateFunc: validation.IntBetween(5, 300), }, "timeout": &schema.Schema{ Type: schema.TypeInt, Required: true, - ValidateFunc: validateIntegerInRange(2, 60), + ValidateFunc: validation.IntBetween(2, 60), }, }, }, @@ -965,18 +966,6 @@ func validateHeathCheckTarget(v interface{}, k string) (ws []string, errors []er return } -func validateListenerProtocol(v interface{}, k string) (ws []string, errors []error) { - value := v.(string) - - if !isValidProtocol(value) { - errors = append(errors, fmt.Errorf( - "%q contains an invalid Listener protocol %q. "+ - "Valid protocols are either %q, %q, %q, or %q.", - k, value, "TCP", "SSL", "HTTP", "HTTPS")) - } - return -} - func isValidProtocol(s string) bool { if s == "" { return false @@ -997,6 +986,15 @@ func isValidProtocol(s string) bool { return true } +func validateListenerProtocol() schema.SchemaValidateFunc { + return validation.StringInSlice([]string{ + "HTTP", + "HTTPS", + "SSL", + "TCP", + }, true) +} + // ELB automatically creates ENI(s) on creation // but the cleanup is asynchronous and may take time // which then blocks IGW, SG or VPC on deletion diff --git a/aws/resource_aws_elb_test.go b/aws/resource_aws_elb_test.go index 386c2461493..61e5c6af792 100644 --- a/aws/resource_aws_elb_test.go +++ b/aws/resource_aws_elb_test.go @@ -909,57 +909,6 @@ func TestResourceAWSELB_validateAccessLogsInterval(t *testing.T) { } -func TestResourceAWSELB_validateListenerProtocol(t *testing.T) { - type testCases struct { - Value string - ErrCount int - } - - invalidCases := []testCases{ - { - Value: "", - ErrCount: 1, - }, - { - Value: "incorrect", - ErrCount: 1, - }, - { - Value: "HTTP:", - ErrCount: 1, - }, - } - - for _, tc := range invalidCases { - _, errors := validateListenerProtocol(tc.Value, "protocol") - if len(errors) != tc.ErrCount { - t.Fatalf("Expected %q to trigger a validation error.", tc.Value) - } - } - - validCases := []testCases{ - { - Value: "TCP", - ErrCount: 0, - }, - { - Value: "ssl", - ErrCount: 0, - }, - { - Value: "HTTP", - ErrCount: 0, - }, - } - - for _, tc := range validCases { - _, errors := validateListenerProtocol(tc.Value, "protocol") - if len(errors) != tc.ErrCount { - t.Fatalf("Expected %q not to trigger a validation error.", tc.Value) - } - } -} - func TestResourceAWSELB_validateHealthCheckTarget(t *testing.T) { type testCase struct { Value string From 5198cab204a2ea89baaf75cf3cb900f0907f451b Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Wed, 20 Jun 2018 11:03:50 -0400 Subject: [PATCH 13/88] tests/resource/aws_elasticache_(cluster|replication_group): Only set parameter_group_name for tests requiring it This is to prevent these new errors since redis4.0 was released (and future ones of similar nature): Error creating Elasticache Replication Group: InvalidParameterCombination: Expected a parameter group of family redis4.0 but found one of family redis3.2 --- aws/resource_aws_elasticache_cluster_test.go | 61 +++++++++++++------ ..._aws_elasticache_replication_group_test.go | 21 ++----- 2 files changed, 47 insertions(+), 35 deletions(-) diff --git a/aws/resource_aws_elasticache_cluster_test.go b/aws/resource_aws_elasticache_cluster_test.go index 89c22bc964d..c36a16e29ce 100644 --- a/aws/resource_aws_elasticache_cluster_test.go +++ b/aws/resource_aws_elasticache_cluster_test.go @@ -149,6 +149,34 @@ func TestAccAWSElasticacheCluster_Engine_Redis_Ec2Classic(t *testing.T) { }) } +func TestAccAWSElasticacheCluster_ParameterGroupName_Default(t *testing.T) { + var ec elasticache.CacheCluster + rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(8)) + resourceName := "aws_elasticache_cluster.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSElasticacheClusterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSElasticacheClusterConfig_ParameterGroupName(rName, "memcached", "1.4.34", "default.memcached1.4"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSElasticacheClusterExists(resourceName, &ec), + resource.TestCheckResourceAttr(resourceName, "engine", "memcached"), + resource.TestCheckResourceAttr(resourceName, "engine_version", "1.4.34"), + resource.TestCheckResourceAttr(resourceName, "parameter_group_name", "default.memcached1.4"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccAWSElasticacheCluster_Port_Ec2Classic(t *testing.T) { oldvar := os.Getenv("AWS_DEFAULT_REGION") os.Setenv("AWS_DEFAULT_REGION", "us-east-1") @@ -724,7 +752,6 @@ func TestAccAWSElasticacheCluster_ReplicationGroupID_SingleReplica_Ec2Classic(t testAccCheckAWSElasticacheClusterReplicationGroupIDAttribute(&cluster, &replicationGroup), resource.TestCheckResourceAttr(clusterResourceName, "engine", "redis"), resource.TestCheckResourceAttr(clusterResourceName, "node_type", "cache.m3.medium"), - resource.TestCheckResourceAttr(clusterResourceName, "parameter_group_name", "default.redis3.2"), resource.TestCheckResourceAttr(clusterResourceName, "port", "6379"), ), }, @@ -759,11 +786,9 @@ func TestAccAWSElasticacheCluster_ReplicationGroupID_MultipleReplica_Ec2Classic( testAccCheckAWSElasticacheClusterReplicationGroupIDAttribute(&cluster2, &replicationGroup), resource.TestCheckResourceAttr(clusterResourceName1, "engine", "redis"), resource.TestCheckResourceAttr(clusterResourceName1, "node_type", "cache.m3.medium"), - resource.TestCheckResourceAttr(clusterResourceName1, "parameter_group_name", "default.redis3.2"), resource.TestCheckResourceAttr(clusterResourceName1, "port", "6379"), resource.TestCheckResourceAttr(clusterResourceName2, "engine", "redis"), resource.TestCheckResourceAttr(clusterResourceName2, "node_type", "cache.m3.medium"), - resource.TestCheckResourceAttr(clusterResourceName2, "parameter_group_name", "default.redis3.2"), resource.TestCheckResourceAttr(clusterResourceName2, "port", "6379"), ), }, @@ -879,7 +904,6 @@ resource "aws_elasticache_cluster" "bar" { engine = "memcached" node_type = "cache.m1.small" num_cache_nodes = 1 - parameter_group_name = "default.memcached1.4" } `, rName) } @@ -891,11 +915,23 @@ resource "aws_elasticache_cluster" "bar" { engine = "redis" node_type = "cache.m1.small" num_cache_nodes = 1 - parameter_group_name = "default.redis3.2" } `, rName) } +func testAccAWSElasticacheClusterConfig_ParameterGroupName(rName, engine, engineVersion, parameterGroupName string) string { + return fmt.Sprintf(` +resource "aws_elasticache_cluster" "test" { + cluster_id = %q + engine = %q + engine_version = %q + node_type = "cache.m1.small" + num_cache_nodes = 1 + parameter_group_name = %q +} +`, rName, engine, engineVersion, parameterGroupName) +} + func testAccAWSElasticacheClusterConfig_Port(rName string, port int) string { return fmt.Sprintf(` resource "aws_elasticache_cluster" "bar" { @@ -903,7 +939,6 @@ resource "aws_elasticache_cluster" "bar" { engine = "memcached" node_type = "cache.m1.small" num_cache_nodes = 1 - parameter_group_name = "default.memcached1.4" port = %d } `, rName, port) @@ -940,7 +975,6 @@ resource "aws_elasticache_cluster" "bar" { node_type = "cache.m1.small" num_cache_nodes = 1 port = 11211 - parameter_group_name = "default.memcached1.4" security_group_names = ["${aws_elasticache_security_group.bar.name}"] } `, acctest.RandInt(), acctest.RandInt(), acctest.RandString(10)) @@ -972,7 +1006,6 @@ resource "aws_elasticache_cluster" "bar" { node_type = "cache.m1.small" num_cache_nodes = 1 port = 6379 - parameter_group_name = "default.redis3.2" security_group_names = ["${aws_elasticache_security_group.bar.name}"] snapshot_window = "05:00-09:00" snapshot_retention_limit = 3 @@ -1006,7 +1039,6 @@ resource "aws_elasticache_cluster" "bar" { node_type = "cache.m1.small" num_cache_nodes = 1 port = 6379 - parameter_group_name = "default.redis3.2" security_group_names = ["${aws_elasticache_security_group.bar.name}"] snapshot_window = "07:00-09:00" snapshot_retention_limit = 7 @@ -1022,7 +1054,6 @@ resource "aws_elasticache_cluster" "bar" { engine = "memcached" node_type = "cache.m1.small" num_cache_nodes = %d - parameter_group_name = "default.memcached1.4" } `, rName, numCacheNodes) } @@ -1042,7 +1073,6 @@ resource "aws_elasticache_cluster" "bar" { engine = "memcached" node_type = "cache.m1.small" num_cache_nodes = %d - parameter_group_name = "default.memcached1.4" preferred_availability_zones = [%s] } `, rName, numCacheNodes, strings.Join(preferredAvailabilityZones, ",")) @@ -1160,7 +1190,6 @@ resource "aws_elasticache_cluster" "bar" { port = 11211 subnet_group_name = "${aws_elasticache_subnet_group.bar.name}" security_group_ids = ["${aws_security_group.bar.id}"] - parameter_group_name = "default.memcached1.4" az_mode = "cross-az" preferred_availability_zones = [ "us-west-2a", @@ -1178,7 +1207,6 @@ resource "aws_elasticache_cluster" "bar" { engine = "memcached" node_type = "cache.m3.medium" num_cache_nodes = 1 - parameter_group_name = "default.memcached1.4" port = 11211 } `, rName, azMode) @@ -1193,7 +1221,6 @@ resource "aws_elasticache_cluster" "bar" { engine = "redis" node_type = "cache.m3.medium" num_cache_nodes = 1 - parameter_group_name = "default.redis3.2" port = 6379 } `, rName, azMode) @@ -1208,7 +1235,6 @@ resource "aws_elasticache_cluster" "bar" { engine_version = "%[2]s" node_type = "cache.m3.medium" num_cache_nodes = 1 - parameter_group_name = "default.memcached1.4" port = 11211 } `, rName, engineVersion) @@ -1223,7 +1249,6 @@ resource "aws_elasticache_cluster" "bar" { engine_version = "%[2]s" node_type = "cache.m3.medium" num_cache_nodes = 1 - parameter_group_name = "default.redis3.2" port = 6379 } `, rName, engineVersion) @@ -1237,7 +1262,6 @@ resource "aws_elasticache_cluster" "bar" { engine = "memcached" node_type = "%[2]s" num_cache_nodes = 1 - parameter_group_name = "default.memcached1.4" port = 11211 } `, rName, nodeType) @@ -1251,7 +1275,6 @@ resource "aws_elasticache_cluster" "bar" { engine = "redis" node_type = "%[2]s" num_cache_nodes = 1 - parameter_group_name = "default.redis3.2" port = 6379 } `, rName, nodeType) @@ -1265,7 +1288,6 @@ resource "aws_elasticache_cluster" "bar" { engine = "redis" node_type = "cache.m3.medium" num_cache_nodes = %[2]d - parameter_group_name = "default.redis3.2" port = 6379 } `, rName, numCacheNodes) @@ -1288,7 +1310,6 @@ resource "aws_elasticache_replication_group" "test" { replication_group_id = "%[1]s" node_type = "cache.m3.medium" number_cache_clusters = 1 - parameter_group_name = "default.redis3.2" port = 6379 lifecycle { diff --git a/aws/resource_aws_elasticache_replication_group_test.go b/aws/resource_aws_elasticache_replication_group_test.go index f452dd1ab89..012994b9126 100644 --- a/aws/resource_aws_elasticache_replication_group_test.go +++ b/aws/resource_aws_elasticache_replication_group_test.go @@ -230,8 +230,8 @@ func TestAccAWSElasticacheReplicationGroup_updateParameterGroup(t *testing.T) { Config: testAccAWSElasticacheReplicationGroupConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSElasticacheReplicationGroupExists("aws_elasticache_replication_group.bar", &rg), - resource.TestCheckResourceAttr( - "aws_elasticache_replication_group.bar", "parameter_group_name", "default.redis3.2"), + resource.TestMatchResourceAttr( + "aws_elasticache_replication_group.bar", "parameter_group_name", regexp.MustCompile(`^default.redis.+`)), ), }, @@ -783,7 +783,6 @@ resource "aws_elasticache_replication_group" "bar" { node_type = "cache.m1.small" number_cache_clusters = 2 port = 6379 - parameter_group_name = "default.redis3.2" security_group_names = ["${aws_elasticache_security_group.bar.name}"] apply_immediately = true auto_minor_version_upgrade = false @@ -831,7 +830,6 @@ resource "aws_elasticache_replication_group" "bar" { node_type = "cache.m1.small" number_cache_clusters = 2 port = 6379 - parameter_group_name = "default.redis3.2" security_group_names = ["${aws_elasticache_security_group.bar.name}"] apply_immediately = true auto_minor_version_upgrade = false @@ -865,7 +863,9 @@ resource "aws_elasticache_security_group" "bar" { resource "aws_elasticache_parameter_group" "bar" { name = "allkeys-lru-%d" - family = "redis3.2" + # We do not have a data source for "latest" Elasticache family + # so unfortunately we must hardcode this for now + family = "redis4.0" parameter { name = "maxmemory-policy" @@ -913,7 +913,6 @@ resource "aws_elasticache_replication_group" "bar" { node_type = "cache.m1.small" number_cache_clusters = 2 port = 6379 - parameter_group_name = "default.redis3.2" security_group_names = ["${aws_elasticache_security_group.bar.name}"] apply_immediately = true auto_minor_version_upgrade = true @@ -948,7 +947,6 @@ resource "aws_elasticache_replication_group" "bar" { node_type = "cache.m1.small" number_cache_clusters = 2 port = 6379 - parameter_group_name = "default.redis3.2" security_group_names = ["${aws_elasticache_security_group.bar.name}"] apply_immediately = true auto_minor_version_upgrade = true @@ -985,7 +983,6 @@ resource "aws_elasticache_replication_group" "bar" { node_type = "cache.m1.medium" number_cache_clusters = 2 port = 6379 - parameter_group_name = "default.redis3.2" security_group_names = ["${aws_elasticache_security_group.bar.name}"] apply_immediately = true }`, rName, rName, rName) @@ -1034,7 +1031,6 @@ resource "aws_elasticache_replication_group" "bar" { port = 6379 subnet_group_name = "${aws_elasticache_subnet_group.bar.name}" security_group_ids = ["${aws_security_group.bar.id}"] - parameter_group_name = "default.redis3.2" availability_zones = ["us-west-2a"] auto_minor_version_upgrade = false } @@ -1096,7 +1092,6 @@ resource "aws_elasticache_replication_group" "bar" { port = 6379 subnet_group_name = "${aws_elasticache_subnet_group.bar.name}" security_group_ids = ["${aws_security_group.bar.id}"] - parameter_group_name = "default.redis3.2" availability_zones = ["us-west-2a","us-west-2b"] automatic_failover_enabled = true snapshot_window = "02:00-03:00" @@ -1159,9 +1154,8 @@ resource "aws_elasticache_replication_group" "bar" { port = 6379 subnet_group_name = "${aws_elasticache_subnet_group.bar.name}" security_group_ids = ["${aws_security_group.bar.id}"] - parameter_group_name = "default.redis3.2.cluster.on" availability_zones = ["us-west-2a","us-west-2b"] - automatic_failover_enabled = true + automatic_failover_enabled = false snapshot_window = "02:00-03:00" snapshot_retention_limit = 7 engine_version = "3.2.4" @@ -1224,7 +1218,6 @@ resource "aws_elasticache_replication_group" "bar" { port = 6379 subnet_group_name = "${aws_elasticache_subnet_group.bar.name}" security_group_ids = ["${aws_security_group.bar.id}"] - parameter_group_name = "default.redis3.2.cluster.on" automatic_failover_enabled = true cluster_mode { replicas_per_node_group = 1 @@ -1289,7 +1282,6 @@ resource "aws_elasticache_replication_group" "bar" { port = 6379 subnet_group_name = "${aws_elasticache_subnet_group.bar.name}" security_group_ids = ["${aws_security_group.bar.id}"] - parameter_group_name = "default.redis3.2.cluster.on" automatic_failover_enabled = true cluster_mode { num_node_groups = %d @@ -1440,7 +1432,6 @@ resource "aws_elasticache_replication_group" "test" { automatic_failover_enabled = %[2]t node_type = "cache.m3.medium" number_cache_clusters = %[3]d - parameter_group_name = "default.redis3.2" replication_group_id = "%[1]s" replication_group_description = "Terraform Acceptance Testing - number_cache_clusters" subnet_group_name = "${aws_elasticache_subnet_group.test.name}" From 0a84a62df499be84b6b5493fa470c9210c815ecc Mon Sep 17 00:00:00 2001 From: Tom Elliff Date: Fri, 22 Jun 2018 00:24:08 +0100 Subject: [PATCH 14/88] Fix the access policy in the ES domain docs If a resource isn't specified AWS automatically adds it to point to the ES domain it has been created for. This causes a perpetual diff where Terraform keeps attempting to remove the Resource key and AWS keeps adding it back. Also adds a note drawing attention to the fact that the values for the advanced options _must_ be strings or this will also cause a perpetual diff. --- website/docs/r/elasticsearch_domain.html.markdown | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/website/docs/r/elasticsearch_domain.html.markdown b/website/docs/r/elasticsearch_domain.html.markdown index d3d1c403983..2b74247d976 100644 --- a/website/docs/r/elasticsearch_domain.html.markdown +++ b/website/docs/r/elasticsearch_domain.html.markdown @@ -12,8 +12,16 @@ description: |- ## Example Usage ```hcl +variable "domain" { + default = "tf-test" +} + +data "aws_region" "current" {} + +data "aws_caller_identity" "current" {} + resource "aws_elasticsearch_domain" "es" { - domain_name = "tf-test" + domain_name = "${var.domain}" elasticsearch_version = "1.5" cluster_config { instance_type = "r3.large.elasticsearch" @@ -31,6 +39,7 @@ resource "aws_elasticsearch_domain" "es" { "Action": "es:*", "Principal": "*", "Effect": "Allow", + "Resource": "arn:aws:es:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:domain/${var.domain}/*" "Condition": { "IpAddress": {"aws:SourceIp": ["66.193.100.22/32"]} } @@ -56,6 +65,9 @@ The following arguments are supported: * `domain_name` - (Required) Name of the domain. * `access_policies` - (Optional) IAM policy document specifying the access policies for the domain * `advanced_options` - (Optional) Key-value string pairs to specify advanced configuration options. + Note that the values for these configuration options must be strings (wrapped in quotes) or they + may be wrong and cause a perpetual diff, causing Terraform to want to recreate your Elasticsearch + domain on every apply. * `ebs_options` - (Optional) EBS related options, may be required based on chosen [instance size](https://aws.amazon.com/elasticsearch-service/pricing/). See below. * `encrypt_at_rest` - (Optional) Encrypt at rest options. Only available for [certain instance types](http://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/aes-supported-instance-types.html). See below. * `cluster_config` - (Optional) Cluster configuration of the domain, see below. From 2db7e9a73d9735811f5fdfb561cea3a9985a0d40 Mon Sep 17 00:00:00 2001 From: Dieter De Meyer Date: Fri, 22 Jun 2018 12:10:36 +0200 Subject: [PATCH 15/88] Also retrieve AWS instances that are in a state other than running --- aws/data_source_aws_instances.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/aws/data_source_aws_instances.go b/aws/data_source_aws_instances.go index 7196e3189e7..33f6e8a760d 100644 --- a/aws/data_source_aws_instances.go +++ b/aws/data_source_aws_instances.go @@ -50,8 +50,14 @@ func dataSourceAwsInstancesRead(d *schema.ResourceData, meta interface{}) error params := &ec2.DescribeInstancesInput{ Filters: []*ec2.Filter{ &ec2.Filter{ - Name: aws.String("instance-state-name"), - Values: []*string{aws.String("running")}, + Name: aws.String("instance-state-name"), + Values: []*string{ + aws.String("running"), + aws.String("stopped"), + aws.String("stopping"), + aws.String("pending"), + aws.String("shutting-down"), + }, }, }, } From 4c3aaf03f9c41650c1b2aecb8ab3375d792938cb Mon Sep 17 00:00:00 2001 From: tf-release-bot Date: Wed, 27 Jun 2018 13:46:47 +0000 Subject: [PATCH 16/88] Cleanup after v1.25.0 release --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 218e5c32e2a..162a3811667 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +## 1.26.0 (Unreleased) ## 1.25.0 (June 27, 2018) NOTES: From bbceaaac4d89eec5ac8adde70f50819f3bddf133 Mon Sep 17 00:00:00 2001 From: Kash Date: Wed, 27 Jun 2018 11:32:50 -0400 Subject: [PATCH 17/88] add import for eip association --- aws/resource_aws_eip_association.go | 3 + aws/resource_aws_eip_association_test.go | 81 ++++++++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/aws/resource_aws_eip_association.go b/aws/resource_aws_eip_association.go index e5a051631b0..7c53cf44ef7 100644 --- a/aws/resource_aws_eip_association.go +++ b/aws/resource_aws_eip_association.go @@ -18,6 +18,9 @@ func resourceAwsEipAssociation() *schema.Resource { Create: resourceAwsEipAssociationCreate, Read: resourceAwsEipAssociationRead, Delete: resourceAwsEipAssociationDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, Schema: map[string]*schema.Schema{ "allocation_id": &schema.Schema{ diff --git a/aws/resource_aws_eip_association_test.go b/aws/resource_aws_eip_association_test.go index 71c873f1c2e..9791dbaa616 100644 --- a/aws/resource_aws_eip_association_test.go +++ b/aws/resource_aws_eip_association_test.go @@ -12,6 +12,46 @@ import ( "github.com/hashicorp/terraform/terraform" ) +func TestAccAWSEIPAssociation_importInstance(t *testing.T) { + resourceName := "aws_eip_association.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSEIPAssociationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSEIPAssociationConfig_instance, + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSEIPAssociation_importNetworkInterface(t *testing.T) { + resourceName := "aws_eip_association.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSEIPAssociationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSEIPAssociationConfig_networkInterface, + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccAWSEIPAssociation_basic(t *testing.T) { var a ec2.Address @@ -345,3 +385,44 @@ resource "aws_eip_association" "test" { } `, testAccAWSSpotInstanceRequestConfig(rInt)) } + +const testAccAWSEIPAssociationConfig_instance = ` +resource "aws_instance" "test" { + # us-west-2 + ami = "ami-4fccb37f" + instance_type = "m1.small" +} + +resource "aws_eip" "test" {} + +resource "aws_eip_association" "test" { + allocation_id = "${aws_eip.test.id}" + instance_id = "${aws_instance.test.id}" +} +` + +const testAccAWSEIPAssociationConfig_networkInterface = ` +resource "aws_vpc" "test" { + cidr_block = "10.1.0.0/16" +} + +resource "aws_subnet" "test" { + vpc_id = "${aws_vpc.test.id}" + cidr_block = "10.1.1.0/24" +} + +resource "aws_internet_gateway" "test" { + vpc_id = "${aws_vpc.test.id}" +} + +resource "aws_network_interface" "test" { + subnet_id = "${aws_subnet.test.id}" +} + +resource "aws_eip" "test" {} + +resource "aws_eip_association" "test" { + allocation_id = "${aws_eip.test.id}" + network_interface_id = "${aws_network_interface.test.id}" +} +` From 198ee6c7cd4ad59741b3b26273cc938bab1ff065 Mon Sep 17 00:00:00 2001 From: Kash Date: Wed, 27 Jun 2018 11:39:23 -0400 Subject: [PATCH 18/88] add documentation --- website/docs/r/eip_association.html.markdown | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/website/docs/r/eip_association.html.markdown b/website/docs/r/eip_association.html.markdown index 42bf1420202..952977e82df 100644 --- a/website/docs/r/eip_association.html.markdown +++ b/website/docs/r/eip_association.html.markdown @@ -68,3 +68,11 @@ address with an instance. * `network_interface_id` - As above * `private_ip_address` - As above * `public_ip` - As above + +## Import + +EIP Assocations can be imported using their association ID. + +``` +$ terraform import aws_eip_association.test eipassoc-ab12c345 +``` From 6958243ccf576779b83c76c3264df4924670c24a Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Wed, 27 Jun 2018 15:55:10 -0400 Subject: [PATCH 19/88] Update CHANGELOG for #5006 --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 162a3811667..eb19a76867a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,9 @@ ## 1.26.0 (Unreleased) + +ENHANCEMENTS: + +* resource/aws_eip_association: Support resource import [GH-5006] + ## 1.25.0 (June 27, 2018) NOTES: From 4dd88866bd8c0b91534fca7b9b7ad91f3452bf38 Mon Sep 17 00:00:00 2001 From: Kash Date: Fri, 2 Mar 2018 17:59:31 -0500 Subject: [PATCH 20/88] add launch configuration data source --- aws/data_source_aws_launch_configuration.go | 221 ++++++++++++++++++ ...ta_source_aws_launch_configuration_test.go | 68 ++++++ aws/provider.go | 1 + 3 files changed, 290 insertions(+) create mode 100644 aws/data_source_aws_launch_configuration.go create mode 100644 aws/data_source_aws_launch_configuration_test.go diff --git a/aws/data_source_aws_launch_configuration.go b/aws/data_source_aws_launch_configuration.go new file mode 100644 index 00000000000..7f257eca24d --- /dev/null +++ b/aws/data_source_aws_launch_configuration.go @@ -0,0 +1,221 @@ +package aws + +import ( + "fmt" + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/autoscaling" + "github.com/hashicorp/terraform/helper/schema" +) + +func dataSourceAwsLaunchConfiguration() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAwsLaunchConfigurationRead, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + + "image_id": { + Type: schema.TypeString, + Computed: true, + }, + + "instance_type": { + Type: schema.TypeString, + Computed: true, + }, + + "iam_instance_profile": { + Type: schema.TypeString, + Computed: true, + }, + + "key_name": { + Type: schema.TypeString, + Computed: true, + }, + + "user_data": { + Type: schema.TypeString, + Computed: true, + }, + + "security_groups": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "vpc_classic_link_id": { + Type: schema.TypeString, + Computed: true, + }, + + "vpc_classic_link_security_groups": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "associate_public_ip_address": { + Type: schema.TypeBool, + Computed: true, + }, + + "spot_price": { + Type: schema.TypeString, + Computed: true, + }, + + "ebs_optimized": { + Type: schema.TypeBool, + Computed: true, + }, + + "placement_tenancy": { + Type: schema.TypeString, + Computed: true, + }, + + "enable_monitoring": { + Type: schema.TypeBool, + Computed: true, + }, + + "ebs_block_device": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "delete_on_termination": { + Type: schema.TypeBool, + Computed: true, + }, + + "device_name": { + Type: schema.TypeString, + Computed: true, + }, + + "iops": { + Type: schema.TypeInt, + Computed: true, + }, + + "snapshot_id": { + Type: schema.TypeString, + Computed: true, + }, + + "volume_size": { + Type: schema.TypeInt, + Computed: true, + }, + + "volume_type": { + Type: schema.TypeString, + Computed: true, + }, + + "encrypted": { + Type: schema.TypeBool, + Computed: true, + }, + }, + }, + }, + + "ephemeral_block_device": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "device_name": { + Type: schema.TypeString, + Computed: true, + }, + + "virtual_name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + + "root_block_device": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "delete_on_termination": { + Type: schema.TypeBool, + Computed: true, + }, + + "iops": { + Type: schema.TypeInt, + Computed: true, + }, + + "volume_size": { + Type: schema.TypeInt, + Computed: true, + }, + + "volume_type": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func dataSourceAwsLaunchConfigurationRead(d *schema.ResourceData, meta interface{}) error { + autoscalingconn := meta.(*AWSClient).autoscalingconn + ec2conn := meta.(*AWSClient).ec2conn + + if v, ok := d.GetOk("name"); ok { + d.SetId(v.(string)) + } + + describeOpts := autoscaling.DescribeLaunchConfigurationsInput{ + LaunchConfigurationNames: []*string{aws.String(d.Id())}, + } + + log.Printf("[DEBUG] launch configuration describe configuration: %s", describeOpts) + describConfs, err := autoscalingconn.DescribeLaunchConfigurations(&describeOpts) + if err != nil { + return fmt.Errorf("Error retrieving launch configuration: %s", err) + } + + lc := describConfs.LaunchConfigurations[0] + + d.Set("key_name", lc.KeyName) + d.Set("image_id", lc.ImageId) + d.Set("instance_type", lc.InstanceType) + d.Set("name", lc.LaunchConfigurationName) + d.Set("user_data", lc.UserData) + + d.Set("iam_instance_profile", lc.IamInstanceProfile) + d.Set("ebs_optimized", lc.EbsOptimized) + d.Set("spot_price", lc.SpotPrice) + d.Set("enable_monitoring", lc.InstanceMonitoring.Enabled) + d.Set("security_groups", lc.SecurityGroups) + d.Set("associate_public_ip_address", lc.AssociatePublicIpAddress) + + d.Set("vpc_classic_link_id", lc.ClassicLinkVPCId) + d.Set("vpc_classic_link_security_groups", lc.ClassicLinkVPCSecurityGroups) + + if err := readLCBlockDevices(d, lc, ec2conn); err != nil { + return err + } + + return nil +} diff --git a/aws/data_source_aws_launch_configuration_test.go b/aws/data_source_aws_launch_configuration_test.go new file mode 100644 index 00000000000..7722c6134c0 --- /dev/null +++ b/aws/data_source_aws_launch_configuration_test.go @@ -0,0 +1,68 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccAWSLaunchConfigurationDataSource_basic(t *testing.T) { + rInt := acctest.RandInt() + rName := "data.aws_launch_configuration.foo" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccLaunchConfigurationDataSourceConfig(rInt), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(rName, "image_id"), + resource.TestCheckResourceAttrSet(rName, "instance_type"), + resource.TestCheckResourceAttrSet(rName, "associate_public_ip_address"), + resource.TestCheckResourceAttrSet(rName, "user_data"), + resource.TestCheckResourceAttr(rName, "root_block_device.#", "1"), + resource.TestCheckResourceAttr(rName, "ebs_block_device.#", "1"), + resource.TestCheckResourceAttr(rName, "ephemeral_block_device.#", "1"), + ), + }, + }, + }) +} + +func testAccLaunchConfigurationDataSourceConfig(rInt int) string { + return fmt.Sprintf(` +resource "aws_launch_configuration" "foo" { + name = "terraform-test-%d" + image_id = "ami-21f78e11" + instance_type = "m1.small" + associate_public_ip_address = true + user_data = "foobar-user-data" + + root_block_device { + volume_type = "gp2" + volume_size = 11 + } + ebs_block_device { + device_name = "/dev/sdb" + volume_size = 9 + } + ebs_block_device { + device_name = "/dev/sdc" + volume_size = 10 + volume_type = "io1" + iops = 100 + } + ephemeral_block_device { + device_name = "/dev/sde" + virtual_name = "ephemeral0" + } +} + +data "aws_launch_configuration" "foo" { + name = "${aws_launch_configuration.foo.name}" +} +`, rInt) +} diff --git a/aws/provider.go b/aws/provider.go index 380e2f75207..a5020dfe052 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -225,6 +225,7 @@ func Provider() terraform.ResourceProvider { "aws_kms_secret": dataSourceAwsKmsSecret(), "aws_lambda_function": dataSourceAwsLambdaFunction(), "aws_lambda_invocation": dataSourceAwsLambdaInvocation(), + "aws_launch_configuration": dataSourceAwsLaunchConfiguration(), "aws_mq_broker": dataSourceAwsMqBroker(), "aws_nat_gateway": dataSourceAwsNatGateway(), "aws_network_acls": dataSourceAwsNetworkAcls(), From 5d6fb812a99ccb1f18d425fe25b619fd34cc7760 Mon Sep 17 00:00:00 2001 From: Kash Date: Mon, 5 Mar 2018 11:03:21 -0500 Subject: [PATCH 21/88] add documentation --- website/aws.erb | 3 ++ .../docs/d/launch_configuration.html.markdown | 48 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 website/docs/d/launch_configuration.html.markdown diff --git a/website/aws.erb b/website/aws.erb index 01fba3efc5e..0070bbdc6e5 100644 --- a/website/aws.erb +++ b/website/aws.erb @@ -253,6 +253,9 @@ > aws_lambda_function + > + aws_launch_configuration + > aws_lb diff --git a/website/docs/d/launch_configuration.html.markdown b/website/docs/d/launch_configuration.html.markdown new file mode 100644 index 00000000000..76b6054639c --- /dev/null +++ b/website/docs/d/launch_configuration.html.markdown @@ -0,0 +1,48 @@ +--- +layout: "aws" +page_title: "AWS: aws_launch_configuration" +sidebar_current: "docs-aws-datasource-launch-configuration" +description: |- + Provides a Launch Configuration data source. +--- + +# aws_launch_configuration + +Provides information about a Launch Configuration. + +## Example Usage + +```hcl +data "aws_launch_configuration" "ubuntu" { + name = "test-launch-config" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the launch configuration. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the launch configuration. +* `name` - The name of the launch configuration. +* `image_id` - The EC2 image ID of the instance. +* `instance_type` - The type of the instance to launch. +* `iam_instance_profile` - The IAM instance profile to associate with launched instances. +* `key_name` - The key name that should be used for the instance. +* `security_groups` - A list of associated security group IDS. +* `associate_public_ip_address` - Whether a public ip address is associated with the instance. +* `vpc_classic_link_id` - The ID of a ClassicLink-enabled VPC. +* `vpc_classic_link_security_groups` - The IDs of one or more security groups for the specified ClassicLink-enabled VPC. +* `user_data` - The user data of the instance. +* `enable_monitoring` - Whether detailed monitoring is enabled. +* `ebs_optimized` - Whether the launched EC2 instance will be EBS-optimized. +* `root_block_device` - The root block device of the instance. +* `ebs_block_device` - The EBS block devices attached to the instance. +* `ephemeral_block_device` - The Ephemeral volumes on the instance. +* `spot_price` - The price to use for reserving spot instances. +* `placement_tenancy` - The tenancy of the instance. From 88ef715ee551acf9e8d13c1b610b8dfe6f137357 Mon Sep 17 00:00:00 2001 From: Kash Date: Thu, 28 Jun 2018 10:18:03 -0400 Subject: [PATCH 22/88] nil checks and update documentation --- aws/data_source_aws_launch_configuration.go | 27 ++++++++-- .../docs/d/launch_configuration.html.markdown | 52 +++++++++++++------ 2 files changed, 59 insertions(+), 20 deletions(-) diff --git a/aws/data_source_aws_launch_configuration.go b/aws/data_source_aws_launch_configuration.go index 7f257eca24d..94822f5c444 100644 --- a/aws/data_source_aws_launch_configuration.go +++ b/aws/data_source_aws_launch_configuration.go @@ -1,6 +1,7 @@ package aws import ( + "errors" "fmt" "log" @@ -195,6 +196,14 @@ func dataSourceAwsLaunchConfigurationRead(d *schema.ResourceData, meta interface return fmt.Errorf("Error retrieving launch configuration: %s", err) } + if describConfs == nil || len(describConfs.LaunchConfigurations) == 0 { + return errors.New("No matching Launch Configuration found") + } + + if len(describConfs.LaunchConfigurations) > 1 { + return errors.New("Multiple matching Launch Configurations found") + } + lc := describConfs.LaunchConfigurations[0] d.Set("key_name", lc.KeyName) @@ -202,16 +211,24 @@ func dataSourceAwsLaunchConfigurationRead(d *schema.ResourceData, meta interface d.Set("instance_type", lc.InstanceType) d.Set("name", lc.LaunchConfigurationName) d.Set("user_data", lc.UserData) - d.Set("iam_instance_profile", lc.IamInstanceProfile) d.Set("ebs_optimized", lc.EbsOptimized) d.Set("spot_price", lc.SpotPrice) - d.Set("enable_monitoring", lc.InstanceMonitoring.Enabled) - d.Set("security_groups", lc.SecurityGroups) d.Set("associate_public_ip_address", lc.AssociatePublicIpAddress) - d.Set("vpc_classic_link_id", lc.ClassicLinkVPCId) - d.Set("vpc_classic_link_security_groups", lc.ClassicLinkVPCSecurityGroups) + d.Set("enable_monitoring", false) + + if lc.InstanceMonitoring != nil { + d.Set("enable_monitoring", lc.InstanceMonitoring.Enabled) + } + + if err := d.Set("security_groups", lc.SecurityGroups); err != nil { + return fmt.Errorf("error setting security_groups: %s", err) + } + + if err := d.Set("vpc_classic_link_security_groups", lc.ClassicLinkVPCSecurityGroups); err != nil { + return fmt.Errorf("error setting vpc_classic_link_security_groups: %s", err) + } if err := readLCBlockDevices(d, lc, ec2conn); err != nil { return err diff --git a/website/docs/d/launch_configuration.html.markdown b/website/docs/d/launch_configuration.html.markdown index 76b6054639c..ff3582b73b0 100644 --- a/website/docs/d/launch_configuration.html.markdown +++ b/website/docs/d/launch_configuration.html.markdown @@ -6,7 +6,7 @@ description: |- Provides a Launch Configuration data source. --- -# aws_launch_configuration +# Data Source: aws_launch_configuration Provides information about a Launch Configuration. @@ -29,20 +29,42 @@ The following arguments are supported: The following attributes are exported: * `id` - The ID of the launch configuration. -* `name` - The name of the launch configuration. -* `image_id` - The EC2 image ID of the instance. -* `instance_type` - The type of the instance to launch. -* `iam_instance_profile` - The IAM instance profile to associate with launched instances. -* `key_name` - The key name that should be used for the instance. -* `security_groups` - A list of associated security group IDS. -* `associate_public_ip_address` - Whether a public ip address is associated with the instance. +* `name` - The Name of the launch configuration. +* `image_id` - The EC2 Image ID of the instance. +* `instance_type` - The Instance Type of the instance to launch. +* `iam_instance_profile` - The IAM Instance Profile to associate with launched instances. +* `key_name` - The Key Name that should be used for the instance. +* `security_groups` - A list of associated Security Group IDS. +* `associate_public_ip_address` - Whether a Public IP address is associated with the instance. * `vpc_classic_link_id` - The ID of a ClassicLink-enabled VPC. -* `vpc_classic_link_security_groups` - The IDs of one or more security groups for the specified ClassicLink-enabled VPC. -* `user_data` - The user data of the instance. -* `enable_monitoring` - Whether detailed monitoring is enabled. +* `vpc_classic_link_security_groups` - The IDs of one or more Security Groups for the specified ClassicLink-enabled VPC. +* `user_data` - The User Data of the instance. +* `enable_monitoring` - Whether Detailed Monitoring is Enabled. * `ebs_optimized` - Whether the launched EC2 instance will be EBS-optimized. -* `root_block_device` - The root block device of the instance. -* `ebs_block_device` - The EBS block devices attached to the instance. +* `root_block_device` - The Root Block Device of the instance. +* `ebs_block_device` - The EBS Block Devices attached to the instance. * `ephemeral_block_device` - The Ephemeral volumes on the instance. -* `spot_price` - The price to use for reserving spot instances. -* `placement_tenancy` - The tenancy of the instance. +* `spot_price` - The Price to use for reserving Spot instances. +* `placement_tenancy` - The Tenancy of the instance. + +`root_block_device` is exported with the following attributes: + +* `delete_on_termination` - Whether the EBS Volume will be deleted on instance termination. +* `iops` - The provisioned IOPs of the volume. +* `volume_size` - The Size of the volume. +* `volume_type` - The Type of the volume. + +`ebs_block_device` is exported with the following attributes: + +* `delete_on_termination` - Whether the EBS Volume will be deleted on instance termination. +* `device_name` - The Name of the device. +* `iops` - The provisioned IOPs of the volume. +* `snapshot_id` - The Snapshot ID of the mount. +* `volume_size` - The Size of the volume. +* `volume_type` - The Type of the volume. +* `encrypted` - Whether the volume is Encrypted. + +`ephemeral_block_device` is exported with the following attributes: + +* `device_name` - The Name of the device. +* `virtual_name` - The Virtual Name of the device. From 40d6ecfb965b8d75b42021ae9c28db45bf039c88 Mon Sep 17 00:00:00 2001 From: Kash Date: Thu, 28 Jun 2018 11:25:15 -0400 Subject: [PATCH 23/88] fix setting security group --- aws/data_source_aws_launch_configuration.go | 12 ++++- ...ta_source_aws_launch_configuration_test.go | 45 ++++++++++++++++++- 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/aws/data_source_aws_launch_configuration.go b/aws/data_source_aws_launch_configuration.go index 94822f5c444..e02cbd2a06a 100644 --- a/aws/data_source_aws_launch_configuration.go +++ b/aws/data_source_aws_launch_configuration.go @@ -222,11 +222,19 @@ func dataSourceAwsLaunchConfigurationRead(d *schema.ResourceData, meta interface d.Set("enable_monitoring", lc.InstanceMonitoring.Enabled) } - if err := d.Set("security_groups", lc.SecurityGroups); err != nil { + vpcSGs := make([]string, 0, len(lc.SecurityGroups)) + for _, sg := range lc.SecurityGroups { + vpcSGs = append(vpcSGs, *sg) + } + if err := d.Set("security_groups", vpcSGs); err != nil { return fmt.Errorf("error setting security_groups: %s", err) } - if err := d.Set("vpc_classic_link_security_groups", lc.ClassicLinkVPCSecurityGroups); err != nil { + classicSGs := make([]string, 0, len(lc.ClassicLinkVPCSecurityGroups)) + for _, sg := range lc.ClassicLinkVPCSecurityGroups { + classicSGs = append(classicSGs, *sg) + } + if err := d.Set("vpc_classic_link_security_groups", classicSGs); err != nil { return fmt.Errorf("error setting vpc_classic_link_security_groups: %s", err) } diff --git a/aws/data_source_aws_launch_configuration_test.go b/aws/data_source_aws_launch_configuration_test.go index 7722c6134c0..880e842e134 100644 --- a/aws/data_source_aws_launch_configuration_test.go +++ b/aws/data_source_aws_launch_configuration_test.go @@ -17,7 +17,7 @@ func TestAccAWSLaunchConfigurationDataSource_basic(t *testing.T) { Providers: testAccProviders, Steps: []resource.TestStep{ { - Config: testAccLaunchConfigurationDataSourceConfig(rInt), + Config: testAccLaunchConfigurationDataSourceConfig_basic(rInt), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrSet(rName, "image_id"), resource.TestCheckResourceAttrSet(rName, "instance_type"), @@ -31,8 +31,25 @@ func TestAccAWSLaunchConfigurationDataSource_basic(t *testing.T) { }, }) } +func TestAccAWSLaunchConfigurationDataSource_securityGroups(t *testing.T) { + rInt := acctest.RandInt() + rName := "data.aws_launch_configuration.foo" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccLaunchConfigurationDataSourceConfig_securityGroups(rInt), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(rName, "security_groups.#", "1"), + ), + }, + }, + }) +} -func testAccLaunchConfigurationDataSourceConfig(rInt int) string { +func testAccLaunchConfigurationDataSourceConfig_basic(rInt int) string { return fmt.Sprintf(` resource "aws_launch_configuration" "foo" { name = "terraform-test-%d" @@ -66,3 +83,27 @@ data "aws_launch_configuration" "foo" { } `, rInt) } + +func testAccLaunchConfigurationDataSourceConfig_securityGroups(rInt int) string { + return fmt.Sprintf(` +resource "aws_vpc" "test" { + cidr_block = "10.1.0.0/16" +} + +resource "aws_security_group" "test" { + name = "terraform-test_%d" + vpc_id = "${aws_vpc.test.id}" +} + +resource "aws_launch_configuration" "test" { + name = "terraform-test-%d" + image_id = "ami-21f78e11" + instance_type = "m1.small" + security_groups = ["${aws_security_group.test.id}"] +} + +data "aws_launch_configuration" "foo" { + name = "${aws_launch_configuration.test.name}" +} +`, rInt, rInt) +} From 37249a597cd3ef07791f1e49f1e609ffb29fdd15 Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Thu, 28 Jun 2018 13:21:41 -0400 Subject: [PATCH 24/88] resource/aws_ssm_document: Support tagging --- aws/resource_aws_ssm_document.go | 21 +++++ aws/resource_aws_ssm_document_test.go | 106 ++++++++++++++++++++++ website/docs/r/ssm_document.html.markdown | 1 + 3 files changed, 128 insertions(+) diff --git a/aws/resource_aws_ssm_document.go b/aws/resource_aws_ssm_document.go index 2bb17929040..73ea866f130 100644 --- a/aws/resource_aws_ssm_document.go +++ b/aws/resource_aws_ssm_document.go @@ -141,6 +141,7 @@ func resourceAwsSsmDocument() *schema.Resource { }, }, }, + "tags": tagsSchema(), }, } } @@ -181,6 +182,10 @@ func resourceAwsSsmDocumentCreate(d *schema.ResourceData, meta interface{}) erro log.Printf("[DEBUG] Not setting permissions for %q", d.Id()) } + if err := setTagsSSM(ssmconn, d, d.Id(), ssm.ResourceTypeForTaggingDocument); err != nil { + return fmt.Errorf("error setting SSM Document tags: %s", err) + } + return resourceAwsSsmDocumentRead(d, meta) } @@ -271,10 +276,26 @@ func resourceAwsSsmDocumentRead(d *schema.ResourceData, meta interface{}) error return err } + tagList, err := ssmconn.ListTagsForResource(&ssm.ListTagsForResourceInput{ + ResourceId: aws.String(d.Id()), + ResourceType: aws.String(ssm.ResourceTypeForTaggingDocument), + }) + if err != nil { + return fmt.Errorf("error listing SSM Document tags for %s: %s", d.Id(), err) + } + d.Set("tags", tagsToMapSSM(tagList.TagList)) + return nil } func resourceAwsSsmDocumentUpdate(d *schema.ResourceData, meta interface{}) error { + ssmconn := meta.(*AWSClient).ssmconn + + if d.HasChange("tags") { + if err := setTagsSSM(ssmconn, d, d.Id(), ssm.ResourceTypeForTaggingDocument); err != nil { + return fmt.Errorf("error setting SSM Document tags: %s", err) + } + } if _, ok := d.GetOk("permissions"); ok { if err := setDocumentPermissions(d, meta); err != nil { diff --git a/aws/resource_aws_ssm_document_test.go b/aws/resource_aws_ssm_document_test.go index 685158d78dc..5794b62dbd5 100644 --- a/aws/resource_aws_ssm_document_test.go +++ b/aws/resource_aws_ssm_document_test.go @@ -27,6 +27,7 @@ func TestAccAWSSSMDocument_basic(t *testing.T) { resource.TestCheckResourceAttr("aws_ssm_document.foo", "document_format", "JSON"), resource.TestMatchResourceAttr("aws_ssm_document.foo", "arn", regexp.MustCompile(`^arn:aws:ssm:[a-z]{2}-[a-z]+-\d{1}:\d{12}:document/.*$`)), + resource.TestCheckResourceAttr("aws_ssm_document.foo", "tags.%", "0"), ), }, }, @@ -184,6 +185,44 @@ mainSteps: }) } +func TestAccAWSSSMDocument_Tags(t *testing.T) { + rName := acctest.RandString(10) + resourceName := "aws_ssm_document.foo" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSSMDocumentDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSSMDocumentConfig_Tags_Single(rName, "key1", "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSSMDocumentExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + Config: testAccAWSSSMDocumentConfig_Tags_Multiple(rName, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSSMDocumentExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + Config: testAccAWSSSMDocumentConfig_Tags_Single(rName, "key2", "value2updated"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSSMDocumentExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2updated"), + ), + }, + }, + }) +} + func testAccCheckAWSSSMDocumentExists(n string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] @@ -518,3 +557,70 @@ DOC } `, rName, content) } + +func testAccAWSSSMDocumentConfig_Tags_Single(rName, key1, value1 string) string { + return fmt.Sprintf(` +resource "aws_ssm_document" "foo" { + document_type = "Command" + name = "test_document-%s" + + content = < Date: Thu, 28 Jun 2018 14:36:55 -0400 Subject: [PATCH 25/88] Update CHANGELOG for #3624 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb19a76867a..9d2a1152474 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## 1.26.0 (Unreleased) +FEATURES: + +* **New Data Source:** `aws_launch_configuration` [GH-3624] + ENHANCEMENTS: * resource/aws_eip_association: Support resource import [GH-5006] From cedf1fc8203093c905a186dc5c7d3dd02a0ea8bc Mon Sep 17 00:00:00 2001 From: Julien Duchesne Date: Thu, 28 Jun 2018 19:29:48 -0400 Subject: [PATCH 26/88] Add pricing vendor and client --- aws/config.go | 3 + .../aws/aws-sdk-go/service/pricing/api.go | 955 ++++++++++++++++++ .../aws/aws-sdk-go/service/pricing/doc.go | 51 + .../aws/aws-sdk-go/service/pricing/errors.go | 37 + .../aws/aws-sdk-go/service/pricing/service.go | 100 ++ vendor/vendor.json | 10 +- 6 files changed, 1155 insertions(+), 1 deletion(-) create mode 100644 vendor/github.com/aws/aws-sdk-go/service/pricing/api.go create mode 100644 vendor/github.com/aws/aws-sdk-go/service/pricing/doc.go create mode 100644 vendor/github.com/aws/aws-sdk-go/service/pricing/errors.go create mode 100644 vendor/github.com/aws/aws-sdk-go/service/pricing/service.go diff --git a/aws/config.go b/aws/config.go index 61852de0477..19a8affccdc 100644 --- a/aws/config.go +++ b/aws/config.go @@ -75,6 +75,7 @@ import ( "github.com/aws/aws-sdk-go/service/neptune" "github.com/aws/aws-sdk-go/service/opsworks" "github.com/aws/aws-sdk-go/service/organizations" + "github.com/aws/aws-sdk-go/service/pricing" "github.com/aws/aws-sdk-go/service/rds" "github.com/aws/aws-sdk-go/service/redshift" "github.com/aws/aws-sdk-go/service/route53" @@ -232,6 +233,7 @@ type AWSClient struct { lexmodelconn *lexmodelbuildingservice.LexModelBuildingService budgetconn *budgets.Budgets neptuneconn *neptune.Neptune + pricingconn *pricing.Pricing } func (c *AWSClient) S3() *s3.S3 { @@ -528,6 +530,7 @@ func (c *Config) Client() (interface{}, error) { client.mediastoreconn = mediastore.New(sess) client.appsyncconn = appsync.New(sess) client.neptuneconn = neptune.New(sess) + client.pricingconn = pricing.New(sess) // Workaround for https://github.com/aws/aws-sdk-go/issues/1376 client.kinesisconn.Handlers.Retry.PushBack(func(r *request.Request) { diff --git a/vendor/github.com/aws/aws-sdk-go/service/pricing/api.go b/vendor/github.com/aws/aws-sdk-go/service/pricing/api.go new file mode 100644 index 00000000000..f5be2db265a --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/service/pricing/api.go @@ -0,0 +1,955 @@ +// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT. + +package pricing + +import ( + "fmt" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awsutil" + "github.com/aws/aws-sdk-go/aws/request" +) + +const opDescribeServices = "DescribeServices" + +// DescribeServicesRequest generates a "aws/request.Request" representing the +// client's request for the DescribeServices operation. The "output" return +// value will be populated with the request's response once the request completes +// successfuly. +// +// Use "Send" method on the returned Request to send the API call to the service. +// the "output" return value is not valid until after Send returns without error. +// +// See DescribeServices for more information on using the DescribeServices +// API call, and error handling. +// +// This method is useful when you want to inject custom logic or configuration +// into the SDK's request lifecycle. Such as custom headers, or retry logic. +// +// +// // Example sending a request using the DescribeServicesRequest method. +// req, resp := client.DescribeServicesRequest(params) +// +// err := req.Send() +// if err == nil { // resp is now filled +// fmt.Println(resp) +// } +// +// See also, https://docs.aws.amazon.com/goto/WebAPI/pricing-2017-10-15/DescribeServices +func (c *Pricing) DescribeServicesRequest(input *DescribeServicesInput) (req *request.Request, output *DescribeServicesOutput) { + op := &request.Operation{ + Name: opDescribeServices, + HTTPMethod: "POST", + HTTPPath: "/", + Paginator: &request.Paginator{ + InputTokens: []string{"NextToken"}, + OutputTokens: []string{"NextToken"}, + LimitToken: "MaxResults", + TruncationToken: "", + }, + } + + if input == nil { + input = &DescribeServicesInput{} + } + + output = &DescribeServicesOutput{} + req = c.newRequest(op, input, output) + return +} + +// DescribeServices API operation for AWS Price List Service. +// +// Returns the metadata for one service or a list of the metadata for all services. +// Use this without a service code to get the service codes for all services. +// Use it with a service code, such as AmazonEC2, to get information specific +// to that service, such as the attribute names available for that service. +// For example, some of the attribute names available for EC2 are volumeType, +// maxIopsVolume, operation, locationType, and instanceCapacity10xlarge. +// +// Returns awserr.Error for service API and SDK errors. Use runtime type assertions +// with awserr.Error's Code and Message methods to get detailed information about +// the error. +// +// See the AWS API reference guide for AWS Price List Service's +// API operation DescribeServices for usage and error information. +// +// Returned Error Codes: +// * ErrCodeInternalErrorException "InternalErrorException" +// An error on the server occurred during the processing of your request. Try +// again later. +// +// * ErrCodeInvalidParameterException "InvalidParameterException" +// One or more parameters had an invalid value. +// +// * ErrCodeNotFoundException "NotFoundException" +// The requested resource can't be found. +// +// * ErrCodeInvalidNextTokenException "InvalidNextTokenException" +// The pagination token is invalid. Try again without a pagination token. +// +// * ErrCodeExpiredNextTokenException "ExpiredNextTokenException" +// The pagination token expired. Try again without a pagination token. +// +// See also, https://docs.aws.amazon.com/goto/WebAPI/pricing-2017-10-15/DescribeServices +func (c *Pricing) DescribeServices(input *DescribeServicesInput) (*DescribeServicesOutput, error) { + req, out := c.DescribeServicesRequest(input) + return out, req.Send() +} + +// DescribeServicesWithContext is the same as DescribeServices with the addition of +// the ability to pass a context and additional request options. +// +// See DescribeServices for details on how to use this API operation. +// +// The context must be non-nil and will be used for request cancellation. If +// the context is nil a panic will occur. In the future the SDK may create +// sub-contexts for http.Requests. See https://golang.org/pkg/context/ +// for more information on using Contexts. +func (c *Pricing) DescribeServicesWithContext(ctx aws.Context, input *DescribeServicesInput, opts ...request.Option) (*DescribeServicesOutput, error) { + req, out := c.DescribeServicesRequest(input) + req.SetContext(ctx) + req.ApplyOptions(opts...) + return out, req.Send() +} + +// DescribeServicesPages iterates over the pages of a DescribeServices operation, +// calling the "fn" function with the response data for each page. To stop +// iterating, return false from the fn function. +// +// See DescribeServices method for more information on how to use this operation. +// +// Note: This operation can generate multiple requests to a service. +// +// // Example iterating over at most 3 pages of a DescribeServices operation. +// pageNum := 0 +// err := client.DescribeServicesPages(params, +// func(page *DescribeServicesOutput, lastPage bool) bool { +// pageNum++ +// fmt.Println(page) +// return pageNum <= 3 +// }) +// +func (c *Pricing) DescribeServicesPages(input *DescribeServicesInput, fn func(*DescribeServicesOutput, bool) bool) error { + return c.DescribeServicesPagesWithContext(aws.BackgroundContext(), input, fn) +} + +// DescribeServicesPagesWithContext same as DescribeServicesPages except +// it takes a Context and allows setting request options on the pages. +// +// The context must be non-nil and will be used for request cancellation. If +// the context is nil a panic will occur. In the future the SDK may create +// sub-contexts for http.Requests. See https://golang.org/pkg/context/ +// for more information on using Contexts. +func (c *Pricing) DescribeServicesPagesWithContext(ctx aws.Context, input *DescribeServicesInput, fn func(*DescribeServicesOutput, bool) bool, opts ...request.Option) error { + p := request.Pagination{ + NewRequest: func() (*request.Request, error) { + var inCpy *DescribeServicesInput + if input != nil { + tmp := *input + inCpy = &tmp + } + req, _ := c.DescribeServicesRequest(inCpy) + req.SetContext(ctx) + req.ApplyOptions(opts...) + return req, nil + }, + } + + cont := true + for p.Next() && cont { + cont = fn(p.Page().(*DescribeServicesOutput), !p.HasNextPage()) + } + return p.Err() +} + +const opGetAttributeValues = "GetAttributeValues" + +// GetAttributeValuesRequest generates a "aws/request.Request" representing the +// client's request for the GetAttributeValues operation. The "output" return +// value will be populated with the request's response once the request completes +// successfuly. +// +// Use "Send" method on the returned Request to send the API call to the service. +// the "output" return value is not valid until after Send returns without error. +// +// See GetAttributeValues for more information on using the GetAttributeValues +// API call, and error handling. +// +// This method is useful when you want to inject custom logic or configuration +// into the SDK's request lifecycle. Such as custom headers, or retry logic. +// +// +// // Example sending a request using the GetAttributeValuesRequest method. +// req, resp := client.GetAttributeValuesRequest(params) +// +// err := req.Send() +// if err == nil { // resp is now filled +// fmt.Println(resp) +// } +// +// See also, https://docs.aws.amazon.com/goto/WebAPI/pricing-2017-10-15/GetAttributeValues +func (c *Pricing) GetAttributeValuesRequest(input *GetAttributeValuesInput) (req *request.Request, output *GetAttributeValuesOutput) { + op := &request.Operation{ + Name: opGetAttributeValues, + HTTPMethod: "POST", + HTTPPath: "/", + Paginator: &request.Paginator{ + InputTokens: []string{"NextToken"}, + OutputTokens: []string{"NextToken"}, + LimitToken: "MaxResults", + TruncationToken: "", + }, + } + + if input == nil { + input = &GetAttributeValuesInput{} + } + + output = &GetAttributeValuesOutput{} + req = c.newRequest(op, input, output) + return +} + +// GetAttributeValues API operation for AWS Price List Service. +// +// Returns a list of attribute values. Attibutes are similar to the details +// in a Price List API offer file. For a list of available attributes, see Offer +// File Definitions (http://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/reading-an-offer.html#pps-defs) +// in the AWS Billing and Cost Management User Guide (http://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/billing-what-is.html). +// +// Returns awserr.Error for service API and SDK errors. Use runtime type assertions +// with awserr.Error's Code and Message methods to get detailed information about +// the error. +// +// See the AWS API reference guide for AWS Price List Service's +// API operation GetAttributeValues for usage and error information. +// +// Returned Error Codes: +// * ErrCodeInternalErrorException "InternalErrorException" +// An error on the server occurred during the processing of your request. Try +// again later. +// +// * ErrCodeInvalidParameterException "InvalidParameterException" +// One or more parameters had an invalid value. +// +// * ErrCodeNotFoundException "NotFoundException" +// The requested resource can't be found. +// +// * ErrCodeInvalidNextTokenException "InvalidNextTokenException" +// The pagination token is invalid. Try again without a pagination token. +// +// * ErrCodeExpiredNextTokenException "ExpiredNextTokenException" +// The pagination token expired. Try again without a pagination token. +// +// See also, https://docs.aws.amazon.com/goto/WebAPI/pricing-2017-10-15/GetAttributeValues +func (c *Pricing) GetAttributeValues(input *GetAttributeValuesInput) (*GetAttributeValuesOutput, error) { + req, out := c.GetAttributeValuesRequest(input) + return out, req.Send() +} + +// GetAttributeValuesWithContext is the same as GetAttributeValues with the addition of +// the ability to pass a context and additional request options. +// +// See GetAttributeValues for details on how to use this API operation. +// +// The context must be non-nil and will be used for request cancellation. If +// the context is nil a panic will occur. In the future the SDK may create +// sub-contexts for http.Requests. See https://golang.org/pkg/context/ +// for more information on using Contexts. +func (c *Pricing) GetAttributeValuesWithContext(ctx aws.Context, input *GetAttributeValuesInput, opts ...request.Option) (*GetAttributeValuesOutput, error) { + req, out := c.GetAttributeValuesRequest(input) + req.SetContext(ctx) + req.ApplyOptions(opts...) + return out, req.Send() +} + +// GetAttributeValuesPages iterates over the pages of a GetAttributeValues operation, +// calling the "fn" function with the response data for each page. To stop +// iterating, return false from the fn function. +// +// See GetAttributeValues method for more information on how to use this operation. +// +// Note: This operation can generate multiple requests to a service. +// +// // Example iterating over at most 3 pages of a GetAttributeValues operation. +// pageNum := 0 +// err := client.GetAttributeValuesPages(params, +// func(page *GetAttributeValuesOutput, lastPage bool) bool { +// pageNum++ +// fmt.Println(page) +// return pageNum <= 3 +// }) +// +func (c *Pricing) GetAttributeValuesPages(input *GetAttributeValuesInput, fn func(*GetAttributeValuesOutput, bool) bool) error { + return c.GetAttributeValuesPagesWithContext(aws.BackgroundContext(), input, fn) +} + +// GetAttributeValuesPagesWithContext same as GetAttributeValuesPages except +// it takes a Context and allows setting request options on the pages. +// +// The context must be non-nil and will be used for request cancellation. If +// the context is nil a panic will occur. In the future the SDK may create +// sub-contexts for http.Requests. See https://golang.org/pkg/context/ +// for more information on using Contexts. +func (c *Pricing) GetAttributeValuesPagesWithContext(ctx aws.Context, input *GetAttributeValuesInput, fn func(*GetAttributeValuesOutput, bool) bool, opts ...request.Option) error { + p := request.Pagination{ + NewRequest: func() (*request.Request, error) { + var inCpy *GetAttributeValuesInput + if input != nil { + tmp := *input + inCpy = &tmp + } + req, _ := c.GetAttributeValuesRequest(inCpy) + req.SetContext(ctx) + req.ApplyOptions(opts...) + return req, nil + }, + } + + cont := true + for p.Next() && cont { + cont = fn(p.Page().(*GetAttributeValuesOutput), !p.HasNextPage()) + } + return p.Err() +} + +const opGetProducts = "GetProducts" + +// GetProductsRequest generates a "aws/request.Request" representing the +// client's request for the GetProducts operation. The "output" return +// value will be populated with the request's response once the request completes +// successfuly. +// +// Use "Send" method on the returned Request to send the API call to the service. +// the "output" return value is not valid until after Send returns without error. +// +// See GetProducts for more information on using the GetProducts +// API call, and error handling. +// +// This method is useful when you want to inject custom logic or configuration +// into the SDK's request lifecycle. Such as custom headers, or retry logic. +// +// +// // Example sending a request using the GetProductsRequest method. +// req, resp := client.GetProductsRequest(params) +// +// err := req.Send() +// if err == nil { // resp is now filled +// fmt.Println(resp) +// } +// +// See also, https://docs.aws.amazon.com/goto/WebAPI/pricing-2017-10-15/GetProducts +func (c *Pricing) GetProductsRequest(input *GetProductsInput) (req *request.Request, output *GetProductsOutput) { + op := &request.Operation{ + Name: opGetProducts, + HTTPMethod: "POST", + HTTPPath: "/", + Paginator: &request.Paginator{ + InputTokens: []string{"NextToken"}, + OutputTokens: []string{"NextToken"}, + LimitToken: "MaxResults", + TruncationToken: "", + }, + } + + if input == nil { + input = &GetProductsInput{} + } + + output = &GetProductsOutput{} + req = c.newRequest(op, input, output) + return +} + +// GetProducts API operation for AWS Price List Service. +// +// Returns a list of all products that match the filter criteria. +// +// Returns awserr.Error for service API and SDK errors. Use runtime type assertions +// with awserr.Error's Code and Message methods to get detailed information about +// the error. +// +// See the AWS API reference guide for AWS Price List Service's +// API operation GetProducts for usage and error information. +// +// Returned Error Codes: +// * ErrCodeInternalErrorException "InternalErrorException" +// An error on the server occurred during the processing of your request. Try +// again later. +// +// * ErrCodeInvalidParameterException "InvalidParameterException" +// One or more parameters had an invalid value. +// +// * ErrCodeNotFoundException "NotFoundException" +// The requested resource can't be found. +// +// * ErrCodeInvalidNextTokenException "InvalidNextTokenException" +// The pagination token is invalid. Try again without a pagination token. +// +// * ErrCodeExpiredNextTokenException "ExpiredNextTokenException" +// The pagination token expired. Try again without a pagination token. +// +// See also, https://docs.aws.amazon.com/goto/WebAPI/pricing-2017-10-15/GetProducts +func (c *Pricing) GetProducts(input *GetProductsInput) (*GetProductsOutput, error) { + req, out := c.GetProductsRequest(input) + return out, req.Send() +} + +// GetProductsWithContext is the same as GetProducts with the addition of +// the ability to pass a context and additional request options. +// +// See GetProducts for details on how to use this API operation. +// +// The context must be non-nil and will be used for request cancellation. If +// the context is nil a panic will occur. In the future the SDK may create +// sub-contexts for http.Requests. See https://golang.org/pkg/context/ +// for more information on using Contexts. +func (c *Pricing) GetProductsWithContext(ctx aws.Context, input *GetProductsInput, opts ...request.Option) (*GetProductsOutput, error) { + req, out := c.GetProductsRequest(input) + req.SetContext(ctx) + req.ApplyOptions(opts...) + return out, req.Send() +} + +// GetProductsPages iterates over the pages of a GetProducts operation, +// calling the "fn" function with the response data for each page. To stop +// iterating, return false from the fn function. +// +// See GetProducts method for more information on how to use this operation. +// +// Note: This operation can generate multiple requests to a service. +// +// // Example iterating over at most 3 pages of a GetProducts operation. +// pageNum := 0 +// err := client.GetProductsPages(params, +// func(page *GetProductsOutput, lastPage bool) bool { +// pageNum++ +// fmt.Println(page) +// return pageNum <= 3 +// }) +// +func (c *Pricing) GetProductsPages(input *GetProductsInput, fn func(*GetProductsOutput, bool) bool) error { + return c.GetProductsPagesWithContext(aws.BackgroundContext(), input, fn) +} + +// GetProductsPagesWithContext same as GetProductsPages except +// it takes a Context and allows setting request options on the pages. +// +// The context must be non-nil and will be used for request cancellation. If +// the context is nil a panic will occur. In the future the SDK may create +// sub-contexts for http.Requests. See https://golang.org/pkg/context/ +// for more information on using Contexts. +func (c *Pricing) GetProductsPagesWithContext(ctx aws.Context, input *GetProductsInput, fn func(*GetProductsOutput, bool) bool, opts ...request.Option) error { + p := request.Pagination{ + NewRequest: func() (*request.Request, error) { + var inCpy *GetProductsInput + if input != nil { + tmp := *input + inCpy = &tmp + } + req, _ := c.GetProductsRequest(inCpy) + req.SetContext(ctx) + req.ApplyOptions(opts...) + return req, nil + }, + } + + cont := true + for p.Next() && cont { + cont = fn(p.Page().(*GetProductsOutput), !p.HasNextPage()) + } + return p.Err() +} + +// The values of a given attribute, such as Throughput Optimized HDD or Provisioned +// IOPS for the Amazon EC2volumeType attribute. +type AttributeValue struct { + _ struct{} `type:"structure"` + + // The specific value of an attributeName. + Value *string `type:"string"` +} + +// String returns the string representation +func (s AttributeValue) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s AttributeValue) GoString() string { + return s.String() +} + +// SetValue sets the Value field's value. +func (s *AttributeValue) SetValue(v string) *AttributeValue { + s.Value = &v + return s +} + +type DescribeServicesInput struct { + _ struct{} `type:"structure"` + + // The format version that you want the response to be in. + // + // Valid values are: aws_v1 + FormatVersion *string `type:"string"` + + // The maximum number of results that you want returned in the response. + MaxResults *int64 `min:"1" type:"integer"` + + // The pagination token that indicates the next set of results that you want + // to retrieve. + NextToken *string `type:"string"` + + // The code for the service whose information you want to retrieve, such as + // AmazonEC2. You can use the ServiceCode to filter the results in a GetProducts + // call. To retrieve a list of all services, leave this blank. + ServiceCode *string `type:"string"` +} + +// String returns the string representation +func (s DescribeServicesInput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s DescribeServicesInput) GoString() string { + return s.String() +} + +// Validate inspects the fields of the type to determine if they are valid. +func (s *DescribeServicesInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "DescribeServicesInput"} + if s.MaxResults != nil && *s.MaxResults < 1 { + invalidParams.Add(request.NewErrParamMinValue("MaxResults", 1)) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + +// SetFormatVersion sets the FormatVersion field's value. +func (s *DescribeServicesInput) SetFormatVersion(v string) *DescribeServicesInput { + s.FormatVersion = &v + return s +} + +// SetMaxResults sets the MaxResults field's value. +func (s *DescribeServicesInput) SetMaxResults(v int64) *DescribeServicesInput { + s.MaxResults = &v + return s +} + +// SetNextToken sets the NextToken field's value. +func (s *DescribeServicesInput) SetNextToken(v string) *DescribeServicesInput { + s.NextToken = &v + return s +} + +// SetServiceCode sets the ServiceCode field's value. +func (s *DescribeServicesInput) SetServiceCode(v string) *DescribeServicesInput { + s.ServiceCode = &v + return s +} + +type DescribeServicesOutput struct { + _ struct{} `type:"structure"` + + // The format version of the response. For example, aws_v1. + FormatVersion *string `type:"string"` + + // The pagination token for the next set of retreivable results. + NextToken *string `type:"string"` + + // The service metadata for the service or services in the response. + Services []*Service `type:"list"` +} + +// String returns the string representation +func (s DescribeServicesOutput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s DescribeServicesOutput) GoString() string { + return s.String() +} + +// SetFormatVersion sets the FormatVersion field's value. +func (s *DescribeServicesOutput) SetFormatVersion(v string) *DescribeServicesOutput { + s.FormatVersion = &v + return s +} + +// SetNextToken sets the NextToken field's value. +func (s *DescribeServicesOutput) SetNextToken(v string) *DescribeServicesOutput { + s.NextToken = &v + return s +} + +// SetServices sets the Services field's value. +func (s *DescribeServicesOutput) SetServices(v []*Service) *DescribeServicesOutput { + s.Services = v + return s +} + +// The constraints that you want all returned products to match. +type Filter struct { + _ struct{} `type:"structure"` + + // The product metadata field that you want to filter on. You can filter by + // just the service code to see all products for a specific service, filter + // by just the attribute name to see a specific attribute for multiple services, + // or use both a service code and an attribute name to retrieve only products + // that match both fields. + // + // Valid values include: ServiceCode, and all attribute names + // + // For example, you can filter by the AmazonEC2 service code and the volumeType + // attribute name to get the prices for only Amazon EC2 volumes. + // + // Field is a required field + Field *string `type:"string" required:"true"` + + // The type of filter that you want to use. + // + // Valid values are: TERM_MATCH. TERM_MATCH returns only products that match + // both the given filter field and the given value. + // + // Type is a required field + Type *string `type:"string" required:"true" enum:"FilterType"` + + // The service code or attribute value that you want to filter by. If you are + // filtering by service code this is the actual service code, such as AmazonEC2. + // If you are filtering by attribute name, this is the attribute value that + // you want the returned products to match, such as a Provisioned IOPS volume. + // + // Value is a required field + Value *string `type:"string" required:"true"` +} + +// String returns the string representation +func (s Filter) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s Filter) GoString() string { + return s.String() +} + +// Validate inspects the fields of the type to determine if they are valid. +func (s *Filter) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "Filter"} + if s.Field == nil { + invalidParams.Add(request.NewErrParamRequired("Field")) + } + if s.Type == nil { + invalidParams.Add(request.NewErrParamRequired("Type")) + } + if s.Value == nil { + invalidParams.Add(request.NewErrParamRequired("Value")) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + +// SetField sets the Field field's value. +func (s *Filter) SetField(v string) *Filter { + s.Field = &v + return s +} + +// SetType sets the Type field's value. +func (s *Filter) SetType(v string) *Filter { + s.Type = &v + return s +} + +// SetValue sets the Value field's value. +func (s *Filter) SetValue(v string) *Filter { + s.Value = &v + return s +} + +type GetAttributeValuesInput struct { + _ struct{} `type:"structure"` + + // The name of the attribute that you want to retrieve the values for, such + // as volumeType. + // + // AttributeName is a required field + AttributeName *string `type:"string" required:"true"` + + // The maximum number of results to return in response. + MaxResults *int64 `min:"1" type:"integer"` + + // The pagination token that indicates the next set of results that you want + // to retrieve. + NextToken *string `type:"string"` + + // The service code for the service whose attributes you want to retrieve. For + // example, if you want the retrieve an EC2 attribute, use AmazonEC2. + // + // ServiceCode is a required field + ServiceCode *string `type:"string" required:"true"` +} + +// String returns the string representation +func (s GetAttributeValuesInput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s GetAttributeValuesInput) GoString() string { + return s.String() +} + +// Validate inspects the fields of the type to determine if they are valid. +func (s *GetAttributeValuesInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "GetAttributeValuesInput"} + if s.AttributeName == nil { + invalidParams.Add(request.NewErrParamRequired("AttributeName")) + } + if s.MaxResults != nil && *s.MaxResults < 1 { + invalidParams.Add(request.NewErrParamMinValue("MaxResults", 1)) + } + if s.ServiceCode == nil { + invalidParams.Add(request.NewErrParamRequired("ServiceCode")) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + +// SetAttributeName sets the AttributeName field's value. +func (s *GetAttributeValuesInput) SetAttributeName(v string) *GetAttributeValuesInput { + s.AttributeName = &v + return s +} + +// SetMaxResults sets the MaxResults field's value. +func (s *GetAttributeValuesInput) SetMaxResults(v int64) *GetAttributeValuesInput { + s.MaxResults = &v + return s +} + +// SetNextToken sets the NextToken field's value. +func (s *GetAttributeValuesInput) SetNextToken(v string) *GetAttributeValuesInput { + s.NextToken = &v + return s +} + +// SetServiceCode sets the ServiceCode field's value. +func (s *GetAttributeValuesInput) SetServiceCode(v string) *GetAttributeValuesInput { + s.ServiceCode = &v + return s +} + +type GetAttributeValuesOutput struct { + _ struct{} `type:"structure"` + + // The list of values for an attribute. For example, Throughput Optimized HDD + // and Provisioned IOPS are two available values for the AmazonEC2volumeType. + AttributeValues []*AttributeValue `type:"list"` + + // The pagination token that indicates the next set of results to retrieve. + NextToken *string `type:"string"` +} + +// String returns the string representation +func (s GetAttributeValuesOutput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s GetAttributeValuesOutput) GoString() string { + return s.String() +} + +// SetAttributeValues sets the AttributeValues field's value. +func (s *GetAttributeValuesOutput) SetAttributeValues(v []*AttributeValue) *GetAttributeValuesOutput { + s.AttributeValues = v + return s +} + +// SetNextToken sets the NextToken field's value. +func (s *GetAttributeValuesOutput) SetNextToken(v string) *GetAttributeValuesOutput { + s.NextToken = &v + return s +} + +type GetProductsInput struct { + _ struct{} `type:"structure"` + + // The list of filters that limit the returned products. only products that + // match all filters are returned. + Filters []*Filter `type:"list"` + + // The format version that you want the response to be in. + // + // Valid values are: aws_v1 + FormatVersion *string `type:"string"` + + // The maximum number of results to return in the response. + MaxResults *int64 `min:"1" type:"integer"` + + // The pagination token that indicates the next set of results that you want + // to retrieve. + NextToken *string `type:"string"` + + // The code for the service whose products you want to retrieve. + ServiceCode *string `type:"string"` +} + +// String returns the string representation +func (s GetProductsInput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s GetProductsInput) GoString() string { + return s.String() +} + +// Validate inspects the fields of the type to determine if they are valid. +func (s *GetProductsInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "GetProductsInput"} + if s.MaxResults != nil && *s.MaxResults < 1 { + invalidParams.Add(request.NewErrParamMinValue("MaxResults", 1)) + } + if s.Filters != nil { + for i, v := range s.Filters { + if v == nil { + continue + } + if err := v.Validate(); err != nil { + invalidParams.AddNested(fmt.Sprintf("%s[%v]", "Filters", i), err.(request.ErrInvalidParams)) + } + } + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + +// SetFilters sets the Filters field's value. +func (s *GetProductsInput) SetFilters(v []*Filter) *GetProductsInput { + s.Filters = v + return s +} + +// SetFormatVersion sets the FormatVersion field's value. +func (s *GetProductsInput) SetFormatVersion(v string) *GetProductsInput { + s.FormatVersion = &v + return s +} + +// SetMaxResults sets the MaxResults field's value. +func (s *GetProductsInput) SetMaxResults(v int64) *GetProductsInput { + s.MaxResults = &v + return s +} + +// SetNextToken sets the NextToken field's value. +func (s *GetProductsInput) SetNextToken(v string) *GetProductsInput { + s.NextToken = &v + return s +} + +// SetServiceCode sets the ServiceCode field's value. +func (s *GetProductsInput) SetServiceCode(v string) *GetProductsInput { + s.ServiceCode = &v + return s +} + +type GetProductsOutput struct { + _ struct{} `type:"structure"` + + // The format version of the response. For example, aws_v1. + FormatVersion *string `type:"string"` + + // The pagination token that indicates the next set of results to retrieve. + NextToken *string `type:"string"` + + // The list of products that match your filters. The list contains both the + // product metadata and the price information. + PriceList []aws.JSONValue `type:"list"` +} + +// String returns the string representation +func (s GetProductsOutput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s GetProductsOutput) GoString() string { + return s.String() +} + +// SetFormatVersion sets the FormatVersion field's value. +func (s *GetProductsOutput) SetFormatVersion(v string) *GetProductsOutput { + s.FormatVersion = &v + return s +} + +// SetNextToken sets the NextToken field's value. +func (s *GetProductsOutput) SetNextToken(v string) *GetProductsOutput { + s.NextToken = &v + return s +} + +// SetPriceList sets the PriceList field's value. +func (s *GetProductsOutput) SetPriceList(v []aws.JSONValue) *GetProductsOutput { + s.PriceList = v + return s +} + +// The metadata for a service, such as the service code and available attribute +// names. +type Service struct { + _ struct{} `type:"structure"` + + // The attributes that are available for this service. + AttributeNames []*string `type:"list"` + + // The code for the AWS service. + ServiceCode *string `type:"string"` +} + +// String returns the string representation +func (s Service) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s Service) GoString() string { + return s.String() +} + +// SetAttributeNames sets the AttributeNames field's value. +func (s *Service) SetAttributeNames(v []*string) *Service { + s.AttributeNames = v + return s +} + +// SetServiceCode sets the ServiceCode field's value. +func (s *Service) SetServiceCode(v string) *Service { + s.ServiceCode = &v + return s +} + +const ( + // FilterTypeTermMatch is a FilterType enum value + FilterTypeTermMatch = "TERM_MATCH" +) diff --git a/vendor/github.com/aws/aws-sdk-go/service/pricing/doc.go b/vendor/github.com/aws/aws-sdk-go/service/pricing/doc.go new file mode 100644 index 00000000000..0555bc4c3cb --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/service/pricing/doc.go @@ -0,0 +1,51 @@ +// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT. + +// Package pricing provides the client and types for making API +// requests to AWS Price List Service. +// +// AWS Price List Service API (AWS Price List Service) is a centralized and +// convenient way to programmatically query Amazon Web Services for services, +// products, and pricing information. The AWS Price List Service uses standardized +// product attributes such as Location, Storage Class, and Operating System, +// and provides prices at the SKU level. You can use the AWS Price List Service +// to build cost control and scenario planning tools, reconcile billing data, +// forecast future spend for budgeting purposes, and provide cost benefit analysis +// that compare your internal workloads with AWS. +// +// Use GetServices without a service code to retrieve the service codes for +// all AWS services, then GetServices with a service code to retreive the attribute +// names for that service. After you have the service code and attribute names, +// you can use GetAttributeValues to see what values are available for an attribute. +// With the service code and an attribute name and value, you can use GetProducts +// to find specific products that you're interested in, such as an AmazonEC2 +// instance, with a Provisioned IOPSvolumeType. +// +// Service Endpoint +// +// AWS Price List Service API provides the following two endpoints: +// +// * https://api.pricing.us-east-1.amazonaws.com +// +// * https://api.pricing.ap-south-1.amazonaws.com +// +// See https://docs.aws.amazon.com/goto/WebAPI/pricing-2017-10-15 for more information on this service. +// +// See pricing package documentation for more information. +// https://docs.aws.amazon.com/sdk-for-go/api/service/pricing/ +// +// Using the Client +// +// To contact AWS Price List Service with the SDK use the New function to create +// a new service client. With that client you can make API requests to the service. +// These clients are safe to use concurrently. +// +// See the SDK's documentation for more information on how to use the SDK. +// https://docs.aws.amazon.com/sdk-for-go/api/ +// +// See aws.Config documentation for more information on configuring SDK clients. +// https://docs.aws.amazon.com/sdk-for-go/api/aws/#Config +// +// See the AWS Price List Service client Pricing for more +// information on creating client for this service. +// https://docs.aws.amazon.com/sdk-for-go/api/service/pricing/#New +package pricing diff --git a/vendor/github.com/aws/aws-sdk-go/service/pricing/errors.go b/vendor/github.com/aws/aws-sdk-go/service/pricing/errors.go new file mode 100644 index 00000000000..10e4c44fe92 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/service/pricing/errors.go @@ -0,0 +1,37 @@ +// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT. + +package pricing + +const ( + + // ErrCodeExpiredNextTokenException for service response error code + // "ExpiredNextTokenException". + // + // The pagination token expired. Try again without a pagination token. + ErrCodeExpiredNextTokenException = "ExpiredNextTokenException" + + // ErrCodeInternalErrorException for service response error code + // "InternalErrorException". + // + // An error on the server occurred during the processing of your request. Try + // again later. + ErrCodeInternalErrorException = "InternalErrorException" + + // ErrCodeInvalidNextTokenException for service response error code + // "InvalidNextTokenException". + // + // The pagination token is invalid. Try again without a pagination token. + ErrCodeInvalidNextTokenException = "InvalidNextTokenException" + + // ErrCodeInvalidParameterException for service response error code + // "InvalidParameterException". + // + // One or more parameters had an invalid value. + ErrCodeInvalidParameterException = "InvalidParameterException" + + // ErrCodeNotFoundException for service response error code + // "NotFoundException". + // + // The requested resource can't be found. + ErrCodeNotFoundException = "NotFoundException" +) diff --git a/vendor/github.com/aws/aws-sdk-go/service/pricing/service.go b/vendor/github.com/aws/aws-sdk-go/service/pricing/service.go new file mode 100644 index 00000000000..90ff33d0a08 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/service/pricing/service.go @@ -0,0 +1,100 @@ +// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT. + +package pricing + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/client" + "github.com/aws/aws-sdk-go/aws/client/metadata" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/aws/signer/v4" + "github.com/aws/aws-sdk-go/private/protocol/jsonrpc" +) + +// Pricing provides the API operation methods for making requests to +// AWS Price List Service. See this package's package overview docs +// for details on the service. +// +// Pricing methods are safe to use concurrently. It is not safe to +// modify mutate any of the struct's properties though. +type Pricing struct { + *client.Client +} + +// Used for custom client initialization logic +var initClient func(*client.Client) + +// Used for custom request initialization logic +var initRequest func(*request.Request) + +// Service information constants +const ( + ServiceName = "api.pricing" // Name of service. + EndpointsID = ServiceName // ID to lookup a service endpoint with. + ServiceID = "Pricing" // ServiceID is a unique identifer of a specific service. +) + +// New creates a new instance of the Pricing client with a session. +// If additional configuration is needed for the client instance use the optional +// aws.Config parameter to add your extra config. +// +// Example: +// // Create a Pricing client from just a session. +// svc := pricing.New(mySession) +// +// // Create a Pricing client with additional configuration +// svc := pricing.New(mySession, aws.NewConfig().WithRegion("us-west-2")) +func New(p client.ConfigProvider, cfgs ...*aws.Config) *Pricing { + c := p.ClientConfig(EndpointsID, cfgs...) + if c.SigningNameDerived || len(c.SigningName) == 0 { + c.SigningName = "pricing" + } + return newClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion, c.SigningName) +} + +// newClient creates, initializes and returns a new service client instance. +func newClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion, signingName string) *Pricing { + svc := &Pricing{ + Client: client.New( + cfg, + metadata.ClientInfo{ + ServiceName: ServiceName, + ServiceID: ServiceID, + SigningName: signingName, + SigningRegion: signingRegion, + Endpoint: endpoint, + APIVersion: "2017-10-15", + JSONVersion: "1.1", + TargetPrefix: "AWSPriceListService", + }, + handlers, + ), + } + + // Handlers + svc.Handlers.Sign.PushBackNamed(v4.SignRequestHandler) + svc.Handlers.Build.PushBackNamed(jsonrpc.BuildHandler) + svc.Handlers.Unmarshal.PushBackNamed(jsonrpc.UnmarshalHandler) + svc.Handlers.UnmarshalMeta.PushBackNamed(jsonrpc.UnmarshalMetaHandler) + svc.Handlers.UnmarshalError.PushBackNamed(jsonrpc.UnmarshalErrorHandler) + + // Run custom client initialization if present + if initClient != nil { + initClient(svc.Client) + } + + return svc +} + +// newRequest creates a new request for a Pricing operation and runs any +// custom request initialization. +func (c *Pricing) newRequest(op *request.Operation, params, data interface{}) *request.Request { + req := c.NewRequest(op, params, data) + + // Run custom request initialization if present + if initRequest != nil { + initRequest(req) + } + + return req +} diff --git a/vendor/vendor.json b/vendor/vendor.json index 1af7c3f7a92..5f332977ed0 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -854,6 +854,14 @@ "version": "v1.14.14", "versionExact": "v1.14.14" }, + { + "checksumSHA1": "j1i1tZ94/kDvvzgpv5xqxwNvgyY=", + "path": "github.com/aws/aws-sdk-go/service/pricing", + "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", + "revisionTime": "2018-06-26T20:42:33Z", + "version": "v1.14.14", + "versionExact": "v1.14.14" + }, { "checksumSHA1": "YtPLtWvoM6wD68VUp7dU+qk6hyM=", "path": "github.com/aws/aws-sdk-go/service/rds", @@ -2083,4 +2091,4 @@ } ], "rootPath": "github.com/terraform-providers/terraform-provider-aws" -} +} \ No newline at end of file From 93e11382b83c1ceb6e1f49f56d6b7e2ca8a534cd Mon Sep 17 00:00:00 2001 From: Daniel White Date: Mon, 25 Jun 2018 18:41:43 +1000 Subject: [PATCH 27/88] New Resource: aws_s3_bucket_inventory --- aws/provider.go | 1 + aws/resource_aws_s3_bucket_inventory.go | 451 ++++++++++++++++++ aws/resource_aws_s3_bucket_inventory_test.go | 296 ++++++++++++ website/aws.erb | 4 + .../docs/r/s3_bucket_inventory.html.markdown | 127 +++++ 5 files changed, 879 insertions(+) create mode 100644 aws/resource_aws_s3_bucket_inventory.go create mode 100644 aws/resource_aws_s3_bucket_inventory_test.go create mode 100644 website/docs/r/s3_bucket_inventory.html.markdown diff --git a/aws/provider.go b/aws/provider.go index a5020dfe052..6a513c7249f 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -559,6 +559,7 @@ func Provider() terraform.ResourceProvider { "aws_s3_bucket_object": resourceAwsS3BucketObject(), "aws_s3_bucket_notification": resourceAwsS3BucketNotification(), "aws_s3_bucket_metric": resourceAwsS3BucketMetric(), + "aws_s3_bucket_inventory": resourceAwsS3BucketInventory(), "aws_security_group": resourceAwsSecurityGroup(), "aws_network_interface_sg_attachment": resourceAwsNetworkInterfaceSGAttachment(), "aws_default_security_group": resourceAwsDefaultSecurityGroup(), diff --git a/aws/resource_aws_s3_bucket_inventory.go b/aws/resource_aws_s3_bucket_inventory.go new file mode 100644 index 00000000000..eb4e8c62b77 --- /dev/null +++ b/aws/resource_aws_s3_bucket_inventory.go @@ -0,0 +1,451 @@ +package aws + +import ( + "fmt" + "log" + "strings" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/s3" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" +) + +func resourceAwsS3BucketInventory() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsS3BucketInventoryPut, + Read: resourceAwsS3BucketInventoryRead, + Update: resourceAwsS3BucketInventoryPut, + Delete: resourceAwsS3BucketInventoryDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "bucket": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateMaxLength(64), + }, + "enabled": { + Type: schema.TypeBool, + Default: true, + Optional: true, + }, + "filter": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "prefix": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + "destination": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + MinItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "bucket": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + MinItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "format": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + s3.InventoryFormatCsv, + s3.InventoryFormatOrc, + }, false), + }, + "bucket_arn": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateArn, + }, + "account_id": { + Type: schema.TypeString, + Optional: true, + }, + "prefix": { + Type: schema.TypeString, + Optional: true, + }, + "encryption": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "sse_kms": &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + ConflictsWith: []string{"destination.0.bucket.0.encryption.0.sse_s3"}, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "key_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateArn, + }, + }, + }, + }, + "sse_s3": &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + ConflictsWith: []string{"destination.0.bucket.0.encryption.0.sse_kms"}, + Elem: &schema.Resource{ + // No options currently; just existence of "sse_s3". + Schema: map[string]*schema.Schema{}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + "schedule": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + MinItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "frequency": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + s3.InventoryFrequencyDaily, + s3.InventoryFrequencyWeekly, + }, false), + }, + }, + }, + }, + // TODO: Is there a sensible default for this? + "included_object_versions": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + s3.InventoryIncludedObjectVersionsCurrent, + s3.InventoryIncludedObjectVersionsAll, + }, false), + }, + "optional_fields": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{ + s3.InventoryOptionalFieldSize, + s3.InventoryOptionalFieldLastModifiedDate, + s3.InventoryOptionalFieldStorageClass, + s3.InventoryOptionalFieldEtag, + s3.InventoryOptionalFieldIsMultipartUploaded, + s3.InventoryOptionalFieldReplicationStatus, + s3.InventoryOptionalFieldEncryptionStatus, + }, false), + }, + Set: schema.HashString, + }, + }, + } +} + +func resourceAwsS3BucketInventoryPut(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).s3conn + bucket := d.Get("bucket").(string) + name := d.Get("name").(string) + + inventoryConfiguration := &s3.InventoryConfiguration{ + Id: aws.String(name), + IsEnabled: aws.Bool(d.Get("enabled").(bool)), + } + + if v, ok := d.GetOk("included_object_versions"); ok { + inventoryConfiguration.IncludedObjectVersions = aws.String(v.(string)) + } + + if v, ok := d.GetOk("optional_fields"); ok { + inventoryConfiguration.OptionalFields = expandStringList(v.(*schema.Set).List()) + } + + if v, ok := d.GetOk("schedule"); ok { + scheduleList := v.([]interface{}) + scheduleMap := scheduleList[0].(map[string]interface{}) + inventoryConfiguration.Schedule = &s3.InventorySchedule{ + Frequency: aws.String(scheduleMap["frequency"].(string)), + } + } + + if v, ok := d.GetOk("filter"); ok { + filterList := v.([]interface{}) + filterMap := filterList[0].(map[string]interface{}) + inventoryConfiguration.Filter = expandS3InventoryFilter(filterMap) + } + + if v, ok := d.GetOk("destination"); ok { + destinationList := v.([]interface{}) + destinationMap := destinationList[0].(map[string]interface{}) + bucketList := destinationMap["bucket"].([]interface{}) + bucketMap := bucketList[0].(map[string]interface{}) + + inventoryConfiguration.Destination = &s3.InventoryDestination{ + S3BucketDestination: expandS3InventoryS3BucketDestination(bucketMap), + } + } + + input := &s3.PutBucketInventoryConfigurationInput{ + Bucket: aws.String(bucket), + Id: aws.String(name), + InventoryConfiguration: inventoryConfiguration, + } + + log.Printf("[DEBUG] Putting S3 bucket inventory configuration: %s", input) + err := resource.Retry(1*time.Minute, func() *resource.RetryError { + _, err := conn.PutBucketInventoryConfiguration(input) + if err != nil { + if isAWSErr(err, s3.ErrCodeNoSuchBucket, "") { + return resource.RetryableError(err) + } + return resource.NonRetryableError(err) + } + return nil + }) + if err != nil { + return fmt.Errorf("Error putting S3 bucket inventory configuration: %s", err) + } + + d.SetId(fmt.Sprintf("%s:%s", bucket, name)) + + return resourceAwsS3BucketInventoryRead(d, meta) +} + +func resourceAwsS3BucketInventoryDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).s3conn + + bucket, name, err := resourceAwsS3BucketInventoryParseID(d.Id()) + if err != nil { + return err + } + + input := &s3.DeleteBucketInventoryConfigurationInput{ + Bucket: aws.String(bucket), + Id: aws.String(name), + } + + log.Printf("[DEBUG] Deleting S3 bucket inventory configuration: %s", input) + _, err = conn.DeleteBucketInventoryConfiguration(input) + if err != nil { + if isAWSErr(err, s3.ErrCodeNoSuchBucket, "") || isAWSErr(err, "NoSuchConfiguration", "The specified configuration does not exist.") { + return nil + } + return fmt.Errorf("Error deleting S3 bucket inventory configuration: %s", err) + } + + return nil +} + +func resourceAwsS3BucketInventoryRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).s3conn + + bucket, name, err := resourceAwsS3BucketInventoryParseID(d.Id()) + if err != nil { + return err + } + + d.Set("bucket", bucket) + d.Set("name", name) + + input := &s3.GetBucketInventoryConfigurationInput{ + Bucket: aws.String(bucket), + Id: aws.String(name), + } + + log.Printf("[DEBUG] Reading S3 bucket inventory configuration: %s", input) + output, err := conn.GetBucketInventoryConfiguration(input) + if err != nil { + if isAWSErr(err, s3.ErrCodeNoSuchBucket, "") || isAWSErr(err, "NoSuchConfiguration", "The specified configuration does not exist.") { + log.Printf("[WARN] %s S3 bucket inventory configuration not found, removing from state.", d.Id()) + d.SetId("") + return nil + } + return err + } + + d.Set("enabled", aws.BoolValue(output.InventoryConfiguration.IsEnabled)) + d.Set("included_object_versions", aws.StringValue(output.InventoryConfiguration.IncludedObjectVersions)) + + if err := d.Set("optional_fields", flattenStringList(output.InventoryConfiguration.OptionalFields)); err != nil { + return fmt.Errorf("error setting optional_fields: %s", err) + } + + if err := d.Set("filter", flattenS3InventoryFilter(output.InventoryConfiguration.Filter)); err != nil { + return fmt.Errorf("error setting filter: %s", err) + } + + if err := d.Set("schedule", flattenS3InventorySchedule(output.InventoryConfiguration.Schedule)); err != nil { + return fmt.Errorf("error setting schedule: %s", err) + } + + if output.InventoryConfiguration.Destination != nil { + destination := map[string]interface{}{ + "bucket": flattenS3InventoryS3BucketDestination(output.InventoryConfiguration.Destination.S3BucketDestination), + } + + if err := d.Set("destination", []map[string]interface{}{destination}); err != nil { + return fmt.Errorf("error setting destination: %s", err) + } + } + + return nil +} + +func expandS3InventoryFilter(m map[string]interface{}) *s3.InventoryFilter { + v, ok := m["prefix"] + if !ok { + return nil + } + return &s3.InventoryFilter{ + Prefix: aws.String(v.(string)), + } +} + +func flattenS3InventoryFilter(filter *s3.InventoryFilter) []map[string]interface{} { + if filter == nil { + return nil + } + + result := make([]map[string]interface{}, 0, 1) + + m := make(map[string]interface{}, 0) + if filter.Prefix != nil { + m["prefix"] = aws.StringValue(filter.Prefix) + } + + result = append(result, m) + + return result +} + +func flattenS3InventorySchedule(schedule *s3.InventorySchedule) []map[string]interface{} { + result := make([]map[string]interface{}, 0, 1) + + m := make(map[string]interface{}, 1) + m["frequency"] = aws.StringValue(schedule.Frequency) + + result = append(result, m) + + return result +} + +func expandS3InventoryS3BucketDestination(m map[string]interface{}) *s3.InventoryS3BucketDestination { + destination := &s3.InventoryS3BucketDestination{ + Format: aws.String(m["format"].(string)), + Bucket: aws.String(m["bucket_arn"].(string)), + } + + if v, ok := m["account_id"]; ok && v.(string) != "" { + destination.AccountId = aws.String(v.(string)) + } + + if v, ok := m["prefix"]; ok && v.(string) != "" { + destination.Prefix = aws.String(v.(string)) + } + + if v, ok := m["encryption"].([]interface{}); ok && len(v) > 0 { + encryptionMap := v[0].(map[string]interface{}) + + encryption := &s3.InventoryEncryption{} + + for k, v := range encryptionMap { + data := v.([]interface{}) + + if len(data) == 0 { + continue + } + + switch k { + case "sse_kms": + m := data[0].(map[string]interface{}) + encryption.SSEKMS = &s3.SSEKMS{ + KeyId: aws.String(m["key_id"].(string)), + } + case "sse_s3": + encryption.SSES3 = &s3.SSES3{} + } + } + + destination.Encryption = encryption + } + + return destination +} + +func flattenS3InventoryS3BucketDestination(destination *s3.InventoryS3BucketDestination) []map[string]interface{} { + result := make([]map[string]interface{}, 0, 1) + + m := map[string]interface{}{ + "format": aws.StringValue(destination.Format), + "bucket_arn": aws.StringValue(destination.Bucket), + } + + if destination.AccountId != nil { + m["account_id"] = aws.StringValue(destination.AccountId) + } + if destination.Prefix != nil { + m["prefix"] = aws.StringValue(destination.Prefix) + } + + if destination.Encryption != nil { + encryption := make(map[string]interface{}, 1) + if destination.Encryption.SSES3 != nil { + encryption["sse_s3"] = []map[string]interface{}{{}} + } else if destination.Encryption.SSEKMS != nil { + encryption["sse_kms"] = []map[string]interface{}{ + { + "key_id": aws.StringValue(destination.Encryption.SSEKMS.KeyId), + }, + } + } + m["encryption"] = []map[string]interface{}{encryption} + } + + result = append(result, m) + + return result +} + +func resourceAwsS3BucketInventoryParseID(id string) (string, string, error) { + idParts := strings.Split(id, ":") + if len(idParts) != 2 { + return "", "", fmt.Errorf("please make sure the ID is in the form BUCKET:NAME (i.e. my-bucket:EntireBucket") + } + bucket := idParts[0] + name := idParts[1] + return bucket, name, nil +} diff --git a/aws/resource_aws_s3_bucket_inventory_test.go b/aws/resource_aws_s3_bucket_inventory_test.go new file mode 100644 index 00000000000..50561fba280 --- /dev/null +++ b/aws/resource_aws_s3_bucket_inventory_test.go @@ -0,0 +1,296 @@ +package aws + +import ( + "fmt" + "log" + "regexp" + "testing" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/s3" + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSS3BucketInventory_basic(t *testing.T) { + var conf s3.InventoryConfiguration + rString := acctest.RandString(8) + resourceName := "aws_s3_bucket_inventory.test" + + bucketName := fmt.Sprintf("tf-acc-bucket-inventory-%s", rString) + inventoryName := t.Name() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSS3BucketInventoryDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSS3BucketInventoryConfig(bucketName, inventoryName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSS3BucketInventoryConfigExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "bucket", bucketName), + resource.TestCheckNoResourceAttr(resourceName, "filter"), + resource.TestCheckResourceAttr(resourceName, "name", inventoryName), + resource.TestCheckResourceAttr(resourceName, "enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "included_object_versions", "All"), + + resource.TestCheckResourceAttr(resourceName, "optional_fields.#", "2"), + + resource.TestCheckResourceAttr(resourceName, "schedule.#", "1"), + resource.TestCheckResourceAttr(resourceName, "schedule.0.frequency", "Weekly"), + + resource.TestCheckResourceAttr(resourceName, "destination.#", "1"), + resource.TestCheckResourceAttr(resourceName, "destination.0.bucket.#", "1"), + resource.TestCheckResourceAttr(resourceName, "destination.0.bucket.0.bucket_arn", "arn:aws:s3:::"+bucketName), + resource.TestCheckResourceAttrSet(resourceName, "destination.0.bucket.0.account_id"), + resource.TestCheckResourceAttr(resourceName, "destination.0.bucket.0.format", "ORC"), + resource.TestCheckResourceAttr(resourceName, "destination.0.bucket.0.prefix", "inventory"), + ), + }, + resource.TestStep{ + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSS3BucketInventory_encryptWithSSES3(t *testing.T) { + var conf s3.InventoryConfiguration + rString := acctest.RandString(8) + resourceName := "aws_s3_bucket_inventory.test" + + bucketName := fmt.Sprintf("tf-acc-bucket-inventory-%s", rString) + inventoryName := t.Name() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSS3BucketInventoryDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSS3BucketInventoryConfigEncryptWithSSES3(bucketName, inventoryName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSS3BucketInventoryConfigExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "destination.0.bucket.0.encryption.0.sse_s3.#", "1"), + ), + }, + resource.TestStep{ + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSS3BucketInventory_encryptWithSSEKMS(t *testing.T) { + var conf s3.InventoryConfiguration + rString := acctest.RandString(8) + resourceName := "aws_s3_bucket_inventory.test" + + bucketName := fmt.Sprintf("tf-acc-bucket-inventory-%s", rString) + inventoryName := t.Name() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSS3BucketInventoryDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSS3BucketInventoryConfigEncryptWithSSEKMS(bucketName, inventoryName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSS3BucketInventoryConfigExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "destination.0.bucket.0.encryption.0.sse_kms.#", "1"), + resource.TestMatchResourceAttr(resourceName, "destination.0.bucket.0.encryption.0.sse_kms.0.key_id", regexp.MustCompile("^arn:aws:kms:")), + ), + }, + resource.TestStep{ + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckAWSS3BucketInventoryConfigExists(n string, res *s3.InventoryConfiguration) 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 S3 bucket inventory configuration ID is set") + } + + conn := testAccProvider.Meta().(*AWSClient).s3conn + bucket, name, err := resourceAwsS3BucketInventoryParseID(rs.Primary.ID) + if err != nil { + return err + } + + input := &s3.GetBucketInventoryConfigurationInput{ + Bucket: aws.String(bucket), + Id: aws.String(name), + } + log.Printf("[DEBUG] Reading S3 bucket inventory configuration: %s", input) + output, err := conn.GetBucketInventoryConfiguration(input) + if err != nil { + return err + } + + *res = *output.InventoryConfiguration + + return nil + } +} + +func testAccCheckAWSS3BucketInventoryDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).s3conn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_s3_bucket_inventory" { + continue + } + + bucket, name, err := resourceAwsS3BucketInventoryParseID(rs.Primary.ID) + if err != nil { + return err + } + + err = resource.Retry(1*time.Minute, func() *resource.RetryError { + input := &s3.GetBucketInventoryConfigurationInput{ + Bucket: aws.String(bucket), + Id: aws.String(name), + } + log.Printf("[DEBUG] Reading S3 bucket inventory configuration: %s", input) + output, err := conn.GetBucketInventoryConfiguration(input) + if err != nil { + if isAWSErr(err, s3.ErrCodeNoSuchBucket, "") || isAWSErr(err, "NoSuchConfiguration", "The specified configuration does not exist.") { + return nil + } + return resource.NonRetryableError(err) + } + if output.InventoryConfiguration != nil { + return resource.RetryableError(fmt.Errorf("S3 bucket inventory configuration exists: %v", output)) + } + return nil + }) + if err != nil { + return err + } + } + return nil +} + +func testAccAWSS3BucketInventoryConfigBucket(name string) string { + return fmt.Sprintf(` +resource "aws_s3_bucket" "bucket" { + bucket = "%s" + acl = "private" +} +`, name) +} + +func testAccAWSS3BucketInventoryConfig(bucketName, inventoryName string) string { + return fmt.Sprintf(` +%s +data "aws_caller_identity" "current" {} + +resource "aws_s3_bucket_inventory" "test" { + bucket = "${aws_s3_bucket.bucket.id}" + name = "%s" + + included_object_versions = "All" + + optional_fields = [ + "Size", + "LastModifiedDate", + ] + + filter { + prefix = "documents/" + } + + schedule { + frequency = "Weekly" + } + + destination { + bucket { + format = "ORC" + bucket_arn = "${aws_s3_bucket.bucket.arn}" + account_id = "${data.aws_caller_identity.current.account_id}" + prefix = "inventory" + } + } +} +`, testAccAWSS3BucketMetricsConfigBucket(bucketName), inventoryName) +} + +func testAccAWSS3BucketInventoryConfigEncryptWithSSES3(bucketName, inventoryName string) string { + return fmt.Sprintf(` +%s +resource "aws_s3_bucket_inventory" "test" { + bucket = "${aws_s3_bucket.bucket.id}" + name = "%s" + + included_object_versions = "Current" + + schedule { + frequency = "Daily" + } + + destination { + bucket { + format = "CSV" + bucket_arn = "${aws_s3_bucket.bucket.arn}" + + encryption { + sse_s3 {} + } + } + } +} +`, testAccAWSS3BucketMetricsConfigBucket(bucketName), inventoryName) +} + +func testAccAWSS3BucketInventoryConfigEncryptWithSSEKMS(bucketName, inventoryName string) string { + return fmt.Sprintf(` +%s +resource "aws_kms_key" "inventory" { + description = "Terraform acc test S3 inventory SSE-KMS encryption: %s" + deletion_window_in_days = 7 +} + +resource "aws_s3_bucket_inventory" "test" { + bucket = "${aws_s3_bucket.bucket.id}" + name = "%s" + + included_object_versions = "Current" + + schedule { + frequency = "Daily" + } + + destination { + bucket { + format = "ORC" + bucket_arn = "${aws_s3_bucket.bucket.arn}" + + encryption { + sse_kms { + key_id = "${aws_kms_key.inventory.arn}" + } + } + } + } +} +`, testAccAWSS3BucketMetricsConfigBucket(bucketName), bucketName, inventoryName) +} diff --git a/website/aws.erb b/website/aws.erb index 0070bbdc6e5..3a8625d111e 100644 --- a/website/aws.erb +++ b/website/aws.erb @@ -1861,6 +1861,10 @@ aws_s3_bucket + > + aws_s3_bucket_inventory + + > aws_s3_bucket_metric diff --git a/website/docs/r/s3_bucket_inventory.html.markdown b/website/docs/r/s3_bucket_inventory.html.markdown new file mode 100644 index 00000000000..2d85b2805b0 --- /dev/null +++ b/website/docs/r/s3_bucket_inventory.html.markdown @@ -0,0 +1,127 @@ +--- +layout: "aws" +page_title: "AWS: aws_s3_bucket_inventory" +sidebar_current: "docs-aws-resource-s3-bucket-inventory" +description: |- + Provides a S3 bucket inventory configuration resource. +--- + +# aws_s3_bucket_inventory + +Provides a S3 bucket [inventory configuration](https://docs.aws.amazon.com/AmazonS3/latest/dev/storage-inventory.html) resource. + +## Example Usage + +### Add inventory configuration + +```hcl +resource "aws_s3_bucket" "test" { + bucket = "my-tf-test-bucket" +} + +resource "aws_s3_bucket" "inventory" { + bucket = "my-tf-inventory-bucket" +} + +resource "aws_s3_bucket_inventory" "test" { + bucket = "${aws_s3_bucket.test.id}" + name = "EntireBucketDaily" + + included_object_versions = "All" + + schedule { + frequency = "Daily" + } + + destination { + bucket { + format = "ORC" + bucket_arn = "${aws_s3_bucket.inventory.arn}" + } +} +``` + +### Add inventory configuration with S3 bucket object prefix + +```hcl +resource "aws_s3_bucket" "test" { + bucket = "my-tf-test-bucket" +} + +resource "aws_s3_bucket" "inventory" { + bucket = "my-tf-inventory-bucket" +} + +resource "aws_s3_bucket_inventory" "test-prefix" { + bucket = "${aws_s3_bucket.test.id}" + name = "DocumentsWeekly" + + included_object_versions = "Weekly" + + schedule { + frequency = "Daily" + } + + filter { + prefix = "documents/" + } + + destination { + bucket { + format = "ORC" + bucket = "${aws_s3_bucket.inventory.arn}" + prefix = "inventory" + } + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `bucket` - (Required) The name of the bucket to put inventory configuration. +* `name` - (Required) Unique identifier of the inventory configuration for the bucket. +* `included_object_versions` - (Required) Object filtering that accepts a prefix (documented below). Can be `All` or `Current`. +* `schedule` - (Required) Contains the frequency for generating inventory results (documented below). +* `destination` - (Required) Destination bucket where inventory list files are written (documented below). +* `enabled` - (Optional, Default: true) Specifies whether the inventory is enabled or disabled. +* `filter` - (Optional) Object filtering that accepts a prefix (documented below). +* `optional_fields` - (Optional) Contains the optional fields that are included in the inventory results. + +The `filter` configuration supports the following: + +* `prefix` - (Optional) Object prefix for filtering (singular). + +The `schedule` configuration supports the following: + +* `frequency` - (Required) Specifies how frequently inventory results are produced. Can be `Daily` or `Weekly`. + +The `destination` configuration supports the following: + +* `bucket` - (Required) The S3 bucket configuration where inventory results are published (documented below). + +The `bucket` configuration supports the following: + +* `bucket_arn` - (Required) The Amazon S3 bucket ARN of the destination. +* `format` - (Required) Specifies the output format of the inventory results. Can be `CSV` or [`ORC`](https://orc.apache.org/). +* `account_id` - (Optional) The ID of the account that owns the destination bucket. Recommended to be set to prevent problems if the destination bucket ownership changes. +* `prefix` - (Optional) The prefix that is prepended to all inventory results. +* `encryption` - (Optional) Contains the type of server-side encryption to use to encrypt the inventory (documented below). + +The `encryption` configuration supports the following: + +* `sse_kms` - (Optional) Specifies to use server-side encryption with AWS KMS-managed keys to encrypt the inventory file (documented below). +* `sse_s3` - (Optional) Specifies to use server-side encryption with Amazon S3-managed keys (SSE-S3) to encrypt the inventory file. + +The `sse_kms` configuration supports the following: + +* `kms_id` - (Required) The ARN of the KMS customer master key (CMK) used to encrypt the inventory file. + +## Import + +S3 bucket inventory configurations can be imported using `bucket:inventory`, e.g. + +``` +$ terraform import aws_s3_bucket_inventory.my-bucket-entire-bucket my-bucket:EntireBucket +``` From 42bbf33109cf97a43d55bdc180209130acc40ba8 Mon Sep 17 00:00:00 2001 From: Daniel White Date: Fri, 29 Jun 2018 15:05:59 +1000 Subject: [PATCH 28/88] aws/resource_aws_s3_bucket_inventory: Retry reads on creation This is to ensure that a recently created resource doesn't immediately fail due to eventual consistency. --- aws/resource_aws_s3_bucket_inventory.go | 26 ++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/aws/resource_aws_s3_bucket_inventory.go b/aws/resource_aws_s3_bucket_inventory.go index eb4e8c62b77..4c3d7c597d4 100644 --- a/aws/resource_aws_s3_bucket_inventory.go +++ b/aws/resource_aws_s3_bucket_inventory.go @@ -287,14 +287,26 @@ func resourceAwsS3BucketInventoryRead(d *schema.ResourceData, meta interface{}) } log.Printf("[DEBUG] Reading S3 bucket inventory configuration: %s", input) - output, err := conn.GetBucketInventoryConfiguration(input) - if err != nil { - if isAWSErr(err, s3.ErrCodeNoSuchBucket, "") || isAWSErr(err, "NoSuchConfiguration", "The specified configuration does not exist.") { - log.Printf("[WARN] %s S3 bucket inventory configuration not found, removing from state.", d.Id()) - d.SetId("") - return nil + var output *s3.GetBucketInventoryConfigurationOutput + err = resource.Retry(1*time.Minute, func() *resource.RetryError { + var err error + output, err = conn.GetBucketInventoryConfiguration(input) + if err != nil { + if isAWSErr(err, s3.ErrCodeNoSuchBucket, "") || isAWSErr(err, "NoSuchConfiguration", "The specified configuration does not exist.") { + if d.IsNewResource() { + return resource.RetryableError(err) + } + return nil + } + return resource.NonRetryableError(err) } - return err + return nil + }) + + if output == nil || output.InventoryConfiguration == nil { + log.Printf("[WARN] %s S3 bucket inventory configuration not found, removing from state.", d.Id()) + d.SetId("") + return nil } d.Set("enabled", aws.BoolValue(output.InventoryConfiguration.IsEnabled)) From 2c4b05b1982e9dd3d3dd1cfe90ccb7f301dc7ee7 Mon Sep 17 00:00:00 2001 From: Daniel White Date: Fri, 29 Jun 2018 09:54:49 +1000 Subject: [PATCH 29/88] aws/resource_aws_s3_bucket_inventory: Prevent import if SSE-S3 encryption is enabled This prevents importing a resource that has the feature enabled so that future updates don't accidentally stomp on the data. The SDK currently fails to marshal this option correctly in a create/update request. The risk is minimal since the create/update request fails anyway, but this will avoid a user being surprised by a failed update _after_ they've imported it into their state. See: https://github.com/aws/aws-sdk-go/issues/2015 --- aws/resource_aws_s3_bucket_inventory.go | 9 +++++++++ aws/resource_aws_s3_bucket_inventory_test.go | 2 ++ website/docs/r/s3_bucket_inventory.html.markdown | 2 ++ 3 files changed, 13 insertions(+) diff --git a/aws/resource_aws_s3_bucket_inventory.go b/aws/resource_aws_s3_bucket_inventory.go index 4c3d7c597d4..87a90f72de0 100644 --- a/aws/resource_aws_s3_bucket_inventory.go +++ b/aws/resource_aws_s3_bucket_inventory.go @@ -1,6 +1,7 @@ package aws import ( + "errors" "fmt" "log" "strings" @@ -325,6 +326,14 @@ func resourceAwsS3BucketInventoryRead(d *schema.ResourceData, meta interface{}) } if output.InventoryConfiguration.Destination != nil { + // Flag the existence of SSE-S3 encryption because it cannot be marshaled when updating a resource. + // Allowing import would risk disabling encryption inadvertently when applying updates. + if output.InventoryConfiguration.Destination.S3BucketDestination.Encryption != nil { + if output.InventoryConfiguration.Destination.S3BucketDestination.Encryption.SSES3 != nil { + return errors.New("sse_s3 encryption is unsupported") + } + } + destination := map[string]interface{}{ "bucket": flattenS3InventoryS3BucketDestination(output.InventoryConfiguration.Destination.S3BucketDestination), } diff --git a/aws/resource_aws_s3_bucket_inventory_test.go b/aws/resource_aws_s3_bucket_inventory_test.go index 50561fba280..b69365ceb78 100644 --- a/aws/resource_aws_s3_bucket_inventory_test.go +++ b/aws/resource_aws_s3_bucket_inventory_test.go @@ -60,6 +60,8 @@ func TestAccAWSS3BucketInventory_basic(t *testing.T) { } func TestAccAWSS3BucketInventory_encryptWithSSES3(t *testing.T) { + t.Skip("SSE-S3 is not supported by the SDK.") + var conf s3.InventoryConfiguration rString := acctest.RandString(8) resourceName := "aws_s3_bucket_inventory.test" diff --git a/website/docs/r/s3_bucket_inventory.html.markdown b/website/docs/r/s3_bucket_inventory.html.markdown index 2d85b2805b0..d648fb13d86 100644 --- a/website/docs/r/s3_bucket_inventory.html.markdown +++ b/website/docs/r/s3_bucket_inventory.html.markdown @@ -111,6 +111,8 @@ The `bucket` configuration supports the following: The `encryption` configuration supports the following: +~> **NOTE:** `sse_s3` is currently unsupported. + * `sse_kms` - (Optional) Specifies to use server-side encryption with AWS KMS-managed keys to encrypt the inventory file (documented below). * `sse_s3` - (Optional) Specifies to use server-side encryption with Amazon S3-managed keys (SSE-S3) to encrypt the inventory file. From 410d387927c45078dd3cec910fb05fe2fc56ce09 Mon Sep 17 00:00:00 2001 From: saravanan30erd Date: Fri, 29 Jun 2018 15:41:29 +0400 Subject: [PATCH 30/88] issue #4266 add Type attribute in aws_spot_fleet_request --- aws/resource_aws_spot_fleet_request.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/aws/resource_aws_spot_fleet_request.go b/aws/resource_aws_spot_fleet_request.go index 21c06cef0d8..4fcf6bd8415 100644 --- a/aws/resource_aws_spot_fleet_request.go +++ b/aws/resource_aws_spot_fleet_request.go @@ -319,6 +319,12 @@ func resourceAwsSpotFleetRequest() *schema.Resource { ForceNew: true, ValidateFunc: validateRFC3339TimeString, }, + "fleet_type": { + Type: schema.TypeString, + Optional: true, + Default: "maintain", + ForceNew: true, + }, "spot_request_state": { Type: schema.TypeString, Computed: true, @@ -600,6 +606,7 @@ func resourceAwsSpotFleetRequestCreate(d *schema.ResourceData, meta interface{}) TerminateInstancesWithExpiration: aws.Bool(d.Get("terminate_instances_with_expiration").(bool)), ReplaceUnhealthyInstances: aws.Bool(d.Get("replace_unhealthy_instances").(bool)), InstanceInterruptionBehavior: aws.String(d.Get("instance_interruption_behaviour").(string)), + Type: aws.String(d.Get("fleet_type").(string)), } if v, ok := d.GetOk("excess_capacity_termination_policy"); ok { @@ -906,6 +913,7 @@ func resourceAwsSpotFleetRequestRead(d *schema.ResourceData, meta interface{}) e d.Set("replace_unhealthy_instances", config.ReplaceUnhealthyInstances) d.Set("instance_interruption_behaviour", config.InstanceInterruptionBehavior) + d.Set("fleet_type", config.Type) d.Set("launch_specification", launchSpecsToSet(config.LaunchSpecifications, conn)) return nil From e205afcffaf88697d8e8df663179f89f7d78d92c Mon Sep 17 00:00:00 2001 From: saravanan30erd Date: Fri, 29 Jun 2018 16:13:49 +0400 Subject: [PATCH 31/88] add acceptance test --- aws/resource_aws_spot_fleet_request_test.go | 95 +++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/aws/resource_aws_spot_fleet_request_test.go b/aws/resource_aws_spot_fleet_request_test.go index d0be16d8ae5..99cd327e34e 100644 --- a/aws/resource_aws_spot_fleet_request_test.go +++ b/aws/resource_aws_spot_fleet_request_test.go @@ -108,6 +108,31 @@ func TestAccAWSSpotFleetRequest_instanceInterruptionBehavior(t *testing.T) { }) } +func TestAccAWSSpotFleetRequest_fleetType(t *testing.T) { + var sfr ec2.SpotFleetRequestConfig + rName := acctest.RandString(10) + rInt := acctest.RandInt() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSpotFleetRequestDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSpotFleetRequestConfigFleetType(rName, rInt), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSSpotFleetRequestExists( + "aws_spot_fleet_request.foo", &sfr), + resource.TestCheckResourceAttr( + "aws_spot_fleet_request.foo", "spot_request_state", "active"), + resource.TestCheckResourceAttr( + "aws_spot_fleet_request.foo", "fleet_type", "request"), + ), + }, + }, + }) +} + func TestAccAWSSpotFleetRequest_iamInstanceProfileArn(t *testing.T) { var sfr ec2.SpotFleetRequestConfig rName := acctest.RandString(10) @@ -830,6 +855,76 @@ resource "aws_spot_fleet_request" "foo" { `, rName, rInt, rInt, rName) } +func testAccAWSSpotFleetRequestConfigFleetType(rName string, rInt int) string { + return fmt.Sprintf(` +resource "aws_iam_policy" "test-policy" { + name = "test-policy-%d" + path = "/" + description = "Spot Fleet Request ACCTest Policy" + policy = < Date: Fri, 29 Jun 2018 08:15:05 -0400 Subject: [PATCH 32/88] vendor: aws-sdk-go@v1.14.16 --- .../aws/csm/{metricChan.go => metric_chan.go} | 0 .../aws/aws-sdk-go/aws/csm/reporter.go | 4 +- .../github.com/aws/aws-sdk-go/aws/version.go | 2 +- .../aws/aws-sdk-go/service/cloudfront/api.go | 136 --- .../aws-sdk-go/service/cloudfront/errors.go | 4 - .../aws-sdk-go/service/codepipeline/api.go | 4 + .../service/elasticbeanstalk/api.go | 50 +- .../aws/aws-sdk-go/service/lambda/api.go | 63 +- .../aws/aws-sdk-go/service/lambda/errors.go | 8 + .../aws-sdk-go/service/secretsmanager/api.go | 91 +- vendor/vendor.json | 992 +++++++++--------- 11 files changed, 640 insertions(+), 714 deletions(-) rename vendor/github.com/aws/aws-sdk-go/aws/csm/{metricChan.go => metric_chan.go} (100%) diff --git a/vendor/github.com/aws/aws-sdk-go/aws/csm/metricChan.go b/vendor/github.com/aws/aws-sdk-go/aws/csm/metric_chan.go similarity index 100% rename from vendor/github.com/aws/aws-sdk-go/aws/csm/metricChan.go rename to vendor/github.com/aws/aws-sdk-go/aws/csm/metric_chan.go diff --git a/vendor/github.com/aws/aws-sdk-go/aws/csm/reporter.go b/vendor/github.com/aws/aws-sdk-go/aws/csm/reporter.go index 1484c8fc5b1..d2481c18d27 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/csm/reporter.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/csm/reporter.go @@ -223,8 +223,10 @@ func (rep *Reporter) InjectHandlers(handlers *request.Handlers) { } apiCallHandler := request.NamedHandler{Name: APICallMetricHandlerName, Fn: rep.sendAPICallMetric} + apiCallAttemptHandler := request.NamedHandler{Name: APICallAttemptMetricHandlerName, Fn: rep.sendAPICallAttemptMetric} + handlers.Complete.PushFrontNamed(apiCallHandler) + handlers.Complete.PushFrontNamed(apiCallAttemptHandler) - apiCallAttemptHandler := request.NamedHandler{Name: APICallAttemptMetricHandlerName, Fn: rep.sendAPICallAttemptMetric} handlers.AfterRetry.PushFrontNamed(apiCallAttemptHandler) } diff --git a/vendor/github.com/aws/aws-sdk-go/aws/version.go b/vendor/github.com/aws/aws-sdk-go/aws/version.go index 96fc1306898..75fbf2f2c3e 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/version.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/version.go @@ -5,4 +5,4 @@ package aws const SDKName = "aws-sdk-go" // SDKVersion is the version of this SDK -const SDKVersion = "1.14.14" +const SDKVersion = "1.14.16" diff --git a/vendor/github.com/aws/aws-sdk-go/service/cloudfront/api.go b/vendor/github.com/aws/aws-sdk-go/service/cloudfront/api.go index f3d4ee55e1d..3c98c555e9e 100644 --- a/vendor/github.com/aws/aws-sdk-go/service/cloudfront/api.go +++ b/vendor/github.com/aws/aws-sdk-go/service/cloudfront/api.go @@ -1638,92 +1638,6 @@ func (c *CloudFront) DeletePublicKeyWithContext(ctx aws.Context, input *DeletePu return out, req.Send() } -const opDeleteServiceLinkedRole = "DeleteServiceLinkedRole2017_10_30" - -// DeleteServiceLinkedRoleRequest generates a "aws/request.Request" representing the -// client's request for the DeleteServiceLinkedRole operation. The "output" return -// value will be populated with the request's response once the request completes -// successfuly. -// -// Use "Send" method on the returned Request to send the API call to the service. -// the "output" return value is not valid until after Send returns without error. -// -// See DeleteServiceLinkedRole for more information on using the DeleteServiceLinkedRole -// API call, and error handling. -// -// This method is useful when you want to inject custom logic or configuration -// into the SDK's request lifecycle. Such as custom headers, or retry logic. -// -// -// // Example sending a request using the DeleteServiceLinkedRoleRequest method. -// req, resp := client.DeleteServiceLinkedRoleRequest(params) -// -// err := req.Send() -// if err == nil { // resp is now filled -// fmt.Println(resp) -// } -// -// See also, https://docs.aws.amazon.com/goto/WebAPI/cloudfront-2017-10-30/DeleteServiceLinkedRole -func (c *CloudFront) DeleteServiceLinkedRoleRequest(input *DeleteServiceLinkedRoleInput) (req *request.Request, output *DeleteServiceLinkedRoleOutput) { - op := &request.Operation{ - Name: opDeleteServiceLinkedRole, - HTTPMethod: "DELETE", - HTTPPath: "/2017-10-30/service-linked-role/{RoleName}", - } - - if input == nil { - input = &DeleteServiceLinkedRoleInput{} - } - - output = &DeleteServiceLinkedRoleOutput{} - req = c.newRequest(op, input, output) - req.Handlers.Unmarshal.Remove(restxml.UnmarshalHandler) - req.Handlers.Unmarshal.PushBackNamed(protocol.UnmarshalDiscardBodyHandler) - return -} - -// DeleteServiceLinkedRole API operation for Amazon CloudFront. -// -// Returns awserr.Error for service API and SDK errors. Use runtime type assertions -// with awserr.Error's Code and Message methods to get detailed information about -// the error. -// -// See the AWS API reference guide for Amazon CloudFront's -// API operation DeleteServiceLinkedRole for usage and error information. -// -// Returned Error Codes: -// * ErrCodeInvalidArgument "InvalidArgument" -// The argument is invalid. -// -// * ErrCodeAccessDenied "AccessDenied" -// Access denied. -// -// * ErrCodeResourceInUse "ResourceInUse" -// -// * ErrCodeNoSuchResource "NoSuchResource" -// -// See also, https://docs.aws.amazon.com/goto/WebAPI/cloudfront-2017-10-30/DeleteServiceLinkedRole -func (c *CloudFront) DeleteServiceLinkedRole(input *DeleteServiceLinkedRoleInput) (*DeleteServiceLinkedRoleOutput, error) { - req, out := c.DeleteServiceLinkedRoleRequest(input) - return out, req.Send() -} - -// DeleteServiceLinkedRoleWithContext is the same as DeleteServiceLinkedRole with the addition of -// the ability to pass a context and additional request options. -// -// See DeleteServiceLinkedRole for details on how to use this API operation. -// -// The context must be non-nil and will be used for request cancellation. If -// the context is nil a panic will occur. In the future the SDK may create -// sub-contexts for http.Requests. See https://golang.org/pkg/context/ -// for more information on using Contexts. -func (c *CloudFront) DeleteServiceLinkedRoleWithContext(ctx aws.Context, input *DeleteServiceLinkedRoleInput, opts ...request.Option) (*DeleteServiceLinkedRoleOutput, error) { - req, out := c.DeleteServiceLinkedRoleRequest(input) - req.SetContext(ctx) - req.ApplyOptions(opts...) - return out, req.Send() -} - const opDeleteStreamingDistribution = "DeleteStreamingDistribution2017_10_30" // DeleteStreamingDistributionRequest generates a "aws/request.Request" representing the @@ -7551,56 +7465,6 @@ func (s DeletePublicKeyOutput) GoString() string { return s.String() } -type DeleteServiceLinkedRoleInput struct { - _ struct{} `type:"structure"` - - // RoleName is a required field - RoleName *string `location:"uri" locationName:"RoleName" type:"string" required:"true"` -} - -// String returns the string representation -func (s DeleteServiceLinkedRoleInput) String() string { - return awsutil.Prettify(s) -} - -// GoString returns the string representation -func (s DeleteServiceLinkedRoleInput) GoString() string { - return s.String() -} - -// Validate inspects the fields of the type to determine if they are valid. -func (s *DeleteServiceLinkedRoleInput) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "DeleteServiceLinkedRoleInput"} - if s.RoleName == nil { - invalidParams.Add(request.NewErrParamRequired("RoleName")) - } - - if invalidParams.Len() > 0 { - return invalidParams - } - return nil -} - -// SetRoleName sets the RoleName field's value. -func (s *DeleteServiceLinkedRoleInput) SetRoleName(v string) *DeleteServiceLinkedRoleInput { - s.RoleName = &v - return s -} - -type DeleteServiceLinkedRoleOutput struct { - _ struct{} `type:"structure"` -} - -// String returns the string representation -func (s DeleteServiceLinkedRoleOutput) String() string { - return awsutil.Prettify(s) -} - -// GoString returns the string representation -func (s DeleteServiceLinkedRoleOutput) GoString() string { - return s.String() -} - // The request to delete a streaming distribution. type DeleteStreamingDistributionInput struct { _ struct{} `type:"structure"` diff --git a/vendor/github.com/aws/aws-sdk-go/service/cloudfront/errors.go b/vendor/github.com/aws/aws-sdk-go/service/cloudfront/errors.go index 9894904782e..ede41494f30 100644 --- a/vendor/github.com/aws/aws-sdk-go/service/cloudfront/errors.go +++ b/vendor/github.com/aws/aws-sdk-go/service/cloudfront/errors.go @@ -301,10 +301,6 @@ const ( // No profile specified for the field-level encryption query argument. ErrCodeQueryArgProfileEmpty = "QueryArgProfileEmpty" - // ErrCodeResourceInUse for service response error code - // "ResourceInUse". - ErrCodeResourceInUse = "ResourceInUse" - // ErrCodeStreamingDistributionAlreadyExists for service response error code // "StreamingDistributionAlreadyExists". ErrCodeStreamingDistributionAlreadyExists = "StreamingDistributionAlreadyExists" diff --git a/vendor/github.com/aws/aws-sdk-go/service/codepipeline/api.go b/vendor/github.com/aws/aws-sdk-go/service/codepipeline/api.go index d308cfb9460..466db4a05d6 100644 --- a/vendor/github.com/aws/aws-sdk-go/service/codepipeline/api.go +++ b/vendor/github.com/aws/aws-sdk-go/service/codepipeline/api.go @@ -2809,6 +2809,10 @@ func (c *CodePipeline) UpdatePipelineRequest(input *UpdatePipelineInput) (req *r // * ErrCodeInvalidStructureException "InvalidStructureException" // The specified structure was specified in an invalid format. // +// * ErrCodeLimitExceededException "LimitExceededException" +// The number of pipelines associated with the AWS account has exceeded the +// limit allowed for the account. +// // See also, https://docs.aws.amazon.com/goto/WebAPI/codepipeline-2015-07-09/UpdatePipeline func (c *CodePipeline) UpdatePipeline(input *UpdatePipelineInput) (*UpdatePipelineOutput, error) { req, out := c.UpdatePipelineRequest(input) diff --git a/vendor/github.com/aws/aws-sdk-go/service/elasticbeanstalk/api.go b/vendor/github.com/aws/aws-sdk-go/service/elasticbeanstalk/api.go index f5e0fc7389a..d3c3004c931 100644 --- a/vendor/github.com/aws/aws-sdk-go/service/elasticbeanstalk/api.go +++ b/vendor/github.com/aws/aws-sdk-go/service/elasticbeanstalk/api.go @@ -2322,8 +2322,8 @@ func (c *ElasticBeanstalk) DescribeInstancesHealthRequest(input *DescribeInstanc // DescribeInstancesHealth API operation for AWS Elastic Beanstalk. // -// Retrives detailed information about the health of instances in your AWS Elastic -// Beanstalk. This operation requires enhanced health reporting (http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/health-enhanced.html). +// Retrieves detailed information about the health of instances in your AWS +// Elastic Beanstalk. This operation requires enhanced health reporting (http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/health-enhanced.html). // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -4039,6 +4039,14 @@ type ApplicationResourceLifecycleConfig struct { _ struct{} `type:"structure"` // The ARN of an IAM service role that Elastic Beanstalk has permission to assume. + // + // The ServiceRole property is required the first time that you provide a VersionLifecycleConfig + // for the application in one of the supporting calls (CreateApplication or + // UpdateApplicationResourceLifecycle). After you provide it once, in either + // one of the calls, Elastic Beanstalk persists the Service Role with the application, + // and you don't need to specify it again in subsequent UpdateApplicationResourceLifecycle + // calls. You can, however, specify it in subsequent calls to change the Service + // Role to another value. ServiceRole *string `type:"string"` // The application version lifecycle configuration. @@ -4112,7 +4120,25 @@ type ApplicationVersionDescription struct { // S3. SourceBundle *S3Location `type:"structure"` - // The processing status of the application version. + // The processing status of the application version. Reflects the state of the + // application version during its creation. Many of the values are only applicable + // if you specified True for the Process parameter of the CreateApplicationVersion + // action. The following list describes the possible values. + // + // * Unprocessed – Application version wasn't pre-processed or validated. + // Elastic Beanstalk will validate configuration files during deployment + // of the application version to an environment. + // + // * Processing – Elastic Beanstalk is currently processing the application + // version. + // + // * Building – Application version is currently undergoing an AWS CodeBuild + // build. + // + // * Processed – Elastic Beanstalk was successfully pre-processed and validated. + // + // * Failed – Either the AWS CodeBuild build failed or configuration files + // didn't pass validation. This application version isn't usable. Status *string `type:"string" enum:"ApplicationVersionStatus"` // A unique identifier for the application version. @@ -5180,11 +5206,15 @@ type CreateApplicationVersionInput struct { // Describes this version. Description *string `type:"string"` - // Preprocesses and validates the environment manifest (env.yaml) and configuration + // Pre-processes and validates the environment manifest (env.yaml) and configuration // files (*.config files in the .ebextensions folder) in the source bundle. // Validating configuration files can identify issues prior to deploying the // application version to an environment. // + // You must turn processing on for application versions that you create using + // AWS CodeBuild or AWS CodeCommit. For application versions built from a source + // bundle in Amazon S3, processing is optional. + // // The Process option validates Elastic Beanstalk configuration files. It doesn't // validate your application's configuration files, like proxy server or Docker // configuration. @@ -5518,6 +5548,9 @@ type CreateEnvironmentInput struct { // This is an alternative to specifying a template name. If specified, AWS Elastic // Beanstalk sets the configuration values to the default values associated // with the specified solution stack. + // + // For a list of current solution stacks, see Elastic Beanstalk Supported Platforms + // (http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/concepts.platforms.html). SolutionStackName *string `type:"string"` // This specifies the tags applied to resources in the environment. @@ -8063,7 +8096,11 @@ type EnvironmentTier struct { // The type of this environment tier. Type *string `type:"string"` - // The version of this environment tier. + // The version of this environment tier. When you don't set a value to it, Elastic + // Beanstalk uses the latest compatible worker tier version. + // + // This member is deprecated. Any specific version that you set may become out + // of date. We recommend leaving it unspecified. Version *string `type:"string"` } @@ -11512,6 +11549,9 @@ const ( // EnvironmentHealthStatusSevere is a EnvironmentHealthStatus enum value EnvironmentHealthStatusSevere = "Severe" + + // EnvironmentHealthStatusSuspended is a EnvironmentHealthStatus enum value + EnvironmentHealthStatusSuspended = "Suspended" ) const ( diff --git a/vendor/github.com/aws/aws-sdk-go/service/lambda/api.go b/vendor/github.com/aws/aws-sdk-go/service/lambda/api.go index 8024623fe93..8babcb6868a 100644 --- a/vendor/github.com/aws/aws-sdk-go/service/lambda/api.go +++ b/vendor/github.com/aws/aws-sdk-go/service/lambda/api.go @@ -269,19 +269,21 @@ func (c *Lambda) CreateEventSourceMappingRequest(input *CreateEventSourceMapping // CreateEventSourceMapping API operation for AWS Lambda. // -// Identifies a stream as an event source for a Lambda function. It can be either -// an Amazon Kinesis stream or an Amazon DynamoDB stream. AWS Lambda invokes -// the specified function when records are posted to the stream. +// Identifies a poll-based event source for a Lambda function. It can be either +// an Amazon Kinesis or DynamoDB stream, or an Amazon SQS queue. AWS Lambda +// invokes the specified function when records are posted to the event source. // -// This association between a stream source and a Lambda function is called +// This association between a poll-based source and a Lambda function is called // the event source mapping. // -// You provide mapping information (for example, which stream to read from and -// which Lambda function to invoke) in the request body. +// You provide mapping information (for example, which stream or SQS queue to +// read from and which Lambda function to invoke) in the request body. // -// Each event source, such as an Amazon Kinesis or a DynamoDB stream, can be -// associated with multiple AWS Lambda functions. A given Lambda function can -// be associated with multiple AWS event sources. +// Amazon Kinesis or DynamoDB stream event sources can be associated with multiple +// AWS Lambda functions and a given Lambda function can be associated with multiple +// AWS event sources. For Amazon SQS, you can configure multiple queues as event +// sources for a single Lambda function, but an SQS queue can be mapped only +// to a single Lambda function. // // If you are using versioning, you can specify a specific function version // or an alias via the function name parameter. For more information about versioning, @@ -605,6 +607,11 @@ func (c *Lambda) DeleteEventSourceMappingRequest(input *DeleteEventSourceMapping // // * ErrCodeTooManyRequestsException "TooManyRequestsException" // +// * ErrCodeResourceInUseException "ResourceInUseException" +// The operation conflicts with the resource's availability. For example, you +// attempted to update an EventSoure Mapping in CREATING, or tried to delete +// a EventSoure mapping currently in the UPDATING state. +// // See also, https://docs.aws.amazon.com/goto/WebAPI/lambda-2015-03-31/DeleteEventSourceMapping func (c *Lambda) DeleteEventSourceMapping(input *DeleteEventSourceMappingInput) (*EventSourceMappingConfiguration, error) { req, out := c.DeleteEventSourceMappingRequest(input) @@ -2935,6 +2942,11 @@ func (c *Lambda) UpdateEventSourceMappingRequest(input *UpdateEventSourceMapping // * ErrCodeResourceConflictException "ResourceConflictException" // The resource already exists. // +// * ErrCodeResourceInUseException "ResourceInUseException" +// The operation conflicts with the resource's availability. For example, you +// attempted to update an EventSoure Mapping in CREATING, or tried to delete +// a EventSoure mapping currently in the UPDATING state. +// // See also, https://docs.aws.amazon.com/goto/WebAPI/lambda-2015-03-31/UpdateEventSourceMapping func (c *Lambda) UpdateEventSourceMapping(input *UpdateEventSourceMappingInput) (*EventSourceMappingConfiguration, error) { req, out := c.UpdateEventSourceMappingRequest(input) @@ -3684,18 +3696,18 @@ type CreateEventSourceMappingInput struct { // The largest number of records that AWS Lambda will retrieve from your event // source at the time of invoking your function. Your function receives an event - // with all the retrieved records. The default is 100 records. + // with all the retrieved records. The default for Amazon Kinesis and Amazon + // DynamoDB is 100 records. For SQS, the default is 1. BatchSize *int64 `min:"1" type:"integer"` // Indicates whether AWS Lambda should begin polling the event source. By default, // Enabled is true. Enabled *bool `type:"boolean"` - // The Amazon Resource Name (ARN) of the Amazon Kinesis or the Amazon DynamoDB - // stream that is the event source. Any record added to this stream could cause - // AWS Lambda to invoke your Lambda function, it depends on the BatchSize. AWS - // Lambda POSTs the Amazon Kinesis event, containing records, to your Lambda - // function as JSON. + // The Amazon Resource Name (ARN) of the event source. Any record added to this + // source could cause AWS Lambda to invoke your Lambda function, it depends + // on the BatchSize. AWS Lambda POSTs the event's records to your Lambda function + // as JSON. // // EventSourceArn is a required field EventSourceArn *string `type:"string" required:"true"` @@ -3724,9 +3736,7 @@ type CreateEventSourceMappingInput struct { // in the Amazon Kinesis API Reference Guide or GetShardIterator (http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_streams_GetShardIterator.html) // in the Amazon DynamoDB API Reference Guide. The AT_TIMESTAMP value is supported // only for Kinesis streams (http://docs.aws.amazon.com/streams/latest/dev/amazon-kinesis-streams.html). - // - // StartingPosition is a required field - StartingPosition *string `type:"string" required:"true" enum:"EventSourcePosition"` + StartingPosition *string `type:"string" enum:"EventSourcePosition"` // The timestamp of the data record from which to start reading. Used with shard // iterator type (http://docs.aws.amazon.com/kinesis/latest/APIReference/API_GetShardIterator.html#Kinesis-GetShardIterator-request-ShardIteratorType) @@ -3762,9 +3772,6 @@ func (s *CreateEventSourceMappingInput) Validate() error { if s.FunctionName != nil && len(*s.FunctionName) < 1 { invalidParams.Add(request.NewErrParamMinLen("FunctionName", 1)) } - if s.StartingPosition == nil { - invalidParams.Add(request.NewErrParamRequired("StartingPosition")) - } if invalidParams.Len() > 0 { return invalidParams @@ -4422,7 +4429,8 @@ func (s *EnvironmentResponse) SetVariables(v map[string]*string) *EnvironmentRes return s } -// Describes mapping between an Amazon Kinesis stream and a Lambda function. +// Describes mapping between an Amazon Kinesis or DynamoDB stream or an Amazon +// SQS queue and a Lambda function. type EventSourceMappingConfiguration struct { _ struct{} `type:"structure"` @@ -4431,11 +4439,12 @@ type EventSourceMappingConfiguration struct { // with all the retrieved records. BatchSize *int64 `min:"1" type:"integer"` - // The Amazon Resource Name (ARN) of the Amazon Kinesis stream that is the source - // of events. + // The Amazon Resource Name (ARN) of the Amazon Kinesis or DynamoDB stream or + // the SQS queue that is the source of events. EventSourceArn *string `type:"string"` - // The Lambda function to invoke when AWS Lambda detects an event on the stream. + // The Lambda function to invoke when AWS Lambda detects an event on the poll-based + // source. FunctionArn *string `type:"string"` // The UTC time string indicating the last time the event mapping was updated. @@ -5648,8 +5657,8 @@ func (s *ListAliasesOutput) SetNextMarker(v string) *ListAliasesOutput { type ListEventSourceMappingsInput struct { _ struct{} `type:"structure"` - // The Amazon Resource Name (ARN) of the Amazon Kinesis stream. (This parameter - // is optional.) + // The Amazon Resource Name (ARN) of the Amazon Kinesis or DynamoDB stream, + // or an SQS queue. (This parameter is optional.) EventSourceArn *string `location:"querystring" locationName:"EventSourceArn" type:"string"` // The name of the Lambda function. diff --git a/vendor/github.com/aws/aws-sdk-go/service/lambda/errors.go b/vendor/github.com/aws/aws-sdk-go/service/lambda/errors.go index 57daa1c34e6..447b49ed1a9 100644 --- a/vendor/github.com/aws/aws-sdk-go/service/lambda/errors.go +++ b/vendor/github.com/aws/aws-sdk-go/service/lambda/errors.go @@ -130,6 +130,14 @@ const ( // The resource already exists. ErrCodeResourceConflictException = "ResourceConflictException" + // ErrCodeResourceInUseException for service response error code + // "ResourceInUseException". + // + // The operation conflicts with the resource's availability. For example, you + // attempted to update an EventSoure Mapping in CREATING, or tried to delete + // a EventSoure mapping currently in the UPDATING state. + ErrCodeResourceInUseException = "ResourceInUseException" + // ErrCodeResourceNotFoundException for service response error code // "ResourceNotFoundException". // diff --git a/vendor/github.com/aws/aws-sdk-go/service/secretsmanager/api.go b/vendor/github.com/aws/aws-sdk-go/service/secretsmanager/api.go index 1cd7f4b96e8..3c89d60c4bc 100644 --- a/vendor/github.com/aws/aws-sdk-go/service/secretsmanager/api.go +++ b/vendor/github.com/aws/aws-sdk-go/service/secretsmanager/api.go @@ -381,7 +381,7 @@ func (c *SecretsManager) DeleteResourcePolicyRequest(input *DeleteResourcePolicy // DeleteResourcePolicy API operation for AWS Secrets Manager. // -// Deletes the resource-based policy currently attached to the secret. +// Deletes the resource-based permission policy that's attached to the secret. // // Minimum permissions // @@ -393,8 +393,8 @@ func (c *SecretsManager) DeleteResourcePolicyRequest(input *DeleteResourcePolicy // // * To attach a resource policy to a secret, use PutResourcePolicy. // -// * To retrieve the current resource-based policy that is attached to a -// secret, use GetResourcePolicy. +// * To retrieve the current resource-based policy that's attached to a secret, +// use GetResourcePolicy. // // * To list all of the currently available secrets, use ListSecrets. // @@ -828,10 +828,10 @@ func (c *SecretsManager) GetResourcePolicyRequest(input *GetResourcePolicyInput) // GetResourcePolicy API operation for AWS Secrets Manager. // -// Retrieves the JSON text of the resource-based policy attached to the specified -// secret. The JSON request string input and response output are shown formatted -// with whitespace and line breaks for better readability. Submit your input -// as a single line JSON string. +// Retrieves the JSON text of the resource-based policy document that's attached +// to the specified secret. The JSON request string input and response output +// are shown formatted with white space and line breaks for better readability. +// Submit your input as a single line JSON string. // // Minimum permissions // @@ -843,7 +843,7 @@ func (c *SecretsManager) GetResourcePolicyRequest(input *GetResourcePolicyInput) // // * To attach a resource policy to a secret, use PutResourcePolicy. // -// * To delete the resource-based policy that is attached to a secret, use +// * To delete the resource-based policy that's attached to a secret, use // DeleteResourcePolicy. // // * To list all of the currently available secrets, use ListSecrets. @@ -1381,13 +1381,14 @@ func (c *SecretsManager) PutResourcePolicyRequest(input *PutResourcePolicyInput) // PutResourcePolicy API operation for AWS Secrets Manager. // -// Attaches the contents of the specified resource-based policy to a secret. -// A resource-based policy is optional. Alternatively, you can use IAM user-based -// policies that specify the secret's ARN in the policy statement's Resources -// element. You can also use a combination of both identity- an resource-based -// policies. The affected users and roles receive the permissions permitted -// by all of the relevant policies. For more information, see Using Resource-Based -// Policies for AWS Secrets Manager (http://docs.aws.amazon.com/secretsmanager/latest/userguide/auth-and-access_resource-based-policies.html). +// Attaches the contents of the specified resource-based permission policy to +// a secret. A resource-based policy is optional. Alternatively, you can use +// IAM identity-based policies that specify the secret's Amazon Resource Name +// (ARN) in the policy statement's Resources element. You can also use a combination +// of both identity-based and resource-based policies. The affected users and +// roles receive the permissions that are permitted by all of the relevant policies. +// For more information, see Using Resource-Based Policies for AWS Secrets Manager +// (http://docs.aws.amazon.com/secretsmanager/latest/userguide/auth-and-access_resource-based-policies.html). // For the complete description of the AWS policy syntax and grammar, see IAM // JSON Policy Reference (http://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies.html) // in the IAM User Guide. @@ -1400,9 +1401,9 @@ func (c *SecretsManager) PutResourcePolicyRequest(input *PutResourcePolicyInput) // // Related operations // -// * To retrieve the resource policy attached to a secret, use GetResourcePolicy. +// * To retrieve the resource policy that's attached to a secret, use GetResourcePolicy. // -// * To delete the resource-based policy that is attached to a secret, use +// * To delete the resource-based policy that's attached to a secret, use // DeleteResourcePolicy. // // * To list all of the currently available secrets, use ListSecrets. @@ -2620,7 +2621,7 @@ type CreateSecretInput struct { // Specifies the friendly name of the new secret. // // The secret name must be ASCII letters, digits, or the following characters - // : /_+=,.@- + // : /_+=.@- // // Name is a required field Name *string `min:"1" type:"string" required:"true"` @@ -2842,9 +2843,9 @@ func (s *CreateSecretOutput) SetVersionId(v string) *CreateSecretOutput { type DeleteResourcePolicyInput struct { _ struct{} `type:"structure"` - // Specifies the secret for which you want to delete the attached resource-based - // policy. You can specify either the Amazon Resource Name (ARN) or the friendly - // name of the secret. + // Specifies the secret that you want to delete the attached resource-based + // policy for. You can specify either the Amazon Resource Name (ARN) or the + // friendly name of the secret. // // SecretId is a required field SecretId *string `min:"1" type:"string" required:"true"` @@ -2885,10 +2886,11 @@ func (s *DeleteResourcePolicyInput) SetSecretId(v string) *DeleteResourcePolicyI type DeleteResourcePolicyOutput struct { _ struct{} `type:"structure"` - // The ARN of the secret for which the resource-based policy was deleted. + // The ARN of the secret that the resource-based policy was deleted for. ARN *string `min:"20" type:"string"` - // The friendly name of the secret for which the resource-based policy was deleted. + // The friendly name of the secret that the resource-based policy was deleted + // for. Name *string `min:"1" type:"string"` } @@ -3347,9 +3349,9 @@ func (s *GetRandomPasswordOutput) SetRandomPassword(v string) *GetRandomPassword type GetResourcePolicyInput struct { _ struct{} `type:"structure"` - // Specifies the secret for which you want to retrieve the attached resource-based - // policy. You can specify either the Amazon Resource Name (ARN) or the friendly - // name of the secret. + // Specifies the secret that you want to retrieve the attached resource-based + // policy for. You can specify either the Amazon Resource Name (ARN) or the + // friendly name of the secret. // // SecretId is a required field SecretId *string `min:"1" type:"string" required:"true"` @@ -3390,18 +3392,19 @@ func (s *GetResourcePolicyInput) SetSecretId(v string) *GetResourcePolicyInput { type GetResourcePolicyOutput struct { _ struct{} `type:"structure"` - // The ARN of the secret for which the resource-based policy was retrieved. + // The ARN of the secret that the resource-based policy was retrieved for. ARN *string `min:"20" type:"string"` - // The friendly name of the secret for which the resource-based policy was retrieved. + // The friendly name of the secret that the resource-based policy was retrieved + // for. Name *string `min:"1" type:"string"` - // A JSON-formatted string that describes the permissions associated with the - // attached secret. These permissions are combined with any permissions associated - // with the user or role who attempts to access this secret. The combined permissions - // specify who can access the secret and what actions they can perform. For - // more information, see Authentication and Access Control for AWS Secrets Manager - // (http://docs.aws.amazon.com/secretsmanager/latest/userguide/auth-and-access.html) + // A JSON-formatted string that describes the permissions that are associated + // with the attached secret. These permissions are combined with any permissions + // that are associated with the user or role that attempts to access this secret. + // The combined permissions specify who can access the secret and what actions + // they can perform. For more information, see Authentication and Access Control + // for AWS Secrets Manager (http://docs.aws.amazon.com/secretsmanager/latest/userguide/auth-and-access.html) // in the AWS Secrets Manager User Guide. ResourcePolicy *string `min:"1" type:"string"` } @@ -3868,19 +3871,18 @@ func (s *ListSecretsOutput) SetSecretList(v []*SecretListEntry) *ListSecretsOutp type PutResourcePolicyInput struct { _ struct{} `type:"structure"` - // A JSON-formatted string constructed according to the grammar and syntax for - // an AWS resource-based policy. The policy in the string identifies who can - // access or manage this secret and its versions. For information on how to - // format a JSON parameter for the various command line tool environments, see - // Using JSON for Parameters (http://docs.aws.amazon.com/cli/latest/userguide/cli-using-param.html#cli-using-param-json) + // A JSON-formatted string that's constructed according to the grammar and syntax + // for an AWS resource-based policy. The policy in the string identifies who + // can access or manage this secret and its versions. For information on how + // to format a JSON parameter for the various command line tool environments, + // see Using JSON for Parameters (http://docs.aws.amazon.com/cli/latest/userguide/cli-using-param.html#cli-using-param-json) // in the AWS CLI User Guide. // // ResourcePolicy is a required field ResourcePolicy *string `min:"1" type:"string" required:"true"` - // Specifies the secret to which you want to attach the resource-based policy. - // You can specify either the Amazon Resource Name (ARN) or the friendly name - // of the secret. + // Specifies the secret that you want to attach the resource-based policy to. + // You can specify either the ARN or the friendly name of the secret. // // SecretId is a required field SecretId *string `min:"1" type:"string" required:"true"` @@ -3933,10 +3935,11 @@ func (s *PutResourcePolicyInput) SetSecretId(v string) *PutResourcePolicyInput { type PutResourcePolicyOutput struct { _ struct{} `type:"structure"` - // The ARN of the secret for which the resource-based policy was retrieved. + // The ARN of the secret that the resource-based policy was retrieved for. ARN *string `min:"20" type:"string"` - // The friendly name of the secret for which the resource-based policy was retrieved. + // The friendly name of the secret that the resource-based policy was retrieved + // for. Name *string `min:"1" type:"string"` } diff --git a/vendor/vendor.json b/vendor/vendor.json index 5f332977ed0..a38b8f0b93a 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -39,980 +39,980 @@ "revisionTime": "2017-07-27T15:54:43Z" }, { - "checksumSHA1": "RVZfumYpBd2IRNpNI0UDX91cba4=", + "checksumSHA1": "f02wA9pKt/oP/zrPtJipDZznSp0=", "path": "github.com/aws/aws-sdk-go/aws", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "DtuTqKH29YnLjrIJkRYX0HQtXY0=", "path": "github.com/aws/aws-sdk-go/aws/arn", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "Y9W+4GimK4Fuxq+vyIskVYFRnX4=", "path": "github.com/aws/aws-sdk-go/aws/awserr", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "yyYr41HZ1Aq0hWc3J5ijXwYEcac=", "path": "github.com/aws/aws-sdk-go/aws/awsutil", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "EwL79Cq6euk+EV/t/n2E+jzPNmU=", "path": "github.com/aws/aws-sdk-go/aws/client", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "uEJU4I6dTKaraQKvrljlYKUZwoc=", "path": "github.com/aws/aws-sdk-go/aws/client/metadata", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "vVSUnICaD9IaBQisCfw0n8zLwig=", "path": "github.com/aws/aws-sdk-go/aws/corehandlers", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "925zPp8gtaXi2gkWrgZ1gAA003A=", "path": "github.com/aws/aws-sdk-go/aws/credentials", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "u3GOAJLmdvbuNUeUEcZSEAOeL/0=", "path": "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "NUJUTWlc1sV8b7WjfiYc4JZbXl0=", "path": "github.com/aws/aws-sdk-go/aws/credentials/endpointcreds", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "JEYqmF83O5n5bHkupAzA6STm0no=", "path": "github.com/aws/aws-sdk-go/aws/credentials/stscreds", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { - "checksumSHA1": "W4R+WW4t2Ty5mbKypFoATk3tqTA=", + "checksumSHA1": "GTdSvwEbZz636RtRQDrQMOU9q9Q=", "path": "github.com/aws/aws-sdk-go/aws/csm", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "HogDW/Wu6ZKTDrQ2HSzG8/vj9Ag=", "path": "github.com/aws/aws-sdk-go/aws/defaults", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "pDnK93CqjQ4ROSW8Y/RuHXjv52M=", "path": "github.com/aws/aws-sdk-go/aws/ec2metadata", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "SiJtmziOCUHNwVwWjrD5ccxbEN0=", "path": "github.com/aws/aws-sdk-go/aws/endpoints", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "CYLheDSqXftEAmmdj+tTiT+83Ko=", "path": "github.com/aws/aws-sdk-go/aws/request", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "+d8hhT6Ih+DsRpkF44pvw+IkI7w=", "path": "github.com/aws/aws-sdk-go/aws/session", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "u3cA2IiD36A+VnFIH7XtGjYk2NU=", "path": "github.com/aws/aws-sdk-go/aws/signer/v4", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "wjxQlU1PYxrDRFoL1Vek8Wch7jk=", "path": "github.com/aws/aws-sdk-go/internal/sdkio", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "MYLldFRnsZh21TfCkgkXCT3maPU=", "path": "github.com/aws/aws-sdk-go/internal/sdkrand", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "04ypv4x12l4q0TksA1zEVsmgpvw=", "path": "github.com/aws/aws-sdk-go/internal/shareddefaults", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "Ij9RP5HrBWBYYb3/LEMHNNZXH9g=", "path": "github.com/aws/aws-sdk-go/private/protocol", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "GQuRJY72iGQrufDqIaB50zG27u0=", "path": "github.com/aws/aws-sdk-go/private/protocol/ec2query", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "2ywNMWQ3qxbz/dzuuOC/42xXzWM=", "path": "github.com/aws/aws-sdk-go/private/protocol/eventstream", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "/JJPEdYxSgTTDqUDTAaSJQSKNn0=", "path": "github.com/aws/aws-sdk-go/private/protocol/eventstream/eventstreamapi", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "yHfT5DTbeCLs4NE2Rgnqrhe15ls=", "path": "github.com/aws/aws-sdk-go/private/protocol/json/jsonutil", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "R00RL5jJXRYq1iiK1+PGvMfvXyM=", "path": "github.com/aws/aws-sdk-go/private/protocol/jsonrpc", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "SBBVYdLcocjdPzMWgDuR8vcOfDQ=", "path": "github.com/aws/aws-sdk-go/private/protocol/query", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "9V1PvtFQ9MObZTc3sa86WcuOtOU=", "path": "github.com/aws/aws-sdk-go/private/protocol/query/queryutil", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "/2Ppb4vP/H+B3cM7bOOEeA7Z0iY=", "path": "github.com/aws/aws-sdk-go/private/protocol/rest", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "Rpu8KBtHZgvhkwHxUfaky+qW+G4=", "path": "github.com/aws/aws-sdk-go/private/protocol/restjson", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "ODo+ko8D6unAxZuN1jGzMcN4QCc=", "path": "github.com/aws/aws-sdk-go/private/protocol/restxml", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "6zosteWJqUGM9+t9dkANQymMA9s=", "path": "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "F6mth+G7dXN1GI+nktaGo8Lx8aE=", "path": "github.com/aws/aws-sdk-go/private/signer/v2", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "ZXFD3oHS8QZ3GgWn+k4lMnUaEUk=", "path": "github.com/aws/aws-sdk-go/service/acm", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "k1TqEWHeP84mmRewv19ZVORhSm8=", "path": "github.com/aws/aws-sdk-go/service/acmpca", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "MKWkj+pGwGygVTVTg/Q38JY+mhU=", "path": "github.com/aws/aws-sdk-go/service/apigateway", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "hq8SWUb/J1wEZ1O3TkV8xma5EXk=", "path": "github.com/aws/aws-sdk-go/service/applicationautoscaling", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "NaaFnxUCj2cKaWagwc9ZP6Su2UQ=", "path": "github.com/aws/aws-sdk-go/service/appsync", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "/nF1VEl/Y9I9EYbUHvUuhqjipUk=", "path": "github.com/aws/aws-sdk-go/service/athena", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "kko+es9ly+l/e9ngcpCdPx2DBAc=", "path": "github.com/aws/aws-sdk-go/service/autoscaling", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "42OCpXRErVgOtgPsuTrdg7y++TA=", "path": "github.com/aws/aws-sdk-go/service/batch", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "kHOf0VEd2Qy8PYZsg7/zG+JkF1o=", "path": "github.com/aws/aws-sdk-go/service/budgets", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "gJiK41PSoCEfE02VEry4f6nBg0s=", "path": "github.com/aws/aws-sdk-go/service/cloud9", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "bbCNma+YJRqcjnIqmTagqs1IwWI=", "path": "github.com/aws/aws-sdk-go/service/cloudformation", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { - "checksumSHA1": "qbncX66lCkonIyPd/YimSRtSzhI=", + "checksumSHA1": "bupt0rs/P2QN+iuFeFC7oY1HZ14=", "path": "github.com/aws/aws-sdk-go/service/cloudfront", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "P8cLAj/ovFt7uH3bB4dRg/2KApI=", "path": "github.com/aws/aws-sdk-go/service/cloudhsmv2", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "eCrAi2QJw0Ft6A8XK5ZGLpsPKwM=", "path": "github.com/aws/aws-sdk-go/service/cloudsearch", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "ItkRLsVGZt8uvkI4+7+mi9Ccy28=", "path": "github.com/aws/aws-sdk-go/service/cloudtrail", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "Y+x1N7TFB7OesWPRoYXRwGOdQzs=", "path": "github.com/aws/aws-sdk-go/service/cloudwatch", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "PxsIahbkFuks12VthBlqFJIUzX0=", "path": "github.com/aws/aws-sdk-go/service/cloudwatchevents", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "ciAsJhe41ygl47dhStzMJfGVFPQ=", "path": "github.com/aws/aws-sdk-go/service/cloudwatchlogs", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "tHLvGt81PahiKYHOXlF7bFvE9cE=", "path": "github.com/aws/aws-sdk-go/service/codebuild", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "Q+W+AVveTI7xM3ay6HAi0FB4Dpc=", "path": "github.com/aws/aws-sdk-go/service/codecommit", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "6lozLpoMkQvw1r2cZMXujYiLqK8=", "path": "github.com/aws/aws-sdk-go/service/codedeploy", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { - "checksumSHA1": "uIURH7FXD0WRKqSv2uNsMM4a1nE=", + "checksumSHA1": "JNLWxWRvZ8UNPV8nWHgzIybuMuE=", "path": "github.com/aws/aws-sdk-go/service/codepipeline", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "iPP1QQAsTiK0TYWVn7ksDLfDXDE=", "path": "github.com/aws/aws-sdk-go/service/cognitoidentity", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "JbgQL/r+cBKIKt+tQY8VSj0C2j8=", "path": "github.com/aws/aws-sdk-go/service/cognitoidentityprovider", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "orboEPrXFUPc3m7ZG8uPhZNZDdM=", "path": "github.com/aws/aws-sdk-go/service/configservice", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "C0by27wccwVfdXwaNNyvDEYUPdA=", "path": "github.com/aws/aws-sdk-go/service/databasemigrationservice", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "/zBS/FARxFnNMfLUg1Vq1FVK95c=", "path": "github.com/aws/aws-sdk-go/service/dax", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "olDBindEWwTHzKfpWaS+xID4y90=", "path": "github.com/aws/aws-sdk-go/service/devicefarm", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "i7fuDfXYhe4JMJhail5nZUg69Dk=", "path": "github.com/aws/aws-sdk-go/service/directconnect", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "jURA8oKlQkbYPPrBQIeOPbQAVN0=", "path": "github.com/aws/aws-sdk-go/service/directoryservice", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "GFq6SaQ4h48iP+gjllWjMyIbQfU=", "path": "github.com/aws/aws-sdk-go/service/dynamodb", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "SfYmxNsUZdSLy6VahSyRVaACeSU=", "path": "github.com/aws/aws-sdk-go/service/ec2", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "T4Yw3GPZg+KwAEudsypRfhbUrVw=", "path": "github.com/aws/aws-sdk-go/service/ecr", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "niBN6QYBEhoHvxQpSH8qWjEMNYo=", "path": "github.com/aws/aws-sdk-go/service/ecs", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "cmqLlPyg/fwIy8M3qaZTdBt0CGQ=", "path": "github.com/aws/aws-sdk-go/service/efs", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "mzgRjocmzU6VLUsim7E8/fffffk=", "path": "github.com/aws/aws-sdk-go/service/eks", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "A5DCzVFhE5iT60fQXjlyLDHKFsg=", "path": "github.com/aws/aws-sdk-go/service/elasticache", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { - "checksumSHA1": "qMusrd+gH5em91bS9/tvXBfnivQ=", + "checksumSHA1": "K0yPG0O4/Wv2pYASbxO6LjH8318=", "path": "github.com/aws/aws-sdk-go/service/elasticbeanstalk", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "aNIs4oJTp3x73Fx24tKO2cDY5OM=", "path": "github.com/aws/aws-sdk-go/service/elasticsearchservice", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "VFjWDQMsGpFMrNfcc//ABRpo6Ew=", "path": "github.com/aws/aws-sdk-go/service/elastictranscoder", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "cRuJDtc9rVpXUqfLrg1cJleaPjQ=", "path": "github.com/aws/aws-sdk-go/service/elb", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "XKNeVP6DCohd9Re9g4xGLuqXe0c=", "path": "github.com/aws/aws-sdk-go/service/elbv2", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "MWADuSvEQnYoVAI/HCnmJetMle4=", "path": "github.com/aws/aws-sdk-go/service/emr", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "X+8twdJk/x1+8y1LxnKXXd3eVoY=", "path": "github.com/aws/aws-sdk-go/service/firehose", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "PJQ5SFC+Ai5Mqn8rfGhuwttfqZk=", "path": "github.com/aws/aws-sdk-go/service/fms", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "xI94HsdAG+dBFCm38FoELBdbe9c=", "path": "github.com/aws/aws-sdk-go/service/gamelift", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "FEfr6yYlSRWsIV0M0kxm0/jOw0E=", "path": "github.com/aws/aws-sdk-go/service/glacier", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "k/XNa0qsR9DMIG8Sqg4cGlePTKE=", "path": "github.com/aws/aws-sdk-go/service/glue", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "HNndTio5+fNOiLr23i+QZpPp8HU=", "path": "github.com/aws/aws-sdk-go/service/guardduty", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "1tAb3rvB34c3zsX+DRY5xDJqNUA=", "path": "github.com/aws/aws-sdk-go/service/iam", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "si/Re/DEfX8xWIjpLS4NNISO/Ns=", "path": "github.com/aws/aws-sdk-go/service/inspector", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "Q2K/MWy6yy/kV9CZ0YhaEHBNgkI=", "path": "github.com/aws/aws-sdk-go/service/iot", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "OvCoIYyhBSlrCvsk8uidGznXIss=", "path": "github.com/aws/aws-sdk-go/service/kinesis", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "44R3VB9KreeYj0LFf3KBd37kPXI=", "path": "github.com/aws/aws-sdk-go/service/kms", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { - "checksumSHA1": "r5C9HizIrv+MWzpgHoJstHKZOOE=", + "checksumSHA1": "H49wXCqvNMWvsuj6+MfzsvLVE9s=", "path": "github.com/aws/aws-sdk-go/service/lambda", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "zrWHv2XG/KP9E82ophSO0XF+Cu8=", "path": "github.com/aws/aws-sdk-go/service/lexmodelbuildingservice", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "xq1jxTLdjjWU+aupMh143C+Yb1Q=", "path": "github.com/aws/aws-sdk-go/service/lightsail", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "+l6bA5aVzmBXH2Isj1xZkd5RKNY=", "path": "github.com/aws/aws-sdk-go/service/macie", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "onhxya8p2Q2wgT2TakO+ZcjwY/I=", "path": "github.com/aws/aws-sdk-go/service/mediaconvert", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "HoX+EX8JW8kDZxJAs8545YRfjuI=", "path": "github.com/aws/aws-sdk-go/service/medialive", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "ahm1SzqWwGUqPih6jPZwIevl2Pg=", "path": "github.com/aws/aws-sdk-go/service/mediapackage", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "0YZZzbKYjappFNT8MUR5qNGQkRY=", "path": "github.com/aws/aws-sdk-go/service/mediastore", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "rrANYUKEHHEofZJYCP1pQhO35TE=", "path": "github.com/aws/aws-sdk-go/service/mediastoredata", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "p/+jOAMB5MSAnPloxJIFy77Hfck=", "path": "github.com/aws/aws-sdk-go/service/mq", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "W6AvJYPtvCLTeA0USbhvid2rP1o=", "path": "github.com/aws/aws-sdk-go/service/neptune", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "yV8F3bM8W2kSZJH4TDx4mJWnrac=", "path": "github.com/aws/aws-sdk-go/service/opsworks", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "Davg8yRIMOcfa7/6zp7Fj6GnpuA=", "path": "github.com/aws/aws-sdk-go/service/organizations", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "32fGeVnRDBhJlp3Oc25r4R2M3oc=", "path": "github.com/aws/aws-sdk-go/service/pinpoint", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "j1i1tZ94/kDvvzgpv5xqxwNvgyY=", "path": "github.com/aws/aws-sdk-go/service/pricing", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "YtPLtWvoM6wD68VUp7dU+qk6hyM=", "path": "github.com/aws/aws-sdk-go/service/rds", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "KRtVvKvBRTs2cTTuiRWzjhJ8OC0=", "path": "github.com/aws/aws-sdk-go/service/redshift", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "oe9xtK3GMCwCDmra0+JC6GwJ99k=", "path": "github.com/aws/aws-sdk-go/service/route53", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "YB8Nvesxefrheq/zAGyO3NzSEYI=", "path": "github.com/aws/aws-sdk-go/service/s3", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "zTqXRGkYH+IyYOH5Uezj2M4B9xM=", "path": "github.com/aws/aws-sdk-go/service/sagemaker", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { - "checksumSHA1": "bFSEGt/5kqW51wnxs2CWyBh83RU=", + "checksumSHA1": "JqG/wC6zOGiK99YlSy31DADaV5w=", "path": "github.com/aws/aws-sdk-go/service/secretsmanager", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "x2vfgk/EnGjOWPywWL+LKJKYCF0=", "path": "github.com/aws/aws-sdk-go/service/servicecatalog", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "Z61MRqI0vu8oGAaCZSCDes9gIvk=", "path": "github.com/aws/aws-sdk-go/service/servicediscovery", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "TPGJU1VzYOhJIdTbWal1ivDwT+4=", "path": "github.com/aws/aws-sdk-go/service/ses", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "7kmVHadImICOa4/MOXErR53CdbQ=", "path": "github.com/aws/aws-sdk-go/service/sfn", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "hliWYTmov/HswyMpYq93zJtdkk0=", "path": "github.com/aws/aws-sdk-go/service/simpledb", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "bW9FW0Qe3VURaSoY305kA/wCFrM=", "path": "github.com/aws/aws-sdk-go/service/sns", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "ECIZck5xhocpUl8GeUAdeSnCgvg=", "path": "github.com/aws/aws-sdk-go/service/sqs", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "Zu/wepnCWpEe8M2DEYTaVTteys8=", "path": "github.com/aws/aws-sdk-go/service/ssm", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "uguCtF1eoCG71dvEVqrYbFs7py0=", "path": "github.com/aws/aws-sdk-go/service/sts", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "vlL9ibWgidiPj0uQ2L/OF6DCVEs=", "path": "github.com/aws/aws-sdk-go/service/swf", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "Ro5RX6aw32UGrGueBK+zhA8+Z30=", "path": "github.com/aws/aws-sdk-go/service/waf", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "uRMuwxPD/AlpvFpKppgAYzvlC0A=", "path": "github.com/aws/aws-sdk-go/service/wafregional", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "UGQwnAeB9SoJKyPPIpbDJVQws5g=", "path": "github.com/aws/aws-sdk-go/service/workspaces", - "revision": "aff39e8473db578a1cec9ac2f829a56813c1d631", - "revisionTime": "2018-06-26T20:42:33Z", - "version": "v1.14.14", - "versionExact": "v1.14.14" + "revision": "5e98666858b97db54a61044bb1150d3b81436860", + "revisionTime": "2018-06-28T21:27:56Z", + "version": "v1.14.16", + "versionExact": "v1.14.16" }, { "checksumSHA1": "usT4LCSQItkFvFOQT7cBlkCuGaE=", @@ -2091,4 +2091,4 @@ } ], "rootPath": "github.com/terraform-providers/terraform-provider-aws" -} \ No newline at end of file +} From 93791224618c0fbfc894089c346298d383f13768 Mon Sep 17 00:00:00 2001 From: saravanan30erd Date: Fri, 29 Jun 2018 16:25:00 +0400 Subject: [PATCH 33/88] update documentation in spot_fleet_request --- website/docs/r/spot_fleet_request.html.markdown | 2 ++ 1 file changed, 2 insertions(+) diff --git a/website/docs/r/spot_fleet_request.html.markdown b/website/docs/r/spot_fleet_request.html.markdown index 0f602a7edca..3226c26ae98 100644 --- a/website/docs/r/spot_fleet_request.html.markdown +++ b/website/docs/r/spot_fleet_request.html.markdown @@ -118,6 +118,8 @@ lowestPrice. * `instance_interruption_behaviour` - (Optional) Indicates whether a Spot instance stops or terminates when it is interrupted. Default is `terminate`. +* `fleet_type` - (Optional) The type of fleet request. Indicates whether the Spot Fleet only requests the target + capacity or also attempts to maintain it. Default is `maintain`. * `valid_until` - (Optional) The end date and time of the request, in UTC [RFC3339](https://tools.ietf.org/html/rfc3339#section-5.8) format(for example, YYYY-MM-DDTHH:MM:SSZ). At this point, no new Spot instance requests are placed or enabled to fulfill the request. Defaults to 24 hours. * `valid_from` - (Optional) The start date and time of the request, in UTC [RFC3339](https://tools.ietf.org/html/rfc3339#section-5.8) format(for example, YYYY-MM-DDTHH:MM:SSZ). The default is to start fulfilling the request immediately. * `load_balancers` (Optional) A list of elastic load balancer names to add to the Spot fleet. From 42e594918a1ae25e35d8d70affbd203a973bcbb0 Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Fri, 29 Jun 2018 08:29:37 -0400 Subject: [PATCH 34/88] docs/resource/aws_s3_bucket_inventory: Fix sidebar link --- website/aws.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/aws.erb b/website/aws.erb index 3a8625d111e..9fdefaafb52 100644 --- a/website/aws.erb +++ b/website/aws.erb @@ -1862,7 +1862,7 @@ > - aws_s3_bucket_inventory + aws_s3_bucket_inventory > From 57e2ae19c6aa9e91121281a73180feda9e17efbe Mon Sep 17 00:00:00 2001 From: Fred Dubois Date: Fri, 29 Jun 2018 08:46:01 -0400 Subject: [PATCH 35/88] Added filter to aws_route_tables --- aws/data_source_aws_route_tables.go | 6 ++++ aws/data_source_aws_route_tables_test.go | 40 ++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/aws/data_source_aws_route_tables.go b/aws/data_source_aws_route_tables.go index 2864ae5ac76..ef34987c37f 100644 --- a/aws/data_source_aws_route_tables.go +++ b/aws/data_source_aws_route_tables.go @@ -15,6 +15,8 @@ func dataSourceAwsRouteTables() *schema.Resource { Read: dataSourceAwsRouteTablesRead, Schema: map[string]*schema.Schema{ + "filter": ec2CustomFiltersSchema(), + "tags": tagsSchemaComputed(), "vpc_id": { @@ -49,6 +51,10 @@ func dataSourceAwsRouteTablesRead(d *schema.ResourceData, meta interface{}) erro tagsFromMap(d.Get("tags").(map[string]interface{})), )...) + req.Filters = append(req.Filters, buildEC2CustomFilterList( + d.Get("filter").(*schema.Set), + )...) + log.Printf("[DEBUG] DescribeRouteTables %s\n", req) resp, err := conn.DescribeRouteTables(req) if err != nil { diff --git a/aws/data_source_aws_route_tables_test.go b/aws/data_source_aws_route_tables_test.go index 7843157a44b..8d5ae449afa 100644 --- a/aws/data_source_aws_route_tables_test.go +++ b/aws/data_source_aws_route_tables_test.go @@ -21,9 +21,10 @@ func TestAccDataSourceAwsRouteTables(t *testing.T) { { Config: testAccDataSourceAwsRouteTablesConfigWithDataSource(rInt), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("data.aws_route_tables.test", "ids.#", "4"), - resource.TestCheckResourceAttr("data.aws_route_tables.private", "ids.#", "2"), + resource.TestCheckResourceAttr("data.aws_route_tables.test", "ids.#", "5"), + resource.TestCheckResourceAttr("data.aws_route_tables.private", "ids.#", "3"), resource.TestCheckResourceAttr("data.aws_route_tables.test2", "ids.#", "1"), + resource.TestCheckResourceAttr("data.aws_route_tables.filter_test", "ids.#", "2"), ), }, }, @@ -54,6 +55,7 @@ resource "aws_route_table" "test_public_a" { tags { Name = "tf-acc-route-tables-data-source-public-a" Tier = "Public" + Component = "Frontend" } } @@ -63,6 +65,7 @@ resource "aws_route_table" "test_private_a" { tags { Name = "tf-acc-route-tables-data-source-private-a" Tier = "Private" + Component = "Database" } } @@ -72,6 +75,17 @@ resource "aws_route_table" "test_private_b" { tags { Name = "tf-acc-route-tables-data-source-private-b" Tier = "Private" + Component = "Backend-1" + } +} + +resource "aws_route_table" "test_private_c" { + vpc_id = "${aws_vpc.test.id}" + + tags { + Name = "tf-acc-route-tables-data-source-private-c" + Tier = "Private" + Component = "Backend-2" } } @@ -89,6 +103,15 @@ data "aws_route_tables" "private" { Tier = "Private" } } + +data "aws_route_tables" "filter_test" { + vpc_id = "${aws_vpc.test.id}" + + filter { + name = "tag:Component" + values = ["Backend*"] + } +} `, rInt, rInt) } @@ -108,6 +131,7 @@ resource "aws_route_table" "test_public_a" { tags { Name = "tf-acc-route-tables-data-source-public-a" Tier = "Public" + Component = "Frontend" } } @@ -117,6 +141,7 @@ resource "aws_route_table" "test_private_a" { tags { Name = "tf-acc-route-tables-data-source-private-a" Tier = "Private" + Component = "Database" } } @@ -126,6 +151,17 @@ resource "aws_route_table" "test_private_b" { tags { Name = "tf-acc-route-tables-data-source-private-b" Tier = "Private" + Component = "Backend-1" + } +} + +resource "aws_route_table" "test_private_c" { + vpc_id = "${aws_vpc.test.id}" + + tags { + Name = "tf-acc-route-tables-data-source-private-c" + Tier = "Private" + Component = "Backend-2" } } `, rInt) From b82237f64c7644b0cf9bd1b304cd9226fda607be Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Fri, 29 Jun 2018 08:57:32 -0400 Subject: [PATCH 36/88] Update CHANGELOG for #5019 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d2a1152474..1db085a98bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ FEATURES: * **New Data Source:** `aws_launch_configuration` [GH-3624] +* **New Resource:** `aws_s3_bucket_inventory` [GH-5019] ENHANCEMENTS: From c006eb226c56c9978ee4898ecb7021f28b50b29a Mon Sep 17 00:00:00 2001 From: stack72 Date: Thu, 31 Aug 2017 16:35:36 +0100 Subject: [PATCH 37/88] New Resource: aws_vpc_associate_cidr_block Adds the ability to associate extra IPv4 or IPv6 CIDR Blocks with a VPC. In order to avoid getting into the same issue as security_group and security_group_rule, we added a diffSuppressFunc that stops people enabling `ipv6` for an existant AWS VPC. We added a note to the documentation to talk about this ``` % make testacc TEST=./aws TESTARGS='-run=TestAccAWSVpc_' ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./aws -v -run=TestAccAWSVpc_ -timeout 120m === RUN TestAccAWSVpc_importBasic --- PASS: TestAccAWSVpc_importBasic (54.16s) === RUN TestAccAWSVpc_basic --- PASS: TestAccAWSVpc_basic (45.26s) === RUN TestAccAWSVpc_enableIpv6 --- PASS: TestAccAWSVpc_enableIpv6 (87.40s) === RUN TestAccAWSVpc_dedicatedTenancy --- PASS: TestAccAWSVpc_dedicatedTenancy (45.51s) === RUN TestAccAWSVpc_tags --- PASS: TestAccAWSVpc_tags (84.80s) === RUN TestAccAWSVpc_update --- PASS: TestAccAWSVpc_update (97.12s) === RUN TestAccAWSVpc_bothDnsOptionsSet --- PASS: TestAccAWSVpc_bothDnsOptionsSet (19.82s) === RUN TestAccAWSVpc_DisabledDnsSupport --- PASS: TestAccAWSVpc_DisabledDnsSupport (44.93s) === RUN TestAccAWSVpc_classiclinkOptionSet --- PASS: TestAccAWSVpc_classiclinkOptionSet (46.18s) === RUN TestAccAWSVpc_classiclinkDnsSupportOptionSet --- PASS: TestAccAWSVpc_classiclinkDnsSupportOptionSet (47.51s) PASS ok github.com/terraform-providers/terraform-provider-aws/aws 572.738s ``` ``` % make testacc TEST=./aws TESTARGS='-run=TestAccAWSVpcAssociat' ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./aws -v -run=TestAccAWSVpcAssociat -timeout 120m === RUN TestAccAWSVpcAssociateIpv4CidrBlock --- PASS: TestAccAWSVpcAssociateIpv4CidrBlock (51.48s) === RUN TestAccAWSVpcAssociateIpv6CidrBlock --- PASS: TestAccAWSVpcAssociateIpv6CidrBlock (50.16s) === RUN TestAccAWSVpcAssociateIpv4AndIpv6CidrBlock --- PASS: TestAccAWSVpcAssociateIpv4AndIpv6CidrBlock (2.13s) PASS ok github.com/terraform-providers/terraform-provider-aws/aws 103.802s ``` --- aws/resource_aws_vpc.go | 100 +++----- aws/resource_aws_vpc_associate_cidr_block.go | 128 +++++++++ ...ource_aws_vpc_associate_cidr_block_test.go | 242 ++++++++++++++++++ aws/resource_aws_vpc_test.go | 20 +- website/aws.erb | 4 + website/docs/r/vpc.html.markdown | 4 + .../r/vpc_associate_cidr_block.html.markdown | 58 +++++ 7 files changed, 472 insertions(+), 84 deletions(-) create mode 100644 aws/resource_aws_vpc_associate_cidr_block.go create mode 100644 aws/resource_aws_vpc_associate_cidr_block_test.go create mode 100644 website/docs/r/vpc_associate_cidr_block.html.markdown diff --git a/aws/resource_aws_vpc.go b/aws/resource_aws_vpc.go index e1687477d0a..6b5d2dea88a 100644 --- a/aws/resource_aws_vpc.go +++ b/aws/resource_aws_vpc.go @@ -20,7 +20,7 @@ func resourceAwsVpc() *schema.Resource { Update: resourceAwsVpcUpdate, Delete: resourceAwsVpcDelete, Importer: &schema.ResourceImporter{ - State: resourceAwsVpcInstanceImport, + State: schema.ImportStatePassthrough, }, CustomizeDiff: resourceAwsVpcCustomizeDiff, @@ -69,7 +69,13 @@ func resourceAwsVpc() *schema.Resource { "assign_generated_ipv6_cidr_block": { Type: schema.TypeBool, Optional: true, - Default: false, + Computed: true, + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + if !d.IsNewResource() && old == "false" { + return true + } + return false + }, }, "main_route_table_id": { @@ -180,6 +186,10 @@ func resourceAwsVpcRead(d *schema.ResourceData, meta interface{}) error { // Tags d.Set("tags", tagsToMap(vpc.Tags)) + if len(vpc.Ipv6CidrBlockAssociationSet) == 0 { + d.Set("assign_generated_ipv6_cidr_block", false) + } + for _, a := range vpc.Ipv6CidrBlockAssociationSet { if *a.Ipv6CidrBlockState.State == "associated" { //we can only ever have 1 IPv6 block associated at once d.Set("assign_generated_ipv6_cidr_block", true) @@ -389,65 +399,31 @@ func resourceAwsVpcUpdate(d *schema.ResourceData, meta interface{}) error { } if d.HasChange("assign_generated_ipv6_cidr_block") && !d.IsNewResource() { - toAssign := d.Get("assign_generated_ipv6_cidr_block").(bool) - - log.Printf("[INFO] Modifying assign_generated_ipv6_cidr_block to %#v", toAssign) - - if toAssign { - modifyOpts := &ec2.AssociateVpcCidrBlockInput{ - VpcId: &vpcid, - AmazonProvidedIpv6CidrBlock: aws.Bool(toAssign), - } - log.Printf("[INFO] Enabling assign_generated_ipv6_cidr_block vpc attribute for %s: %#v", - d.Id(), modifyOpts) - resp, err := conn.AssociateVpcCidrBlock(modifyOpts) - if err != nil { - return err - } - - // Wait for the CIDR to become available - log.Printf( - "[DEBUG] Waiting for IPv6 CIDR (%s) to become associated", - d.Id()) - stateConf := &resource.StateChangeConf{ - Pending: []string{"associating", "disassociated"}, - Target: []string{"associated"}, - Refresh: Ipv6CidrStateRefreshFunc(conn, d.Id(), *resp.Ipv6CidrBlockAssociation.AssociationId), - Timeout: 1 * time.Minute, - } - if _, err := stateConf.WaitForState(); err != nil { - return fmt.Errorf( - "Error waiting for IPv6 CIDR (%s) to become associated: %s", - d.Id(), err) - } - } else { - modifyOpts := &ec2.DisassociateVpcCidrBlockInput{ - AssociationId: aws.String(d.Get("ipv6_association_id").(string)), - } - log.Printf("[INFO] Disabling assign_generated_ipv6_cidr_block vpc attribute for %s: %#v", - d.Id(), modifyOpts) - if _, err := conn.DisassociateVpcCidrBlock(modifyOpts); err != nil { - return err - } - - // Wait for the CIDR to become available - log.Printf( - "[DEBUG] Waiting for IPv6 CIDR (%s) to become disassociated", - d.Id()) - stateConf := &resource.StateChangeConf{ - Pending: []string{"disassociating", "associated"}, - Target: []string{"disassociated"}, - Refresh: Ipv6CidrStateRefreshFunc(conn, d.Id(), d.Get("ipv6_association_id").(string)), - Timeout: 1 * time.Minute, - } - if _, err := stateConf.WaitForState(); err != nil { - return fmt.Errorf( - "Error waiting for IPv6 CIDR (%s) to become disassociated: %s", - d.Id(), err) - } + //we know that the change is only going to be to disable IPv6 as we suppress the func for enabling + modifyOpts := &ec2.DisassociateVpcCidrBlockInput{ + AssociationId: aws.String(d.Get("ipv6_association_id").(string)), + } + log.Printf("[INFO] Disabling assign_generated_ipv6_cidr_block vpc attribute for %s: %#v", + d.Id(), modifyOpts) + if _, err := conn.DisassociateVpcCidrBlock(modifyOpts); err != nil { + return err } - d.SetPartial("assign_generated_ipv6_cidr_block") + // Wait for the CIDR to become available + log.Printf( + "[DEBUG] Waiting for IPv6 CIDR (%s) to become disassociated", + d.Id()) + stateConf := &resource.StateChangeConf{ + Pending: []string{"disassociating", "associated"}, + Target: []string{"disassociated"}, + Refresh: Ipv6CidrStateRefreshFunc(conn, d.Id(), d.Get("ipv6_association_id").(string)), + Timeout: 1 * time.Minute, + } + if _, err := stateConf.WaitForState(); err != nil { + return fmt.Errorf( + "Error waiting for IPv6 CIDR (%s) to become disassociated: %s", + d.Id(), err) + } } if d.HasChange("instance_tenancy") && !d.IsNewResource() { @@ -656,12 +632,6 @@ func resourceAwsVpcSetDefaultRouteTable(conn *ec2.EC2, d *schema.ResourceData) e return nil } -func resourceAwsVpcInstanceImport( - d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - d.Set("assign_generated_ipv6_cidr_block", false) - return []*schema.ResourceData{d}, nil -} - func awsVpcDescribeVpcAttribute(attribute string, vpcId string, conn *ec2.EC2) (*ec2.DescribeVpcAttributeOutput, error) { describeAttrOpts := &ec2.DescribeVpcAttributeInput{ Attribute: aws.String(attribute), diff --git a/aws/resource_aws_vpc_associate_cidr_block.go b/aws/resource_aws_vpc_associate_cidr_block.go new file mode 100644 index 00000000000..9c19a90f2e5 --- /dev/null +++ b/aws/resource_aws_vpc_associate_cidr_block.go @@ -0,0 +1,128 @@ +package aws + +import ( + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAwsVpcAssociateCidrBlock() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsVpcAssociateCidrBlockCreate, + Read: resourceAwsVpcAssociateCidrBlockRead, + Delete: resourceAwsVpcAssociateCidrBlockDelete, + + Schema: map[string]*schema.Schema{ + + "vpc_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "ipv4_cidr_block": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validateCIDRNetworkAddress, + }, + + "assign_generated_ipv6_cidr_block": { + Type: schema.TypeBool, + Optional: true, + Default: false, + ForceNew: true, + ConflictsWith: []string{"ipv4_cidr_block"}, + }, + + "ipv6_cidr_block": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceAwsVpcAssociateCidrBlockCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + + params := &ec2.AssociateVpcCidrBlockInput{ + VpcId: aws.String(d.Get("vpc_id").(string)), + AmazonProvidedIpv6CidrBlock: aws.Bool(d.Get("assign_generated_ipv6_cidr_block").(bool)), + } + + if v, ok := d.GetOk("ipv4_cidr_block"); ok { + params.CidrBlock = aws.String(v.(string)) + } + + resp, err := conn.AssociateVpcCidrBlock(params) + if err != nil { + return err + } + + if d.Get("assign_generated_ipv6_cidr_block").(bool) == true { + d.SetId(*resp.Ipv6CidrBlockAssociation.AssociationId) + } else { + d.SetId(*resp.CidrBlockAssociation.AssociationId) + } + + return resourceAwsVpcAssociateCidrBlockRead(d, meta) +} + +func resourceAwsVpcAssociateCidrBlockRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + + vpcRaw, _, err := VPCStateRefreshFunc(conn, d.Get("vpc_id").(string))() + if err != nil { + return err + } + + if vpcRaw == nil { + log.Printf("[INFO] No VPC Found for id %q", d.Get("vpc_id").(string)) + d.SetId("") + return nil + } + + vpc := vpcRaw.(*ec2.Vpc) + found := false + + if d.Get("assign_generated_ipv6_cidr_block").(bool) == true { + for _, ipv6Association := range vpc.Ipv6CidrBlockAssociationSet { + if *ipv6Association.AssociationId == d.Id() { + found = true + d.Set("ipv6_cidr_block", ipv6Association.Ipv6CidrBlock) + break + } + } + } else { + for _, cidrAssociation := range vpc.CidrBlockAssociationSet { + if *cidrAssociation.AssociationId == d.Id() { + found = true + d.Set("ipv4_cidr_block", cidrAssociation.CidrBlock) + } + } + } + + if !found { + log.Printf("[INFO] No VPC CIDR Association found for ID: %q", d.Id()) + } + + return nil +} + +func resourceAwsVpcAssociateCidrBlockDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + + params := &ec2.DisassociateVpcCidrBlockInput{ + AssociationId: aws.String(d.Id()), + } + + _, err := conn.DisassociateVpcCidrBlock(params) + if err != nil { + return err + } + + return nil +} diff --git a/aws/resource_aws_vpc_associate_cidr_block_test.go b/aws/resource_aws_vpc_associate_cidr_block_test.go new file mode 100644 index 00000000000..f8a00942520 --- /dev/null +++ b/aws/resource_aws_vpc_associate_cidr_block_test.go @@ -0,0 +1,242 @@ +package aws + +import ( + "fmt" + "testing" + + "regexp" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSVpcAssociateIpv4CidrBlock(t *testing.T) { + var association ec2.VpcCidrBlockAssociation + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckVpcAssociateCidrBlockDestroy, + Steps: []resource.TestStep{ + { + Config: testAccVpcIpv4CidrAssociationConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckVpcIpv4CidrAssociationExists("aws_vpc_associate_cidr_block.secondary_cidr", &association), + testAccCheckVpcIpv4AssociationCidr(&association, "172.2.0.0/16"), + ), + }, + }, + }) +} + +func TestAccAWSVpcAssociateIpv6CidrBlock(t *testing.T) { + var association ec2.VpcIpv6CidrBlockAssociation + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckVpcAssociateCidrBlockDestroy, + Steps: []resource.TestStep{ + { + Config: testAccVpcIpv6CidrAssociationConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckVpcIpv6CidrAssociationExists("aws_vpc_associate_cidr_block.secondary_ipv6_cidr", &association), + resource.TestCheckResourceAttrSet("aws_vpc_associate_cidr_block.secondary_ipv6_cidr", "ipv6_cidr_block"), + ), + }, + }, + }) +} + +func TestAccAWSVpcAssociateIpv4AndIpv6CidrBlock(t *testing.T) { + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckVpcAssociateCidrBlockDestroy, + Steps: []resource.TestStep{ + { + Config: testAccVpcIpv4AndIpv6CidrAssociationConfig, + ExpectError: regexp.MustCompile(`: conflicts with`), + }, + }, + }) +} + +func testAccCheckVpcIpv4AssociationCidr(association *ec2.VpcCidrBlockAssociation, expected string) resource.TestCheckFunc { + return func(s *terraform.State) error { + CIDRBlock := association.CidrBlock + if *CIDRBlock != expected { + return fmt.Errorf("Bad cidr: %s", *association.CidrBlock) + } + + return nil + } +} + +func testAccCheckVpcAssociateCidrBlockDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).ec2conn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_vpc_associate_cidr_block" { + continue + } + + // Try to find the VPC + DescribeVpcOpts := &ec2.DescribeVpcsInput{ + VpcIds: []*string{aws.String(rs.Primary.Attributes["vpc_id"])}, + } + resp, err := conn.DescribeVpcs(DescribeVpcOpts) + if err == nil { + vpc := resp.Vpcs[0] + + for _, ipv4Association := range vpc.CidrBlockAssociationSet { + if *ipv4Association.AssociationId == rs.Primary.ID { + return fmt.Errorf("VPC CIDR Association still exists.") + } + } + + for _, ipv6Association := range vpc.Ipv6CidrBlockAssociationSet { + if *ipv6Association.AssociationId == rs.Primary.ID { + return fmt.Errorf("VPC CIDR Association still exists.") + } + } + + return nil + } + + // Verify the error is what we want + ec2err, ok := err.(awserr.Error) + if !ok { + return err + } + if ec2err.Code() != "InvalidVpcID.NotFound" { + return err + } + } + + return nil +} + +func testAccCheckVpcIpv4CidrAssociationExists(n string, association *ec2.VpcCidrBlockAssociation) 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 VPC ID is set") + } + + conn := testAccProvider.Meta().(*AWSClient).ec2conn + DescribeVpcOpts := &ec2.DescribeVpcsInput{ + VpcIds: []*string{aws.String(rs.Primary.Attributes["vpc_id"])}, + } + resp, err := conn.DescribeVpcs(DescribeVpcOpts) + if err != nil { + return err + } + if len(resp.Vpcs) == 0 { + return fmt.Errorf("VPC not found") + } + + vpc := resp.Vpcs[0] + found := false + for _, cidrAssociation := range vpc.CidrBlockAssociationSet { + if *cidrAssociation.AssociationId == rs.Primary.ID { + *association = *cidrAssociation + found = true + } + } + + if !found { + return fmt.Errorf("VPC CIDR Association not found") + } + + return nil + } +} + +func testAccCheckVpcIpv6CidrAssociationExists(n string, association *ec2.VpcIpv6CidrBlockAssociation) 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 VPC ID is set") + } + + conn := testAccProvider.Meta().(*AWSClient).ec2conn + DescribeVpcOpts := &ec2.DescribeVpcsInput{ + VpcIds: []*string{aws.String(rs.Primary.Attributes["vpc_id"])}, + } + resp, err := conn.DescribeVpcs(DescribeVpcOpts) + if err != nil { + return err + } + if len(resp.Vpcs) == 0 { + return fmt.Errorf("VPC not found") + } + + vpc := resp.Vpcs[0] + found := false + for _, cidrAssociation := range vpc.Ipv6CidrBlockAssociationSet { + if *cidrAssociation.AssociationId == rs.Primary.ID { + *association = *cidrAssociation + found = true + } + } + + if !found { + return fmt.Errorf("VPC CIDR Association not found") + } + + return nil + } +} + +const testAccVpcIpv4CidrAssociationConfig = ` +resource "aws_vpc" "foo" { + cidr_block = "10.1.0.0/16" +} + +resource "aws_vpc_associate_cidr_block" "secondary_cidr" { + vpc_id = "${aws_vpc.foo.id}" + ipv4_cidr_block = "172.2.0.0/16" +} + +resource "aws_vpc_associate_cidr_block" "tertiary_cidr" { + vpc_id = "${aws_vpc.foo.id}" + ipv4_cidr_block = "170.2.0.0/16" +} +` + +const testAccVpcIpv6CidrAssociationConfig = ` +resource "aws_vpc" "foo" { + cidr_block = "10.1.0.0/16" +} + +resource "aws_vpc_associate_cidr_block" "secondary_ipv6_cidr" { + vpc_id = "${aws_vpc.foo.id}" + assign_generated_ipv6_cidr_block = true +} +` + +const testAccVpcIpv4AndIpv6CidrAssociationConfig = ` +resource "aws_vpc" "foo" { + cidr_block = "10.1.0.0/16" + assign_generated_ipv6_cidr_block = true +} + +resource "aws_vpc_associate_cidr_block" "secondary_ipv6_cidr" { + vpc_id = "${aws_vpc.foo.id}" + ipv4_cidr_block = "172.2.0.0/16" + assign_generated_ipv6_cidr_block = true +} +` diff --git a/aws/resource_aws_vpc_test.go b/aws/resource_aws_vpc_test.go index 1059b255975..d13f0055b15 100644 --- a/aws/resource_aws_vpc_test.go +++ b/aws/resource_aws_vpc_test.go @@ -113,7 +113,6 @@ func TestAccAWSVpc_enableIpv6(t *testing.T) { Config: testAccVpcConfigIpv6Enabled, Check: resource.ComposeAggregateTestCheckFunc( testAccCheckVpcExists("aws_vpc.foo", &vpc), - testAccCheckVpcCidr(&vpc, "10.1.0.0/16"), resource.TestCheckResourceAttr( "aws_vpc.foo", "cidr_block", "10.1.0.0/16"), resource.TestCheckResourceAttrSet( @@ -128,28 +127,10 @@ func TestAccAWSVpc_enableIpv6(t *testing.T) { Config: testAccVpcConfigIpv6Disabled, Check: resource.ComposeAggregateTestCheckFunc( testAccCheckVpcExists("aws_vpc.foo", &vpc), - testAccCheckVpcCidr(&vpc, "10.1.0.0/16"), - resource.TestCheckResourceAttr( - "aws_vpc.foo", "cidr_block", "10.1.0.0/16"), resource.TestCheckResourceAttr( "aws_vpc.foo", "assign_generated_ipv6_cidr_block", "false"), ), }, - { - Config: testAccVpcConfigIpv6Enabled, - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckVpcExists("aws_vpc.foo", &vpc), - testAccCheckVpcCidr(&vpc, "10.1.0.0/16"), - resource.TestCheckResourceAttr( - "aws_vpc.foo", "cidr_block", "10.1.0.0/16"), - resource.TestCheckResourceAttrSet( - "aws_vpc.foo", "ipv6_association_id"), - resource.TestCheckResourceAttrSet( - "aws_vpc.foo", "ipv6_cidr_block"), - resource.TestCheckResourceAttr( - "aws_vpc.foo", "assign_generated_ipv6_cidr_block", "true"), - ), - }, }, }) } @@ -461,6 +442,7 @@ resource "aws_vpc" "foo" { const testAccVpcConfigIpv6Disabled = ` resource "aws_vpc" "foo" { cidr_block = "10.1.0.0/16" + assign_generated_ipv6_cidr_block = false tags { Name = "terraform-testacc-vpc-ipv6" } diff --git a/website/aws.erb b/website/aws.erb index 9fdefaafb52..7c2e6635788 100644 --- a/website/aws.erb +++ b/website/aws.erb @@ -2198,6 +2198,10 @@ aws_vpc + > + aws_vpc_associate_cidr_block + + > aws_vpc_dhcp_options diff --git a/website/docs/r/vpc.html.markdown b/website/docs/r/vpc.html.markdown index 16e7bfe851f..8bc99e93f51 100644 --- a/website/docs/r/vpc.html.markdown +++ b/website/docs/r/vpc.html.markdown @@ -51,6 +51,10 @@ block with a /56 prefix length for the VPC. You cannot specify the range of IP a the size of the CIDR block. Default is `false`. * `tags` - (Optional) A mapping of tags to assign to the resource. +~> **Note:** If you create a VPC with `assign_generated_ipv6_cidr_block` set to `false` and want to +update that to `true`, Terraform will no longer support this operation and we suggest to use `aws_vpc_associate_cidr_block` resource instead. +The VPC won't reflect the new IPv6 values from the new resource until `terraform refresh` happens. + ## Attributes Reference In addition to all arguments above, the following attributes are exported: diff --git a/website/docs/r/vpc_associate_cidr_block.html.markdown b/website/docs/r/vpc_associate_cidr_block.html.markdown new file mode 100644 index 00000000000..276f5beec6d --- /dev/null +++ b/website/docs/r/vpc_associate_cidr_block.html.markdown @@ -0,0 +1,58 @@ +--- +layout: "aws" +page_title: "AWS: aws_vpc_associate_cidr_block" +sidebar_current: "docs-aws-resource-vpc-associate-cidr-block" +description: |- + Associates a CIDR block to a VPC +--- + +# aws_vpc_associate_cidr_block + +Associates a CIDR block to a VPC + +## Example Usage + +IPv4 CIDR Block Association: + +```hcl +resource "aws_vpc" "main" { + cidr_block = "10.0.0.0/16" +} + +resource "aws_vpc_associate_cidr_block" "secondary_cidr" { + vpc_id = "${aws_vpc.main.id}" + ipv4_cidr_block = "172.2.0.0/16" +} +``` + +IPv6 CIDR Block Association: + +```hcl +resource "aws_vpc" "main" { + cidr_block = "10.0.0.0/16" + assign_generated_ipv6_cidr_block = true +} + +resource "aws_vpc_associate_cidr_block" "secondary_ipv6_cidr" { + vpc_id = "${aws_vpc.main.id}" + assign_generated_ipv6_cidr_block = true +} +``` + +## Argument Reference + +The following arguments are supported: + +* `vpc_id` - (Required) The ID of the VPC to make the association with +* `cidr_block` - (Optional) The IPv4 CIDR block to associate to the VPC. +* `assign_generated_ipv6_cidr_block` - (Optional) Requests an Amazon-provided IPv6 CIDR +block with a /56 prefix length for the VPC. You cannot specify the range of IP addresses, or +the size of the CIDR block. Default is `false`. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the VPC CIDR Association +* `ipv6_cidr_block` - The IPv6 CIDR block. + From 3e43f34c3c4a0db8c419ceef7d4ebe2384eebacf Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 9 Mar 2018 14:50:17 -0500 Subject: [PATCH 38/88] Restore original 'aws_vpc.assign_generated_ipv6_cidr_block' functionality. --- aws/resource_aws_vpc.go | 100 ++++++++++++++++++++----------- aws/resource_aws_vpc_test.go | 20 ++++++- website/docs/r/vpc.html.markdown | 4 -- 3 files changed, 84 insertions(+), 40 deletions(-) diff --git a/aws/resource_aws_vpc.go b/aws/resource_aws_vpc.go index 6b5d2dea88a..e1687477d0a 100644 --- a/aws/resource_aws_vpc.go +++ b/aws/resource_aws_vpc.go @@ -20,7 +20,7 @@ func resourceAwsVpc() *schema.Resource { Update: resourceAwsVpcUpdate, Delete: resourceAwsVpcDelete, Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, + State: resourceAwsVpcInstanceImport, }, CustomizeDiff: resourceAwsVpcCustomizeDiff, @@ -69,13 +69,7 @@ func resourceAwsVpc() *schema.Resource { "assign_generated_ipv6_cidr_block": { Type: schema.TypeBool, Optional: true, - Computed: true, - DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - if !d.IsNewResource() && old == "false" { - return true - } - return false - }, + Default: false, }, "main_route_table_id": { @@ -186,10 +180,6 @@ func resourceAwsVpcRead(d *schema.ResourceData, meta interface{}) error { // Tags d.Set("tags", tagsToMap(vpc.Tags)) - if len(vpc.Ipv6CidrBlockAssociationSet) == 0 { - d.Set("assign_generated_ipv6_cidr_block", false) - } - for _, a := range vpc.Ipv6CidrBlockAssociationSet { if *a.Ipv6CidrBlockState.State == "associated" { //we can only ever have 1 IPv6 block associated at once d.Set("assign_generated_ipv6_cidr_block", true) @@ -399,31 +389,65 @@ func resourceAwsVpcUpdate(d *schema.ResourceData, meta interface{}) error { } if d.HasChange("assign_generated_ipv6_cidr_block") && !d.IsNewResource() { - //we know that the change is only going to be to disable IPv6 as we suppress the func for enabling - modifyOpts := &ec2.DisassociateVpcCidrBlockInput{ - AssociationId: aws.String(d.Get("ipv6_association_id").(string)), - } - log.Printf("[INFO] Disabling assign_generated_ipv6_cidr_block vpc attribute for %s: %#v", - d.Id(), modifyOpts) - if _, err := conn.DisassociateVpcCidrBlock(modifyOpts); err != nil { - return err - } + toAssign := d.Get("assign_generated_ipv6_cidr_block").(bool) - // Wait for the CIDR to become available - log.Printf( - "[DEBUG] Waiting for IPv6 CIDR (%s) to become disassociated", - d.Id()) - stateConf := &resource.StateChangeConf{ - Pending: []string{"disassociating", "associated"}, - Target: []string{"disassociated"}, - Refresh: Ipv6CidrStateRefreshFunc(conn, d.Id(), d.Get("ipv6_association_id").(string)), - Timeout: 1 * time.Minute, - } - if _, err := stateConf.WaitForState(); err != nil { - return fmt.Errorf( - "Error waiting for IPv6 CIDR (%s) to become disassociated: %s", - d.Id(), err) + log.Printf("[INFO] Modifying assign_generated_ipv6_cidr_block to %#v", toAssign) + + if toAssign { + modifyOpts := &ec2.AssociateVpcCidrBlockInput{ + VpcId: &vpcid, + AmazonProvidedIpv6CidrBlock: aws.Bool(toAssign), + } + log.Printf("[INFO] Enabling assign_generated_ipv6_cidr_block vpc attribute for %s: %#v", + d.Id(), modifyOpts) + resp, err := conn.AssociateVpcCidrBlock(modifyOpts) + if err != nil { + return err + } + + // Wait for the CIDR to become available + log.Printf( + "[DEBUG] Waiting for IPv6 CIDR (%s) to become associated", + d.Id()) + stateConf := &resource.StateChangeConf{ + Pending: []string{"associating", "disassociated"}, + Target: []string{"associated"}, + Refresh: Ipv6CidrStateRefreshFunc(conn, d.Id(), *resp.Ipv6CidrBlockAssociation.AssociationId), + Timeout: 1 * time.Minute, + } + if _, err := stateConf.WaitForState(); err != nil { + return fmt.Errorf( + "Error waiting for IPv6 CIDR (%s) to become associated: %s", + d.Id(), err) + } + } else { + modifyOpts := &ec2.DisassociateVpcCidrBlockInput{ + AssociationId: aws.String(d.Get("ipv6_association_id").(string)), + } + log.Printf("[INFO] Disabling assign_generated_ipv6_cidr_block vpc attribute for %s: %#v", + d.Id(), modifyOpts) + if _, err := conn.DisassociateVpcCidrBlock(modifyOpts); err != nil { + return err + } + + // Wait for the CIDR to become available + log.Printf( + "[DEBUG] Waiting for IPv6 CIDR (%s) to become disassociated", + d.Id()) + stateConf := &resource.StateChangeConf{ + Pending: []string{"disassociating", "associated"}, + Target: []string{"disassociated"}, + Refresh: Ipv6CidrStateRefreshFunc(conn, d.Id(), d.Get("ipv6_association_id").(string)), + Timeout: 1 * time.Minute, + } + if _, err := stateConf.WaitForState(); err != nil { + return fmt.Errorf( + "Error waiting for IPv6 CIDR (%s) to become disassociated: %s", + d.Id(), err) + } } + + d.SetPartial("assign_generated_ipv6_cidr_block") } if d.HasChange("instance_tenancy") && !d.IsNewResource() { @@ -632,6 +656,12 @@ func resourceAwsVpcSetDefaultRouteTable(conn *ec2.EC2, d *schema.ResourceData) e return nil } +func resourceAwsVpcInstanceImport( + d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + d.Set("assign_generated_ipv6_cidr_block", false) + return []*schema.ResourceData{d}, nil +} + func awsVpcDescribeVpcAttribute(attribute string, vpcId string, conn *ec2.EC2) (*ec2.DescribeVpcAttributeOutput, error) { describeAttrOpts := &ec2.DescribeVpcAttributeInput{ Attribute: aws.String(attribute), diff --git a/aws/resource_aws_vpc_test.go b/aws/resource_aws_vpc_test.go index d13f0055b15..1059b255975 100644 --- a/aws/resource_aws_vpc_test.go +++ b/aws/resource_aws_vpc_test.go @@ -113,6 +113,7 @@ func TestAccAWSVpc_enableIpv6(t *testing.T) { Config: testAccVpcConfigIpv6Enabled, Check: resource.ComposeAggregateTestCheckFunc( testAccCheckVpcExists("aws_vpc.foo", &vpc), + testAccCheckVpcCidr(&vpc, "10.1.0.0/16"), resource.TestCheckResourceAttr( "aws_vpc.foo", "cidr_block", "10.1.0.0/16"), resource.TestCheckResourceAttrSet( @@ -127,10 +128,28 @@ func TestAccAWSVpc_enableIpv6(t *testing.T) { Config: testAccVpcConfigIpv6Disabled, Check: resource.ComposeAggregateTestCheckFunc( testAccCheckVpcExists("aws_vpc.foo", &vpc), + testAccCheckVpcCidr(&vpc, "10.1.0.0/16"), + resource.TestCheckResourceAttr( + "aws_vpc.foo", "cidr_block", "10.1.0.0/16"), resource.TestCheckResourceAttr( "aws_vpc.foo", "assign_generated_ipv6_cidr_block", "false"), ), }, + { + Config: testAccVpcConfigIpv6Enabled, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckVpcExists("aws_vpc.foo", &vpc), + testAccCheckVpcCidr(&vpc, "10.1.0.0/16"), + resource.TestCheckResourceAttr( + "aws_vpc.foo", "cidr_block", "10.1.0.0/16"), + resource.TestCheckResourceAttrSet( + "aws_vpc.foo", "ipv6_association_id"), + resource.TestCheckResourceAttrSet( + "aws_vpc.foo", "ipv6_cidr_block"), + resource.TestCheckResourceAttr( + "aws_vpc.foo", "assign_generated_ipv6_cidr_block", "true"), + ), + }, }, }) } @@ -442,7 +461,6 @@ resource "aws_vpc" "foo" { const testAccVpcConfigIpv6Disabled = ` resource "aws_vpc" "foo" { cidr_block = "10.1.0.0/16" - assign_generated_ipv6_cidr_block = false tags { Name = "terraform-testacc-vpc-ipv6" } diff --git a/website/docs/r/vpc.html.markdown b/website/docs/r/vpc.html.markdown index 8bc99e93f51..16e7bfe851f 100644 --- a/website/docs/r/vpc.html.markdown +++ b/website/docs/r/vpc.html.markdown @@ -51,10 +51,6 @@ block with a /56 prefix length for the VPC. You cannot specify the range of IP a the size of the CIDR block. Default is `false`. * `tags` - (Optional) A mapping of tags to assign to the resource. -~> **Note:** If you create a VPC with `assign_generated_ipv6_cidr_block` set to `false` and want to -update that to `true`, Terraform will no longer support this operation and we suggest to use `aws_vpc_associate_cidr_block` resource instead. -The VPC won't reflect the new IPv6 values from the new resource until `terraform refresh` happens. - ## Attributes Reference In addition to all arguments above, the following attributes are exported: From 839d50badc5a0fcf53ccbfd284176d8a9a6e9203 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 9 Mar 2018 15:52:27 -0500 Subject: [PATCH 39/88] 'aws_vpc_associate_cidr_block' -> 'aws_vpc_secondary_ipv4_cidr_block'. --- aws/aws_vpc_secondary_ipv4_cidr_block.go | 96 +++++++ aws/aws_vpc_secondary_ipv4_cidr_block_test.go | 138 ++++++++++ aws/provider.go | 1 + aws/resource_aws_vpc_associate_cidr_block.go | 128 --------- ...ource_aws_vpc_associate_cidr_block_test.go | 242 ------------------ website/aws.erb | 8 +- .../r/vpc_associate_cidr_block.html.markdown | 58 ----- ...pc_secondary_ipv4_cidr_block.html.markdown | 37 +++ 8 files changed, 276 insertions(+), 432 deletions(-) create mode 100644 aws/aws_vpc_secondary_ipv4_cidr_block.go create mode 100644 aws/aws_vpc_secondary_ipv4_cidr_block_test.go delete mode 100644 aws/resource_aws_vpc_associate_cidr_block.go delete mode 100644 aws/resource_aws_vpc_associate_cidr_block_test.go delete mode 100644 website/docs/r/vpc_associate_cidr_block.html.markdown create mode 100644 website/docs/r/vpc_secondary_ipv4_cidr_block.html.markdown diff --git a/aws/aws_vpc_secondary_ipv4_cidr_block.go b/aws/aws_vpc_secondary_ipv4_cidr_block.go new file mode 100644 index 00000000000..ca6f81ae714 --- /dev/null +++ b/aws/aws_vpc_secondary_ipv4_cidr_block.go @@ -0,0 +1,96 @@ +package aws + +import ( + "fmt" + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" +) + +func resourceAwsVpcSecondaryIpv4CidrBlock() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsVpcSecondaryIpv4CidrBlockCreate, + Read: resourceAwsVpcSecondaryIpv4CidrBlockRead, + Delete: resourceAwsVpcSecondaryIpv4CidrBlockDelete, + + Schema: map[string]*schema.Schema{ + "vpc_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "ipv4_cidr_block": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.CIDRNetwork(16, 28), // The allowed block size is between a /28 netmask and /16 netmask. + }, + }, + } +} + +func resourceAwsVpcSecondaryIpv4CidrBlockCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + + req := &ec2.AssociateVpcCidrBlockInput{ + VpcId: aws.String(d.Get("vpc_id").(string)), + CidrBlock: aws.String(d.Get("ipv4_cidr_block").(string)), + } + log.Printf("[DEBUG] Creating VPC secondary IPv4 CIDR block: %#v", req) + resp, err := conn.AssociateVpcCidrBlock(req) + if err != nil { + return fmt.Errorf("Error creating VPC secondary IPv4 CIDR block: %s", err.Error()) + } + + d.SetId(aws.StringValue(resp.CidrBlockAssociation.AssociationId)) + + return resourceAwsVpcSecondaryIpv4CidrBlockRead(d, meta) +} + +func resourceAwsVpcSecondaryIpv4CidrBlockRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + + vpcId := d.Get("vpc_id").(string) + vpcRaw, _, err := VPCStateRefreshFunc(conn, vpcId)() + if err != nil { + return fmt.Errorf("Error reading VPC: %s", err.Error()) + } + if vpcRaw == nil { + log.Printf("[WARN] VPC (%s) not found, removing secondary IPv4 CIDR block from state", vpcId) + d.SetId("") + return nil + } + + vpc := vpcRaw.(*ec2.Vpc) + found := false + for _, cidrAssociation := range vpc.CidrBlockAssociationSet { + if aws.StringValue(cidrAssociation.AssociationId) == d.Id() { + found = true + d.Set("ipv4_cidr_block", cidrAssociation.CidrBlock) + } + } + if !found { + log.Printf("[WARN] VPC secondary IPv4 CIDR block (%s) not found, removing from state", d.Id()) + d.SetId("") + } + + return nil +} + +func resourceAwsVpcSecondaryIpv4CidrBlockDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + + log.Printf("[DEBUG] Deleting VPC secondary IPv4 CIDR block: %s", d.Id()) + _, err := conn.DisassociateVpcCidrBlock(&ec2.DisassociateVpcCidrBlockInput{ + AssociationId: aws.String(d.Id()), + }) + if err != nil { + return fmt.Errorf("Error deleting VPC secondary IPv4 CIDR block: %s", err.Error()) + } + + return nil +} diff --git a/aws/aws_vpc_secondary_ipv4_cidr_block_test.go b/aws/aws_vpc_secondary_ipv4_cidr_block_test.go new file mode 100644 index 00000000000..c603368e356 --- /dev/null +++ b/aws/aws_vpc_secondary_ipv4_cidr_block_test.go @@ -0,0 +1,138 @@ +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/ec2" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAwsVpcSecondaryIpv4CidrBlock_basic(t *testing.T) { + var associationSecondary, associationTertiary ec2.VpcCidrBlockAssociation + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsVpcSecondaryIpv4CidrBlockDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsVpcSecondaryIpv4CidrBlockConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsVpcSecondaryIpv4CidrBlockExists("aws_vpc_secondary_ipv4_cidr_block.secondary_cidr", &associationSecondary), + testAccCheckAwsVpcSecondaryIpv4CidrBlock(&associationSecondary, "172.2.0.0/16"), + testAccCheckAwsVpcSecondaryIpv4CidrBlockExists("aws_vpc_secondary_ipv4_cidr_block.tertiary_cidr", &associationTertiary), + testAccCheckAwsVpcSecondaryIpv4CidrBlock(&associationTertiary, "170.2.0.0/16"), + ), + }, + }, + }) +} + +func testAccCheckAwsVpcSecondaryIpv4CidrBlock(association *ec2.VpcCidrBlockAssociation, expected string) resource.TestCheckFunc { + return func(s *terraform.State) error { + CIDRBlock := association.CidrBlock + if *CIDRBlock != expected { + return fmt.Errorf("Bad CIDR: %s", *association.CidrBlock) + } + + return nil + } +} + +func testAccCheckAwsVpcSecondaryIpv4CidrBlockDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).ec2conn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_vpc_secondary_ipv4_cidr_block" { + continue + } + + // Try to find the VPC + DescribeVpcOpts := &ec2.DescribeVpcsInput{ + VpcIds: []*string{aws.String(rs.Primary.Attributes["vpc_id"])}, + } + resp, err := conn.DescribeVpcs(DescribeVpcOpts) + if err == nil { + vpc := resp.Vpcs[0] + + for _, ipv4Association := range vpc.CidrBlockAssociationSet { + if *ipv4Association.AssociationId == rs.Primary.ID { + return fmt.Errorf("VPC secondary CIDR block still exists") + } + } + + return nil + } + + // Verify the error is what we want + ec2err, ok := err.(awserr.Error) + if !ok { + return err + } + if ec2err.Code() != "InvalidVpcID.NotFound" { + return err + } + } + + return nil +} + +func testAccCheckAwsVpcSecondaryIpv4CidrBlockExists(n string, association *ec2.VpcCidrBlockAssociation) 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 VPC ID is set") + } + + conn := testAccProvider.Meta().(*AWSClient).ec2conn + DescribeVpcOpts := &ec2.DescribeVpcsInput{ + VpcIds: []*string{aws.String(rs.Primary.Attributes["vpc_id"])}, + } + resp, err := conn.DescribeVpcs(DescribeVpcOpts) + if err != nil { + return err + } + if len(resp.Vpcs) == 0 { + return fmt.Errorf("VPC not found") + } + + vpc := resp.Vpcs[0] + found := false + for _, cidrAssociation := range vpc.CidrBlockAssociationSet { + if *cidrAssociation.AssociationId == rs.Primary.ID { + *association = *cidrAssociation + found = true + } + } + + if !found { + return fmt.Errorf("VPC secondary CIDR block not found") + } + + return nil + } +} + +const testAccAwsVpcSecondaryIpv4CidrBlockConfig = ` +resource "aws_vpc" "foo" { + cidr_block = "10.1.0.0/16" +} + +resource "aws_vpc_secondary_ipv4_cidr_block" "secondary_cidr" { + vpc_id = "${aws_vpc.foo.id}" + ipv4_cidr_block = "172.2.0.0/16" +} + +resource "aws_vpc_secondary_ipv4_cidr_block" "tertiary_cidr" { + vpc_id = "${aws_vpc.foo.id}" + ipv4_cidr_block = "170.2.0.0/16" +} +` diff --git a/aws/provider.go b/aws/provider.go index 6a513c7249f..a5c7e9e1b4e 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -609,6 +609,7 @@ func Provider() terraform.ResourceProvider { "aws_vpc_endpoint_subnet_association": resourceAwsVpcEndpointSubnetAssociation(), "aws_vpc_endpoint_service": resourceAwsVpcEndpointService(), "aws_vpc_endpoint_service_allowed_principal": resourceAwsVpcEndpointServiceAllowedPrincipal(), + "aws_vpc_ipv4_cidr_block_association": resourceAwsVpcIpv4CidrBlockAssociation(), "aws_vpn_connection": resourceAwsVpnConnection(), "aws_vpn_connection_route": resourceAwsVpnConnectionRoute(), "aws_vpn_gateway": resourceAwsVpnGateway(), diff --git a/aws/resource_aws_vpc_associate_cidr_block.go b/aws/resource_aws_vpc_associate_cidr_block.go deleted file mode 100644 index 9c19a90f2e5..00000000000 --- a/aws/resource_aws_vpc_associate_cidr_block.go +++ /dev/null @@ -1,128 +0,0 @@ -package aws - -import ( - "log" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/hashicorp/terraform/helper/schema" -) - -func resourceAwsVpcAssociateCidrBlock() *schema.Resource { - return &schema.Resource{ - Create: resourceAwsVpcAssociateCidrBlockCreate, - Read: resourceAwsVpcAssociateCidrBlockRead, - Delete: resourceAwsVpcAssociateCidrBlockDelete, - - Schema: map[string]*schema.Schema{ - - "vpc_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - - "ipv4_cidr_block": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - ValidateFunc: validateCIDRNetworkAddress, - }, - - "assign_generated_ipv6_cidr_block": { - Type: schema.TypeBool, - Optional: true, - Default: false, - ForceNew: true, - ConflictsWith: []string{"ipv4_cidr_block"}, - }, - - "ipv6_cidr_block": { - Type: schema.TypeString, - Computed: true, - }, - }, - } -} - -func resourceAwsVpcAssociateCidrBlockCreate(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*AWSClient).ec2conn - - params := &ec2.AssociateVpcCidrBlockInput{ - VpcId: aws.String(d.Get("vpc_id").(string)), - AmazonProvidedIpv6CidrBlock: aws.Bool(d.Get("assign_generated_ipv6_cidr_block").(bool)), - } - - if v, ok := d.GetOk("ipv4_cidr_block"); ok { - params.CidrBlock = aws.String(v.(string)) - } - - resp, err := conn.AssociateVpcCidrBlock(params) - if err != nil { - return err - } - - if d.Get("assign_generated_ipv6_cidr_block").(bool) == true { - d.SetId(*resp.Ipv6CidrBlockAssociation.AssociationId) - } else { - d.SetId(*resp.CidrBlockAssociation.AssociationId) - } - - return resourceAwsVpcAssociateCidrBlockRead(d, meta) -} - -func resourceAwsVpcAssociateCidrBlockRead(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*AWSClient).ec2conn - - vpcRaw, _, err := VPCStateRefreshFunc(conn, d.Get("vpc_id").(string))() - if err != nil { - return err - } - - if vpcRaw == nil { - log.Printf("[INFO] No VPC Found for id %q", d.Get("vpc_id").(string)) - d.SetId("") - return nil - } - - vpc := vpcRaw.(*ec2.Vpc) - found := false - - if d.Get("assign_generated_ipv6_cidr_block").(bool) == true { - for _, ipv6Association := range vpc.Ipv6CidrBlockAssociationSet { - if *ipv6Association.AssociationId == d.Id() { - found = true - d.Set("ipv6_cidr_block", ipv6Association.Ipv6CidrBlock) - break - } - } - } else { - for _, cidrAssociation := range vpc.CidrBlockAssociationSet { - if *cidrAssociation.AssociationId == d.Id() { - found = true - d.Set("ipv4_cidr_block", cidrAssociation.CidrBlock) - } - } - } - - if !found { - log.Printf("[INFO] No VPC CIDR Association found for ID: %q", d.Id()) - } - - return nil -} - -func resourceAwsVpcAssociateCidrBlockDelete(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*AWSClient).ec2conn - - params := &ec2.DisassociateVpcCidrBlockInput{ - AssociationId: aws.String(d.Id()), - } - - _, err := conn.DisassociateVpcCidrBlock(params) - if err != nil { - return err - } - - return nil -} diff --git a/aws/resource_aws_vpc_associate_cidr_block_test.go b/aws/resource_aws_vpc_associate_cidr_block_test.go deleted file mode 100644 index f8a00942520..00000000000 --- a/aws/resource_aws_vpc_associate_cidr_block_test.go +++ /dev/null @@ -1,242 +0,0 @@ -package aws - -import ( - "fmt" - "testing" - - "regexp" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/hashicorp/terraform/helper/resource" - "github.com/hashicorp/terraform/terraform" -) - -func TestAccAWSVpcAssociateIpv4CidrBlock(t *testing.T) { - var association ec2.VpcCidrBlockAssociation - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckVpcAssociateCidrBlockDestroy, - Steps: []resource.TestStep{ - { - Config: testAccVpcIpv4CidrAssociationConfig, - Check: resource.ComposeTestCheckFunc( - testAccCheckVpcIpv4CidrAssociationExists("aws_vpc_associate_cidr_block.secondary_cidr", &association), - testAccCheckVpcIpv4AssociationCidr(&association, "172.2.0.0/16"), - ), - }, - }, - }) -} - -func TestAccAWSVpcAssociateIpv6CidrBlock(t *testing.T) { - var association ec2.VpcIpv6CidrBlockAssociation - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckVpcAssociateCidrBlockDestroy, - Steps: []resource.TestStep{ - { - Config: testAccVpcIpv6CidrAssociationConfig, - Check: resource.ComposeTestCheckFunc( - testAccCheckVpcIpv6CidrAssociationExists("aws_vpc_associate_cidr_block.secondary_ipv6_cidr", &association), - resource.TestCheckResourceAttrSet("aws_vpc_associate_cidr_block.secondary_ipv6_cidr", "ipv6_cidr_block"), - ), - }, - }, - }) -} - -func TestAccAWSVpcAssociateIpv4AndIpv6CidrBlock(t *testing.T) { - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckVpcAssociateCidrBlockDestroy, - Steps: []resource.TestStep{ - { - Config: testAccVpcIpv4AndIpv6CidrAssociationConfig, - ExpectError: regexp.MustCompile(`: conflicts with`), - }, - }, - }) -} - -func testAccCheckVpcIpv4AssociationCidr(association *ec2.VpcCidrBlockAssociation, expected string) resource.TestCheckFunc { - return func(s *terraform.State) error { - CIDRBlock := association.CidrBlock - if *CIDRBlock != expected { - return fmt.Errorf("Bad cidr: %s", *association.CidrBlock) - } - - return nil - } -} - -func testAccCheckVpcAssociateCidrBlockDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*AWSClient).ec2conn - - for _, rs := range s.RootModule().Resources { - if rs.Type != "aws_vpc_associate_cidr_block" { - continue - } - - // Try to find the VPC - DescribeVpcOpts := &ec2.DescribeVpcsInput{ - VpcIds: []*string{aws.String(rs.Primary.Attributes["vpc_id"])}, - } - resp, err := conn.DescribeVpcs(DescribeVpcOpts) - if err == nil { - vpc := resp.Vpcs[0] - - for _, ipv4Association := range vpc.CidrBlockAssociationSet { - if *ipv4Association.AssociationId == rs.Primary.ID { - return fmt.Errorf("VPC CIDR Association still exists.") - } - } - - for _, ipv6Association := range vpc.Ipv6CidrBlockAssociationSet { - if *ipv6Association.AssociationId == rs.Primary.ID { - return fmt.Errorf("VPC CIDR Association still exists.") - } - } - - return nil - } - - // Verify the error is what we want - ec2err, ok := err.(awserr.Error) - if !ok { - return err - } - if ec2err.Code() != "InvalidVpcID.NotFound" { - return err - } - } - - return nil -} - -func testAccCheckVpcIpv4CidrAssociationExists(n string, association *ec2.VpcCidrBlockAssociation) 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 VPC ID is set") - } - - conn := testAccProvider.Meta().(*AWSClient).ec2conn - DescribeVpcOpts := &ec2.DescribeVpcsInput{ - VpcIds: []*string{aws.String(rs.Primary.Attributes["vpc_id"])}, - } - resp, err := conn.DescribeVpcs(DescribeVpcOpts) - if err != nil { - return err - } - if len(resp.Vpcs) == 0 { - return fmt.Errorf("VPC not found") - } - - vpc := resp.Vpcs[0] - found := false - for _, cidrAssociation := range vpc.CidrBlockAssociationSet { - if *cidrAssociation.AssociationId == rs.Primary.ID { - *association = *cidrAssociation - found = true - } - } - - if !found { - return fmt.Errorf("VPC CIDR Association not found") - } - - return nil - } -} - -func testAccCheckVpcIpv6CidrAssociationExists(n string, association *ec2.VpcIpv6CidrBlockAssociation) 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 VPC ID is set") - } - - conn := testAccProvider.Meta().(*AWSClient).ec2conn - DescribeVpcOpts := &ec2.DescribeVpcsInput{ - VpcIds: []*string{aws.String(rs.Primary.Attributes["vpc_id"])}, - } - resp, err := conn.DescribeVpcs(DescribeVpcOpts) - if err != nil { - return err - } - if len(resp.Vpcs) == 0 { - return fmt.Errorf("VPC not found") - } - - vpc := resp.Vpcs[0] - found := false - for _, cidrAssociation := range vpc.Ipv6CidrBlockAssociationSet { - if *cidrAssociation.AssociationId == rs.Primary.ID { - *association = *cidrAssociation - found = true - } - } - - if !found { - return fmt.Errorf("VPC CIDR Association not found") - } - - return nil - } -} - -const testAccVpcIpv4CidrAssociationConfig = ` -resource "aws_vpc" "foo" { - cidr_block = "10.1.0.0/16" -} - -resource "aws_vpc_associate_cidr_block" "secondary_cidr" { - vpc_id = "${aws_vpc.foo.id}" - ipv4_cidr_block = "172.2.0.0/16" -} - -resource "aws_vpc_associate_cidr_block" "tertiary_cidr" { - vpc_id = "${aws_vpc.foo.id}" - ipv4_cidr_block = "170.2.0.0/16" -} -` - -const testAccVpcIpv6CidrAssociationConfig = ` -resource "aws_vpc" "foo" { - cidr_block = "10.1.0.0/16" -} - -resource "aws_vpc_associate_cidr_block" "secondary_ipv6_cidr" { - vpc_id = "${aws_vpc.foo.id}" - assign_generated_ipv6_cidr_block = true -} -` - -const testAccVpcIpv4AndIpv6CidrAssociationConfig = ` -resource "aws_vpc" "foo" { - cidr_block = "10.1.0.0/16" - assign_generated_ipv6_cidr_block = true -} - -resource "aws_vpc_associate_cidr_block" "secondary_ipv6_cidr" { - vpc_id = "${aws_vpc.foo.id}" - ipv4_cidr_block = "172.2.0.0/16" - assign_generated_ipv6_cidr_block = true -} -` diff --git a/website/aws.erb b/website/aws.erb index 7c2e6635788..e65e202886a 100644 --- a/website/aws.erb +++ b/website/aws.erb @@ -2198,10 +2198,6 @@ aws_vpc - > - aws_vpc_associate_cidr_block - - > aws_vpc_dhcp_options @@ -2246,6 +2242,10 @@ aws_vpc_peering_connection_options + > + aws_vpc_secondary_ipv4_cidr_block + + > aws_vpn_connection diff --git a/website/docs/r/vpc_associate_cidr_block.html.markdown b/website/docs/r/vpc_associate_cidr_block.html.markdown deleted file mode 100644 index 276f5beec6d..00000000000 --- a/website/docs/r/vpc_associate_cidr_block.html.markdown +++ /dev/null @@ -1,58 +0,0 @@ ---- -layout: "aws" -page_title: "AWS: aws_vpc_associate_cidr_block" -sidebar_current: "docs-aws-resource-vpc-associate-cidr-block" -description: |- - Associates a CIDR block to a VPC ---- - -# aws_vpc_associate_cidr_block - -Associates a CIDR block to a VPC - -## Example Usage - -IPv4 CIDR Block Association: - -```hcl -resource "aws_vpc" "main" { - cidr_block = "10.0.0.0/16" -} - -resource "aws_vpc_associate_cidr_block" "secondary_cidr" { - vpc_id = "${aws_vpc.main.id}" - ipv4_cidr_block = "172.2.0.0/16" -} -``` - -IPv6 CIDR Block Association: - -```hcl -resource "aws_vpc" "main" { - cidr_block = "10.0.0.0/16" - assign_generated_ipv6_cidr_block = true -} - -resource "aws_vpc_associate_cidr_block" "secondary_ipv6_cidr" { - vpc_id = "${aws_vpc.main.id}" - assign_generated_ipv6_cidr_block = true -} -``` - -## Argument Reference - -The following arguments are supported: - -* `vpc_id` - (Required) The ID of the VPC to make the association with -* `cidr_block` - (Optional) The IPv4 CIDR block to associate to the VPC. -* `assign_generated_ipv6_cidr_block` - (Optional) Requests an Amazon-provided IPv6 CIDR -block with a /56 prefix length for the VPC. You cannot specify the range of IP addresses, or -the size of the CIDR block. Default is `false`. - -## Attributes Reference - -The following attributes are exported: - -* `id` - The ID of the VPC CIDR Association -* `ipv6_cidr_block` - The IPv6 CIDR block. - diff --git a/website/docs/r/vpc_secondary_ipv4_cidr_block.html.markdown b/website/docs/r/vpc_secondary_ipv4_cidr_block.html.markdown new file mode 100644 index 00000000000..b8d8dfd8e46 --- /dev/null +++ b/website/docs/r/vpc_secondary_ipv4_cidr_block.html.markdown @@ -0,0 +1,37 @@ +--- +layout: "aws" +page_title: "AWS: aws_vpc_secondary_ipv4_cidr_block" +sidebar_current: "docs-aws-resource-vpc-secondary-ipv4-cidr-block" +description: |- + Associate a secondary IPv4 CIDR blocks with a VPC +--- + +# aws_vpc_secondary_ipv4_cidr_block + +Provides a resource to associate a secondary IPv4 CIDR blocks with a VPC. + +## Example Usage + +```hcl +resource "aws_vpc" "main" { + cidr_block = "10.0.0.0/16" +} + +resource "aws_vpc_secondary_ipv4_cidr_block" "secondary_cidr" { + vpc_id = "${aws_vpc.main.id}" + ipv4_cidr_block = "172.2.0.0/16" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `vpc_id` - (Required) The ID of the VPC to make the association with. +* `ipv4_cidr_block` - (Required) The secondary IPv4 CIDR block to associate with the VPC. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the VPC CIDR association From ecad19d21bc83a51acb0e41dc2e7080c1aa47196 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 9 Mar 2018 16:38:27 -0500 Subject: [PATCH 40/88] 'ipv4_cidr_block' -> 'cidr_block'. --- aws/aws_vpc_secondary_ipv4_cidr_block.go | 6 +++--- aws/aws_vpc_secondary_ipv4_cidr_block_test.go | 4 ++-- website/docs/r/vpc_secondary_ipv4_cidr_block.html.markdown | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/aws/aws_vpc_secondary_ipv4_cidr_block.go b/aws/aws_vpc_secondary_ipv4_cidr_block.go index ca6f81ae714..49ad422bbb4 100644 --- a/aws/aws_vpc_secondary_ipv4_cidr_block.go +++ b/aws/aws_vpc_secondary_ipv4_cidr_block.go @@ -23,7 +23,7 @@ func resourceAwsVpcSecondaryIpv4CidrBlock() *schema.Resource { ForceNew: true, }, - "ipv4_cidr_block": { + "cidr_block": { Type: schema.TypeString, Required: true, ForceNew: true, @@ -38,7 +38,7 @@ func resourceAwsVpcSecondaryIpv4CidrBlockCreate(d *schema.ResourceData, meta int req := &ec2.AssociateVpcCidrBlockInput{ VpcId: aws.String(d.Get("vpc_id").(string)), - CidrBlock: aws.String(d.Get("ipv4_cidr_block").(string)), + CidrBlock: aws.String(d.Get("cidr_block").(string)), } log.Printf("[DEBUG] Creating VPC secondary IPv4 CIDR block: %#v", req) resp, err := conn.AssociateVpcCidrBlock(req) @@ -70,7 +70,7 @@ func resourceAwsVpcSecondaryIpv4CidrBlockRead(d *schema.ResourceData, meta inter for _, cidrAssociation := range vpc.CidrBlockAssociationSet { if aws.StringValue(cidrAssociation.AssociationId) == d.Id() { found = true - d.Set("ipv4_cidr_block", cidrAssociation.CidrBlock) + d.Set("cidr_block", cidrAssociation.CidrBlock) } } if !found { diff --git a/aws/aws_vpc_secondary_ipv4_cidr_block_test.go b/aws/aws_vpc_secondary_ipv4_cidr_block_test.go index c603368e356..42582d29234 100644 --- a/aws/aws_vpc_secondary_ipv4_cidr_block_test.go +++ b/aws/aws_vpc_secondary_ipv4_cidr_block_test.go @@ -128,11 +128,11 @@ resource "aws_vpc" "foo" { resource "aws_vpc_secondary_ipv4_cidr_block" "secondary_cidr" { vpc_id = "${aws_vpc.foo.id}" - ipv4_cidr_block = "172.2.0.0/16" + cidr_block = "172.2.0.0/16" } resource "aws_vpc_secondary_ipv4_cidr_block" "tertiary_cidr" { vpc_id = "${aws_vpc.foo.id}" - ipv4_cidr_block = "170.2.0.0/16" + cidr_block = "170.2.0.0/16" } ` diff --git a/website/docs/r/vpc_secondary_ipv4_cidr_block.html.markdown b/website/docs/r/vpc_secondary_ipv4_cidr_block.html.markdown index b8d8dfd8e46..74c4b1d5561 100644 --- a/website/docs/r/vpc_secondary_ipv4_cidr_block.html.markdown +++ b/website/docs/r/vpc_secondary_ipv4_cidr_block.html.markdown @@ -19,7 +19,7 @@ resource "aws_vpc" "main" { resource "aws_vpc_secondary_ipv4_cidr_block" "secondary_cidr" { vpc_id = "${aws_vpc.main.id}" - ipv4_cidr_block = "172.2.0.0/16" + cidr_block = "172.2.0.0/16" } ``` @@ -28,7 +28,7 @@ resource "aws_vpc_secondary_ipv4_cidr_block" "secondary_cidr" { The following arguments are supported: * `vpc_id` - (Required) The ID of the VPC to make the association with. -* `ipv4_cidr_block` - (Required) The secondary IPv4 CIDR block to associate with the VPC. +* `cidr_block` - (Required) The secondary IPv4 CIDR block to associate with the VPC. ## Attributes Reference From 043935747383a40418239e4489f3de868801bb37 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 29 Jun 2018 10:44:58 -0400 Subject: [PATCH 41/88] 'aws_vpc_secondary_ipv4_cidr_block' -> 'aws_vpc_ipv4_cidr_block_association'. --- ...=> aws_vpc_ipv4_cidr_block_association.go} | 30 +++++++------- ...s_vpc_ipv4_cidr_block_association_test.go} | 35 ++++++++-------- website/aws.erb | 8 ++-- ..._ipv4_cidr_block_association.html.markdown | 40 +++++++++++++++++++ ...pc_secondary_ipv4_cidr_block.html.markdown | 37 ----------------- 5 files changed, 78 insertions(+), 72 deletions(-) rename aws/{aws_vpc_secondary_ipv4_cidr_block.go => aws_vpc_ipv4_cidr_block_association.go} (59%) rename aws/{aws_vpc_secondary_ipv4_cidr_block_test.go => aws_vpc_ipv4_cidr_block_association_test.go} (63%) create mode 100644 website/docs/r/vpc_ipv4_cidr_block_association.html.markdown delete mode 100644 website/docs/r/vpc_secondary_ipv4_cidr_block.html.markdown diff --git a/aws/aws_vpc_secondary_ipv4_cidr_block.go b/aws/aws_vpc_ipv4_cidr_block_association.go similarity index 59% rename from aws/aws_vpc_secondary_ipv4_cidr_block.go rename to aws/aws_vpc_ipv4_cidr_block_association.go index 49ad422bbb4..a07a4f731ca 100644 --- a/aws/aws_vpc_secondary_ipv4_cidr_block.go +++ b/aws/aws_vpc_ipv4_cidr_block_association.go @@ -10,11 +10,11 @@ import ( "github.com/hashicorp/terraform/helper/validation" ) -func resourceAwsVpcSecondaryIpv4CidrBlock() *schema.Resource { +func resourceAwsVpcIpv4CidrBlockAssociation() *schema.Resource { return &schema.Resource{ - Create: resourceAwsVpcSecondaryIpv4CidrBlockCreate, - Read: resourceAwsVpcSecondaryIpv4CidrBlockRead, - Delete: resourceAwsVpcSecondaryIpv4CidrBlockDelete, + Create: resourceAwsVpcIpv4CidrBlockAssociationCreate, + Read: resourceAwsVpcIpv4CidrBlockAssociationRead, + Delete: resourceAwsVpcIpv4CidrBlockAssociationDelete, Schema: map[string]*schema.Schema{ "vpc_id": { @@ -33,34 +33,34 @@ func resourceAwsVpcSecondaryIpv4CidrBlock() *schema.Resource { } } -func resourceAwsVpcSecondaryIpv4CidrBlockCreate(d *schema.ResourceData, meta interface{}) error { +func resourceAwsVpcIpv4CidrBlockAssociationCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).ec2conn req := &ec2.AssociateVpcCidrBlockInput{ VpcId: aws.String(d.Get("vpc_id").(string)), CidrBlock: aws.String(d.Get("cidr_block").(string)), } - log.Printf("[DEBUG] Creating VPC secondary IPv4 CIDR block: %#v", req) + log.Printf("[DEBUG] Creating VPC IPv4 CIDR block association: %#v", req) resp, err := conn.AssociateVpcCidrBlock(req) if err != nil { - return fmt.Errorf("Error creating VPC secondary IPv4 CIDR block: %s", err.Error()) + return fmt.Errorf("Error creating VPC IPv4 CIDR block association: %s", err) } d.SetId(aws.StringValue(resp.CidrBlockAssociation.AssociationId)) - return resourceAwsVpcSecondaryIpv4CidrBlockRead(d, meta) + return resourceAwsVpcIpv4CidrBlockAssociationRead(d, meta) } -func resourceAwsVpcSecondaryIpv4CidrBlockRead(d *schema.ResourceData, meta interface{}) error { +func resourceAwsVpcIpv4CidrBlockAssociationRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).ec2conn vpcId := d.Get("vpc_id").(string) vpcRaw, _, err := VPCStateRefreshFunc(conn, vpcId)() if err != nil { - return fmt.Errorf("Error reading VPC: %s", err.Error()) + return fmt.Errorf("Error reading VPC: %s", err) } if vpcRaw == nil { - log.Printf("[WARN] VPC (%s) not found, removing secondary IPv4 CIDR block from state", vpcId) + log.Printf("[WARN] VPC (%s) not found, removing IPv4 CIDR block association from state", vpcId) d.SetId("") return nil } @@ -74,22 +74,22 @@ func resourceAwsVpcSecondaryIpv4CidrBlockRead(d *schema.ResourceData, meta inter } } if !found { - log.Printf("[WARN] VPC secondary IPv4 CIDR block (%s) not found, removing from state", d.Id()) + log.Printf("[WARN] VPC IPv4 CIDR block association (%s) not found, removing from state", d.Id()) d.SetId("") } return nil } -func resourceAwsVpcSecondaryIpv4CidrBlockDelete(d *schema.ResourceData, meta interface{}) error { +func resourceAwsVpcIpv4CidrBlockAssociationDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).ec2conn - log.Printf("[DEBUG] Deleting VPC secondary IPv4 CIDR block: %s", d.Id()) + log.Printf("[DEBUG] Deleting VPC IPv4 CIDR block association: %s", d.Id()) _, err := conn.DisassociateVpcCidrBlock(&ec2.DisassociateVpcCidrBlockInput{ AssociationId: aws.String(d.Id()), }) if err != nil { - return fmt.Errorf("Error deleting VPC secondary IPv4 CIDR block: %s", err.Error()) + return fmt.Errorf("Error deleting VPC IPv4 CIDR block association: %s", err) } return nil diff --git a/aws/aws_vpc_secondary_ipv4_cidr_block_test.go b/aws/aws_vpc_ipv4_cidr_block_association_test.go similarity index 63% rename from aws/aws_vpc_secondary_ipv4_cidr_block_test.go rename to aws/aws_vpc_ipv4_cidr_block_association_test.go index 42582d29234..7a60173a612 100644 --- a/aws/aws_vpc_secondary_ipv4_cidr_block_test.go +++ b/aws/aws_vpc_ipv4_cidr_block_association_test.go @@ -11,28 +11,28 @@ import ( "github.com/hashicorp/terraform/terraform" ) -func TestAccAwsVpcSecondaryIpv4CidrBlock_basic(t *testing.T) { +func TestAccAwsVpcIpv4CidrBlockAssociation_basic(t *testing.T) { var associationSecondary, associationTertiary ec2.VpcCidrBlockAssociation resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, - CheckDestroy: testAccCheckAwsVpcSecondaryIpv4CidrBlockDestroy, + CheckDestroy: testAccCheckAwsVpcIpv4CidrBlockAssociationDestroy, Steps: []resource.TestStep{ { - Config: testAccAwsVpcSecondaryIpv4CidrBlockConfig, + Config: testAccAwsVpcIpv4CidrBlockAssociationConfig, Check: resource.ComposeTestCheckFunc( - testAccCheckAwsVpcSecondaryIpv4CidrBlockExists("aws_vpc_secondary_ipv4_cidr_block.secondary_cidr", &associationSecondary), - testAccCheckAwsVpcSecondaryIpv4CidrBlock(&associationSecondary, "172.2.0.0/16"), - testAccCheckAwsVpcSecondaryIpv4CidrBlockExists("aws_vpc_secondary_ipv4_cidr_block.tertiary_cidr", &associationTertiary), - testAccCheckAwsVpcSecondaryIpv4CidrBlock(&associationTertiary, "170.2.0.0/16"), + testAccCheckAwsVpcIpv4CidrBlockAssociationExists("aws_vpc_ipv4_cidr_block_association.secondary_cidr", &associationSecondary), + testAccCheckAdditionalAwsVpcIpv4CidrBlock(&associationSecondary, "172.2.0.0/16"), + testAccCheckAwsVpcIpv4CidrBlockAssociationExists("aws_vpc_ipv4_cidr_block_association.tertiary_cidr", &associationTertiary), + testAccCheckAdditionalAwsVpcIpv4CidrBlock(&associationTertiary, "170.2.0.0/16"), ), }, }, }) } -func testAccCheckAwsVpcSecondaryIpv4CidrBlock(association *ec2.VpcCidrBlockAssociation, expected string) resource.TestCheckFunc { +func testAccCheckAdditionalAwsVpcIpv4CidrBlock(association *ec2.VpcCidrBlockAssociation, expected string) resource.TestCheckFunc { return func(s *terraform.State) error { CIDRBlock := association.CidrBlock if *CIDRBlock != expected { @@ -43,11 +43,11 @@ func testAccCheckAwsVpcSecondaryIpv4CidrBlock(association *ec2.VpcCidrBlockAssoc } } -func testAccCheckAwsVpcSecondaryIpv4CidrBlockDestroy(s *terraform.State) error { +func testAccCheckAwsVpcIpv4CidrBlockAssociationDestroy(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).ec2conn for _, rs := range s.RootModule().Resources { - if rs.Type != "aws_vpc_secondary_ipv4_cidr_block" { + if rs.Type != "aws_vpc_ipv4_cidr_block_association" { continue } @@ -61,7 +61,7 @@ func testAccCheckAwsVpcSecondaryIpv4CidrBlockDestroy(s *terraform.State) error { for _, ipv4Association := range vpc.CidrBlockAssociationSet { if *ipv4Association.AssociationId == rs.Primary.ID { - return fmt.Errorf("VPC secondary CIDR block still exists") + return fmt.Errorf("VPC CIDR block association still exists") } } @@ -81,7 +81,7 @@ func testAccCheckAwsVpcSecondaryIpv4CidrBlockDestroy(s *terraform.State) error { return nil } -func testAccCheckAwsVpcSecondaryIpv4CidrBlockExists(n string, association *ec2.VpcCidrBlockAssociation) resource.TestCheckFunc { +func testAccCheckAwsVpcIpv4CidrBlockAssociationExists(n string, association *ec2.VpcCidrBlockAssociation) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -114,24 +114,27 @@ func testAccCheckAwsVpcSecondaryIpv4CidrBlockExists(n string, association *ec2.V } if !found { - return fmt.Errorf("VPC secondary CIDR block not found") + return fmt.Errorf("VPC CIDR block association not found") } return nil } } -const testAccAwsVpcSecondaryIpv4CidrBlockConfig = ` +const testAccAwsVpcIpv4CidrBlockAssociationConfig = ` resource "aws_vpc" "foo" { cidr_block = "10.1.0.0/16" + tags { + Name = "terraform-testacc-vpc-ipv4-cidr-block-association" + } } -resource "aws_vpc_secondary_ipv4_cidr_block" "secondary_cidr" { +resource "aws_vpc_ipv4_cidr_block_association" "secondary_cidr" { vpc_id = "${aws_vpc.foo.id}" cidr_block = "172.2.0.0/16" } -resource "aws_vpc_secondary_ipv4_cidr_block" "tertiary_cidr" { +resource "aws_vpc_ipv4_cidr_block_association" "tertiary_cidr" { vpc_id = "${aws_vpc.foo.id}" cidr_block = "170.2.0.0/16" } diff --git a/website/aws.erb b/website/aws.erb index e65e202886a..f4d36921980 100644 --- a/website/aws.erb +++ b/website/aws.erb @@ -2230,6 +2230,10 @@ aws_vpc_endpoint_subnet_association + > + aws_vpc_ipv4_cidr_block_association + + > aws_vpc_peering_connection @@ -2242,10 +2246,6 @@ aws_vpc_peering_connection_options - > - aws_vpc_secondary_ipv4_cidr_block - - > aws_vpn_connection diff --git a/website/docs/r/vpc_ipv4_cidr_block_association.html.markdown b/website/docs/r/vpc_ipv4_cidr_block_association.html.markdown new file mode 100644 index 00000000000..14a82e76fbb --- /dev/null +++ b/website/docs/r/vpc_ipv4_cidr_block_association.html.markdown @@ -0,0 +1,40 @@ +--- +layout: "aws" +page_title: "AWS: aws_vpc_ipv4_cidr_block_association" +sidebar_current: "docs-aws-resource-vpc-ipv4-cidr-block-association" +description: |- + Associate additional IPv4 CIDR blocks with a VPC +--- + +# aws_vpc_ipv4_cidr_block_association + +Provides a resource to associate additional IPv4 CIDR blocks with a VPC. + +When a VPC is created, a primary IPv4 CIDR block for the VPC must be specified. +The `aws_vpc_ipv4_cidr_block_association` resource allows further IPv4 CIDR blocks to be added to the VPC. + +## Example Usage + +```hcl +resource "aws_vpc" "main" { + cidr_block = "10.0.0.0/16" +} + +resource "aws_vpc_ipv4_cidr_block_association" "secondary_cidr" { + vpc_id = "${aws_vpc.main.id}" + cidr_block = "172.2.0.0/16" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `cidr_block` - (Required) The additional IPv4 CIDR block to associate with the VPC. +* `vpc_id` - (Required) The ID of the VPC to make the association with. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the VPC CIDR association diff --git a/website/docs/r/vpc_secondary_ipv4_cidr_block.html.markdown b/website/docs/r/vpc_secondary_ipv4_cidr_block.html.markdown deleted file mode 100644 index 74c4b1d5561..00000000000 --- a/website/docs/r/vpc_secondary_ipv4_cidr_block.html.markdown +++ /dev/null @@ -1,37 +0,0 @@ ---- -layout: "aws" -page_title: "AWS: aws_vpc_secondary_ipv4_cidr_block" -sidebar_current: "docs-aws-resource-vpc-secondary-ipv4-cidr-block" -description: |- - Associate a secondary IPv4 CIDR blocks with a VPC ---- - -# aws_vpc_secondary_ipv4_cidr_block - -Provides a resource to associate a secondary IPv4 CIDR blocks with a VPC. - -## Example Usage - -```hcl -resource "aws_vpc" "main" { - cidr_block = "10.0.0.0/16" -} - -resource "aws_vpc_secondary_ipv4_cidr_block" "secondary_cidr" { - vpc_id = "${aws_vpc.main.id}" - cidr_block = "172.2.0.0/16" -} -``` - -## Argument Reference - -The following arguments are supported: - -* `vpc_id` - (Required) The ID of the VPC to make the association with. -* `cidr_block` - (Required) The secondary IPv4 CIDR block to associate with the VPC. - -## Attributes Reference - -The following attributes are exported: - -* `id` - The ID of the VPC CIDR association From 884792a43a87e7096b882ac58c7e7a1b329e23b6 Mon Sep 17 00:00:00 2001 From: Fred Dubois Date: Fri, 29 Jun 2018 11:04:37 -0400 Subject: [PATCH 42/88] Add documentation for new aws_route_tables filter arg --- website/docs/d/route_tables.html.markdown | 27 ++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/website/docs/d/route_tables.html.markdown b/website/docs/d/route_tables.html.markdown index 7456017b91c..f33dd113f85 100644 --- a/website/docs/d/route_tables.html.markdown +++ b/website/docs/d/route_tables.html.markdown @@ -12,19 +12,25 @@ This resource can be useful for getting back a list of route table ids to be ref ## Example Usage -The following adds a route for a particular cidr block to every route table -in a specified vpc to use a particular vpc peering connection. +The following adds a route for a particular cidr block to every (private +kops) route table in a specified vpc to use a particular vpc peering +connection. ```hcl data "aws_route_tables" "rts" { vpc_id = "${var.vpc_id}" + + filter { + name = "tag:kubernetes.io/kops/role" + values = ["private*"] + } } resource "aws_route" "r" { - count = "${length(data.aws_route_tables.rts.ids)}" - route_table_id = "${data.aws_route_tables.rts.ids[count.index]}" - destination_cidr_block = "10.0.1.0/22" + count = "${length(data.aws_route_tables.rts.ids)}" + route_table_id = "${data.aws_route_tables.rts.ids[count.index]}" + destination_cidr_block = "10.0.1.0/22" vpc_peering_connection_id = "pcx-0e9a7a9ecd137dc54" } @@ -32,11 +38,22 @@ resource "aws_route" "r" { ## Argument Reference +* `filter` - (Optional) Custom filter block as described below. + * `vpc_id` - (Optional) The VPC ID that you want to filter from. * `tags` - (Optional) A mapping of tags, each pair of which must exactly match a pair on the desired route tables. +More complex filters can be expressed using one or more `filter` sub-blocks, +which take the following arguments: + +* `name` - (Required) The name of the field to filter by, as defined by + [the underlying AWS API](http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeRouteTables.html). + +* `values` - (Required) Set of values that are accepted for the given field. + A Route Table will be selected if any one of the given values matches. + ## Attributes Reference * `ids` - A list of all the route table ids found. This data source will fail if none are found. From ebdcb40cb70a7ed8b8ea6862413f301197915ef7 Mon Sep 17 00:00:00 2001 From: saravanan30erd Date: Fri, 29 Jun 2018 19:28:48 +0400 Subject: [PATCH 43/88] add validation func --- aws/resource_aws_spot_fleet_request.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/aws/resource_aws_spot_fleet_request.go b/aws/resource_aws_spot_fleet_request.go index 4fcf6bd8415..d794d09c508 100644 --- a/aws/resource_aws_spot_fleet_request.go +++ b/aws/resource_aws_spot_fleet_request.go @@ -322,8 +322,12 @@ func resourceAwsSpotFleetRequest() *schema.Resource { "fleet_type": { Type: schema.TypeString, Optional: true, - Default: "maintain", + Default: ec2.FleetTypeMaintain, ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + ec2.FleetTypeMaintain, + ec2.FleetTypeRequest, + }, false), }, "spot_request_state": { Type: schema.TypeString, From 506cc150f7f25b12aec689807e0d7e3323cd8718 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 29 Jun 2018 11:29:08 -0400 Subject: [PATCH 44/88] Minor changes after code review. --- aws/aws_vpc_ipv4_cidr_block_association.go | 4 ++++ website/docs/r/subnet.html.markdown | 21 ++++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/aws/aws_vpc_ipv4_cidr_block_association.go b/aws/aws_vpc_ipv4_cidr_block_association.go index a07a4f731ca..1aa5deee43a 100644 --- a/aws/aws_vpc_ipv4_cidr_block_association.go +++ b/aws/aws_vpc_ipv4_cidr_block_association.go @@ -71,6 +71,7 @@ func resourceAwsVpcIpv4CidrBlockAssociationRead(d *schema.ResourceData, meta int if aws.StringValue(cidrAssociation.AssociationId) == d.Id() { found = true d.Set("cidr_block", cidrAssociation.CidrBlock) + break } } if !found { @@ -89,6 +90,9 @@ func resourceAwsVpcIpv4CidrBlockAssociationDelete(d *schema.ResourceData, meta i AssociationId: aws.String(d.Id()), }) if err != nil { + if isAWSErr(err, "InvalidVpcID.NotFound", "") { + return nil + } return fmt.Errorf("Error deleting VPC IPv4 CIDR block association: %s", err) } diff --git a/website/docs/r/subnet.html.markdown b/website/docs/r/subnet.html.markdown index cda826371de..32a6a0e41cb 100644 --- a/website/docs/r/subnet.html.markdown +++ b/website/docs/r/subnet.html.markdown @@ -12,6 +12,8 @@ Provides an VPC subnet resource. ## Example Usage +### Basic Usage + ```hcl resource "aws_subnet" "main" { vpc_id = "${aws_vpc.main.id}" @@ -23,6 +25,23 @@ resource "aws_subnet" "main" { } ``` +### Subnets In Secondary VPC CIDR Blocks + +When managing subnets in one of a VPC's secondary CIDR blocks created using a [`aws_vpc_ipv4_cidr_block_association`](vpc_ipv4_cidr_block_association.html) +resource, it is recommended to reference that resource's `vpc_id` attribute to ensure correct dependency ordering. + +```hcl +resource "aws_vpc_ipv4_cidr_block_association" "secondary_cidr" { + vpc_id = "${aws_vpc.main.id}" + cidr_block = "172.2.0.0/16" +} + +resource "aws_subnet" "in_secondary_cidr" { + vpc_id = "${aws_vpc_ipv4_cidr_block_association.secondary_cidr.vpc_id}" + cidr_block = "172.2.0.0/24" +} +``` + ## Argument Reference The following arguments are supported: @@ -57,4 +76,4 @@ Subnets can be imported using the `subnet id`, e.g. ``` $ terraform import aws_subnet.public_subnet subnet-9d4a7b6c -``` \ No newline at end of file +``` From ca2beb36c90f8819c3a75dbc6397e279337313bc Mon Sep 17 00:00:00 2001 From: Logan Attwood Date: Fri, 29 Jun 2018 12:48:59 -0300 Subject: [PATCH 45/88] Update aws_db_instance documentation to use correct units Reference for gibibytes over gigabytes: https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_CreateDBInstance.html --- website/docs/r/db_instance.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/db_instance.html.markdown b/website/docs/r/db_instance.html.markdown index 04e81c9787a..481c8881da9 100644 --- a/website/docs/r/db_instance.html.markdown +++ b/website/docs/r/db_instance.html.markdown @@ -58,7 +58,7 @@ documentation](http://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_Crea The following arguments are supported: * `allocated_storage` - (Required unless a `snapshot_identifier` or -`replicate_source_db` is provided) The allocated storage in gigabytes. +`replicate_source_db` is provided) The allocated storage in gibibytes. * `allow_major_version_upgrade` - (Optional) Indicates that major version upgrades are allowed. Changing this parameter does not result in an outage and the change is asynchronously applied as soon as possible. From 5fca4227f68d7bd9c9befc9a151469f38537b516 Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Fri, 29 Jun 2018 12:12:26 -0400 Subject: [PATCH 46/88] Update CHANGELOG for #3951 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1db085a98bb..62328ebb4c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ FEATURES: ENHANCEMENTS: * resource/aws_eip_association: Support resource import [GH-5006] +* resource/aws_network_acl_rule: Add plan time conflict validation with `cidr_block` and `ipv6_cidr_block` [GH-3951] ## 1.25.0 (June 27, 2018) From 0ef33b6cfa9ae724f197b0801aec503791e2fe96 Mon Sep 17 00:00:00 2001 From: Marcos Diez Date: Fri, 29 Jun 2018 13:16:04 -0300 Subject: [PATCH 47/88] codedeploy documentation --- .../r/codedeploy_deployment_group.html.markdown | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/website/docs/r/codedeploy_deployment_group.html.markdown b/website/docs/r/codedeploy_deployment_group.html.markdown index 93500147ab8..38d73f1fe84 100644 --- a/website/docs/r/codedeploy_deployment_group.html.markdown +++ b/website/docs/r/codedeploy_deployment_group.html.markdown @@ -50,9 +50,20 @@ resource "aws_iam_role_policy" "example" { "autoscaling:DescribeLifecycleHooks", "autoscaling:PutLifecycleHook", "autoscaling:RecordLifecycleActionHeartbeat", - "codedeploy:*", + + "codedeploy:Batch*", + "codedeploy:Get*", + "codedeploy:List*", + "ec2:DescribeInstances", "ec2:DescribeInstanceStatus", + + "elasticloadbalancing:Describe*", + "elasticloadbalancing:DeregisterInstancesFromLoadBalancer", + "elasticloadbalancing:RegisterInstancesWithLoadBalancer", + "elasticloadbalancing:RegisterTargets", + "elasticloadbalancing:DeregisterTargets", + "tag:GetTags", "tag:GetResources", "sns:Publish" From a980ff400f670d115c0acd911785c5a2a4187928 Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Fri, 29 Jun 2018 13:30:47 -0400 Subject: [PATCH 48/88] Update CHANGELOG for #4010 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 62328ebb4c9..fe69b7215cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ ENHANCEMENTS: * resource/aws_eip_association: Support resource import [GH-5006] * resource/aws_network_acl_rule: Add plan time conflict validation with `cidr_block` and `ipv6_cidr_block` [GH-3951] +BUG FIXES: + +* resource/aws_kms_alias: Fix perpetual plan when `target_key_id` is ARN [GH-4010] + ## 1.25.0 (June 27, 2018) NOTES: From 3855870058db7702225d05a1fd2bb436d4efae83 Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Fri, 29 Jun 2018 13:42:22 -0400 Subject: [PATCH 49/88] Update CHANGELOG for #5020 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe69b7215cb..22ba59ca948 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ENHANCEMENTS: * resource/aws_eip_association: Support resource import [GH-5006] * resource/aws_network_acl_rule: Add plan time conflict validation with `cidr_block` and `ipv6_cidr_block` [GH-3951] +* resource/aws_ssm_document: Add `tags` argument (support tagging) [GH-5020] BUG FIXES: From ff10478925671c53f5d3e91af0b0604d96245a0b Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 29 Jun 2018 13:51:07 -0400 Subject: [PATCH 50/88] Correctly handle VPC IPv4 CIDR block association state changes. --- aws/aws_vpc_ipv4_cidr_block_association.go | 80 ++++++++++++++----- aws/resource_aws_vpc.go | 30 +++++++ ..._ipv4_cidr_block_association.html.markdown | 8 ++ 3 files changed, 100 insertions(+), 18 deletions(-) diff --git a/aws/aws_vpc_ipv4_cidr_block_association.go b/aws/aws_vpc_ipv4_cidr_block_association.go index 1aa5deee43a..54cf6c01e12 100644 --- a/aws/aws_vpc_ipv4_cidr_block_association.go +++ b/aws/aws_vpc_ipv4_cidr_block_association.go @@ -3,13 +3,19 @@ package aws import ( "fmt" "log" + "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" + "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" ) +const ( + VpcCidrBlockStateCodeDeleted = "deleted" +) + func resourceAwsVpcIpv4CidrBlockAssociation() *schema.Resource { return &schema.Resource{ Create: resourceAwsVpcIpv4CidrBlockAssociationCreate, @@ -30,6 +36,11 @@ func resourceAwsVpcIpv4CidrBlockAssociation() *schema.Resource { ValidateFunc: validation.CIDRNetwork(16, 28), // The allowed block size is between a /28 netmask and /16 netmask. }, }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(10 * time.Minute), + Delete: schema.DefaultTimeout(10 * time.Minute), + }, } } @@ -48,36 +59,37 @@ func resourceAwsVpcIpv4CidrBlockAssociationCreate(d *schema.ResourceData, meta i d.SetId(aws.StringValue(resp.CidrBlockAssociation.AssociationId)) + stateConf := &resource.StateChangeConf{ + Pending: []string{ec2.VpcCidrBlockStateCodeAssociating}, + Target: []string{ec2.VpcCidrBlockStateCodeAssociated}, + Refresh: vpcIpv4CidrBlockAssociationStateRefresh(conn, d.Get("vpc_id").(string), d.Id()), + Timeout: d.Timeout(schema.TimeoutCreate), + Delay: 10 * time.Second, + MinTimeout: 5 * time.Second, + } + _, err = stateConf.WaitForState() + if err != nil { + return fmt.Errorf("Error waiting for IPv4 CIDR block association (%s) to become available: %s", d.Id(), err) + } + return resourceAwsVpcIpv4CidrBlockAssociationRead(d, meta) } func resourceAwsVpcIpv4CidrBlockAssociationRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).ec2conn - vpcId := d.Get("vpc_id").(string) - vpcRaw, _, err := VPCStateRefreshFunc(conn, vpcId)() + assocRaw, state, err := vpcIpv4CidrBlockAssociationStateRefresh(conn, d.Get("vpc_id").(string), d.Id())() if err != nil { - return fmt.Errorf("Error reading VPC: %s", err) + return fmt.Errorf("Error reading IPv4 CIDR block association: %s", err) } - if vpcRaw == nil { - log.Printf("[WARN] VPC (%s) not found, removing IPv4 CIDR block association from state", vpcId) + if state == VpcCidrBlockStateCodeDeleted { + log.Printf("[WARN] IPv4 CIDR block association (%s) not found, removing from state", d.Id()) d.SetId("") return nil } - vpc := vpcRaw.(*ec2.Vpc) - found := false - for _, cidrAssociation := range vpc.CidrBlockAssociationSet { - if aws.StringValue(cidrAssociation.AssociationId) == d.Id() { - found = true - d.Set("cidr_block", cidrAssociation.CidrBlock) - break - } - } - if !found { - log.Printf("[WARN] VPC IPv4 CIDR block association (%s) not found, removing from state", d.Id()) - d.SetId("") - } + assoc := assocRaw.(*ec2.VpcCidrBlockAssociation) + d.Set("cidr_block", assoc.CidrBlock) return nil } @@ -96,5 +108,37 @@ func resourceAwsVpcIpv4CidrBlockAssociationDelete(d *schema.ResourceData, meta i return fmt.Errorf("Error deleting VPC IPv4 CIDR block association: %s", err) } + stateConf := &resource.StateChangeConf{ + Pending: []string{ec2.VpcCidrBlockStateCodeDisassociating}, + Target: []string{ec2.VpcCidrBlockStateCodeDisassociated, VpcCidrBlockStateCodeDeleted}, + Refresh: vpcIpv4CidrBlockAssociationStateRefresh(conn, d.Get("vpc_id").(string), d.Id()), + Timeout: d.Timeout(schema.TimeoutDelete), + Delay: 10 * time.Second, + MinTimeout: 5 * time.Second, + } + _, err = stateConf.WaitForState() + if err != nil { + return fmt.Errorf("Error waiting for VPC IPv4 CIDR block association (%s) to be deleted: %s", d.Id(), err.Error()) + } + return nil } + +func vpcIpv4CidrBlockAssociationStateRefresh(conn *ec2.EC2, vpcId, assocId string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + vpc, err := vpcDescribe(conn, vpcId) + if err != nil { + return nil, "", err + } + + if vpc != nil { + for _, cidrAssociation := range vpc.CidrBlockAssociationSet { + if aws.StringValue(cidrAssociation.AssociationId) == assocId { + return cidrAssociation, aws.StringValue(cidrAssociation.CidrBlockState.State), nil + } + } + } + + return "", VpcCidrBlockStateCodeDeleted, nil + } +} diff --git a/aws/resource_aws_vpc.go b/aws/resource_aws_vpc.go index e1687477d0a..1006d9d29ca 100644 --- a/aws/resource_aws_vpc.go +++ b/aws/resource_aws_vpc.go @@ -674,3 +674,33 @@ func awsVpcDescribeVpcAttribute(attribute string, vpcId string, conn *ec2.EC2) ( return resp, nil } + +// vpcDescribe returns EC2 API information about the specified VPC. +// If the VPC doesn't exist, return nil. +func vpcDescribe(conn *ec2.EC2, vpcId string) (*ec2.Vpc, error) { + resp, err := conn.DescribeVpcs(&ec2.DescribeVpcsInput{ + VpcIds: aws.StringSlice([]string{vpcId}), + }) + if err != nil { + if !isAWSErr(err, "InvalidVpcID.NotFound", "") { + return nil, err + } + resp = nil + } + + if resp == nil { + return nil, nil + } + + n := len(resp.Vpcs) + switch n { + case 0: + return nil, nil + + case 1: + return resp.Vpcs[0], nil + + default: + return nil, fmt.Errorf("Found %d VPCs for %s, expected 1", n, vpcId) + } +} diff --git a/website/docs/r/vpc_ipv4_cidr_block_association.html.markdown b/website/docs/r/vpc_ipv4_cidr_block_association.html.markdown index 14a82e76fbb..e33cc898faa 100644 --- a/website/docs/r/vpc_ipv4_cidr_block_association.html.markdown +++ b/website/docs/r/vpc_ipv4_cidr_block_association.html.markdown @@ -33,6 +33,14 @@ The following arguments are supported: * `cidr_block` - (Required) The additional IPv4 CIDR block to associate with the VPC. * `vpc_id` - (Required) The ID of the VPC to make the association with. +## Timeouts + +`aws_vpc_ipv4_cidr_block_association` provides the following +[Timeouts](/docs/configuration/resources.html#timeouts) configuration options: + +- `create` - (Default `10 minutes`) Used for creating the association +- `delete` - (Default `10 minutes`) Used for destroying the association + ## Attributes Reference The following attributes are exported: From 1fa528d4b46a0db351bf35154321d8323954ec20 Mon Sep 17 00:00:00 2001 From: Marcos Diez Date: Fri, 29 Jun 2018 15:49:32 -0300 Subject: [PATCH 51/88] aws_iam_role_policy_attachment documentation update2 --- .../codedeploy_deployment_group.html.markdown | 43 ++----------------- 1 file changed, 3 insertions(+), 40 deletions(-) diff --git a/website/docs/r/codedeploy_deployment_group.html.markdown b/website/docs/r/codedeploy_deployment_group.html.markdown index 38d73f1fe84..92ae2a6dc1c 100644 --- a/website/docs/r/codedeploy_deployment_group.html.markdown +++ b/website/docs/r/codedeploy_deployment_group.html.markdown @@ -33,46 +33,9 @@ resource "aws_iam_role" "example" { EOF } -resource "aws_iam_role_policy" "example" { - name = "example-policy" - role = "${aws_iam_role.example.id}" - - policy = < Date: Fri, 29 Jun 2018 14:56:52 -0400 Subject: [PATCH 52/88] Update CHANGELOG for #5032 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 22ba59ca948..68944500297 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ENHANCEMENTS: * resource/aws_eip_association: Support resource import [GH-5006] * resource/aws_network_acl_rule: Add plan time conflict validation with `cidr_block` and `ipv6_cidr_block` [GH-3951] +* resource/aws_spot_fleet_request: Add `fleet_type` argument [GH-5032] * resource/aws_ssm_document: Add `tags` argument (support tagging) [GH-5020] BUG FIXES: From 208c9eb7aa41d62f041f7594e97490c41902e109 Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Fri, 29 Jun 2018 14:59:35 -0400 Subject: [PATCH 53/88] Update CHANGELOG for #5035 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68944500297..da738fc0bb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ FEATURES: ENHANCEMENTS: +* data-source/aws_route_tables: Add `filter` argument [GH-5035] * resource/aws_eip_association: Support resource import [GH-5006] * resource/aws_network_acl_rule: Add plan time conflict validation with `cidr_block` and `ipv6_cidr_block` [GH-3951] * resource/aws_spot_fleet_request: Add `fleet_type` argument [GH-5032] From 994e919b29225bee91111b2bac025983f84aa5d3 Mon Sep 17 00:00:00 2001 From: Kash Date: Fri, 29 Jun 2018 15:40:52 -0400 Subject: [PATCH 54/88] Add filtering to subnet ids data source --- aws/data_source_aws_subnet_ids.go | 31 ++++++++++---- aws/data_source_aws_subnet_ids_test.go | 56 ++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 8 deletions(-) diff --git a/aws/data_source_aws_subnet_ids.go b/aws/data_source_aws_subnet_ids.go index a2f5f0c46aa..21339f1ab91 100644 --- a/aws/data_source_aws_subnet_ids.go +++ b/aws/data_source_aws_subnet_ids.go @@ -12,6 +12,7 @@ func dataSourceAwsSubnetIDs() *schema.Resource { return &schema.Resource{ Read: dataSourceAwsSubnetIDsRead, Schema: map[string]*schema.Schema{ + "filter": ec2CustomFiltersSchema(), "tags": tagsSchemaComputed(), @@ -35,15 +36,29 @@ func dataSourceAwsSubnetIDsRead(d *schema.ResourceData, meta interface{}) error req := &ec2.DescribeSubnetsInput{} - req.Filters = buildEC2AttributeFilterList( - map[string]string{ - "vpc-id": d.Get("vpc_id").(string), - }, - ) + if vpc, vpcOk := d.GetOk("vpc_id"); vpcOk { + req.Filters = buildEC2AttributeFilterList( + map[string]string{ + "vpc-id": vpc.(string), + }, + ) + } + + if tags, tagsOk := d.GetOk("tags"); tagsOk { + req.Filters = append(req.Filters, buildEC2TagFilterList( + tagsFromMap(tags.(map[string]interface{})), + )...) + } + + if filters, filtersOk := d.GetOk("filter"); filtersOk { + req.Filters = append(req.Filters, buildEC2CustomFilterList( + filters.(*schema.Set), + )...) + } - req.Filters = append(req.Filters, buildEC2TagFilterList( - tagsFromMap(d.Get("tags").(map[string]interface{})), - )...) + if len(req.Filters) == 0 { + req.Filters = nil + } log.Printf("[DEBUG] DescribeSubnets %s\n", req) resp, err := conn.DescribeSubnets(req) diff --git a/aws/data_source_aws_subnet_ids_test.go b/aws/data_source_aws_subnet_ids_test.go index c4826446b2e..3702c3cf47f 100644 --- a/aws/data_source_aws_subnet_ids_test.go +++ b/aws/data_source_aws_subnet_ids_test.go @@ -29,6 +29,25 @@ func TestAccDataSourceAwsSubnetIDs(t *testing.T) { }) } +func TestAccDataSourceAwsSubnetIDs_filter(t *testing.T) { + rInt := acctest.RandIntRange(0, 256) + rName := "data.aws_subnet_ids.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckVpcDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceAwsSubnetIDs_filter(rInt), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(rName, "ids.#", "2"), + ), + }, + }, + }) +} + func testAccDataSourceAwsSubnetIDsConfigWithDataSource(rInt int) string { return fmt.Sprintf(` resource "aws_vpc" "test" { @@ -129,3 +148,40 @@ resource "aws_subnet" "test_private_b" { } `, rInt, rInt, rInt, rInt) } + +func testAccDataSourceAwsSubnetIDs_filter(rInt int) string { + return fmt.Sprintf(` +resource "aws_vpc" "test" { + cidr_block = "172.%d.0.0/16" + tags { + Name = "terraform-testacc-subnet-ids-data-source" + } +} + +resource "aws_subnet" "test_a_one" { + vpc_id = "${aws_vpc.test.id}" + cidr_block = "172.%d.1.0/24" + availability_zone = "us-west-2a" +} + +resource "aws_subnet" "test_a_two" { + vpc_id = "${aws_vpc.test.id}" + cidr_block = "172.%d.2.0/24" + availability_zone = "us-west-2a" +} + +resource "aws_subnet" "test_b" { + vpc_id = "${aws_vpc.test.id}" + cidr_block = "172.%d.3.0/24" + availability_zone = "us-west-2b" +} + +data "aws_subnet_ids" "test" { + vpc_id = "${aws_subnet.test_a_two.vpc_id}" + filter { + name = "availabilityZone" + values = ["${aws_subnet.test_a_one.availability_zone}"] + } +} +`, rInt, rInt, rInt, rInt) +} From 7c3c613d11fba32a9ec0df31d4bb96b55d53ce20 Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Fri, 29 Jun 2018 16:39:06 -0400 Subject: [PATCH 55/88] Update CHANGELOG for #3723 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index da738fc0bb7..902bd9cf89c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ FEATURES: * **New Data Source:** `aws_launch_configuration` [GH-3624] * **New Resource:** `aws_s3_bucket_inventory` [GH-5019] +* **New Resource:** `aws_vpc_ipv4_cidr_block_association` [GH-3723] ENHANCEMENTS: From 2b7d3dab10d61442fd65ce2fe8da2f3eda39e3b0 Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Fri, 29 Jun 2018 16:47:55 -0400 Subject: [PATCH 56/88] Update CHANGELOG for #5038 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 902bd9cf89c..4b7d58a93d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ FEATURES: ENHANCEMENTS: * data-source/aws_route_tables: Add `filter` argument [GH-5035] +* data-source/aws_subnet_ids: Add `filter` argument [GH-5038] * resource/aws_eip_association: Support resource import [GH-5006] * resource/aws_network_acl_rule: Add plan time conflict validation with `cidr_block` and `ipv6_cidr_block` [GH-3951] * resource/aws_spot_fleet_request: Add `fleet_type` argument [GH-5032] From 2e427e657df595771de4a0d7e95d194eb1af1171 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 28 Jun 2018 15:35:37 -0700 Subject: [PATCH 57/88] Make EventSourceMapping.starting_position optional. Amazon recently made it possible to have SQS events trigger Lambdas. See: https://aws.amazon.com/blogs/aws/aws-lambda-adds-amazon-simple-queue-service-to-supported-event-sources/ for more details. As part of this they specifically utilize CreateSourceEventMapping to describe the mapping between SQS and Lambda: https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html *However*, as part of this, they changed the StartingPosition message property from required to optional. You can see that it was previous required here: https://web.archive.org/web/20171222204154/https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-eventsourcemapping.html ``` StartingPosition The position in the stream where Lambda starts reading. For valid values, see CreateEventSourceMapping in the AWS Lambda Developer Guide. Required: Yes Type: String Update requires: Replacement ``` But now is optional in the current set of docs: ``` StartingPosition The position in the stream where Lambda starts reading. For valid values, see CreateEventSourceMapping in the AWS Lambda Developer Guide. Required: No Type: String Update requires: Replacement ``` Unfortunately, this makes is (afaict) impossible to use terraform to configure this EventSourceMapping. If i leave out the starting_position property, terraform complains with: ``` Plan apply failed: Error creating Lambda event source mapping: ValidationException: 1 validation error detected: Value '' at 'startingPosition' failed to satisfy constraint: Member must satisfy enum value set: [LATEST, AT_TIMESTAMP, TRIM_HORIZON] ``` However, if i supply any of those, AWS itself errors out with: ``` Plan apply failed: Error creating Lambda event source mapping: InvalidParameterValueException: StartingPosition is not valid for SQS event sources. ``` This PR attempts to rectify things by making this property optional to match current AWS expectations. --- aws/resource_aws_lambda_event_source_mapping.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_lambda_event_source_mapping.go b/aws/resource_aws_lambda_event_source_mapping.go index 579b3c5a7c1..5bb1f58ac29 100644 --- a/aws/resource_aws_lambda_event_source_mapping.go +++ b/aws/resource_aws_lambda_event_source_mapping.go @@ -35,7 +35,7 @@ func resourceAwsLambdaEventSourceMapping() *schema.Resource { }, "starting_position": { Type: schema.TypeString, - Required: true, + Optional: true, ForceNew: true, }, "batch_size": { From 9fbac0df4005a28c13d47dec1e0bd4e1f260b99d Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 28 Jun 2018 15:40:33 -0700 Subject: [PATCH 58/88] Update documentatoin as well. --- website/docs/r/lambda_event_source_mapping.html.markdown | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/website/docs/r/lambda_event_source_mapping.html.markdown b/website/docs/r/lambda_event_source_mapping.html.markdown index 931f9e220f3..d00effbe38e 100644 --- a/website/docs/r/lambda_event_source_mapping.html.markdown +++ b/website/docs/r/lambda_event_source_mapping.html.markdown @@ -3,12 +3,12 @@ layout: "aws" page_title: "AWS: aws_lambda_event_source_mapping" sidebar_current: "docs-aws-resource-lambda-event-source-mapping" description: |- - Provides a Lambda event source mapping. This allows Lambda functions to get events from Kinesis and DynamoDB. + Provides a Lambda event source mapping. This allows Lambda functions to get events from Kinesis, DynamoDB and SQS --- # aws_lambda_event_source_mapping -Provides a Lambda event source mapping. This allows Lambda functions to get events from Kinesis and DynamoDB. +Provides a Lambda event source mapping. This allows Lambda functions to get events from Kinesis, DynamoDB and SQS For information about Lambda and how to use it, see [What is AWS Lambda?][1] For information about event source mappings, see [CreateEventSourceMapping][2] in the API docs. @@ -31,7 +31,7 @@ resource "aws_lambda_event_source_mapping" "event_source_mapping" { * `event_source_arn` - (Required) The event source ARN - can either be a Kinesis or DynamoDB stream. * `enabled` - (Optional) Determines if the mapping will be enabled on creation. Defaults to `true`. * `function_name` - (Required) The name or the ARN of the Lambda function that will be subscribing to events. -* `starting_position` - (Required) The position in the stream where AWS Lambda should start reading. Can be one of either `TRIM_HORIZON` or `LATEST`. +* `starting_position` - (Optional) The position in the stream where AWS Lambda should start reading. Must be one of either `TRIM_HORIZON` or `LATEST` if getting events from Kinesis or DynamoDB. Must not be provided if getting events from SQS. ## Attributes Reference @@ -53,4 +53,4 @@ Lambda Event Source Mappings can be imported using the `UUID` (event source mapp ``` $ terraform import aws_lambda_event_source_mapping.event_source_mapping 12345kxodurf3443 -``` \ No newline at end of file +``` From 6230a5c3ea13ef056e8c645c37608e0c6c29dd95 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 29 Jun 2018 14:31:42 -0700 Subject: [PATCH 59/88] Add event source mapping tests without a starting_position. --- ...esource_aws_lambda_event_source_mapping.go | 31 +- ...ce_aws_lambda_event_source_mapping_test.go | 307 ++++++++++++++++-- 2 files changed, 301 insertions(+), 37 deletions(-) diff --git a/aws/resource_aws_lambda_event_source_mapping.go b/aws/resource_aws_lambda_event_source_mapping.go index 5bb1f58ac29..d9cc71130ba 100644 --- a/aws/resource_aws_lambda_event_source_mapping.go +++ b/aws/resource_aws_lambda_event_source_mapping.go @@ -87,11 +87,14 @@ func resourceAwsLambdaEventSourceMappingCreate(d *schema.ResourceData, meta inte log.Printf("[DEBUG] Creating Lambda event source mapping: source %s to function %s", eventSourceArn, functionName) params := &lambda.CreateEventSourceMappingInput{ - EventSourceArn: aws.String(eventSourceArn), - FunctionName: aws.String(functionName), - StartingPosition: aws.String(d.Get("starting_position").(string)), - BatchSize: aws.Int64(int64(d.Get("batch_size").(int))), - Enabled: aws.Bool(d.Get("enabled").(bool)), + EventSourceArn: aws.String(eventSourceArn), + FunctionName: aws.String(functionName), + BatchSize: aws.Int64(int64(d.Get("batch_size").(int))), + Enabled: aws.Bool(d.Get("enabled").(bool)), + } + + if startingPosition := d.Get("starting_position"); startingPosition != "" { + params.StartingPosition = aws.String(startingPosition.(string)) } // IAM profiles and roles can take some time to propagate in AWS: @@ -170,7 +173,19 @@ func resourceAwsLambdaEventSourceMappingDelete(d *schema.ResourceData, meta inte UUID: aws.String(d.Id()), } - _, err := conn.DeleteEventSourceMapping(params) + err := resource.Retry(5*time.Minute, func() *resource.RetryError { + _, err := conn.DeleteEventSourceMapping(params) + if err != nil { + if awserr, ok := err.(awserr.Error); ok { + if awserr.Code() == "ResourceInUseException" { + return resource.RetryableError(awserr) + } + } + return resource.NonRetryableError(err) + } + return nil + }) + if err != nil { return fmt.Errorf("Error deleting Lambda event source mapping: %s", err) } @@ -196,7 +211,9 @@ func resourceAwsLambdaEventSourceMappingUpdate(d *schema.ResourceData, meta inte _, err := conn.UpdateEventSourceMapping(params) if err != nil { if awserr, ok := err.(awserr.Error); ok { - if awserr.Code() == "InvalidParameterValueException" { + if awserr.Code() == "InvalidParameterValueException" || + awserr.Code() == "ResourceInUseException" { + return resource.RetryableError(awserr) } } diff --git a/aws/resource_aws_lambda_event_source_mapping_test.go b/aws/resource_aws_lambda_event_source_mapping_test.go index 9823081a115..bf586bbd64b 100644 --- a/aws/resource_aws_lambda_event_source_mapping_test.go +++ b/aws/resource_aws_lambda_event_source_mapping_test.go @@ -33,14 +33,14 @@ func TestAccAWSLambdaEventSourceMapping_basic(t *testing.T) { CheckDestroy: testAccCheckLambdaEventSourceMappingDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSLambdaEventSourceMappingConfig(roleName, policyName, attName, streamName, funcName, uFuncName), + Config: testAccAWSLambdaEventSourceMappingConfig_kinesis(roleName, policyName, attName, streamName, funcName, uFuncName), Check: resource.ComposeTestCheckFunc( testAccCheckAwsLambdaEventSourceMappingExists("aws_lambda_event_source_mapping.lambda_event_source_mapping_test", &conf), testAccCheckAWSLambdaEventSourceMappingAttributes(&conf), ), }, { - Config: testAccAWSLambdaEventSourceMappingConfigUpdate(roleName, policyName, attName, streamName, funcName, uFuncName), + Config: testAccAWSLambdaEventSourceMappingConfigUpdate_kinesis(roleName, policyName, attName, streamName, funcName, uFuncName), Check: resource.ComposeTestCheckFunc( testAccCheckAwsLambdaEventSourceMappingExists("aws_lambda_event_source_mapping.lambda_event_source_mapping_test", &conf), resource.TestCheckResourceAttr("aws_lambda_event_source_mapping.lambda_event_source_mapping_test", @@ -49,6 +49,52 @@ func TestAccAWSLambdaEventSourceMapping_basic(t *testing.T) { "enabled", strconv.FormatBool(false)), resource.TestMatchResourceAttr("aws_lambda_event_source_mapping.lambda_event_source_mapping_test", "function_arn", uFuncArnRe), + resource.TestCheckResourceAttr("aws_lambda_event_source_mapping.lambda_event_source_mapping_test", + "starting_position", "TRIM_HORIZON"), + ), + }, + }, + }) +} + +func TestAccAWSLambdaEventSourceMapping_sqsBasic(t *testing.T) { + var conf lambda.EventSourceMappingConfiguration + + rString := acctest.RandString(8) + roleName := fmt.Sprintf("tf_acc_role_lambda_sqs_basic_%s", rString) + policyName := fmt.Sprintf("tf_acc_policy_lambda_sqs_basic_%s", rString) + attName := fmt.Sprintf("tf_acc_att_lambda_sqs_basic_%s", rString) + streamName := fmt.Sprintf("tf_acc_stream_lambda_sqs_basic_%s", rString) + funcName := fmt.Sprintf("tf_acc_lambda_sqs_basic_%s", rString) + uFuncName := fmt.Sprintf("tf_acc_lambda_sqs_basic_updated_%s", rString) + uFuncArnRe := regexp.MustCompile(":" + uFuncName + "$") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckLambdaEventSourceMappingDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLambdaEventSourceMappingConfig_sqs(roleName, policyName, attName, streamName, funcName, uFuncName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsLambdaEventSourceMappingExists("aws_lambda_event_source_mapping.lambda_event_source_mapping_test", &conf), + testAccCheckAWSLambdaEventSourceMappingAttributes(&conf), + resource.TestCheckNoResourceAttr("aws_lambda_event_source_mapping.lambda_event_source_mapping_test", + "starting_position"), + ), + }, + { + Config: testAccAWSLambdaEventSourceMappingConfigUpdate_sqs(roleName, policyName, attName, streamName, funcName, uFuncName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsLambdaEventSourceMappingExists("aws_lambda_event_source_mapping.lambda_event_source_mapping_test", &conf), + resource.TestCheckResourceAttr("aws_lambda_event_source_mapping.lambda_event_source_mapping_test", + "batch_size", strconv.Itoa(5)), + resource.TestCheckResourceAttr("aws_lambda_event_source_mapping.lambda_event_source_mapping_test", + "enabled", strconv.FormatBool(false)), + resource.TestMatchResourceAttr("aws_lambda_event_source_mapping.lambda_event_source_mapping_test", + "function_arn", uFuncArnRe), + resource.TestCheckNoResourceAttr("aws_lambda_event_source_mapping.lambda_event_source_mapping_test", + "starting_position"), ), }, }, @@ -72,7 +118,7 @@ func TestAccAWSLambdaEventSourceMapping_importBasic(t *testing.T) { CheckDestroy: testAccCheckLambdaEventSourceMappingDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSLambdaEventSourceMappingConfig(roleName, policyName, attName, streamName, funcName, uFuncName), + Config: testAccAWSLambdaEventSourceMappingConfig_kinesis(roleName, policyName, attName, streamName, funcName, uFuncName), }, { @@ -102,7 +148,35 @@ func TestAccAWSLambdaEventSourceMapping_disappears(t *testing.T) { CheckDestroy: testAccCheckLambdaEventSourceMappingDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSLambdaEventSourceMappingConfig(roleName, policyName, attName, streamName, funcName, uFuncName), + Config: testAccAWSLambdaEventSourceMappingConfig_kinesis(roleName, policyName, attName, streamName, funcName, uFuncName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsLambdaEventSourceMappingExists("aws_lambda_event_source_mapping.lambda_event_source_mapping_test", &conf), + testAccCheckAWSLambdaEventSourceMappingDisappears(&conf), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAWSLambdaEventSourceMapping_sqsDisappears(t *testing.T) { + var conf lambda.EventSourceMappingConfiguration + + rString := acctest.RandString(8) + roleName := fmt.Sprintf("tf_acc_role_lambda_sqs_import_%s", rString) + policyName := fmt.Sprintf("tf_acc_policy_lambda_sqs_import_%s", rString) + attName := fmt.Sprintf("tf_acc_att_lambda_sqs_import_%s", rString) + streamName := fmt.Sprintf("tf_acc_stream_lambda_sqs_import_%s", rString) + funcName := fmt.Sprintf("tf_acc_lambda_sqs_import_%s", rString) + uFuncName := fmt.Sprintf("tf_acc_lambda_sqs_import_updated_%s", rString) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckLambdaEventSourceMappingDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLambdaEventSourceMappingConfig_sqs(roleName, policyName, attName, streamName, funcName, uFuncName), Check: resource.ComposeTestCheckFunc( testAccCheckAwsLambdaEventSourceMappingExists("aws_lambda_event_source_mapping.lambda_event_source_mapping_test", &conf), testAccCheckAWSLambdaEventSourceMappingDisappears(&conf), @@ -117,32 +191,49 @@ func testAccCheckAWSLambdaEventSourceMappingDisappears(conf *lambda.EventSourceM return func(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).lambdaconn - params := &lambda.DeleteEventSourceMappingInput{ - UUID: conf.UUID, - } - - _, err := conn.DeleteEventSourceMapping(params) - if err != nil { + err := resource.Retry(10*time.Minute, func() *resource.RetryError { + params := &lambda.DeleteEventSourceMappingInput{ + UUID: conf.UUID, + } + _, err := conn.DeleteEventSourceMapping(params) if err != nil { - return err + cgw, ok := err.(awserr.Error) + if ok { + if cgw.Code() == "ResourceNotFoundException" { + return nil + } + + if cgw.Code() == "ResourceInUseException" { + return resource.RetryableError(fmt.Errorf( + "Waiting for Lambda Event Source Mapping to delete: %v", conf.UUID)) + } + } + return resource.NonRetryableError( + fmt.Errorf("Error deleting Lambda Event Source Mapping: %s", err)) } + + return nil + }) + + if err != nil { + return err } return resource.Retry(10*time.Minute, func() *resource.RetryError { params := &lambda.GetEventSourceMappingInput{ UUID: conf.UUID, } - _, err := conn.GetEventSourceMapping(params) + _, err = conn.GetEventSourceMapping(params) if err != nil { cgw, ok := err.(awserr.Error) if ok && cgw.Code() == "ResourceNotFoundException" { return nil } return resource.NonRetryableError( - fmt.Errorf("Error retrieving Lambda Event Source Mapping: %s", err)) + fmt.Errorf("Error getting Lambda Event Source Mapping: %s", err)) } return resource.RetryableError(fmt.Errorf( - "Waiting for Lambda Event Source Mapping: %v", conf.UUID)) + "Waiting to get Lambda Event Source Mapping: %v", conf.UUID)) }) } } @@ -209,7 +300,7 @@ func testAccCheckAWSLambdaEventSourceMappingAttributes(mapping *lambda.EventSour } } -func testAccAWSLambdaEventSourceMappingConfig(roleName, policyName, attName, streamName, +func testAccAWSLambdaEventSourceMappingConfig_kinesis(roleName, policyName, attName, streamName, funcName, uFuncName string) string { return fmt.Sprintf(` resource "aws_iam_role" "iam_for_lambda" { @@ -234,7 +325,7 @@ EOF resource "aws_iam_policy" "policy_for_role" { name = "%s" path = "/" - description = "IAM policy for for Lamda event mapping testing" + description = "IAM policy for for Lambda event mapping testing" policy = < Date: Mon, 2 Jul 2018 02:44:41 -0700 Subject: [PATCH 60/88] Remove the batch size default. It no longer applies as the sqs mapping default is 10. --- aws/resource_aws_lambda_event_source_mapping.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/aws/resource_aws_lambda_event_source_mapping.go b/aws/resource_aws_lambda_event_source_mapping.go index d9cc71130ba..e62e9d24b0d 100644 --- a/aws/resource_aws_lambda_event_source_mapping.go +++ b/aws/resource_aws_lambda_event_source_mapping.go @@ -41,7 +41,6 @@ func resourceAwsLambdaEventSourceMapping() *schema.Resource { "batch_size": { Type: schema.TypeInt, Optional: true, - Default: 100, }, "enabled": { Type: schema.TypeBool, @@ -89,11 +88,14 @@ func resourceAwsLambdaEventSourceMappingCreate(d *schema.ResourceData, meta inte params := &lambda.CreateEventSourceMappingInput{ EventSourceArn: aws.String(eventSourceArn), FunctionName: aws.String(functionName), - BatchSize: aws.Int64(int64(d.Get("batch_size").(int))), Enabled: aws.Bool(d.Get("enabled").(bool)), } - if startingPosition := d.Get("starting_position"); startingPosition != "" { + if batchSize, ok := d.GetOk("batch_size"); ok { + params.BatchSize = aws.Int64(int64(batchSize.(int))) + } + + if startingPosition, ok := d.GetOk("starting_position"); ok { params.StartingPosition = aws.String(startingPosition.(string)) } From 79eb7fd7e4e3f3a76833ddb78aa6273e91f8f3c7 Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Mon, 2 Jul 2018 08:33:59 -0400 Subject: [PATCH 61/88] resource/aws_lambda_alias: Consolidate update function to also use expandLambdaAliasRoutingConfiguration and remove readAdditionalVersionWeights --- aws/resource_aws_lambda_alias.go | 30 ++---------------------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/aws/resource_aws_lambda_alias.go b/aws/resource_aws_lambda_alias.go index 09d2416a4e9..b60eb99c0ed 100644 --- a/aws/resource_aws_lambda_alias.go +++ b/aws/resource_aws_lambda_alias.go @@ -1,7 +1,6 @@ package aws import ( - "errors" "fmt" "log" "strings" @@ -74,10 +73,7 @@ func resourceAwsLambdaAliasCreate(d *schema.ResourceData, meta interface{}) erro FunctionName: aws.String(functionName), FunctionVersion: aws.String(d.Get("function_version").(string)), Name: aws.String(aliasName), - } - - if v, ok := d.GetOk("routing_config"); ok { - params.RoutingConfig = expandLambdaAliasRoutingConfiguration(v.([]interface{})) + RoutingConfig: expandLambdaAliasRoutingConfiguration(d.Get("routing_config").([]interface{})), } aliasConfiguration, err := conn.CreateAlias(params) @@ -157,20 +153,7 @@ func resourceAwsLambdaAliasUpdate(d *schema.ResourceData, meta interface{}) erro FunctionName: aws.String(d.Get("function_name").(string)), FunctionVersion: aws.String(d.Get("function_version").(string)), Name: aws.String(d.Get("name").(string)), - RoutingConfig: &lambda.AliasRoutingConfiguration{}, - } - - if v, ok := d.GetOk("routing_config"); ok { - routingConfigs := v.([]interface{}) - routingConfig, ok := routingConfigs[0].(map[string]interface{}) - if !ok { - return errors.New("At least one field is expected inside routing_config") - } - - if additionalVersionWeights, ok := routingConfig["additional_version_weights"]; ok { - weights := readAdditionalVersionWeights(additionalVersionWeights.(map[string]interface{})) - params.RoutingConfig.AdditionalVersionWeights = aws.Float64Map(weights) - } + RoutingConfig: expandLambdaAliasRoutingConfiguration(d.Get("routing_config").([]interface{})), } _, err := conn.UpdateAlias(params) @@ -196,12 +179,3 @@ func expandLambdaAliasRoutingConfiguration(l []interface{}) *lambda.AliasRouting return aliasRoutingConfiguration } - -func readAdditionalVersionWeights(avw map[string]interface{}) map[string]float64 { - weights := make(map[string]float64) - for k, v := range avw { - weights[k] = v.(float64) - } - - return weights -} From 2860515609842ab2a0afbb6606430e575f642887 Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Mon, 2 Jul 2018 08:36:22 -0400 Subject: [PATCH 62/88] Update CHANGELOG for #3316 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b7d58a93d9..216464b62b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ ENHANCEMENTS: * data-source/aws_route_tables: Add `filter` argument [GH-5035] * data-source/aws_subnet_ids: Add `filter` argument [GH-5038] * resource/aws_eip_association: Support resource import [GH-5006] +* resource/aws_lambda_alias: Add `routing_config` argument (support traffic shifting) [GH-3316] * resource/aws_network_acl_rule: Add plan time conflict validation with `cidr_block` and `ipv6_cidr_block` [GH-3951] * resource/aws_spot_fleet_request: Add `fleet_type` argument [GH-5032] * resource/aws_ssm_document: Add `tags` argument (support tagging) [GH-5020] From b335935a8f22492e5db5edd06d776367d386d0e9 Mon Sep 17 00:00:00 2001 From: chroju Date: Mon, 2 Jul 2018 21:54:26 +0900 Subject: [PATCH 63/88] docs/resource/aws_wafregional_ipset: Fix typo --- website/docs/r/wafregional_ipset.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/wafregional_ipset.html.markdown b/website/docs/r/wafregional_ipset.html.markdown index d0248e0a3c6..c799da6e3da 100644 --- a/website/docs/r/wafregional_ipset.html.markdown +++ b/website/docs/r/wafregional_ipset.html.markdown @@ -33,7 +33,7 @@ resource "aws_wafregional_ipset" "ipset" { The following arguments are supported: * `name` - (Required) The name or description of the IPSet. -* `ip_set_descriptor` - (Optional) One or more pairs specifying the IP address type (IPV4 or IPV5) and the IP address range (in CIDR notation) from which web requests originate. +* `ip_set_descriptor` - (Optional) One or more pairs specifying the IP address type (IPV4 or IPV6) and the IP address range (in CIDR notation) from which web requests originate. ## Nested Blocks From f238f0c51fd74031c9abc62dcf01d81db75f1378 Mon Sep 17 00:00:00 2001 From: eggsbenjamin Date: Mon, 2 Jul 2018 16:15:26 +0100 Subject: [PATCH 64/88] re-added tests --- aws/resource_aws_lambda_alias_test.go | 30 +++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/aws/resource_aws_lambda_alias_test.go b/aws/resource_aws_lambda_alias_test.go index 683bf0b7317..0fc5b9fa2a3 100644 --- a/aws/resource_aws_lambda_alias_test.go +++ b/aws/resource_aws_lambda_alias_test.go @@ -32,6 +32,7 @@ func TestAccAWSLambdaAlias_basic(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckAwsLambdaAliasExists("aws_lambda_alias.lambda_alias_test", &conf), testAccCheckAwsLambdaAttributes(&conf), + testAccCheckAwsLambdaAliasRoutingConfigDoesNotExist(&conf), resource.TestMatchResourceAttr("aws_lambda_alias.lambda_alias_test", "arn", regexp.MustCompile(`^arn:aws:lambda:[a-z]+-[a-z]+-[0-9]+:\d{12}:function:`+funcName+`:`+aliasName+`$`)), ), @@ -78,6 +79,35 @@ func TestAccAWSLambdaAlias_nameupdate(t *testing.T) { }) } +func TestAccAWSLambdaAlias_routingconfig(t *testing.T) { + var conf lambda.AliasConfiguration + + rString := acctest.RandString(8) + roleName := fmt.Sprintf("tf_acc_role_lambda_alias_basic_%s", rString) + policyName := fmt.Sprintf("tf_acc_policy_lambda_alias_basic_%s", rString) + attachmentName := fmt.Sprintf("tf_acc_attachment_%s", rString) + funcName := fmt.Sprintf("tf_acc_lambda_func_alias_basic_%s", rString) + aliasName := fmt.Sprintf("tf_acc_lambda_alias_basic_%s", rString) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsLambdaAliasDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsLambdaAliasConfigWithRoutingConfig(roleName, policyName, attachmentName, funcName, aliasName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsLambdaAliasExists("aws_lambda_alias.lambda_alias_test", &conf), + testAccCheckAwsLambdaAttributes(&conf), + testAccCheckAwsLambdaAliasRoutingConfigExists(&conf), + resource.TestMatchResourceAttr("aws_lambda_alias.lambda_alias_test", "arn", + regexp.MustCompile(`^arn:aws:lambda:[a-z]+-[a-z]+-[0-9]+:\d{12}:function:`+funcName+`:`+aliasName+`$`)), + ), + }, + }, + }) +} + func testAccCheckAwsLambdaAliasDestroy(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).lambdaconn From 92971fd3c82c0d3127e6869d9414268a10f9a785 Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Mon, 2 Jul 2018 13:10:14 -0400 Subject: [PATCH 65/88] resousrce/aws_codebuild_project: Prevent panic with environment variable difference handling --- aws/resource_aws_codebuild_project.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/aws/resource_aws_codebuild_project.go b/aws/resource_aws_codebuild_project.go index 2db1752d114..e0ba5134dac 100644 --- a/aws/resource_aws_codebuild_project.go +++ b/aws/resource_aws_codebuild_project.go @@ -835,7 +835,12 @@ func resourceAwsCodeBuildProjectEnvironmentHash(v interface{}) int { for _, e := range environmentVariables { if e != nil { // Old statefiles might have nil values in them ev := e.(map[string]interface{}) - buf.WriteString(fmt.Sprintf("%s:%s:%s-", ev["name"].(string), ev["type"].(string), ev["value"].(string))) + buf.WriteString(fmt.Sprintf("%s:", ev["name"].(string))) + // type is sometimes not returned by the API + if v, ok := ev["type"]; ok { + buf.WriteString(fmt.Sprintf("%s:", v.(string))) + } + buf.WriteString(fmt.Sprintf("%s-", ev["value"].(string))) } } From 879ea1bed3ff54c48ffbb64bbfd9244bc7b98720 Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Mon, 2 Jul 2018 14:25:52 -0400 Subject: [PATCH 66/88] Update CHANGELOG for #5052 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 216464b62b6..e6d31bcfd6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ ENHANCEMENTS: BUG FIXES: +* resource/aws_codebuild_project: Prevent panic with missing environment variable type [GH-5052] * resource/aws_kms_alias: Fix perpetual plan when `target_key_id` is ARN [GH-4010] ## 1.25.0 (June 27, 2018) From 424624d260f9b4cd03196cb6c4a51e1b799685b6 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 2 Jul 2018 11:53:18 -0700 Subject: [PATCH 67/88] use appropriate terraform error helpers. --- aws/resource_aws_lambda_event_source_mapping.go | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/aws/resource_aws_lambda_event_source_mapping.go b/aws/resource_aws_lambda_event_source_mapping.go index e62e9d24b0d..f2a491b8921 100644 --- a/aws/resource_aws_lambda_event_source_mapping.go +++ b/aws/resource_aws_lambda_event_source_mapping.go @@ -178,10 +178,8 @@ func resourceAwsLambdaEventSourceMappingDelete(d *schema.ResourceData, meta inte err := resource.Retry(5*time.Minute, func() *resource.RetryError { _, err := conn.DeleteEventSourceMapping(params) if err != nil { - if awserr, ok := err.(awserr.Error); ok { - if awserr.Code() == "ResourceInUseException" { - return resource.RetryableError(awserr) - } + if isAWSErr(err, lambda.ErrCodeResourceInUseException, "") { + return resource.RetryableError(err) } return resource.NonRetryableError(err) } @@ -212,12 +210,10 @@ func resourceAwsLambdaEventSourceMappingUpdate(d *schema.ResourceData, meta inte err := resource.Retry(5*time.Minute, func() *resource.RetryError { _, err := conn.UpdateEventSourceMapping(params) if err != nil { - if awserr, ok := err.(awserr.Error); ok { - if awserr.Code() == "InvalidParameterValueException" || - awserr.Code() == "ResourceInUseException" { + if isAWSErr(err, lambda.ErrCodeInvalidParameterValueException, "") || + isAWSErr(err, lambda.ErrCodeResourceInUseException, "") { - return resource.RetryableError(awserr) - } + return resource.RetryableError(err) } return resource.NonRetryableError(err) } From ee2848777b6d6eb37850383b2447f4cd277eb698 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 2 Jul 2018 12:06:28 -0700 Subject: [PATCH 68/88] Fix test text duplication. --- aws/resource_aws_lambda_event_source_mapping_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/aws/resource_aws_lambda_event_source_mapping_test.go b/aws/resource_aws_lambda_event_source_mapping_test.go index bf586bbd64b..3794187da02 100644 --- a/aws/resource_aws_lambda_event_source_mapping_test.go +++ b/aws/resource_aws_lambda_event_source_mapping_test.go @@ -325,7 +325,7 @@ EOF resource "aws_iam_policy" "policy_for_role" { name = "%s" path = "/" - description = "IAM policy for for Lambda event mapping testing" + description = "IAM policy for Lambda event mapping testing" policy = < Date: Mon, 2 Jul 2018 13:41:35 -0700 Subject: [PATCH 69/88] Suppress diffs when batch_size is removed, but keeps the default size. --- ...esource_aws_lambda_event_source_mapping.go | 29 ++++ ...ce_aws_lambda_event_source_mapping_test.go | 139 +++++++++++++++++- 2 files changed, 164 insertions(+), 4 deletions(-) diff --git a/aws/resource_aws_lambda_event_source_mapping.go b/aws/resource_aws_lambda_event_source_mapping.go index f2a491b8921..3917529dea4 100644 --- a/aws/resource_aws_lambda_event_source_mapping.go +++ b/aws/resource_aws_lambda_event_source_mapping.go @@ -6,8 +6,12 @@ import ( "time" "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/dynamodb" + "github.com/aws/aws-sdk-go/service/kinesis" "github.com/aws/aws-sdk-go/service/lambda" + "github.com/aws/aws-sdk-go/service/sqs" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" @@ -41,6 +45,31 @@ func resourceAwsLambdaEventSourceMapping() *schema.Resource { "batch_size": { Type: schema.TypeInt, Optional: true, + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + // When AWS repurposed EventSourceMapping for use with SQS they kept + // the default for BatchSize at 100 for Kinesis and DynamoDB, but made + // the default 10 for SWS. As such, we had to make batch_size optional. + // Because of this, we need to ensure that if someone doesn't have + // batch_size specified that it is not treated as a diff for those + + eventSourceARN, err := arn.Parse(d.Get("event_source_arn").(string)) + if err != nil { + return false + } + switch eventSourceARN.Service { + case dynamodb.ServiceName, kinesis.ServiceName: + if old == "100" && (new == "" || new == "0") { + return true + } + case sqs.ServiceName: + if old == "1" && (new == "" || new == "0") { + return true + } + default: + panic(eventSourceARN.Service) + } + return false + }, }, "enabled": { Type: schema.TypeBool, diff --git a/aws/resource_aws_lambda_event_source_mapping_test.go b/aws/resource_aws_lambda_event_source_mapping_test.go index 3794187da02..9e4452a895a 100644 --- a/aws/resource_aws_lambda_event_source_mapping_test.go +++ b/aws/resource_aws_lambda_event_source_mapping_test.go @@ -15,7 +15,7 @@ import ( "github.com/hashicorp/terraform/terraform" ) -func TestAccAWSLambdaEventSourceMapping_basic(t *testing.T) { +func TestAccAWSLambdaEventSourceMapping_kinesis_basic(t *testing.T) { var conf lambda.EventSourceMappingConfiguration rString := acctest.RandString(8) @@ -57,7 +57,51 @@ func TestAccAWSLambdaEventSourceMapping_basic(t *testing.T) { }) } -func TestAccAWSLambdaEventSourceMapping_sqsBasic(t *testing.T) { +func TestAccAWSLambdaEventSourceMapping_kinesis_removeBatchSize(t *testing.T) { + // batch_size became optional. Ensure that if the user supplies the default + // value, but then moves to not providing the value, that we don't consider this + // a diff. + + var conf lambda.EventSourceMappingConfiguration + + rString := acctest.RandString(8) + roleName := fmt.Sprintf("tf_acc_role_lambda_esm_basic_%s", rString) + policyName := fmt.Sprintf("tf_acc_policy_lambda_esm_basic_%s", rString) + attName := fmt.Sprintf("tf_acc_att_lambda_esm_basic_%s", rString) + streamName := fmt.Sprintf("tf_acc_stream_lambda_esm_basic_%s", rString) + funcName := fmt.Sprintf("tf_acc_lambda_esm_basic_%s", rString) + uFuncName := fmt.Sprintf("tf_acc_lambda_esm_basic_updated_%s", rString) + uFuncArnRe := regexp.MustCompile(":" + uFuncName + "$") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckLambdaEventSourceMappingDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLambdaEventSourceMappingConfig_kinesis(roleName, policyName, attName, streamName, funcName, uFuncName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsLambdaEventSourceMappingExists("aws_lambda_event_source_mapping.lambda_event_source_mapping_test", &conf), + testAccCheckAWSLambdaEventSourceMappingAttributes(&conf), + ), + }, + { + Config: testAccAWSLambdaEventSourceMappingConfigUpdate_kinesis_removeBatchSize(roleName, policyName, attName, streamName, funcName, uFuncName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsLambdaEventSourceMappingExists("aws_lambda_event_source_mapping.lambda_event_source_mapping_test", &conf), + resource.TestCheckResourceAttr("aws_lambda_event_source_mapping.lambda_event_source_mapping_test", + "batch_size", strconv.Itoa(100)), + resource.TestCheckResourceAttr("aws_lambda_event_source_mapping.lambda_event_source_mapping_test", + "enabled", strconv.FormatBool(true)), + resource.TestCheckResourceAttr("aws_lambda_event_source_mapping.lambda_event_source_mapping_test", + "starting_position", "TRIM_HORIZON"), + ), + }, + }, + }) +} + +func TestAccAWSLambdaEventSourceMapping_sqs_basic(t *testing.T) { var conf lambda.EventSourceMappingConfiguration rString := acctest.RandString(8) @@ -101,7 +145,7 @@ func TestAccAWSLambdaEventSourceMapping_sqsBasic(t *testing.T) { }) } -func TestAccAWSLambdaEventSourceMapping_importBasic(t *testing.T) { +func TestAccAWSLambdaEventSourceMapping_kinesis_import(t *testing.T) { resourceName := "aws_lambda_event_source_mapping.lambda_event_source_mapping_test" rString := acctest.RandString(8) @@ -131,7 +175,7 @@ func TestAccAWSLambdaEventSourceMapping_importBasic(t *testing.T) { }) } -func TestAccAWSLambdaEventSourceMapping_disappears(t *testing.T) { +func TestAccAWSLambdaEventSourceMapping_kinesis_disappears(t *testing.T) { var conf lambda.EventSourceMappingConfiguration rString := acctest.RandString(8) @@ -388,6 +432,93 @@ resource "aws_lambda_event_source_mapping" "lambda_event_source_mapping_test" { }`, roleName, policyName, attName, streamName, funcName, uFuncName) } +func testAccAWSLambdaEventSourceMappingConfigUpdate_kinesis_removeBatchSize(roleName, policyName, attName, streamName, + funcName, uFuncName string) string { + return fmt.Sprintf(` +resource "aws_iam_role" "iam_for_lambda" { + name = "%s" + assume_role_policy = < Date: Mon, 2 Jul 2018 13:54:05 -0700 Subject: [PATCH 70/88] Cleanup. --- aws/resource_aws_lambda_event_source_mapping.go | 9 ++++++--- aws/resource_aws_lambda_event_source_mapping_test.go | 1 - 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/aws/resource_aws_lambda_event_source_mapping.go b/aws/resource_aws_lambda_event_source_mapping.go index 3917529dea4..8dc51aaecf9 100644 --- a/aws/resource_aws_lambda_event_source_mapping.go +++ b/aws/resource_aws_lambda_event_source_mapping.go @@ -48,9 +48,12 @@ func resourceAwsLambdaEventSourceMapping() *schema.Resource { DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { // When AWS repurposed EventSourceMapping for use with SQS they kept // the default for BatchSize at 100 for Kinesis and DynamoDB, but made - // the default 10 for SWS. As such, we had to make batch_size optional. + // the default 10 for SQS. As such, we had to make batch_size optional. // Because of this, we need to ensure that if someone doesn't have // batch_size specified that it is not treated as a diff for those + if new != "" && new != "0" { + return false + } eventSourceARN, err := arn.Parse(d.Get("event_source_arn").(string)) if err != nil { @@ -58,11 +61,11 @@ func resourceAwsLambdaEventSourceMapping() *schema.Resource { } switch eventSourceARN.Service { case dynamodb.ServiceName, kinesis.ServiceName: - if old == "100" && (new == "" || new == "0") { + if old == "100" { return true } case sqs.ServiceName: - if old == "1" && (new == "" || new == "0") { + if old == "10" { return true } default: diff --git a/aws/resource_aws_lambda_event_source_mapping_test.go b/aws/resource_aws_lambda_event_source_mapping_test.go index 9e4452a895a..06661465338 100644 --- a/aws/resource_aws_lambda_event_source_mapping_test.go +++ b/aws/resource_aws_lambda_event_source_mapping_test.go @@ -71,7 +71,6 @@ func TestAccAWSLambdaEventSourceMapping_kinesis_removeBatchSize(t *testing.T) { streamName := fmt.Sprintf("tf_acc_stream_lambda_esm_basic_%s", rString) funcName := fmt.Sprintf("tf_acc_lambda_esm_basic_%s", rString) uFuncName := fmt.Sprintf("tf_acc_lambda_esm_basic_updated_%s", rString) - uFuncArnRe := regexp.MustCompile(":" + uFuncName + "$") resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, From 33f00e5fca6e874468ede05d27f1bb91b6cfd709 Mon Sep 17 00:00:00 2001 From: eggsbenjamin Date: Mon, 2 Jul 2018 23:28:05 +0100 Subject: [PATCH 71/88] re-added old acceptance tests --- aws/resource_aws_lambda_alias.go | 4 ++-- aws/resource_aws_lambda_alias_test.go | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/aws/resource_aws_lambda_alias.go b/aws/resource_aws_lambda_alias.go index 09d2416a4e9..110ea8cf4ea 100644 --- a/aws/resource_aws_lambda_alias.go +++ b/aws/resource_aws_lambda_alias.go @@ -190,8 +190,8 @@ func expandLambdaAliasRoutingConfiguration(l []interface{}) *lambda.AliasRouting m := l[0].(map[string]interface{}) - if _, ok := m["additional_version_weights"]; ok { - aliasRoutingConfiguration.AdditionalVersionWeights = expandFloat64Map(m) + if v, ok := m["additional_version_weights"]; ok { + aliasRoutingConfiguration.AdditionalVersionWeights = expandFloat64Map(v.(map[string]interface{})) } return aliasRoutingConfiguration diff --git a/aws/resource_aws_lambda_alias_test.go b/aws/resource_aws_lambda_alias_test.go index 0fc5b9fa2a3..6a6113700d4 100644 --- a/aws/resource_aws_lambda_alias_test.go +++ b/aws/resource_aws_lambda_alias_test.go @@ -94,6 +94,15 @@ func TestAccAWSLambdaAlias_routingconfig(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckAwsLambdaAliasDestroy, Steps: []resource.TestStep{ + { + Config: testAccAwsLambdaAliasConfig(roleName, policyName, attachmentName, funcName, aliasName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsLambdaAliasExists("aws_lambda_alias.lambda_alias_test", &conf), + testAccCheckAwsLambdaAttributes(&conf), + resource.TestMatchResourceAttr("aws_lambda_alias.lambda_alias_test", "arn", + regexp.MustCompile(`^arn:aws:lambda:[a-z]+-[a-z]+-[0-9]+:\d{12}:function:`+funcName+`:`+aliasName+`$`)), + ), + }, { Config: testAccAwsLambdaAliasConfigWithRoutingConfig(roleName, policyName, attachmentName, funcName, aliasName), Check: resource.ComposeTestCheckFunc( @@ -104,6 +113,16 @@ func TestAccAWSLambdaAlias_routingconfig(t *testing.T) { regexp.MustCompile(`^arn:aws:lambda:[a-z]+-[a-z]+-[0-9]+:\d{12}:function:`+funcName+`:`+aliasName+`$`)), ), }, + { + Config: testAccAwsLambdaAliasConfig(roleName, policyName, attachmentName, funcName, aliasName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsLambdaAliasExists("aws_lambda_alias.lambda_alias_test", &conf), + testAccCheckAwsLambdaAttributes(&conf), + testAccCheckAwsLambdaAliasRoutingConfigDoesNotExist(&conf), + resource.TestMatchResourceAttr("aws_lambda_alias.lambda_alias_test", "arn", + regexp.MustCompile(`^arn:aws:lambda:[a-z]+-[a-z]+-[0-9]+:\d{12}:function:`+funcName+`:`+aliasName+`$`)), + ), + }, }, }) } From fff24f7a1fa0c6b167e6a614c63b95f6979e1d38 Mon Sep 17 00:00:00 2001 From: Julien Duchesne Date: Mon, 2 Jul 2018 18:54:33 -0400 Subject: [PATCH 72/88] Implement aws pricing data source --- aws/data_source_aws_pricing_product.go | 110 + aws/provider.go | 1 + vendor/github.com/tidwall/gjson/LICENSE | 20 + vendor/github.com/tidwall/gjson/README.md | 401 ++++ vendor/github.com/tidwall/gjson/gjson.go | 2087 +++++++++++++++++ vendor/github.com/tidwall/gjson/gjson_gae.go | 10 + vendor/github.com/tidwall/gjson/gjson_ngae.go | 73 + vendor/github.com/tidwall/gjson/logo.png | Bin 0 -> 15936 bytes vendor/github.com/tidwall/match/LICENSE | 20 + vendor/github.com/tidwall/match/README.md | 32 + vendor/github.com/tidwall/match/match.go | 192 ++ vendor/vendor.json | 14 + 12 files changed, 2960 insertions(+) create mode 100644 aws/data_source_aws_pricing_product.go create mode 100644 vendor/github.com/tidwall/gjson/LICENSE create mode 100644 vendor/github.com/tidwall/gjson/README.md create mode 100644 vendor/github.com/tidwall/gjson/gjson.go create mode 100644 vendor/github.com/tidwall/gjson/gjson_gae.go create mode 100644 vendor/github.com/tidwall/gjson/gjson_ngae.go create mode 100644 vendor/github.com/tidwall/gjson/logo.png create mode 100644 vendor/github.com/tidwall/match/LICENSE create mode 100644 vendor/github.com/tidwall/match/README.md create mode 100644 vendor/github.com/tidwall/match/match.go diff --git a/aws/data_source_aws_pricing_product.go b/aws/data_source_aws_pricing_product.go new file mode 100644 index 00000000000..e354f4daad6 --- /dev/null +++ b/aws/data_source_aws_pricing_product.go @@ -0,0 +1,110 @@ +package aws + +import ( + "log" + + "encoding/json" + "fmt" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/pricing" + "github.com/hashicorp/terraform/helper/hashcode" + "github.com/hashicorp/terraform/helper/schema" + "github.com/tidwall/gjson" +) + +const ( + awsPricingTermMatch = "TERM_MATCH" +) + +func dataSourceAwsPricingProduct() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAwsPricingProductRead, + Schema: map[string]*schema.Schema{ + "service_code": { + Type: schema.TypeString, + Required: true, + }, + "filters": { + Type: schema.TypeList, + Required: true, + MinItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "field": { + Type: schema.TypeString, + Required: true, + }, + "value": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + "json_query": { + Type: schema.TypeString, + Required: true, + }, + "query_result": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func dataSourceAwsPricingProductRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).pricingconn + + params := &pricing.GetProductsInput{ + ServiceCode: aws.String(d.Get("service_code").(string)), + Filters: []*pricing.Filter{}, + } + + filters := d.Get("filters") + for _, v := range filters.([]interface{}) { + m := v.(map[string]interface{}) + params.Filters = append(params.Filters, &pricing.Filter{ + Field: aws.String(m["field"].(string)), + Value: aws.String(m["value"].(string)), + Type: aws.String(awsPricingTermMatch), + }) + } + + log.Printf("[DEBUG] Reading Pricing of EC2 products: %s", params) + resp, err := conn.GetProducts(params) + if err != nil { + return fmt.Errorf("Error reading Pricing of EC2 products: %s", err) + } + + if err = verifyProductsPriceListLength(resp.PriceList); err != nil { + return err + } + + pricingResult, err := json.Marshal(resp.PriceList[0]) + if err != nil { + return fmt.Errorf("Invalid JSON value returned by AWS: %s", err) + } + + jsonQuery := d.Get("json_query").(string) + queryResult := gjson.Get(string(pricingResult), jsonQuery) + + d.SetId(fmt.Sprintf("%d-%d", hashcode.String(params.String()), hashcode.String(jsonQuery))) + d.Set("query_result", queryResult.String()) + return nil +} + +func verifyProductsPriceListLength(priceList []aws.JSONValue) error { + numberOfElements := len(priceList) + if numberOfElements == 0 { + return fmt.Errorf("Pricing product query did not return any elements") + } else if numberOfElements > 1 { + priceListBytes, err := json.Marshal(priceList) + priceListString := string(priceListBytes) + if err != nil { + priceListString = err.Error() + } + return fmt.Errorf("Pricing product query not precise enough. Returned more than one element: %s", priceListString) + } + return nil +} diff --git a/aws/provider.go b/aws/provider.go index a5c7e9e1b4e..4cab3f6c79f 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -232,6 +232,7 @@ func Provider() terraform.ResourceProvider { "aws_network_interface": dataSourceAwsNetworkInterface(), "aws_partition": dataSourceAwsPartition(), "aws_prefix_list": dataSourceAwsPrefixList(), + "aws_pricing_product": dataSourceAwsPricingProduct(), "aws_rds_cluster": dataSourceAwsRdsCluster(), "aws_redshift_cluster": dataSourceAwsRedshiftCluster(), "aws_redshift_service_account": dataSourceAwsRedshiftServiceAccount(), diff --git a/vendor/github.com/tidwall/gjson/LICENSE b/vendor/github.com/tidwall/gjson/LICENSE new file mode 100644 index 00000000000..58f5819a438 --- /dev/null +++ b/vendor/github.com/tidwall/gjson/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2016 Josh Baker + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/tidwall/gjson/README.md b/vendor/github.com/tidwall/gjson/README.md new file mode 100644 index 00000000000..70240d99279 --- /dev/null +++ b/vendor/github.com/tidwall/gjson/README.md @@ -0,0 +1,401 @@ +

+GJSON +
+Build Status +GoDoc +GJSON Playground +

+ + + +

get json values quickly

+ +GJSON is a Go package that provides a [fast](#performance) and [simple](#get-a-value) way to get values from a json document. +It has features such as [one line retrieval](#get-a-value), [dot notation paths](#path-syntax), [iteration](#iterate-through-an-object-or-array), and [parsing json lines](#json-lines). + +Also check out [SJSON](https://github.com/tidwall/sjson) for modifying json, and the [JJ](https://github.com/tidwall/jj) command line tool. + +Getting Started +=============== + +## Installing + +To start using GJSON, install Go and run `go get`: + +```sh +$ go get -u github.com/tidwall/gjson +``` + +This will retrieve the library. + +## Get a value +Get searches json for the specified path. A path is in dot syntax, such as "name.last" or "age". When the value is found it's returned immediately. + +```go +package main + +import "github.com/tidwall/gjson" + +const json = `{"name":{"first":"Janet","last":"Prichard"},"age":47}` + +func main() { + value := gjson.Get(json, "name.last") + println(value.String()) +} +``` + +This will print: + +``` +Prichard +``` +*There's also the [GetMany](#get-multiple-values-at-once) function to get multiple values at once, and [GetBytes](#working-with-bytes) for working with JSON byte slices.* + +## Path Syntax + +A path is a series of keys separated by a dot. +A key may contain special wildcard characters '\*' and '?'. +To access an array value use the index as the key. +To get the number of elements in an array or to access a child path, use the '#' character. +The dot and wildcard characters can be escaped with '\\'. + +```json +{ + "name": {"first": "Tom", "last": "Anderson"}, + "age":37, + "children": ["Sara","Alex","Jack"], + "fav.movie": "Deer Hunter", + "friends": [ + {"first": "Dale", "last": "Murphy", "age": 44}, + {"first": "Roger", "last": "Craig", "age": 68}, + {"first": "Jane", "last": "Murphy", "age": 47} + ] +} +``` +``` +"name.last" >> "Anderson" +"age" >> 37 +"children" >> ["Sara","Alex","Jack"] +"children.#" >> 3 +"children.1" >> "Alex" +"child*.2" >> "Jack" +"c?ildren.0" >> "Sara" +"fav\.movie" >> "Deer Hunter" +"friends.#.first" >> ["Dale","Roger","Jane"] +"friends.1.last" >> "Craig" +``` + +You can also query an array for the first match by using `#[...]`, or find all matches with `#[...]#`. +Queries support the `==`, `!=`, `<`, `<=`, `>`, `>=` comparison operators and the simple pattern matching `%` operator. + +``` +friends.#[last=="Murphy"].first >> "Dale" +friends.#[last=="Murphy"]#.first >> ["Dale","Jane"] +friends.#[age>45]#.last >> ["Craig","Murphy"] +friends.#[first%"D*"].last >> "Murphy" +``` + +## JSON Lines + +There's support for [JSON Lines](http://jsonlines.org/) using the `..` prefix, which treats a multilined document as an array. + +For example: + +``` +{"name": "Gilbert", "age": 61} +{"name": "Alexa", "age": 34} +{"name": "May", "age": 57} +{"name": "Deloise", "age": 44} +``` + +``` +..# >> 4 +..1 >> {"name": "Alexa", "age": 34} +..3 >> {"name": "Deloise", "age": 44} +..#.name >> ["Gilbert","Alexa","May","Deloise"] +..#[name="May"].age >> 57 +``` + +The `ForEachLines` function will iterate through JSON lines. + +```go +gjson.ForEachLine(json, func(line gjson.Result) bool{ + println(line.String()) + return true +}) +``` + +## Result Type + +GJSON supports the json types `string`, `number`, `bool`, and `null`. +Arrays and Objects are returned as their raw json types. + +The `Result` type holds one of these: + +``` +bool, for JSON booleans +float64, for JSON numbers +string, for JSON string literals +nil, for JSON null +``` + +To directly access the value: + +```go +result.Type // can be String, Number, True, False, Null, or JSON +result.Str // holds the string +result.Num // holds the float64 number +result.Raw // holds the raw json +result.Index // index of raw value in original json, zero means index unknown +``` + +There are a variety of handy functions that work on a result: + +```go +result.Exists() bool +result.Value() interface{} +result.Int() int64 +result.Uint() uint64 +result.Float() float64 +result.String() string +result.Bool() bool +result.Time() time.Time +result.Array() []gjson.Result +result.Map() map[string]gjson.Result +result.Get(path string) Result +result.ForEach(iterator func(key, value Result) bool) +result.Less(token Result, caseSensitive bool) bool +``` + +The `result.Value()` function returns an `interface{}` which requires type assertion and is one of the following Go types: + +The `result.Array()` function returns back an array of values. +If the result represents a non-existent value, then an empty array will be returned. +If the result is not a JSON array, the return value will be an array containing one result. + +```go +boolean >> bool +number >> float64 +string >> string +null >> nil +array >> []interface{} +object >> map[string]interface{} +``` + +### 64-bit integers + +The `result.Int()` and `result.Uint()` calls are capable of reading all 64 bits, allowing for large JSON integers. + +```go +result.Int() int64 // -9223372036854775808 to 9223372036854775807 +result.Uint() int64 // 0 to 18446744073709551615 +``` + +## Get nested array values + +Suppose you want all the last names from the following json: + +```json +{ + "programmers": [ + { + "firstName": "Janet", + "lastName": "McLaughlin", + }, { + "firstName": "Elliotte", + "lastName": "Hunter", + }, { + "firstName": "Jason", + "lastName": "Harold", + } + ] +} +``` + +You would use the path "programmers.#.lastName" like such: + +```go +result := gjson.Get(json, "programmers.#.lastName") +for _, name := range result.Array() { + println(name.String()) +} +``` + +You can also query an object inside an array: + +```go +name := gjson.Get(json, `programmers.#[lastName="Hunter"].firstName`) +println(name.String()) // prints "Elliotte" +``` + +## Iterate through an object or array + +The `ForEach` function allows for quickly iterating through an object or array. +The key and value are passed to the iterator function for objects. +Only the value is passed for arrays. +Returning `false` from an iterator will stop iteration. + +```go +result := gjson.Get(json, "programmers") +result.ForEach(func(key, value gjson.Result) bool { + println(value.String()) + return true // keep iterating +}) +``` + +## Simple Parse and Get + +There's a `Parse(json)` function that will do a simple parse, and `result.Get(path)` that will search a result. + +For example, all of these will return the same result: + +```go +gjson.Parse(json).Get("name").Get("last") +gjson.Get(json, "name").Get("last") +gjson.Get(json, "name.last") +``` + +## Check for the existence of a value + +Sometimes you just want to know if a value exists. + +```go +value := gjson.Get(json, "name.last") +if !value.Exists() { + println("no last name") +} else { + println(value.String()) +} + +// Or as one step +if gjson.Get(json, "name.last").Exists() { + println("has a last name") +} +``` + +## Validate JSON + +The `Get*` and `Parse*` functions expects that the json is well-formed. Bad json will not panic, but it may return back unexpected results. + +If you are consuming JSON from an unpredictable source then you may want to validate prior to using GJSON. + +```go +if !gjson.Valid(json) { + return errors.New("invalid json") +} +value := gjson.Get(json, "name.last") +``` + +## Unmarshal to a map + +To unmarshal to a `map[string]interface{}`: + +```go +m, ok := gjson.Parse(json).Value().(map[string]interface{}) +if !ok { + // not a map +} +``` + +## Working with Bytes + +If your JSON is contained in a `[]byte` slice, there's the [GetBytes](https://godoc.org/github.com/tidwall/gjson#GetBytes) function. This is preferred over `Get(string(data), path)`. + +```go +var json []byte = ... +result := gjson.GetBytes(json, path) +``` + +If you are using the `gjson.GetBytes(json, path)` function and you want to avoid converting `result.Raw` to a `[]byte`, then you can use this pattern: + +```go +var json []byte = ... +result := gjson.GetBytes(json, path) +var raw []byte +if result.Index > 0 { + raw = json[result.Index:result.Index+len(result.Raw)] +} else { + raw = []byte(result.Raw) +} +``` + +This is a best-effort no allocation sub slice of the original json. This method utilizes the `result.Index` field, which is the position of the raw data in the original json. It's possible that the value of `result.Index` equals zero, in which case the `result.Raw` is converted to a `[]byte`. + +## Get multiple values at once + +The `GetMany` function can be used to get multiple values at the same time. + +```go +results := gjson.GetMany(json, "name.first", "name.last", "age") +``` + +The return value is a `[]Result`, which will always contain exactly the same number of items as the input paths. + +## Performance + +Benchmarks of GJSON alongside [encoding/json](https://golang.org/pkg/encoding/json/), +[ffjson](https://github.com/pquerna/ffjson), +[EasyJSON](https://github.com/mailru/easyjson), +[jsonparser](https://github.com/buger/jsonparser), +and [json-iterator](https://github.com/json-iterator/go) + +``` +BenchmarkGJSONGet-8 3000000 372 ns/op 0 B/op 0 allocs/op +BenchmarkGJSONUnmarshalMap-8 900000 4154 ns/op 1920 B/op 26 allocs/op +BenchmarkJSONUnmarshalMap-8 600000 9019 ns/op 3048 B/op 69 allocs/op +BenchmarkJSONDecoder-8 300000 14120 ns/op 4224 B/op 184 allocs/op +BenchmarkFFJSONLexer-8 1500000 3111 ns/op 896 B/op 8 allocs/op +BenchmarkEasyJSONLexer-8 3000000 887 ns/op 613 B/op 6 allocs/op +BenchmarkJSONParserGet-8 3000000 499 ns/op 21 B/op 0 allocs/op +BenchmarkJSONIterator-8 3000000 812 ns/op 544 B/op 9 allocs/op +``` + +JSON document used: + +```json +{ + "widget": { + "debug": "on", + "window": { + "title": "Sample Konfabulator Widget", + "name": "main_window", + "width": 500, + "height": 500 + }, + "image": { + "src": "Images/Sun.png", + "hOffset": 250, + "vOffset": 250, + "alignment": "center" + }, + "text": { + "data": "Click Here", + "size": 36, + "style": "bold", + "vOffset": 100, + "alignment": "center", + "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" + } + } +} +``` + +Each operation was rotated though one of the following search paths: + +``` +widget.window.name +widget.image.hOffset +widget.text.onMouseUp +``` + +*These benchmarks were run on a MacBook Pro 15" 2.8 GHz Intel Core i7 using Go 1.8 and can be be found [here](https://github.com/tidwall/gjson-benchmarks).* + + +## Contact +Josh Baker [@tidwall](http://twitter.com/tidwall) + +## License + +GJSON source code is available under the MIT [License](/LICENSE). diff --git a/vendor/github.com/tidwall/gjson/gjson.go b/vendor/github.com/tidwall/gjson/gjson.go new file mode 100644 index 00000000000..9ffd77dad5d --- /dev/null +++ b/vendor/github.com/tidwall/gjson/gjson.go @@ -0,0 +1,2087 @@ +// Package gjson provides searching for json strings. +package gjson + +import ( + "encoding/base64" + "encoding/json" + "errors" + "reflect" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" + "unicode/utf16" + "unicode/utf8" + + "github.com/tidwall/match" +) + +// Type is Result type +type Type int + +const ( + // Null is a null json value + Null Type = iota + // False is a json false boolean + False + // Number is json number + Number + // String is a json string + String + // True is a json true boolean + True + // JSON is a raw block of JSON + JSON +) + +// String returns a string representation of the type. +func (t Type) String() string { + switch t { + default: + return "" + case Null: + return "Null" + case False: + return "False" + case Number: + return "Number" + case String: + return "String" + case True: + return "True" + case JSON: + return "JSON" + } +} + +// Result represents a json value that is returned from Get(). +type Result struct { + // Type is the json type + Type Type + // Raw is the raw json + Raw string + // Str is the json string + Str string + // Num is the json number + Num float64 + // Index of raw value in original json, zero means index unknown + Index int +} + +// String returns a string representation of the value. +func (t Result) String() string { + switch t.Type { + default: + return "" + case False: + return "false" + case Number: + return strconv.FormatFloat(t.Num, 'f', -1, 64) + case String: + return t.Str + case JSON: + return t.Raw + case True: + return "true" + } +} + +// Bool returns an boolean representation. +func (t Result) Bool() bool { + switch t.Type { + default: + return false + case True: + return true + case String: + return t.Str != "" && t.Str != "0" && t.Str != "false" + case Number: + return t.Num != 0 + } +} + +// Int returns an integer representation. +func (t Result) Int() int64 { + switch t.Type { + default: + return 0 + case True: + return 1 + case String: + n, _ := parseInt(t.Str) + return n + case Number: + // try to directly convert the float64 to int64 + n, ok := floatToInt(t.Num) + if !ok { + // now try to parse the raw string + n, ok = parseInt(t.Raw) + if !ok { + // fallback to a standard conversion + return int64(t.Num) + } + } + return n + } +} + +// Uint returns an unsigned integer representation. +func (t Result) Uint() uint64 { + switch t.Type { + default: + return 0 + case True: + return 1 + case String: + n, _ := parseUint(t.Str) + return n + case Number: + // try to directly convert the float64 to uint64 + n, ok := floatToUint(t.Num) + if !ok { + // now try to parse the raw string + n, ok = parseUint(t.Raw) + if !ok { + // fallback to a standard conversion + return uint64(t.Num) + } + } + return n + } +} + +// Float returns an float64 representation. +func (t Result) Float() float64 { + switch t.Type { + default: + return 0 + case True: + return 1 + case String: + n, _ := strconv.ParseFloat(t.Str, 64) + return n + case Number: + return t.Num + } +} + +// Time returns a time.Time representation. +func (t Result) Time() time.Time { + res, _ := time.Parse(time.RFC3339, t.String()) + return res +} + +// Array returns back an array of values. +// If the result represents a non-existent value, then an empty array will be returned. +// If the result is not a JSON array, the return value will be an array containing one result. +func (t Result) Array() []Result { + if t.Type == Null { + return []Result{} + } + if t.Type != JSON { + return []Result{t} + } + r := t.arrayOrMap('[', false) + return r.a +} + +// IsObject returns true if the result value is a JSON object. +func (t Result) IsObject() bool { + return t.Type == JSON && len(t.Raw) > 0 && t.Raw[0] == '{' +} + +// IsArray returns true if the result value is a JSON array. +func (t Result) IsArray() bool { + return t.Type == JSON && len(t.Raw) > 0 && t.Raw[0] == '[' +} + +// ForEach iterates through values. +// If the result represents a non-existent value, then no values will be iterated. +// If the result is an Object, the iterator will pass the key and value of each item. +// If the result is an Array, the iterator will only pass the value of each item. +// If the result is not a JSON array or object, the iterator will pass back one value equal to the result. +func (t Result) ForEach(iterator func(key, value Result) bool) { + if !t.Exists() { + return + } + if t.Type != JSON { + iterator(Result{}, t) + return + } + json := t.Raw + var keys bool + var i int + var key, value Result + for ; i < len(json); i++ { + if json[i] == '{' { + i++ + key.Type = String + keys = true + break + } else if json[i] == '[' { + i++ + break + } + if json[i] > ' ' { + return + } + } + var str string + var vesc bool + var ok bool + for ; i < len(json); i++ { + if keys { + if json[i] != '"' { + continue + } + s := i + i, str, vesc, ok = parseString(json, i+1) + if !ok { + return + } + if vesc { + key.Str = unescape(str[1 : len(str)-1]) + } else { + key.Str = str[1 : len(str)-1] + } + key.Raw = str + key.Index = s + } + for ; i < len(json); i++ { + if json[i] <= ' ' || json[i] == ',' || json[i] == ':' { + continue + } + break + } + s := i + i, value, ok = parseAny(json, i, true) + if !ok { + return + } + value.Index = s + if !iterator(key, value) { + return + } + } +} + +// Map returns back an map of values. The result should be a JSON array. +func (t Result) Map() map[string]Result { + if t.Type != JSON { + return map[string]Result{} + } + r := t.arrayOrMap('{', false) + return r.o +} + +// Get searches result for the specified path. +// The result should be a JSON array or object. +func (t Result) Get(path string) Result { + return Get(t.Raw, path) +} + +type arrayOrMapResult struct { + a []Result + ai []interface{} + o map[string]Result + oi map[string]interface{} + vc byte +} + +func (t Result) arrayOrMap(vc byte, valueize bool) (r arrayOrMapResult) { + var json = t.Raw + var i int + var value Result + var count int + var key Result + if vc == 0 { + for ; i < len(json); i++ { + if json[i] == '{' || json[i] == '[' { + r.vc = json[i] + i++ + break + } + if json[i] > ' ' { + goto end + } + } + } else { + for ; i < len(json); i++ { + if json[i] == vc { + i++ + break + } + if json[i] > ' ' { + goto end + } + } + r.vc = vc + } + if r.vc == '{' { + if valueize { + r.oi = make(map[string]interface{}) + } else { + r.o = make(map[string]Result) + } + } else { + if valueize { + r.ai = make([]interface{}, 0) + } else { + r.a = make([]Result, 0) + } + } + for ; i < len(json); i++ { + if json[i] <= ' ' { + continue + } + // get next value + if json[i] == ']' || json[i] == '}' { + break + } + switch json[i] { + default: + if (json[i] >= '0' && json[i] <= '9') || json[i] == '-' { + value.Type = Number + value.Raw, value.Num = tonum(json[i:]) + } else { + continue + } + case '{', '[': + value.Type = JSON + value.Raw = squash(json[i:]) + case 'n': + value.Type = Null + value.Raw = tolit(json[i:]) + case 't': + value.Type = True + value.Raw = tolit(json[i:]) + case 'f': + value.Type = False + value.Raw = tolit(json[i:]) + case '"': + value.Type = String + value.Raw, value.Str = tostr(json[i:]) + } + i += len(value.Raw) - 1 + + if r.vc == '{' { + if count%2 == 0 { + key = value + } else { + if valueize { + r.oi[key.Str] = value.Value() + } else { + r.o[key.Str] = value + } + } + count++ + } else { + if valueize { + r.ai = append(r.ai, value.Value()) + } else { + r.a = append(r.a, value) + } + } + } +end: + return +} + +// Parse parses the json and returns a result. +// +// This function expects that the json is well-formed, and does not validate. +// Invalid json will not panic, but it may return back unexpected results. +// If you are consuming JSON from an unpredictable source then you may want to +// use the Valid function first. +func Parse(json string) Result { + var value Result + for i := 0; i < len(json); i++ { + if json[i] == '{' || json[i] == '[' { + value.Type = JSON + value.Raw = json[i:] // just take the entire raw + break + } + if json[i] <= ' ' { + continue + } + switch json[i] { + default: + if (json[i] >= '0' && json[i] <= '9') || json[i] == '-' { + value.Type = Number + value.Raw, value.Num = tonum(json[i:]) + } else { + return Result{} + } + case 'n': + value.Type = Null + value.Raw = tolit(json[i:]) + case 't': + value.Type = True + value.Raw = tolit(json[i:]) + case 'f': + value.Type = False + value.Raw = tolit(json[i:]) + case '"': + value.Type = String + value.Raw, value.Str = tostr(json[i:]) + } + break + } + return value +} + +// ParseBytes parses the json and returns a result. +// If working with bytes, this method preferred over Parse(string(data)) +func ParseBytes(json []byte) Result { + return Parse(string(json)) +} + +func squash(json string) string { + // expects that the lead character is a '[' or '{' + // squash the value, ignoring all nested arrays and objects. + // the first '[' or '{' has already been read + depth := 1 + for i := 1; i < len(json); i++ { + if json[i] >= '"' && json[i] <= '}' { + switch json[i] { + case '"': + i++ + s2 := i + for ; i < len(json); i++ { + if json[i] > '\\' { + continue + } + if json[i] == '"' { + // look for an escaped slash + if json[i-1] == '\\' { + n := 0 + for j := i - 2; j > s2-1; j-- { + if json[j] != '\\' { + break + } + n++ + } + if n%2 == 0 { + continue + } + } + break + } + } + case '{', '[': + depth++ + case '}', ']': + depth-- + if depth == 0 { + return json[:i+1] + } + } + } + } + return json +} + +func tonum(json string) (raw string, num float64) { + for i := 1; i < len(json); i++ { + // less than dash might have valid characters + if json[i] <= '-' { + if json[i] <= ' ' || json[i] == ',' { + // break on whitespace and comma + raw = json[:i] + num, _ = strconv.ParseFloat(raw, 64) + return + } + // could be a '+' or '-'. let's assume so. + continue + } + if json[i] < ']' { + // probably a valid number + continue + } + if json[i] == 'e' || json[i] == 'E' { + // allow for exponential numbers + continue + } + // likely a ']' or '}' + raw = json[:i] + num, _ = strconv.ParseFloat(raw, 64) + return + } + raw = json + num, _ = strconv.ParseFloat(raw, 64) + return +} + +func tolit(json string) (raw string) { + for i := 1; i < len(json); i++ { + if json[i] < 'a' || json[i] > 'z' { + return json[:i] + } + } + return json +} + +func tostr(json string) (raw string, str string) { + // expects that the lead character is a '"' + for i := 1; i < len(json); i++ { + if json[i] > '\\' { + continue + } + if json[i] == '"' { + return json[:i+1], json[1:i] + } + if json[i] == '\\' { + i++ + for ; i < len(json); i++ { + if json[i] > '\\' { + continue + } + if json[i] == '"' { + // look for an escaped slash + if json[i-1] == '\\' { + n := 0 + for j := i - 2; j > 0; j-- { + if json[j] != '\\' { + break + } + n++ + } + if n%2 == 0 { + continue + } + } + break + } + } + var ret string + if i+1 < len(json) { + ret = json[:i+1] + } else { + ret = json[:i] + } + return ret, unescape(json[1:i]) + } + } + return json, json[1:] +} + +// Exists returns true if value exists. +// +// if gjson.Get(json, "name.last").Exists(){ +// println("value exists") +// } +func (t Result) Exists() bool { + return t.Type != Null || len(t.Raw) != 0 +} + +// Value returns one of these types: +// +// bool, for JSON booleans +// float64, for JSON numbers +// Number, for JSON numbers +// string, for JSON string literals +// nil, for JSON null +// map[string]interface{}, for JSON objects +// []interface{}, for JSON arrays +// +func (t Result) Value() interface{} { + if t.Type == String { + return t.Str + } + switch t.Type { + default: + return nil + case False: + return false + case Number: + return t.Num + case JSON: + r := t.arrayOrMap(0, true) + if r.vc == '{' { + return r.oi + } else if r.vc == '[' { + return r.ai + } + return nil + case True: + return true + } +} + +func parseString(json string, i int) (int, string, bool, bool) { + var s = i + for ; i < len(json); i++ { + if json[i] > '\\' { + continue + } + if json[i] == '"' { + return i + 1, json[s-1 : i+1], false, true + } + if json[i] == '\\' { + i++ + for ; i < len(json); i++ { + if json[i] > '\\' { + continue + } + if json[i] == '"' { + // look for an escaped slash + if json[i-1] == '\\' { + n := 0 + for j := i - 2; j > 0; j-- { + if json[j] != '\\' { + break + } + n++ + } + if n%2 == 0 { + continue + } + } + return i + 1, json[s-1 : i+1], true, true + } + } + break + } + } + return i, json[s-1:], false, false +} + +func parseNumber(json string, i int) (int, string) { + var s = i + i++ + for ; i < len(json); i++ { + if json[i] <= ' ' || json[i] == ',' || json[i] == ']' || json[i] == '}' { + return i, json[s:i] + } + } + return i, json[s:] +} + +func parseLiteral(json string, i int) (int, string) { + var s = i + i++ + for ; i < len(json); i++ { + if json[i] < 'a' || json[i] > 'z' { + return i, json[s:i] + } + } + return i, json[s:] +} + +type arrayPathResult struct { + part string + path string + more bool + alogok bool + arrch bool + alogkey string + query struct { + on bool + path string + op string + value string + all bool + } +} + +func parseArrayPath(path string) (r arrayPathResult) { + for i := 0; i < len(path); i++ { + if path[i] == '.' { + r.part = path[:i] + r.path = path[i+1:] + r.more = true + return + } + if path[i] == '#' { + r.arrch = true + if i == 0 && len(path) > 1 { + if path[1] == '.' { + r.alogok = true + r.alogkey = path[2:] + r.path = path[:1] + } else if path[1] == '[' { + r.query.on = true + // query + i += 2 + // whitespace + for ; i < len(path); i++ { + if path[i] > ' ' { + break + } + } + s := i + for ; i < len(path); i++ { + if path[i] <= ' ' || + path[i] == '!' || + path[i] == '=' || + path[i] == '<' || + path[i] == '>' || + path[i] == '%' || + path[i] == ']' { + break + } + } + r.query.path = path[s:i] + // whitespace + for ; i < len(path); i++ { + if path[i] > ' ' { + break + } + } + if i < len(path) { + s = i + if path[i] == '!' { + if i < len(path)-1 && path[i+1] == '=' { + i++ + } + } else if path[i] == '<' || path[i] == '>' { + if i < len(path)-1 && path[i+1] == '=' { + i++ + } + } else if path[i] == '=' { + if i < len(path)-1 && path[i+1] == '=' { + s++ + i++ + } + } + i++ + r.query.op = path[s:i] + // whitespace + for ; i < len(path); i++ { + if path[i] > ' ' { + break + } + } + s = i + for ; i < len(path); i++ { + if path[i] == '"' { + i++ + s2 := i + for ; i < len(path); i++ { + if path[i] > '\\' { + continue + } + if path[i] == '"' { + // look for an escaped slash + if path[i-1] == '\\' { + n := 0 + for j := i - 2; j > s2-1; j-- { + if path[j] != '\\' { + break + } + n++ + } + if n%2 == 0 { + continue + } + } + break + } + } + } else if path[i] == ']' { + if i+1 < len(path) && path[i+1] == '#' { + r.query.all = true + } + break + } + } + if i > len(path) { + i = len(path) + } + v := path[s:i] + for len(v) > 0 && v[len(v)-1] <= ' ' { + v = v[:len(v)-1] + } + r.query.value = v + } + } + } + continue + } + } + r.part = path + r.path = "" + return +} + +type objectPathResult struct { + part string + path string + wild bool + more bool +} + +func parseObjectPath(path string) (r objectPathResult) { + for i := 0; i < len(path); i++ { + if path[i] == '.' { + r.part = path[:i] + r.path = path[i+1:] + r.more = true + return + } + if path[i] == '*' || path[i] == '?' { + r.wild = true + continue + } + if path[i] == '\\' { + // go into escape mode. this is a slower path that + // strips off the escape character from the part. + epart := []byte(path[:i]) + i++ + if i < len(path) { + epart = append(epart, path[i]) + i++ + for ; i < len(path); i++ { + if path[i] == '\\' { + i++ + if i < len(path) { + epart = append(epart, path[i]) + } + continue + } else if path[i] == '.' { + r.part = string(epart) + r.path = path[i+1:] + r.more = true + return + } else if path[i] == '*' || path[i] == '?' { + r.wild = true + } + epart = append(epart, path[i]) + } + } + // append the last part + r.part = string(epart) + return + } + } + r.part = path + return +} + +func parseSquash(json string, i int) (int, string) { + // expects that the lead character is a '[' or '{' + // squash the value, ignoring all nested arrays and objects. + // the first '[' or '{' has already been read + s := i + i++ + depth := 1 + for ; i < len(json); i++ { + if json[i] >= '"' && json[i] <= '}' { + switch json[i] { + case '"': + i++ + s2 := i + for ; i < len(json); i++ { + if json[i] > '\\' { + continue + } + if json[i] == '"' { + // look for an escaped slash + if json[i-1] == '\\' { + n := 0 + for j := i - 2; j > s2-1; j-- { + if json[j] != '\\' { + break + } + n++ + } + if n%2 == 0 { + continue + } + } + break + } + } + case '{', '[': + depth++ + case '}', ']': + depth-- + if depth == 0 { + i++ + return i, json[s:i] + } + } + } + } + return i, json[s:] +} + +func parseObject(c *parseContext, i int, path string) (int, bool) { + var pmatch, kesc, vesc, ok, hit bool + var key, val string + rp := parseObjectPath(path) + for i < len(c.json) { + for ; i < len(c.json); i++ { + if c.json[i] == '"' { + // parse_key_string + // this is slightly different from getting s string value + // because we don't need the outer quotes. + i++ + var s = i + for ; i < len(c.json); i++ { + if c.json[i] > '\\' { + continue + } + if c.json[i] == '"' { + i, key, kesc, ok = i+1, c.json[s:i], false, true + goto parse_key_string_done + } + if c.json[i] == '\\' { + i++ + for ; i < len(c.json); i++ { + if c.json[i] > '\\' { + continue + } + if c.json[i] == '"' { + // look for an escaped slash + if c.json[i-1] == '\\' { + n := 0 + for j := i - 2; j > 0; j-- { + if c.json[j] != '\\' { + break + } + n++ + } + if n%2 == 0 { + continue + } + } + i, key, kesc, ok = i+1, c.json[s:i], true, true + goto parse_key_string_done + } + } + break + } + } + key, kesc, ok = c.json[s:], false, false + parse_key_string_done: + break + } + if c.json[i] == '}' { + return i + 1, false + } + } + if !ok { + return i, false + } + if rp.wild { + if kesc { + pmatch = match.Match(unescape(key), rp.part) + } else { + pmatch = match.Match(key, rp.part) + } + } else { + if kesc { + pmatch = rp.part == unescape(key) + } else { + pmatch = rp.part == key + } + } + hit = pmatch && !rp.more + for ; i < len(c.json); i++ { + switch c.json[i] { + default: + continue + case '"': + i++ + i, val, vesc, ok = parseString(c.json, i) + if !ok { + return i, false + } + if hit { + if vesc { + c.value.Str = unescape(val[1 : len(val)-1]) + } else { + c.value.Str = val[1 : len(val)-1] + } + c.value.Raw = val + c.value.Type = String + return i, true + } + case '{': + if pmatch && !hit { + i, hit = parseObject(c, i+1, rp.path) + if hit { + return i, true + } + } else { + i, val = parseSquash(c.json, i) + if hit { + c.value.Raw = val + c.value.Type = JSON + return i, true + } + } + case '[': + if pmatch && !hit { + i, hit = parseArray(c, i+1, rp.path) + if hit { + return i, true + } + } else { + i, val = parseSquash(c.json, i) + if hit { + c.value.Raw = val + c.value.Type = JSON + return i, true + } + } + case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + i, val = parseNumber(c.json, i) + if hit { + c.value.Raw = val + c.value.Type = Number + c.value.Num, _ = strconv.ParseFloat(val, 64) + return i, true + } + case 't', 'f', 'n': + vc := c.json[i] + i, val = parseLiteral(c.json, i) + if hit { + c.value.Raw = val + switch vc { + case 't': + c.value.Type = True + case 'f': + c.value.Type = False + } + return i, true + } + } + break + } + } + return i, false +} +func queryMatches(rp *arrayPathResult, value Result) bool { + rpv := rp.query.value + if len(rpv) > 2 && rpv[0] == '"' && rpv[len(rpv)-1] == '"' { + rpv = rpv[1 : len(rpv)-1] + } + switch value.Type { + case String: + switch rp.query.op { + case "=": + return value.Str == rpv + case "!=": + return value.Str != rpv + case "<": + return value.Str < rpv + case "<=": + return value.Str <= rpv + case ">": + return value.Str > rpv + case ">=": + return value.Str >= rpv + case "%": + return match.Match(value.Str, rpv) + } + case Number: + rpvn, _ := strconv.ParseFloat(rpv, 64) + switch rp.query.op { + case "=": + return value.Num == rpvn + case "!=": + return value.Num != rpvn + case "<": + return value.Num < rpvn + case "<=": + return value.Num <= rpvn + case ">": + return value.Num > rpvn + case ">=": + return value.Num >= rpvn + } + case True: + switch rp.query.op { + case "=": + return rpv == "true" + case "!=": + return rpv != "true" + case ">": + return rpv == "false" + case ">=": + return true + } + case False: + switch rp.query.op { + case "=": + return rpv == "false" + case "!=": + return rpv != "false" + case "<": + return rpv == "true" + case "<=": + return true + } + } + return false +} +func parseArray(c *parseContext, i int, path string) (int, bool) { + var pmatch, vesc, ok, hit bool + var val string + var h int + var alog []int + var partidx int + var multires []byte + rp := parseArrayPath(path) + if !rp.arrch { + n, ok := parseUint(rp.part) + if !ok { + partidx = -1 + } else { + partidx = int(n) + } + } + for i < len(c.json)+1 { + if !rp.arrch { + pmatch = partidx == h + hit = pmatch && !rp.more + } + h++ + if rp.alogok { + alog = append(alog, i) + } + for ; ; i++ { + var ch byte + if i > len(c.json) { + break + } else if i == len(c.json) { + ch = ']' + } else { + ch = c.json[i] + } + switch ch { + default: + continue + case '"': + i++ + i, val, vesc, ok = parseString(c.json, i) + if !ok { + return i, false + } + if hit { + if rp.alogok { + break + } + if vesc { + c.value.Str = unescape(val[1 : len(val)-1]) + } else { + c.value.Str = val[1 : len(val)-1] + } + c.value.Raw = val + c.value.Type = String + return i, true + } + case '{': + if pmatch && !hit { + i, hit = parseObject(c, i+1, rp.path) + if hit { + if rp.alogok { + break + } + return i, true + } + } else { + i, val = parseSquash(c.json, i) + if rp.query.on { + res := Get(val, rp.query.path) + if queryMatches(&rp, res) { + if rp.more { + res = Get(val, rp.path) + } else { + res = Result{Raw: val, Type: JSON} + } + if rp.query.all { + if len(multires) == 0 { + multires = append(multires, '[') + } else { + multires = append(multires, ',') + } + multires = append(multires, res.Raw...) + } else { + c.value = res + return i, true + } + } + } else if hit { + if rp.alogok { + break + } + c.value.Raw = val + c.value.Type = JSON + return i, true + } + } + case '[': + if pmatch && !hit { + i, hit = parseArray(c, i+1, rp.path) + if hit { + if rp.alogok { + break + } + return i, true + } + } else { + i, val = parseSquash(c.json, i) + if hit { + if rp.alogok { + break + } + c.value.Raw = val + c.value.Type = JSON + return i, true + } + } + case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + i, val = parseNumber(c.json, i) + if hit { + if rp.alogok { + break + } + c.value.Raw = val + c.value.Type = Number + c.value.Num, _ = strconv.ParseFloat(val, 64) + return i, true + } + case 't', 'f', 'n': + vc := c.json[i] + i, val = parseLiteral(c.json, i) + if hit { + if rp.alogok { + break + } + c.value.Raw = val + switch vc { + case 't': + c.value.Type = True + case 'f': + c.value.Type = False + } + return i, true + } + case ']': + if rp.arrch && rp.part == "#" { + if rp.alogok { + var jsons = make([]byte, 0, 64) + jsons = append(jsons, '[') + + for j, k := 0, 0; j < len(alog); j++ { + _, res, ok := parseAny(c.json, alog[j], true) + if ok { + res := res.Get(rp.alogkey) + if res.Exists() { + if k > 0 { + jsons = append(jsons, ',') + } + jsons = append(jsons, []byte(res.Raw)...) + k++ + } + } + } + jsons = append(jsons, ']') + c.value.Type = JSON + c.value.Raw = string(jsons) + return i + 1, true + } + if rp.alogok { + break + } + c.value.Raw = val + c.value.Type = Number + c.value.Num = float64(h - 1) + c.calcd = true + return i + 1, true + } + if len(multires) > 0 && !c.value.Exists() { + c.value = Result{ + Raw: string(append(multires, ']')), + Type: JSON, + } + } + return i + 1, false + } + break + } + } + return i, false +} + +// ForEachLine iterates through lines of JSON as specified by the JSON Lines +// format (http://jsonlines.org/). +// Each line is returned as a GJSON Result. +func ForEachLine(json string, iterator func(line Result) bool) { + var res Result + var i int + for { + i, res, _ = parseAny(json, i, true) + if !res.Exists() { + break + } + if !iterator(res) { + return + } + } +} + +type parseContext struct { + json string + value Result + calcd bool + lines bool +} + +// Get searches json for the specified path. +// A path is in dot syntax, such as "name.last" or "age". +// When the value is found it's returned immediately. +// +// A path is a series of keys searated by a dot. +// A key may contain special wildcard characters '*' and '?'. +// To access an array value use the index as the key. +// To get the number of elements in an array or to access a child path, use the '#' character. +// The dot and wildcard character can be escaped with '\'. +// +// { +// "name": {"first": "Tom", "last": "Anderson"}, +// "age":37, +// "children": ["Sara","Alex","Jack"], +// "friends": [ +// {"first": "James", "last": "Murphy"}, +// {"first": "Roger", "last": "Craig"} +// ] +// } +// "name.last" >> "Anderson" +// "age" >> 37 +// "children" >> ["Sara","Alex","Jack"] +// "children.#" >> 3 +// "children.1" >> "Alex" +// "child*.2" >> "Jack" +// "c?ildren.0" >> "Sara" +// "friends.#.first" >> ["James","Roger"] +// +// This function expects that the json is well-formed, and does not validate. +// Invalid json will not panic, but it may return back unexpected results. +// If you are consuming JSON from an unpredictable source then you may want to +// use the Valid function first. +func Get(json, path string) Result { + var i int + var c = &parseContext{json: json} + if len(path) >= 2 && path[0] == '.' && path[1] == '.' { + c.lines = true + parseArray(c, 0, path[2:]) + } else { + for ; i < len(c.json); i++ { + if c.json[i] == '{' { + i++ + parseObject(c, i, path) + break + } + if c.json[i] == '[' { + i++ + parseArray(c, i, path) + break + } + } + } + fillIndex(json, c) + return c.value +} + +// GetBytes searches json for the specified path. +// If working with bytes, this method preferred over Get(string(data), path) +func GetBytes(json []byte, path string) Result { + return getBytes(json, path) +} + +// runeit returns the rune from the the \uXXXX +func runeit(json string) rune { + n, _ := strconv.ParseUint(json[:4], 16, 64) + return rune(n) +} + +// unescape unescapes a string +func unescape(json string) string { //, error) { + var str = make([]byte, 0, len(json)) + for i := 0; i < len(json); i++ { + switch { + default: + str = append(str, json[i]) + case json[i] < ' ': + return string(str) + case json[i] == '\\': + i++ + if i >= len(json) { + return string(str) + } + switch json[i] { + default: + return string(str) + case '\\': + str = append(str, '\\') + case '/': + str = append(str, '/') + case 'b': + str = append(str, '\b') + case 'f': + str = append(str, '\f') + case 'n': + str = append(str, '\n') + case 'r': + str = append(str, '\r') + case 't': + str = append(str, '\t') + case '"': + str = append(str, '"') + case 'u': + if i+5 > len(json) { + return string(str) + } + r := runeit(json[i+1:]) + i += 5 + if utf16.IsSurrogate(r) { + // need another code + if len(json[i:]) >= 6 && json[i] == '\\' && json[i+1] == 'u' { + // we expect it to be correct so just consume it + r = utf16.DecodeRune(r, runeit(json[i+2:])) + i += 6 + } + } + // provide enough space to encode the largest utf8 possible + str = append(str, 0, 0, 0, 0, 0, 0, 0, 0) + n := utf8.EncodeRune(str[len(str)-8:], r) + str = str[:len(str)-8+n] + i-- // backtrack index by one + } + } + } + return string(str) +} + +// Less return true if a token is less than another token. +// The caseSensitive paramater is used when the tokens are Strings. +// The order when comparing two different type is: +// +// Null < False < Number < String < True < JSON +// +func (t Result) Less(token Result, caseSensitive bool) bool { + if t.Type < token.Type { + return true + } + if t.Type > token.Type { + return false + } + if t.Type == String { + if caseSensitive { + return t.Str < token.Str + } + return stringLessInsensitive(t.Str, token.Str) + } + if t.Type == Number { + return t.Num < token.Num + } + return t.Raw < token.Raw +} + +func stringLessInsensitive(a, b string) bool { + for i := 0; i < len(a) && i < len(b); i++ { + if a[i] >= 'A' && a[i] <= 'Z' { + if b[i] >= 'A' && b[i] <= 'Z' { + // both are uppercase, do nothing + if a[i] < b[i] { + return true + } else if a[i] > b[i] { + return false + } + } else { + // a is uppercase, convert a to lowercase + if a[i]+32 < b[i] { + return true + } else if a[i]+32 > b[i] { + return false + } + } + } else if b[i] >= 'A' && b[i] <= 'Z' { + // b is uppercase, convert b to lowercase + if a[i] < b[i]+32 { + return true + } else if a[i] > b[i]+32 { + return false + } + } else { + // neither are uppercase + if a[i] < b[i] { + return true + } else if a[i] > b[i] { + return false + } + } + } + return len(a) < len(b) +} + +// parseAny parses the next value from a json string. +// A Result is returned when the hit param is set. +// The return values are (i int, res Result, ok bool) +func parseAny(json string, i int, hit bool) (int, Result, bool) { + var res Result + var val string + for ; i < len(json); i++ { + if json[i] == '{' || json[i] == '[' { + i, val = parseSquash(json, i) + if hit { + res.Raw = val + res.Type = JSON + } + return i, res, true + } + if json[i] <= ' ' { + continue + } + switch json[i] { + case '"': + i++ + var vesc bool + var ok bool + i, val, vesc, ok = parseString(json, i) + if !ok { + return i, res, false + } + if hit { + res.Type = String + res.Raw = val + if vesc { + res.Str = unescape(val[1 : len(val)-1]) + } else { + res.Str = val[1 : len(val)-1] + } + } + return i, res, true + case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + i, val = parseNumber(json, i) + if hit { + res.Raw = val + res.Type = Number + res.Num, _ = strconv.ParseFloat(val, 64) + } + return i, res, true + case 't', 'f', 'n': + vc := json[i] + i, val = parseLiteral(json, i) + if hit { + res.Raw = val + switch vc { + case 't': + res.Type = True + case 'f': + res.Type = False + } + return i, res, true + } + } + } + return i, res, false +} + +var ( // used for testing + testWatchForFallback bool + testLastWasFallback bool +) + +// GetMany searches json for the multiple paths. +// The return value is a Result array where the number of items +// will be equal to the number of input paths. +func GetMany(json string, path ...string) []Result { + res := make([]Result, len(path)) + for i, path := range path { + res[i] = Get(json, path) + } + return res +} + +// GetManyBytes searches json for the multiple paths. +// The return value is a Result array where the number of items +// will be equal to the number of input paths. +func GetManyBytes(json []byte, path ...string) []Result { + return GetMany(string(json), path...) +} + +var fieldsmu sync.RWMutex +var fields = make(map[string]map[string]int) + +func assign(jsval Result, goval reflect.Value) { + if jsval.Type == Null { + return + } + switch goval.Kind() { + default: + case reflect.Ptr: + if !goval.IsNil() { + newval := reflect.New(goval.Elem().Type()) + assign(jsval, newval.Elem()) + goval.Elem().Set(newval.Elem()) + } else { + newval := reflect.New(goval.Type().Elem()) + assign(jsval, newval.Elem()) + goval.Set(newval) + } + case reflect.Struct: + fieldsmu.RLock() + sf := fields[goval.Type().String()] + fieldsmu.RUnlock() + if sf == nil { + fieldsmu.Lock() + sf = make(map[string]int) + for i := 0; i < goval.Type().NumField(); i++ { + f := goval.Type().Field(i) + tag := strings.Split(f.Tag.Get("json"), ",")[0] + if tag != "-" { + if tag != "" { + sf[tag] = i + sf[f.Name] = i + } else { + sf[f.Name] = i + } + } + } + fields[goval.Type().String()] = sf + fieldsmu.Unlock() + } + jsval.ForEach(func(key, value Result) bool { + if idx, ok := sf[key.Str]; ok { + f := goval.Field(idx) + if f.CanSet() { + assign(value, f) + } + } + return true + }) + case reflect.Slice: + if goval.Type().Elem().Kind() == reflect.Uint8 && jsval.Type == String { + data, _ := base64.StdEncoding.DecodeString(jsval.String()) + goval.Set(reflect.ValueOf(data)) + } else { + jsvals := jsval.Array() + slice := reflect.MakeSlice(goval.Type(), len(jsvals), len(jsvals)) + for i := 0; i < len(jsvals); i++ { + assign(jsvals[i], slice.Index(i)) + } + goval.Set(slice) + } + case reflect.Array: + i, n := 0, goval.Len() + jsval.ForEach(func(_, value Result) bool { + if i == n { + return false + } + assign(value, goval.Index(i)) + i++ + return true + }) + case reflect.Map: + if goval.Type().Key().Kind() == reflect.String && goval.Type().Elem().Kind() == reflect.Interface { + goval.Set(reflect.ValueOf(jsval.Value())) + } + case reflect.Interface: + goval.Set(reflect.ValueOf(jsval.Value())) + case reflect.Bool: + goval.SetBool(jsval.Bool()) + case reflect.Float32, reflect.Float64: + goval.SetFloat(jsval.Float()) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + goval.SetInt(jsval.Int()) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + goval.SetUint(jsval.Uint()) + case reflect.String: + goval.SetString(jsval.String()) + } + if len(goval.Type().PkgPath()) > 0 { + v := goval.Addr() + if v.Type().NumMethod() > 0 { + if u, ok := v.Interface().(json.Unmarshaler); ok { + u.UnmarshalJSON([]byte(jsval.Raw)) + } + } + } +} + +var validate uintptr = 1 + +// UnmarshalValidationEnabled provides the option to disable JSON validation +// during the Unmarshal routine. Validation is enabled by default. +// +// Deprecated: Use encoder/json.Unmarshal instead +func UnmarshalValidationEnabled(enabled bool) { + if enabled { + atomic.StoreUintptr(&validate, 1) + } else { + atomic.StoreUintptr(&validate, 0) + } +} + +// Unmarshal loads the JSON data into the value pointed to by v. +// +// This function works almost identically to json.Unmarshal except that +// gjson.Unmarshal will automatically attempt to convert JSON values to any Go +// type. For example, the JSON string "100" or the JSON number 100 can be equally +// assigned to Go string, int, byte, uint64, etc. This rule applies to all types. +// +// Deprecated: Use encoder/json.Unmarshal instead +func Unmarshal(data []byte, v interface{}) error { + if atomic.LoadUintptr(&validate) == 1 { + _, ok := validpayload(data, 0) + if !ok { + return errors.New("invalid json") + } + } + if v := reflect.ValueOf(v); v.Kind() == reflect.Ptr { + assign(ParseBytes(data), v) + } + return nil +} + +func validpayload(data []byte, i int) (outi int, ok bool) { + for ; i < len(data); i++ { + switch data[i] { + default: + i, ok = validany(data, i) + if !ok { + return i, false + } + for ; i < len(data); i++ { + switch data[i] { + default: + return i, false + case ' ', '\t', '\n', '\r': + continue + } + } + return i, true + case ' ', '\t', '\n', '\r': + continue + } + } + return i, false +} +func validany(data []byte, i int) (outi int, ok bool) { + for ; i < len(data); i++ { + switch data[i] { + default: + return i, false + case ' ', '\t', '\n', '\r': + continue + case '{': + return validobject(data, i+1) + case '[': + return validarray(data, i+1) + case '"': + return validstring(data, i+1) + case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + return validnumber(data, i+1) + case 't': + return validtrue(data, i+1) + case 'f': + return validfalse(data, i+1) + case 'n': + return validnull(data, i+1) + } + } + return i, false +} +func validobject(data []byte, i int) (outi int, ok bool) { + for ; i < len(data); i++ { + switch data[i] { + default: + return i, false + case ' ', '\t', '\n', '\r': + continue + case '}': + return i + 1, true + case '"': + key: + if i, ok = validstring(data, i+1); !ok { + return i, false + } + if i, ok = validcolon(data, i); !ok { + return i, false + } + if i, ok = validany(data, i); !ok { + return i, false + } + if i, ok = validcomma(data, i, '}'); !ok { + return i, false + } + if data[i] == '}' { + return i + 1, true + } + i++ + for ; i < len(data); i++ { + switch data[i] { + default: + return i, false + case ' ', '\t', '\n', '\r': + continue + case '"': + goto key + } + } + return i, false + } + } + return i, false +} +func validcolon(data []byte, i int) (outi int, ok bool) { + for ; i < len(data); i++ { + switch data[i] { + default: + return i, false + case ' ', '\t', '\n', '\r': + continue + case ':': + return i + 1, true + } + } + return i, false +} +func validcomma(data []byte, i int, end byte) (outi int, ok bool) { + for ; i < len(data); i++ { + switch data[i] { + default: + return i, false + case ' ', '\t', '\n', '\r': + continue + case ',': + return i, true + case end: + return i, true + } + } + return i, false +} +func validarray(data []byte, i int) (outi int, ok bool) { + for ; i < len(data); i++ { + switch data[i] { + default: + for ; i < len(data); i++ { + if i, ok = validany(data, i); !ok { + return i, false + } + if i, ok = validcomma(data, i, ']'); !ok { + return i, false + } + if data[i] == ']' { + return i + 1, true + } + } + case ' ', '\t', '\n', '\r': + continue + case ']': + return i + 1, true + } + } + return i, false +} +func validstring(data []byte, i int) (outi int, ok bool) { + for ; i < len(data); i++ { + if data[i] < ' ' { + return i, false + } else if data[i] == '\\' { + i++ + if i == len(data) { + return i, false + } + switch data[i] { + default: + return i, false + case '"', '\\', '/', 'b', 'f', 'n', 'r', 't': + case 'u': + for j := 0; j < 4; j++ { + i++ + if i >= len(data) { + return i, false + } + if !((data[i] >= '0' && data[i] <= '9') || + (data[i] >= 'a' && data[i] <= 'f') || + (data[i] >= 'A' && data[i] <= 'F')) { + return i, false + } + } + } + } else if data[i] == '"' { + return i + 1, true + } + } + return i, false +} +func validnumber(data []byte, i int) (outi int, ok bool) { + i-- + // sign + if data[i] == '-' { + i++ + } + // int + if i == len(data) { + return i, false + } + if data[i] == '0' { + i++ + } else { + for ; i < len(data); i++ { + if data[i] >= '0' && data[i] <= '9' { + continue + } + break + } + } + // frac + if i == len(data) { + return i, true + } + if data[i] == '.' { + i++ + if i == len(data) { + return i, false + } + if data[i] < '0' || data[i] > '9' { + return i, false + } + i++ + for ; i < len(data); i++ { + if data[i] >= '0' && data[i] <= '9' { + continue + } + break + } + } + // exp + if i == len(data) { + return i, true + } + if data[i] == 'e' || data[i] == 'E' { + i++ + if i == len(data) { + return i, false + } + if data[i] == '+' || data[i] == '-' { + i++ + } + if i == len(data) { + return i, false + } + if data[i] < '0' || data[i] > '9' { + return i, false + } + i++ + for ; i < len(data); i++ { + if data[i] >= '0' && data[i] <= '9' { + continue + } + break + } + } + return i, true +} + +func validtrue(data []byte, i int) (outi int, ok bool) { + if i+3 <= len(data) && data[i] == 'r' && data[i+1] == 'u' && data[i+2] == 'e' { + return i + 3, true + } + return i, false +} +func validfalse(data []byte, i int) (outi int, ok bool) { + if i+4 <= len(data) && data[i] == 'a' && data[i+1] == 'l' && data[i+2] == 's' && data[i+3] == 'e' { + return i + 4, true + } + return i, false +} +func validnull(data []byte, i int) (outi int, ok bool) { + if i+3 <= len(data) && data[i] == 'u' && data[i+1] == 'l' && data[i+2] == 'l' { + return i + 3, true + } + return i, false +} + +// Valid returns true if the input is valid json. +// +// if !gjson.Valid(json) { +// return errors.New("invalid json") +// } +// value := gjson.Get(json, "name.last") +// +func Valid(json string) bool { + _, ok := validpayload([]byte(json), 0) + return ok +} + +// ValidBytes returns true if the input is valid json. +// +// if !gjson.Valid(json) { +// return errors.New("invalid json") +// } +// value := gjson.Get(json, "name.last") +// +// If working with bytes, this method preferred over Valid(string(data)) +// +func ValidBytes(json []byte) bool { + _, ok := validpayload(json, 0) + return ok +} + +func parseUint(s string) (n uint64, ok bool) { + var i int + if i == len(s) { + return 0, false + } + for ; i < len(s); i++ { + if s[i] >= '0' && s[i] <= '9' { + n = n*10 + uint64(s[i]-'0') + } else { + return 0, false + } + } + return n, true +} + +func parseInt(s string) (n int64, ok bool) { + var i int + var sign bool + if len(s) > 0 && s[0] == '-' { + sign = true + i++ + } + if i == len(s) { + return 0, false + } + for ; i < len(s); i++ { + if s[i] >= '0' && s[i] <= '9' { + n = n*10 + int64(s[i]-'0') + } else { + return 0, false + } + } + if sign { + return n * -1, true + } + return n, true +} + +const minUint53 = 0 +const maxUint53 = 4503599627370495 +const minInt53 = -2251799813685248 +const maxInt53 = 2251799813685247 + +func floatToUint(f float64) (n uint64, ok bool) { + n = uint64(f) + if float64(n) == f && n >= minUint53 && n <= maxUint53 { + return n, true + } + return 0, false +} + +func floatToInt(f float64) (n int64, ok bool) { + n = int64(f) + if float64(n) == f && n >= minInt53 && n <= maxInt53 { + return n, true + } + return 0, false +} diff --git a/vendor/github.com/tidwall/gjson/gjson_gae.go b/vendor/github.com/tidwall/gjson/gjson_gae.go new file mode 100644 index 00000000000..cbe2ab420b7 --- /dev/null +++ b/vendor/github.com/tidwall/gjson/gjson_gae.go @@ -0,0 +1,10 @@ +//+build appengine + +package gjson + +func getBytes(json []byte, path string) Result { + return Get(string(json), path) +} +func fillIndex(json string, c *parseContext) { + // noop. Use zero for the Index value. +} diff --git a/vendor/github.com/tidwall/gjson/gjson_ngae.go b/vendor/github.com/tidwall/gjson/gjson_ngae.go new file mode 100644 index 00000000000..ff313a78796 --- /dev/null +++ b/vendor/github.com/tidwall/gjson/gjson_ngae.go @@ -0,0 +1,73 @@ +//+build !appengine + +package gjson + +import ( + "reflect" + "unsafe" +) + +// getBytes casts the input json bytes to a string and safely returns the +// results as uniquely allocated data. This operation is intended to minimize +// copies and allocations for the large json string->[]byte. +func getBytes(json []byte, path string) Result { + var result Result + if json != nil { + // unsafe cast to string + result = Get(*(*string)(unsafe.Pointer(&json)), path) + result = fromBytesGet(result) + } + return result +} + +func fromBytesGet(result Result) Result { + // safely get the string headers + rawhi := *(*reflect.StringHeader)(unsafe.Pointer(&result.Raw)) + strhi := *(*reflect.StringHeader)(unsafe.Pointer(&result.Str)) + // create byte slice headers + rawh := reflect.SliceHeader{Data: rawhi.Data, Len: rawhi.Len} + strh := reflect.SliceHeader{Data: strhi.Data, Len: strhi.Len} + if strh.Data == 0 { + // str is nil + if rawh.Data == 0 { + // raw is nil + result.Raw = "" + } else { + // raw has data, safely copy the slice header to a string + result.Raw = string(*(*[]byte)(unsafe.Pointer(&rawh))) + } + result.Str = "" + } else if rawh.Data == 0 { + // raw is nil + result.Raw = "" + // str has data, safely copy the slice header to a string + result.Str = string(*(*[]byte)(unsafe.Pointer(&strh))) + } else if strh.Data >= rawh.Data && + int(strh.Data)+strh.Len <= int(rawh.Data)+rawh.Len { + // Str is a substring of Raw. + start := int(strh.Data - rawh.Data) + // safely copy the raw slice header + result.Raw = string(*(*[]byte)(unsafe.Pointer(&rawh))) + // substring the raw + result.Str = result.Raw[start : start+strh.Len] + } else { + // safely copy both the raw and str slice headers to strings + result.Raw = string(*(*[]byte)(unsafe.Pointer(&rawh))) + result.Str = string(*(*[]byte)(unsafe.Pointer(&strh))) + } + return result +} + +// fillIndex finds the position of Raw data and assigns it to the Index field +// of the resulting value. If the position cannot be found then Index zero is +// used instead. +func fillIndex(json string, c *parseContext) { + if len(c.value.Raw) > 0 && !c.calcd { + jhdr := *(*reflect.StringHeader)(unsafe.Pointer(&json)) + rhdr := *(*reflect.StringHeader)(unsafe.Pointer(&(c.value.Raw))) + c.value.Index = int(rhdr.Data - jhdr.Data) + if c.value.Index < 0 || c.value.Index >= len(json) { + c.value.Index = 0 + } + } +} diff --git a/vendor/github.com/tidwall/gjson/logo.png b/vendor/github.com/tidwall/gjson/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..17a8bbe9d651e5d79cbd59a3645a152443440ad6 GIT binary patch literal 15936 zcmdseRa9I})9xU_0znfrFi3EBcXtB8-Q8tyw?MEE+&u)>;O-g-!QF!n?wrm0ecyln zi+`Pqb92@@b204MboZ|AuIj4isoIgsic)AO1SlX72u(&>{38ei4_w2jBEbT8g#0}> zfj{`J5}K~64(6^NM$Tp+5mN_aGq8-Ek%ieuGb2+^ry(I^6HUo1oax$2(u&{u+covw&X$WR|Y3j=W4v9v&Wy9&C&b&K688JUl#1%&bhTtPDU41{Y6zS0f06 zy$kt&Mi4i1F>$tXbhUD@2fvJHWbEMPDnJHE`mZV2IVvdp*TD8J|6V9y$(SHUj!Z0! z%uIH6FZ24RwTtUVv;Qr||Jd3^&C}70>7$v8gPXGnupj2+|LF{@-T(PPFAV`{c$J*3 zfK4&76?ZUkvoo`Il@S*p1OCHkYGukR$|buuylm3H z<}7aJY~^ldD(UQC2mWW3d9D5jDR|g;*tw0_nHkJkjZ7HWS$Vh_jJR2u8Q6HtIgQO& zSd4j$Sjqm~-}Jw&-oLaIxC6|@@jn9bnzJ+W7@M$}GZ-0hn=-JlaPctk7_+f5@NjXm zahsTP8Jln!kud=xGQB9ye^aFY+yb=o6ILj1BiHWiOsHjwSN(6-S#&IQQX(?!~oRWla6k zvj3m#lk>9cCv!wc6L6R*c~`T5NT-O!e%&k`LRLWCk5V)xyH@qD}#ba7*ern zD^KwwL26x@_H9n^_xJEwj%`LF z2_M&3l{$q7;~URr@StEw0z3u!#j3ZdE5%$S1w0iM^K2@_(3KW1T0xFBPH3#1nB2_f zvBMJo!z+(+E_6G$0(R-JL<$OXHd^e=iug&_x%->yfVBZnFV3sIHbvZbAFwXaW7AoKsJ9QG z>$+mQV_~P4&z8jBcSvrf!^$eZC-BSslJQJOm4Sir>t!xQeA0}meB*bk78Ll6JYotw zFN_tm1+Ok3dz)A2wN_Xinfj#Ee?*6SsMzd_g;n4n@*KNU9c5|uUDdVb5;09jU=BA8 z7Ip^CUs>Q(aGI&ybtyH$7K|L2U(LdFslcD0RJYaHH%xx{O^TZmsdabO$yfZSTX|)r z=-oR$toP)*A6N)!Ci&fYk#B|NMo}VC;kw_(;DJ=HK8MnXxCRjx zm+Ele5%M~`%RXZFhRaja zofW}k`izaw_BnN>(4NODngeC2ev#$a0Za@}FunO>tS9?CYq6*J5?4v-!SCmNEg}($ zsiIfoly7`UXRh?F`!)YcJa>S4yLz&L#;kL&6#~9Ni+?|0z}yLl+KUVh*{UN zQL82>H7J0_BwzsZbE2-owg@HT^}ncXoZ;dh4@Ag(^&lQPK)^E=LOJs3m1N!Uj9F54V7Fy*9;Hf!S| z2Vtfjj8{AZ6|UJ&-*wZR;=h8&K-WF?$U44F^rNysF*k#YfwM3ww(UIiz!K$Vl6g^; zpZSmDI41>YtUMi>*8?muaBUxB;C6#-g+)6l$2v@q$uZDbJ6wES8#l*s2D<1?VzXJ$ zNn3AE*NNnAtmKenlM+7=mM9>ZV6zb+`lI$2@hpIeP1DdcS*Cvz5A~9(XQ5ee8Zy?1 zV$H!Cd=InD(OPcd;^t`I|2d8dNC%ws6z&4#gegDv>rH+oz!8Nz>NP}eD-R;bVvA0S z5fJb?Ou@|fK(P*e**ICmfISbcs}Y$fZuREW@ZFBDNYmhXW7PA6V7+}jLHzU1y=p!n z^hyRvQ|hIqYYoin6oO1NuT}m&C#3Y93YZnNA2Tz$8cr96%FkEFIxLhO1c8xa?YS>1 zYfbRvnrv%W@wwZlMg$41zv!F1Uthy~PJ0n;XM;%WG#G1Z(D~^_heW34*YaC}4fwaY zQ_|5S2@Q6+L&grf$wpF2KXm2n`%skl-*5HsEQC3gz~7nJ8i!$efQc!-p`}FGdT|bp zjc+K291ok>nAU4-{|as^#|^q`l>3ommlA=!Yk*~f4lIlN?BhIKO<)tnThs?ySx%(Q zZ{BqMNZOg7QO=}w219Yn;z5ayrjclN12jx{->DqdA?>)@B*M55Z6*>9uOVG^g4+gB$$ z&!XEFAyF0vK@Hi45CHqg@K*IEb;v3e@Qc{QP%M+qSM^o+6flzV`6>&uh)6U4UOl>Y zUko)LSj?`l;=xRs21!m{!rvOi>)at<`A6r#TDg{HRsnKvk6eoC&S-x#Mq{7vf>j;i zpoU-IgJbwqGu*^D+T5dUG0a_I-j;HEIj*%e?#p&>hhO%ho;0*k&ga~7^2w21Ks!^N zJ88HQ;e-ECm0XE^;oQz-+JymauGTlg2o=jQD#D!&eneT>3#Y{`@=}~!gct3epErdK zU`nI?9GgnOgA%X>7A8>kKcDjv8zn1;!d*!BC&n1*8?K}4xYS*8J-uGJt;RUTVXir%{}C0=q5B02D<=xOiYxcn?4l1}Em4lV07IMeqs8t@G*e)leU z^LZ&bK;v9xiiAiYvcgAIY^ z{hsN<54t}2vf2`*A~p<)503a+JOop=cS87$R%0Y9b}n%u{HMx2QvUVcnksc$0lfh| zO+0575+Z)~?&@C8M99dsa?f$2%sd?hq|Ds&;G=9fK537ECE{4uO>1(q_^&|Vlh_Gr>&p4j#CTe1bR2DJ_0}1+G9&6IH~WZ{A=YBXE4q6-nx9bn^kCGD zKq`a|i1oH?5f(Dp2PUamK}6jL3(BY6_{%!xtDA#drfkJ=#M6{`HRjVF)bpUQq&Ef6 zyRCSNEr>MF%a_m1u9p-Py~pK8D7c5rSo^`&g9y&-@)_~;#h*R~7VEJ@5*>Y+Ig5{* z{KaPMCB3%XAcWYoc^46OK;ZSTx9HGO-}GxhBWm@2u4qc+2r6b_aBFo$7yfX_H3CEQ zaAx(~OTHSE^w9jPe|Db0^FFGfRAv|V-o14(YFFLKu2^KV)OYV$MilR}0%8L8mXrX# z?aXNsv{y&t%MTik%-A};(yQA&Eexy&Tel+t@?5=F>qE*mRdi^0cko1yYtB`K{ocK2 ze?^g1EeKeXFLrFjvhUiy$_GM26OFkX+qLLsXfuf_ zAiE6m$DGN>RDMk}<35n$|sHc(gYzBw5(l8USpk56G_tt5v zT2RPk>)0{R5nwf2>%09ZBb^t1O4UtbrXz%J=bQ7Aj>{^+T`g!YD&6-HAA&h~?oH{F z-*(;OvA=)%wE6M!TY|;d-~Kce;ebbI&U$_a!QI&6cp@;QC-Ynh;FOeB9wl}vAGWi_ zI6Eb)y%&;;oN5BIUcqEK(H~zuZ*l#_kGGu+l9nrvvTET;x{?m&Pz z)6(6l?`Nf+N{at87srvs@R$3YNC|1B5vn8BpnndfCUEtywUobq0}Eh3Y)W$a((OMt zeVTMUi_J2y7^X9|!b=N>O53xrkSYtG=HIjP+ixUv0p0}H|0JbRs^%hA!Jwk}>L&MM z80l=$8*p?p5nqzB2flUC+qS5co0cA{{~DgUCuuOfxzg5oVle|X6R|&z9HyjTBz7=W z6(hQ3!ou1{BT@o3I#0`zwh{7Z9D2aZ8IS0|LDbPB~{a9 zxp|-)J|}zIePx$CI@kZ{&VL_`9mP8HDU)6(_8QG5JF(05;fr7V+K5bA1Etfxgh#5W>6P@@ucg&Jzly%#@3VwSKV{@&j#^M( zTlz^^ZmfJ%6rHQ3%yEH?9(L!Qb(!Do<{dt5FB1GvxNV4LI{Pjr0R!MUrMb5A4pX>C zAxvpRy)idQ1E-@e{fUAX=xnu;V!H}&V4CM$%=yv*v=$M;MOYIcj@A-jG z?Rpip=$Wr@_3CPVC#tvr@>g2)Q4aUQ5cATrcEjnW2##{myh=>MLI$B&y6jSWY!swkxM01Od*SEg zL*Mh~ni#4$)U3y+i&vQhK|9qf!@Ie%77J-pfjx;IqN%|*VX(lO%{8>t0l{p^gB)j@ zJH57Z00N>gy2Swd*!!iFW0?|!a>O(rOZL2g&I>ppw~Od1Z5(JPKd=Q&^ICR7OvUvu z?=;QVzQ(dwYT=aA*Bg?3&P}yAThCa2|K}GvR-m-L*_!MseF=o;M zOc|!hnA_@||7VYZk>zZ=UNBMA*Vtu^yo#aiaXg2+Bh3A_ z*k(u*3VX`)Fpa0tfiD<{pqWjZHrb&h8UI8iP-1;S@Ctq-P$n^!{&NzMp(LvzzPbW5 z&{drM`nf{0E^A*AS;QZ4;qFHOENld#)$;q_vGmq_=O^r}ovg!Le`|uzGHczAnUBcG z`*F2&g&n(N8{#Biba+rq`+AT}7E##C*+mWAHcl~e8IF*BlD}ptBDDp7inFHgCu%CG zMQGj9<9umYwTdv9IK}7?I-Az9B4JJeXGNp&Y~9dg6EMByhM;=CJgzMOHPo|HyPwEF z>AJ~_8b(2SFF>9)iwy@AzPGmoydb?M{G9dKy6!c@N}i|Lt*7q!;Msy>FWlp16n&Oo zmA=5&{-W||a^yg79H=mpEMh=;B08fpy9ZhoVvt6ohvYk&9Dko*huy|_Vcj-#n-^%Y zs(?&;92eWq(+`)_??I^-a?v+g0KQKt*?l$Eba3dO7`iLIHz8VPfP?g+cWvw%=+?0$ zw8JC0o-q5rI(C{(+>M|Yd?#NqbO%~Z8MLGynQ3(_s2no9^BqZSUb>Vsq3+-&Z~pLd z_+I&t*zl@gEawwB70Gx!2LBP^FYe$`QL1{2+{*4>GM~87Lx9~C-tG~*?hRhhc=M|v zKz`3aEk0yz8Txxs?~Urdp+R0uG5?!T)2G;O-?IC#fm9y}er2kwt<)7;#|LG5_jh)$ z2Sc<@EHgtod#3K*7;KqZ%_)YTiJL+t8o!4XIkwUN@t`E$Yq7=AK*!U9H?3HzBPCK4 z1;{FwV^;P{uD18>%3tX3j%6R_MIMv&@f1V4k%w(DWcZccnI;9GVC^$jwAX_dysW1j zM`z~{y%DsP60N1h*GBx4LRqvFn|B3AG`4>4XHx@(`6tg$H8rQaVQ_b9%`q`{{4}r2 z=OP;`SSJVDA#p82i#aqb_VL+eJ|9?gaG4go0A~hZ1+MA=FDhbXUeMCt&#CDcTTP6} zxtZStJ{EiC|G-Fk6MeJpZ%|$9Rn@xfF(WyQBgyfy#vHNweKWz*P#EpEYl?H6KHpMe zvf?*tGh4Og7>Gv?^om*Qpu)Eyho@?PDF~@Ea^wN^&DqhE;aD3KU>Xh(01Gsi_yYOZ*mM@2DpdkOw<+vQpws53fo{ z&wajB`u(vzs_9!4?h&i8?CuR2u+G84+26IV*Kn`^r>fu|-veN(1Zn=_`3YN(XcZ|e1tk=THcWJbl zsYeS(c=;b95uXNv-?n*7v=S{~uMPFRje+fM2G~eIo<0k{7KhLSksr739qkp}2P;MF z#RHd6+;5fK#zzu#)US{5c4HuGKPuG*;1FXZM{@sop4IH+(rfX}ZIm^whc{X<>-WUx=Qlw6^^~L>&&#_wbjA%NM-BO- ziWfJ(=D?Q^MhL3 zhf&VlDz1)-0Zchw2k$O-icKDM^}Uif#Kk9m;R`tpuwOp5cG?~QR}6ZxbSN_q>1prP zakr4;NOzZ;Y^{A;uGpF=CV3`sPEq~4a=}w;)_$M_5ewbN6EZm-FnM6t)+yo$1Xzo8 zIpW+8HNz7 zlkMVkuwUHCF6C}Aw-E@>xDL3Tfbn_4oq*Rv>wJw&{jvDbvdHt}yMJ0Gb!csez+k{> zteQ9MH2^!E*)-B03O|-kOhidM>4!7jddvni6e+3Rhgmz{jFUl94}&j}0ME)Fp%mZ0 zXlE;DA$P!z`Ey7={LOdHkNp(zqOd+a5rWUY-&`B1w6#jF;9ooo#D?cdNf9r=4ZwBl zOS1FBam;Twsy90oaUIT{CrE+w=xr2`$no9-7C=scPgFEzAOBOZ<*$GeQ77bZwMSg@@7fXHj)VSZO3m{PYxnm7QPW=ydOs z{#chl`}vB`$gV*CX!a!p0D-WAVE#D;v=)%6LUt~fUM3C-2r&%X-h~3n92m=ymRYwXeipL(xBw46A4m}b zz`ipJfSi^h{{;9`Am@=3^@?4(vH)Plzr`-h3~f>NWL@{d&CLaZiG|xLcAC$!-`noB z4&_SEZ1pq&)UusTnZ1RoHFypZcN}#Eq#vv|MOIILF6sZa{{;$X363&PR_E3MC1@C^ zx;^*2YnYQiXAqu1qRWO2EB%sab?nau7YG4niID=(CE2JAH3TMJ{gmd4t!{42oMXA5 z=pHOdBnf^{06X{BZ;N29axO0saaETG~y-q@bv})p+406EHxk$8IMo93knK5R6iV?^+?4JEa=@BB{Wr;7$09%)WWpwdSJpc&gy?Xb#y=!13d4 znM6rJyf+X%lZo?o%SI9DF_A11@m+d`!3G!<29W9k_{MPczg;h)nTL@kmCpN_655)+ zV4#=PKa9~L!Z2&Ah&=5fCU1*LOJ_x$W27L$LOfiib%hP1*Z~zUp#+jQq7DbI-U%rP zk^T9Tl?*Eoupe)4-y&KT~+O##fDEImn4y>NutF;IVJ3=m*! z*rEW%TlVNiM?a>>U$u_$JuA>L6CZFXb!s@yP&g!R3gv~Bp}AgpK2=m0jV8VvIZv2C zmd^lRyVaiKaS$v6oYCt@$k#%)MsE3;z}yQ2L<8`{hpSucn4z6B`)k9mk!I(}zCa8Y zec8sli-a|~e-I8yodRg%#-Fe zUUq-YRYyjqA8u02I*uj{d;W>=)zk|nH^OPBH+T%!|4ND7zpZ*;!cs{XDZAc=)TpYy zygF?tJ_g{6^&8==X_&x=`hHuSa{O~;8JsY}g+Ry)97Lmf`m;+MhVx9uQw2LJB)YCN z@x%=aA>jGW@-K6kNsyeSxy?!*B;mpG3B8IhiJ`aL(iYA4>ejWW`Ba}KuFQ=qb|9UD zd1@S)Q6&PF^2(HexS@I93W4L)kM;M0t02}wjNHH_W_t5)TVGK6pzr>0Q^W#T7H~ar z!`O{mW@83Sl9!-P1c_I-0Gd=n(Al2Ah)MotJ8AbzM;HOuxA3PI2G5Xa4-KFnd(R<& zl~(O&-Jsc|!_LVQcPi|XD}`wzR1Xn(cG2(D_N3D~k*V(8k4i+i9K+ulxOuCy!l zG3U%zV9j+$hqsRH4K8qiX)Y%5kpNKe-@i7c^ly-X9EHpqEaj*3o>oZwj-~iM`20S0 zT+X3*f&BF+O!NkR{xf~GM<9^RlYs$K*d}c0boMm+LsFzfZDJ%7->?En1@nR69#5W9 zha7r+_<*>ozO^~mkB|Jz*z!F@=x~3DwK8pu~`vT=$gd#s-trjR06bA&FSRn zXosx!+Ar!=CCEZdm*mgGVygGr=a;bPp3`1$ojoLd5*z)h`E`i{b~Tk6I_M{*hrhdq z%km?-j{K^V=N^-HEYrs)!oparg}x|u^fEHypM^&WQ?T7d$N;k6=t`n8b5A;R7+fd< zweEqw_&I7h2Q`^|Nhl+xehysMZI!+Zv%IfOO*?!yw98BGB}51~t1J8V4~T$`Y~Rpg zC&EB-_kDWzSVdXeG7o$eVQN}aM@*<;RL@^rCgVF8kWxLDx|}q#y}Sgg z??KODbbqBH3$u%uWAZp`x1p_9+vh$RDq>q8DArehLiv#? zS7p(k_klZ2h^UNb09!*yqk7YKmjb|*xuZ{kcYnK_eZDQDgCKrv@~G^rCEkhh1KQ8t zT~&5d1+9jn{HLO`4hJ}XbT4W*8iQ!OH&R^30MdVf>goXR<52$G7-HwbKISxdG2?gX zUWamaU{hM>`{N6CBF`m|UO7f;#%00^HUQjv90sS3{=D}p=qL&|w9G5VQ=hdE?qpB* z8CVyn;Vhr1QP!CQcS~LOSrN!b4hgV%PU9lhY4qrPo;TID@g4v|;8auwD2G~SY|8TM zRd4Zg*tNyl*2c@A_Z{0+cYF_Pnwm+58d;%$qhs2q2XkwXMBxG|t)dcF{495+0LXAt z%=xkC)NYk>2z6<8b(io?P7{c7z!dMrujH=okhn5qE$`KV2r?QH33y;KVw{~_a|@d1 zgc0Z`b1>*sRl_Co)qBcExZwdxbAVy&napFK=rMOLMPFe2WYbbO%19sPa*w$o;yi)Q z){i?j*$ujpB;L{(m!Q9Sdnfg#tTM-#YfR{CBw}t`;qUq-!x&bmVfcN}-Po_I4}X}_ z^t5y1VNMmk9z)``Ic6iQsFY7DwU8AJM3TN>l5#qSx#b5zyk}gA%k0LtM)YSep6s(K^!|HPUZnNmZx2o z56)-Om(G2o{0FL>zz=R>3h!{P^*+b!qaH#4@l(IQkuiEnE!ScPnFiaVAO&1gyai|l zAdJrT+kK3Se%<(USIy}n=jk}2Ht2IX#e*edEZ~-uUSJSqWC~k*1T5@YJrKyPnw|X< z+W3-Y{WKrQdHr~R3#5&-TZ3zM;w6TqCbhn|Rj2*Z?S*6e-R?XXaRSs|oId=TVXLu{ zj|URNRPe7+kR)4)8|nkVLsFM7<=Vmf~HeV0cp1|XXS5L@wjNj5yKie+KA z%09g+z^n`}M7P_vg>2)FYXMz=YLgchlQ~%ARY;7G(ys;3HB-ed^wLOTkupT@Nfk&o zqI^Bl$76XEwk1?h=qO6mkigTz!Fhq&Y~rX|9n!A;SBB16W?nFjd=|4(?*6`uT>Om4 zq!ivu+WiqM6jPn_bYNMC+&k!sajqnXW6Bh6mHZwrOTOLYAspD>VRI&w0>>Ym%QSk7jV!gm~4K#@3Ekv@C& z1ZW^9I}1pIxbnRqUm@~9k$hFsgO$SclmV)dsY(@axX-x;K@08<7z*M;!)D+)GnB-h_pX#E4&BdOm}hnZK05O34%JkmnSPvltCp%pfh1YkKaB; z3r{eK1y}F0zteZ!VbKxN(mGE+RBO2}il&HvJsmW5)9f&i7~9pNsoJ;9UuU{fdVZPdaTpl3xn;a*6FGldFooO)ID5!f~o{A(@Q|Z#mgd97e)$p~ZNmCyO%SVP)0qcao?}@xnBznLG_-jRz&F1t?k^&(O-9@Xe66ob6E_pADB_QBgqLn-gy;LfXQBSJbXm4Qa z)@0fUbjL-DMg$ghS|B?CBkpf7|ESPv)cUf`>w<=*s{H62A8V`IiK$3LELwT3jzOb8 zl!S`+Tx&GX2;@<}_wVf9BLV{P^(7He508+NcJ@gXb$|bH_KiMExuqD$AfHUdt4cDv znhtuostMM!A9W3zyP;)%a8-~&aywa>^w*%F9kkeh*?k3sl)5}_ql5OAK9{{G;i=&{ zUtHfQwYq)Mv2b#<<*l>m4R5ck-D}D4z}ASJ5n|#5oacfBD7c8Ej7x(YE2s{iT$}G8 zwe%kv{OB)T?3TF&{@pgAVDqA%#pj(8sb{K9MGKWIs8|2FC9!(el>JYu3twNt=QY>E zEfe)z$ve+s#4HImOHv>x>;K~XCmW++46hoJ3_n6i`N8IeHpqE;laOh)fhBpsdn`Zm z=#=<=nmx{M{f?01b_7r#Sw*A=7N%8-I`SyqirAZn#cfO6?4!h|ub2b6?Zr>?Gx+w7 z<{S4WRHS>hqD7{`zLA&m^E6s)FM4@tcOl;dBnnQWimCjB_^)p8k672RtIbfAuFEw6 zgYuq`4j>h|oy0tO_clJuOF6%_;JJXGg4^VC^xm%7&7Hy10VSjE=3!N?H7J&iqdP}I zNOMAsr-?az+$AOgF>TROKSqm>+JU2`&iM{Ko72ZNnN>6ZNGPvfr7rwp!%~yBJSI?n zB3R6?SX`*e^MKP=??Xlg7}vTlQ%RIYvO<*`dtIXjAi@O&*XGS5Uh^+>(R3{UOg9!# zeFU6}>N+X$p5K_^4*d4ma(tvz--++&_^^mulRJJ-8!^^{Pd@#l&?DVnNZmyNE?sJo z1v4i@CO?oX!Dg1Lqv3TWiOw2-FS)k+Ljd}Ilpuew^DX&cs7!n~Bu5*_ zbNkTWkOG>zvpx~^d~foOLY<`#J9-4ss8Adupv(u66p=MohfRAYxhNA>HSU|gUFPg!w!n|-*n&2dvMVX3i(q* z9avlqEO5CvT+~(A1xN=y58K~En@L`vMnKT5ufo5MXk%ca7vW2=31I*Km~>+KC%76EmzQ{Epl*zpBy66^=k| zgxepQc7MxTEKkj~;bY@6Gd!n`K!v>&``bhrGUsu ziUsg|6^14}8h@IKz!**2yt0@~gdZ8TQ6A456}yw%F8pOXchJfG;IsFRiK@Z=(_=hNf1EVaQxVt%sqi_6s+|aA2Hs^Z1^jkF} z)1c0f3(n&QqBF4M>5l>N+2Cviy}C>Nvp)=5PwiM9D`yM8h&eBJ^iH_7m4)X9Q%5MW z!_c~><|XVWOt+Ve`j5xtKb#eCe342+D}=)aV%}@?*l_nK;*CILu4J}tqmJH`|Q{N79k>Ago$ zw+kbnqFbA;2Yd~D;IrHI?7OkQTl}|AhyXHl!kF?*%64_xllGp7XvzmFLZx)x^DQ#( z8yw9_Kg)1_V%`v*<7w+#aJ1^1V0 zi}6B_vKX`{IOQ9xZOX1JvESZ!V(|c{SDSNfEs(d|CL$lQ?eu!R|NTpI?eS+Kf7vNl z%!IbKTC}f)@d@}t?+klTHUjTrMxbJXOeb(+XezwY*G|WOOma4Pdp;kk0Fl~k&%h$# zkNqIcqQ*;D3DT7}0g$zkqn+g9IR#X2V^fthIf z6xut?2mox2Q$mF~UZ_v*={bqjQ*QF8smcNS^TPHE8vGd1^gz*sl^2S)?%;8oMxCX* z0SKz`wGxRnWD$dRPEJ3QEZIK37gU^>f=z&GmJiDQqp92G7HAK&!?*nn%c^fgy_Rc6 z2i%y;23Y}W_x9$Q$t)tiX=};p&i0?ga{!QL{KM0y9!)RXqLg$ix6$n3zDrBF#amXMJ8=viyxAutbQYXsIV&Y%NuDPMK&4 z49)cj0zMhnla%IgMc=|Vc20Hv6p#_`b$l?h_yQC$DIz&u_>1yOOYIZ}$7ErKy`9Iw zO&^zH_d%1a{z{drpduoOO9>4K0hs_Wqej8OE3;~Jz;l|XDy($&>P*2><8H_*uLF

Bg_)L$48&o}ack zt!HAQladuqH6nxcdZjxF6vTK*7%AV0IOX}JZ^u_OkMumMel#gIzv1VJUb|?$j71?Q zE%B-a&vFk0&Z20HLn$L5hH9dQ4Ef5bKHOX_RfA5_`gJvR%bW4}0DCGg0Va&YC>upU z1&8GC>xLY}LkxejF;yq2sOY~5Cqo4UPpDVfIk~O=P(Gb%k`up^XsPRv9)fXL9y}y5 z_nYE{jUJUly7pJ?$r_eiajL0B@N?t6efF1pS9lk2UGDMWIj>@eS3OH*kme)>qMs4c zXm_q!8hu`I8Dhv5J1?UvlYLJ*X6D(~=y^sb&mT@}>JxIp-$sTvzSdP`p2 zJSTpI%GX_eG4+h<`8LXOC{XRWB^Zd0<_&>b2eoF`!(!~j1?f~Xrey`D;63!OSo@1j zhVkNITfkbp3}EkdgcmMq1ZjCrPrG8@X$!y3nZFDG<8?k82gdmVJ<<=koN(R0uQJ&q&;Hm9we)+ z>EiW_<2L!S=W{bXQR{t-Xk&^=i3!xJ1GH*$ft^*`kFY2U5$7}?R(xpv1OE!wXhM-U z{|-m&g!{Wci5J~AR7xhAcz2%T1X}X;Lm5kqn$n>CdZG%!jlgyEgu}8cSE=Tna>KaW zE{i{1$MRasX61aWzh$Kz+gg_XXfmLI)gJjN8%-etjvKrZa0TIVu@FNzi_DBbgG}?X zc%`<^VRHLdaERnl?k6tkmy~V-6wDDz--2n{wg8GA9pEX|*>WDA)uFS%@&MN%-fruuv;9cz5hkb^M5ny{e|a>hsQrwX-D^ zNBO*f*^1b~?2NHQ2g~@Hiq$_V;Z8|Q@sz(OJ-_;@xOeF3^57a-&;bjXTR0^(9&O1`Qs5mDXKaZ{gYh~^a4>N0el@Q8ji@;G0VwtnJC=gh zkvhS5%hY0~-FeS=8*`P?hYjBB(TJ(h3vvLJ;q{z?7Jf~hQZOlh z8(M!wZ6}#fur(A{mkQvWp^(z{kJ9Qcf&hRH6HK!c5b1?W28wck^aQ{Ju^0{PD&(m< zLbu{`I17`!l>tz)uDi~j;Q*7#M!X;*rP#E#K`0MHVO=C_Y>Kpq3I%tz)KS21z(70w zu+S+efLBlwQ<3knLW3-aM&dW%uuv1r?kQqDWsn|wHc35K!jBl%t~9oLn(8w!(iF2Y zj)Ca3zC|-dT69O*c44Drzb zTWRcGB4pjuzq;tNLKJ7H7&3CTH`^5&fs^&Csh+4TdNT~n>o18P123FK1<6Ap7>9OE zH?~wjjt8TicxY-E;Ld@{^P%j9BpeC^HJlw8iW2I<*Vc#1VH5Y&k@$T23>riN5a^fE z1xtK3We+L~pTm`C@mKj&g@flbKtRjuc?i2+3nW^d516YT2Qx*|&}0%IIZ{BK)n@-A z7W$f>T8g%2qR?15Ezb7#Al1jSng=Um;<;^4CZ)UF4*B#s16}WxXDR&e;IFDemyJBP zQD>pCTMx2W)Iwg$ckc4ElkYeRp?hVt(&_O^6u-=WO4y!T>T70vx)M)1f17nmsqw{$ zZhYP=C<2j}m^Lg-ex&8RKf~0WrfrL!~NLQ^Xk?)No>Z1xiNn~MBa)Y#@)-369lm6 zDH#U3Pv&kw{mt3+_t3uW8=*{`^%?Rl0~ff@XZrjZDZCjasI5PtLP6)zBrRO$pYlfV z4DCdS&(8)uR*23AuP?jhTi@#6yL=G*b~7!5d3?2d_2DzzR3V+SVCZe@ULo|*Qz`CV z*j}+=>D|^uy$<{s0tgCLYba;t*4jWsj}CfRI?T4cS90us!fy8Lz;(qp7V}H&`+loKEpPZr6wt`2EqWMWj z6-EjjNWU@D@IQdNmlvT5>qUq{`9mBuild Status +GoDoc + +Match is a very simple pattern matcher where '*' matches on any +number characters and '?' matches on any one character. + +Installing +---------- + +``` +go get -u github.com/tidwall/match +``` + +Example +------- + +```go +match.Match("hello", "*llo") +match.Match("jello", "?ello") +match.Match("hello", "h*o") +``` + + +Contact +------- +Josh Baker [@tidwall](http://twitter.com/tidwall) + +License +------- +Redcon source code is available under the MIT [License](/LICENSE). diff --git a/vendor/github.com/tidwall/match/match.go b/vendor/github.com/tidwall/match/match.go new file mode 100644 index 00000000000..8885add63c8 --- /dev/null +++ b/vendor/github.com/tidwall/match/match.go @@ -0,0 +1,192 @@ +// Match provides a simple pattern matcher with unicode support. +package match + +import "unicode/utf8" + +// Match returns true if str matches pattern. This is a very +// simple wildcard match where '*' matches on any number characters +// and '?' matches on any one character. + +// pattern: +// { term } +// term: +// '*' matches any sequence of non-Separator characters +// '?' matches any single non-Separator character +// c matches character c (c != '*', '?', '\\') +// '\\' c matches character c +// +func Match(str, pattern string) bool { + if pattern == "*" { + return true + } + return deepMatch(str, pattern) +} +func deepMatch(str, pattern string) bool { + for len(pattern) > 0 { + if pattern[0] > 0x7f { + return deepMatchRune(str, pattern) + } + switch pattern[0] { + default: + if len(str) == 0 { + return false + } + if str[0] > 0x7f { + return deepMatchRune(str, pattern) + } + if str[0] != pattern[0] { + return false + } + case '?': + if len(str) == 0 { + return false + } + case '*': + return deepMatch(str, pattern[1:]) || + (len(str) > 0 && deepMatch(str[1:], pattern)) + } + str = str[1:] + pattern = pattern[1:] + } + return len(str) == 0 && len(pattern) == 0 +} + +func deepMatchRune(str, pattern string) bool { + var sr, pr rune + var srsz, prsz int + + // read the first rune ahead of time + if len(str) > 0 { + if str[0] > 0x7f { + sr, srsz = utf8.DecodeRuneInString(str) + } else { + sr, srsz = rune(str[0]), 1 + } + } else { + sr, srsz = utf8.RuneError, 0 + } + if len(pattern) > 0 { + if pattern[0] > 0x7f { + pr, prsz = utf8.DecodeRuneInString(pattern) + } else { + pr, prsz = rune(pattern[0]), 1 + } + } else { + pr, prsz = utf8.RuneError, 0 + } + // done reading + for pr != utf8.RuneError { + switch pr { + default: + if srsz == utf8.RuneError { + return false + } + if sr != pr { + return false + } + case '?': + if srsz == utf8.RuneError { + return false + } + case '*': + return deepMatchRune(str, pattern[prsz:]) || + (srsz > 0 && deepMatchRune(str[srsz:], pattern)) + } + str = str[srsz:] + pattern = pattern[prsz:] + // read the next runes + if len(str) > 0 { + if str[0] > 0x7f { + sr, srsz = utf8.DecodeRuneInString(str) + } else { + sr, srsz = rune(str[0]), 1 + } + } else { + sr, srsz = utf8.RuneError, 0 + } + if len(pattern) > 0 { + if pattern[0] > 0x7f { + pr, prsz = utf8.DecodeRuneInString(pattern) + } else { + pr, prsz = rune(pattern[0]), 1 + } + } else { + pr, prsz = utf8.RuneError, 0 + } + // done reading + } + + return srsz == 0 && prsz == 0 +} + +var maxRuneBytes = func() []byte { + b := make([]byte, 4) + if utf8.EncodeRune(b, '\U0010FFFF') != 4 { + panic("invalid rune encoding") + } + return b +}() + +// Allowable parses the pattern and determines the minimum and maximum allowable +// values that the pattern can represent. +// When the max cannot be determined, 'true' will be returned +// for infinite. +func Allowable(pattern string) (min, max string) { + if pattern == "" || pattern[0] == '*' { + return "", "" + } + + minb := make([]byte, 0, len(pattern)) + maxb := make([]byte, 0, len(pattern)) + var wild bool + for i := 0; i < len(pattern); i++ { + if pattern[i] == '*' { + wild = true + break + } + if pattern[i] == '?' { + minb = append(minb, 0) + maxb = append(maxb, maxRuneBytes...) + } else { + minb = append(minb, pattern[i]) + maxb = append(maxb, pattern[i]) + } + } + if wild { + r, n := utf8.DecodeLastRune(maxb) + if r != utf8.RuneError { + if r < utf8.MaxRune { + r++ + if r > 0x7f { + b := make([]byte, 4) + nn := utf8.EncodeRune(b, r) + maxb = append(maxb[:len(maxb)-n], b[:nn]...) + } else { + maxb = append(maxb[:len(maxb)-n], byte(r)) + } + } + } + } + return string(minb), string(maxb) + /* + return + if wild { + r, n := utf8.DecodeLastRune(maxb) + if r != utf8.RuneError { + if r < utf8.MaxRune { + infinite = true + } else { + r++ + if r > 0x7f { + b := make([]byte, 4) + nn := utf8.EncodeRune(b, r) + maxb = append(maxb[:len(maxb)-n], b[:nn]...) + } else { + maxb = append(maxb[:len(maxb)-n], byte(r)) + } + } + } + } + return string(minb), string(maxb), infinite + */ +} diff --git a/vendor/vendor.json b/vendor/vendor.json index a38b8f0b93a..18a8bfd6b5b 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -1717,6 +1717,20 @@ "version": "v0.1.0", "versionExact": "v0.1.0" }, + { + "checksumSHA1": "d+4iuDj/qQ+2LiWEbQ+AWjxEJOc=", + "path": "github.com/tidwall/gjson", + "revision": "f123b340873a0084cb27267eddd8ff615115fbff", + "revisionTime": "2018-06-21T18:09:58Z", + "version": "v1.1.2", + "versionExact": "v1.1.2" + }, + { + "checksumSHA1": "Tk+aXpWRuTQhURlO79bd7YogcU0=", + "path": "github.com/tidwall/match", + "revision": "1731857f09b1f38450e2c12409748407822dc6be", + "revisionTime": "2017-10-02T07:59:45Z" + }, { "checksumSHA1": "qgMa75aMGbkFY0jIqqqgVnCUoNA=", "path": "github.com/ulikunitz/xz", From a2e194b726a40310667022403bd5e26e4e568c35 Mon Sep 17 00:00:00 2001 From: Dieter De Meyer Date: Tue, 3 Jul 2018 10:34:19 +0200 Subject: [PATCH 73/88] Added argument for defining instance state names + added test case --- aws/data_source_aws_instances.go | 31 +++++++++++---- aws/data_source_aws_instances_test.go | 54 ++++++++++++++++++++++++++ website/docs/d/instances.html.markdown | 4 ++ 3 files changed, 81 insertions(+), 8 deletions(-) diff --git a/aws/data_source_aws_instances.go b/aws/data_source_aws_instances.go index 33f6e8a760d..e0b094e2042 100644 --- a/aws/data_source_aws_instances.go +++ b/aws/data_source_aws_instances.go @@ -8,6 +8,7 @@ import ( "github.com/aws/aws-sdk-go/service/ec2" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" ) func dataSourceAwsInstances() *schema.Resource { @@ -17,6 +18,21 @@ func dataSourceAwsInstances() *schema.Resource { Schema: map[string]*schema.Schema{ "filter": dataSourceFiltersSchema(), "instance_tags": tagsSchemaComputed(), + "instance_state_names": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{ + ec2.InstanceStateNamePending, + ec2.InstanceStateNameRunning, + ec2.InstanceStateNameShuttingDown, + ec2.InstanceStateNameStopped, + ec2.InstanceStateNameStopping, + ec2.InstanceStateNameTerminated, + }, false), + }, + }, "ids": { Type: schema.TypeList, @@ -47,20 +63,19 @@ func dataSourceAwsInstancesRead(d *schema.ResourceData, meta interface{}) error return fmt.Errorf("One of filters or instance_tags must be assigned") } + instanceStateNames := []*string{aws.String(ec2.InstanceStateNameRunning)} + if v, ok := d.GetOk("instance_state_names"); ok && len(v.(*schema.Set).List()) > 0 { + instanceStateNames = expandStringSet(v.(*schema.Set)) + } params := &ec2.DescribeInstancesInput{ Filters: []*ec2.Filter{ &ec2.Filter{ - Name: aws.String("instance-state-name"), - Values: []*string{ - aws.String("running"), - aws.String("stopped"), - aws.String("stopping"), - aws.String("pending"), - aws.String("shutting-down"), - }, + Name: aws.String("instance-state-name"), + Values: instanceStateNames, }, }, } + if filtersOk { params.Filters = append(params.Filters, buildAwsDataSourceFilters(filters.(*schema.Set))...) diff --git a/aws/data_source_aws_instances_test.go b/aws/data_source_aws_instances_test.go index 2dd92bfa5b1..60c7f7cb2e3 100644 --- a/aws/data_source_aws_instances_test.go +++ b/aws/data_source_aws_instances_test.go @@ -43,6 +43,22 @@ func TestAccAWSInstancesDataSource_tags(t *testing.T) { }) } +func TestAccAWSInstancesDataSource_instance_state_names(t *testing.T) { + rInt := acctest.RandInt() + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccInstancesDataSourceConfig_instance_state_names(rInt), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.aws_instances.test", "ids.#", "2"), + ), + }, + }, + }) +} + const testAccInstancesDataSourceConfig_ids = ` data "aws_ami" "ubuntu" { most_recent = true @@ -113,3 +129,41 @@ data "aws_instances" "test" { } `, rInt) } + +func testAccInstancesDataSourceConfig_instance_state_names(rInt int) string { + return fmt.Sprintf(` +data "aws_ami" "ubuntu" { + most_recent = true + + filter { + name = "name" + values = ["ubuntu/images/hvm-ssd/ubuntu-trusty-14.04-amd64-server-*"] + } + + filter { + name = "virtualization-type" + values = ["hvm"] + } + + owners = ["099720109477"] # Canonical +} + +resource "aws_instance" "test" { + count = 2 + ami = "${data.aws_ami.ubuntu.id}" + instance_type = "t2.micro" + tags { + Name = "TfAccTest-HelloWorld" + TestSeed = "%[1]d" + } +} + +data "aws_instances" "test" { + instance_tags { + Name = "*" + } + + instance_state_names = [ "pending", "running" ] +} +`, rInt) +} diff --git a/website/docs/d/instances.html.markdown b/website/docs/d/instances.html.markdown index d09e16c3cc6..c67f990862d 100644 --- a/website/docs/d/instances.html.markdown +++ b/website/docs/d/instances.html.markdown @@ -32,6 +32,8 @@ data "aws_instances" "test" { name = "instance.group-id" values = ["sg-12345678"] } + + instance_state_names = [ "running", "stopped" ] } resource "aws_eip" "test" { @@ -45,6 +47,8 @@ resource "aws_eip" "test" { * `instance_tags` - (Optional) A mapping of tags, each pair of which must exactly match a pair on desired instances. +* `instance_state_names` - (Optional) A list of instance states that should be applicable to the desired instances. The permitted values are: `pending, running, shutting-down, stopped, stopping, terminated` + * `filter` - (Optional) One or more name/value pairs to use as filters. There are several valid keys, for a full reference, check out [describe-instances in the AWS CLI reference][1]. From 8674ed80526390d70a9066a083764920cc5b8a63 Mon Sep 17 00:00:00 2001 From: Andrew Haines Date: Tue, 3 Jul 2018 10:34:27 +0100 Subject: [PATCH 74/88] Add member_clusters to aws_elasticache_replication_group --- aws/data_source_aws_elasticache_replication_group.go | 7 +++++++ aws/data_source_aws_elasticache_replication_group_test.go | 1 + aws/resource_aws_elasticache_replication_group.go | 8 ++++++++ aws/resource_aws_elasticache_replication_group_test.go | 2 ++ .../docs/d/elasticache_replication_group.html.markdown | 1 + .../docs/r/elasticache_replication_group.html.markdown | 1 + 6 files changed, 20 insertions(+) diff --git a/aws/data_source_aws_elasticache_replication_group.go b/aws/data_source_aws_elasticache_replication_group.go index 938eea1a613..77ec2e8780c 100644 --- a/aws/data_source_aws_elasticache_replication_group.go +++ b/aws/data_source_aws_elasticache_replication_group.go @@ -45,6 +45,12 @@ func dataSourceAwsElasticacheReplicationGroup() *schema.Resource { Type: schema.TypeInt, Computed: true, }, + "member_clusters": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + }, "node_type": { Type: schema.TypeString, Computed: true, @@ -106,6 +112,7 @@ func dataSourceAwsElasticacheReplicationGroupRead(d *schema.ResourceData, meta i d.Set("primary_endpoint_address", rg.NodeGroups[0].PrimaryEndpoint.Address) } d.Set("number_cache_clusters", len(rg.MemberClusters)) + d.Set("member_clusters", flattenStringList(rg.MemberClusters)) d.Set("node_type", rg.CacheNodeType) d.Set("snapshot_window", rg.SnapshotWindow) d.Set("snapshot_retention_limit", rg.SnapshotRetentionLimit) diff --git a/aws/data_source_aws_elasticache_replication_group_test.go b/aws/data_source_aws_elasticache_replication_group_test.go index 0a13345b2ee..00e361bbfcf 100644 --- a/aws/data_source_aws_elasticache_replication_group_test.go +++ b/aws/data_source_aws_elasticache_replication_group_test.go @@ -24,6 +24,7 @@ func TestAccDataSourceAwsElasticacheReplicationGroup_basic(t *testing.T) { resource.TestCheckResourceAttr("data.aws_elasticache_replication_group.bar", "port", "6379"), resource.TestCheckResourceAttrSet("data.aws_elasticache_replication_group.bar", "primary_endpoint_address"), resource.TestCheckResourceAttr("data.aws_elasticache_replication_group.bar", "number_cache_clusters", "2"), + resource.TestCheckResourceAttrSet("data.aws_elasticache_replication_group.bar", "member_clusters"), resource.TestCheckResourceAttr("data.aws_elasticache_replication_group.bar", "node_type", "cache.m1.small"), resource.TestCheckResourceAttr("data.aws_elasticache_replication_group.bar", "snapshot_window", "01:00-02:00"), ), diff --git a/aws/resource_aws_elasticache_replication_group.go b/aws/resource_aws_elasticache_replication_group.go index 192ffbacecd..db52c9eb2cc 100644 --- a/aws/resource_aws_elasticache_replication_group.go +++ b/aws/resource_aws_elasticache_replication_group.go @@ -50,6 +50,13 @@ func resourceAwsElasticacheReplicationGroup() *schema.Resource { Optional: true, } + resourceSchema["member_clusters"] = &schema.Schema{ + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + } + resourceSchema["primary_endpoint_address"] = &schema.Schema{ Type: schema.TypeString, Computed: true, @@ -313,6 +320,7 @@ func resourceAwsElasticacheReplicationGroupRead(d *schema.ResourceData, meta int d.Set("replication_group_description", rgp.Description) d.Set("number_cache_clusters", len(rgp.MemberClusters)) + d.Set("member_clusters", flattenStringList(rgp.MemberClusters)) if err := d.Set("cluster_mode", flattenElasticacheNodeGroupsToClusterMode(aws.BoolValue(rgp.ClusterEnabled), rgp.NodeGroups)); err != nil { return fmt.Errorf("error setting cluster_mode attribute: %s", err) } diff --git a/aws/resource_aws_elasticache_replication_group_test.go b/aws/resource_aws_elasticache_replication_group_test.go index 012994b9126..03425bf836e 100644 --- a/aws/resource_aws_elasticache_replication_group_test.go +++ b/aws/resource_aws_elasticache_replication_group_test.go @@ -89,6 +89,8 @@ func TestAccAWSElasticacheReplicationGroup_basic(t *testing.T) { "aws_elasticache_replication_group.bar", "cluster_mode.#", "0"), resource.TestCheckResourceAttr( "aws_elasticache_replication_group.bar", "number_cache_clusters", "2"), + resource.TestCheckResourceAttrSet( + "aws_elasticache_replication_group.bar", "member_clusters"), resource.TestCheckResourceAttr( "aws_elasticache_replication_group.bar", "auto_minor_version_upgrade", "false"), ), diff --git a/website/docs/d/elasticache_replication_group.html.markdown b/website/docs/d/elasticache_replication_group.html.markdown index 8d658bf6e37..ec0f9d71c6c 100644 --- a/website/docs/d/elasticache_replication_group.html.markdown +++ b/website/docs/d/elasticache_replication_group.html.markdown @@ -34,6 +34,7 @@ In addition to all arguments above, the following attributes are exported: * `automatic_failover_enabled` - A flag whether a read-only replica will be automatically promoted to read/write primary if the existing primary fails. * `node_type` – The cluster node type. * `number_cache_clusters` – The number of cache clusters that the replication group has. +* `member_clusters` - The identifiers of all the nodes that are part of this replication group. * `snapshot_window` - The daily time range (in UTC) during which ElastiCache begins taking a daily snapshot of your node group (shard). * `snapshot_retention_limit` - The number of days for which ElastiCache retains automatic cache cluster snapshots before deleting them. * `port` – The port number on which the configuration endpoint will accept connections. diff --git a/website/docs/r/elasticache_replication_group.html.markdown b/website/docs/r/elasticache_replication_group.html.markdown index c2cdc13b4ed..70e2982abe2 100644 --- a/website/docs/r/elasticache_replication_group.html.markdown +++ b/website/docs/r/elasticache_replication_group.html.markdown @@ -140,6 +140,7 @@ In addition to all arguments above, the following attributes are exported: * `id` - The ID of the ElastiCache Replication Group. * `configuration_endpoint_address` - The address of the replication group configuration endpoint when cluster mode is enabled. * `primary_endpoint_address` - (Redis only) The address of the endpoint for the primary node in the replication group, if the cluster mode is disabled. +* `member_clusters` - The identifiers of all the nodes that are part of this replication group. ## Timeouts From d3e1cd50fc2ccfa7b62c8d68c7aeb4b084736f5e Mon Sep 17 00:00:00 2001 From: Julien Duchesne Date: Tue, 3 Jul 2018 07:33:51 -0400 Subject: [PATCH 75/88] Added tests for aws_pricing_product --- aws/data_source_aws_pricing_product_test.go | 144 ++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 aws/data_source_aws_pricing_product_test.go diff --git a/aws/data_source_aws_pricing_product_test.go b/aws/data_source_aws_pricing_product_test.go new file mode 100644 index 00000000000..8f90c5224f3 --- /dev/null +++ b/aws/data_source_aws_pricing_product_test.go @@ -0,0 +1,144 @@ +package aws + +import ( + "fmt" + "os" + "strconv" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccDataSourceAwsPricingProduct_ec2(t *testing.T) { + os.Setenv("AWS_DEFAULT_REGION", "us-east-1") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceAwsPricingProductConfigEc2("test1", "c5.large") + testAccDataSourceAwsPricingProductConfigEc2("test2", "c5.xlarge"), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrSet("data.aws_pricing_product.test1", "query_result"), + resource.TestCheckResourceAttrSet("data.aws_pricing_product.test2", "query_result"), + testAccPricingCheckValueIsFloat("data.aws_pricing_product.test1"), + testAccPricingCheckValueIsFloat("data.aws_pricing_product.test2"), + testAccPricingCheckGreaterValue("data.aws_pricing_product.test2", "data.aws_pricing_product.test1"), + ), + }, + }, + }) +} + +func TestAccDataSourceAwsPricingProduct_redshift(t *testing.T) { + os.Setenv("AWS_DEFAULT_REGION", "us-east-1") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceAwsPricingProductConfigRedshift(), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrSet("data.aws_pricing_product.test", "query_result"), + testAccPricingCheckValueIsFloat("data.aws_pricing_product.test"), + ), + }, + }, + }) +} + +func testAccDataSourceAwsPricingProductConfigEc2(dataName string, instanceType string) string { + return fmt.Sprintf(`data "aws_pricing_product" "%s" { + service_code = "AmazonEC2" + + filters = [ + { + field = "instanceType" + value = "%s" + }, + { + field = "operatingSystem" + value = "Linux" + }, + { + field = "location" + value = "US East (N. Virginia)" + }, + { + field = "preInstalledSw" + value = "NA" + }, + { + field = "licenseModel" + value = "No License required" + }, + { + field = "tenancy" + value = "Shared" + }, + ] + + json_query = "terms.OnDemand.*.priceDimensions.*.pricePerUnit.USD" +} +`, dataName, instanceType) +} + +func testAccDataSourceAwsPricingProductConfigRedshift() string { + return fmt.Sprintf(`data "aws_pricing_product" "test" { + service_code = "AmazonRedshift" + + filters = [ + { + field = "instanceType" + value = "ds1.xlarge" + }, + { + field = "location" + value = "US East (N. Virginia)" + }, + ] + + json_query = "terms.OnDemand.*.priceDimensions.*.pricePerUnit.USD" +} +`) +} + +func testAccPricingCheckValueIsFloat(data string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[data] + + if !ok { + return fmt.Errorf("Can't find resource: %s", data) + } + + queryResult := rs.Primary.Attributes["query_result"] + if _, err := strconv.ParseFloat(queryResult, 32); err != nil { + return fmt.Errorf("%s query_result value (%s) is not a float: %s", data, queryResult, err) + } + + return nil + } +} + +func testAccPricingCheckGreaterValue(dataWithGreaterValue string, otherData string) resource.TestCheckFunc { + return func(s *terraform.State) error { + greaterResource, ok := s.RootModule().Resources[dataWithGreaterValue] + if !ok { + return fmt.Errorf("Can't find resource: %s", dataWithGreaterValue) + } + + lesserResource, ok := s.RootModule().Resources[otherData] + if !ok { + return fmt.Errorf("Can't find resource: %s", otherData) + } + + greaterValue := greaterResource.Primary.Attributes["query_result"] + lesserValue := lesserResource.Primary.Attributes["query_result"] + + if greaterValue <= lesserValue { + return fmt.Errorf("%s (%s) has a greater value than %s (%s). Should have been the opposite", otherData, lesserValue, dataWithGreaterValue, greaterValue) + } + + return nil + } +} From 68d64c17c55a1f37405968c9a8d8fef41b2d8b03 Mon Sep 17 00:00:00 2001 From: Julien Duchesne Date: Tue, 3 Jul 2018 07:53:22 -0400 Subject: [PATCH 76/88] Add aws_pricing_product doc --- aws/data_source_aws_pricing_product.go | 4 +- website/docs/d/pricing_product.html.markdown | 76 ++++++++++++++++++++ 2 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 website/docs/d/pricing_product.html.markdown diff --git a/aws/data_source_aws_pricing_product.go b/aws/data_source_aws_pricing_product.go index e354f4daad6..43502b06688 100644 --- a/aws/data_source_aws_pricing_product.go +++ b/aws/data_source_aws_pricing_product.go @@ -71,10 +71,10 @@ func dataSourceAwsPricingProductRead(d *schema.ResourceData, meta interface{}) e }) } - log.Printf("[DEBUG] Reading Pricing of EC2 products: %s", params) + log.Printf("[DEBUG] Reading pricing of products: %s", params) resp, err := conn.GetProducts(params) if err != nil { - return fmt.Errorf("Error reading Pricing of EC2 products: %s", err) + return fmt.Errorf("Error reading pricing of products: %s", err) } if err = verifyProductsPriceListLength(resp.PriceList); err != nil { diff --git a/website/docs/d/pricing_product.html.markdown b/website/docs/d/pricing_product.html.markdown new file mode 100644 index 00000000000..1233f314515 --- /dev/null +++ b/website/docs/d/pricing_product.html.markdown @@ -0,0 +1,76 @@ +--- +layout: "aws" +page_title: "AWS: aws_pricing_product" +sidebar_current: "docs-aws-datasource-pricing-product" +description: |- + Get information regarding the pricing of an Amazon product +--- + +# Data Source: aws_pricing_product + +Use this data source to get the pricing information of all products in AWS. +This data source is only available in a us-east-1 or ap-south-1 provider. + +## Example Usage + +```hcl +data "aws_pricing_product" "test1" { + service_code = "AmazonEC2" + + filters = [ + { + field = "instanceType" + value = "c5.xlarge" + }, + { + field = "operatingSystem" + value = "Linux" + }, + { + field = "location" + value = "US East (N. Virginia)" + }, + { + field = "preInstalledSw" + value = "NA" + }, + { + field = "licenseModel" + value = "No License required" + }, + { + field = "tenancy" + value = "Shared" + }, + ] + + json_query = "terms.OnDemand.*.priceDimensions.*.pricePerUnit.USD" +} + +data "aws_pricing_product" "test2" { + service_code = "AmazonRedshift" + + filters = [ + { + field = "instanceType" + value = "ds1.xlarge" + }, + { + field = "location" + value = "US East (N. Virginia)" + }, + ] + + json_query = "terms.OnDemand.*.priceDimensions.*.pricePerUnit.USD" +} +``` + +## Argument Reference + + * `service_code` - (Required) The code of the service. Available service codes can be fetched using the DescribeServices pricing API call. + * `filters` - (Required) A list of filters. Passed directly to the API (see GetProducts API reference). These filters must describe a single product, this resource will fail if more than one product is returned by the API. + * `json_query` - (Required) The JSON query used to fetch the wanted value. In a [GJSON format](https://github.com/tidwall/gjson). + +## Attributes Reference + + * `query_result` - Set to the result of the JSON query applied on the product returned from the API. From 35790716f0bda46491b771611686459e176a4188 Mon Sep 17 00:00:00 2001 From: Dieter De Meyer Date: Tue, 3 Jul 2018 20:10:20 +0200 Subject: [PATCH 77/88] Fix test case + update documentation --- aws/data_source_aws_instances_test.go | 2 +- website/docs/d/instances.html.markdown | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/aws/data_source_aws_instances_test.go b/aws/data_source_aws_instances_test.go index 60c7f7cb2e3..a886e3b68af 100644 --- a/aws/data_source_aws_instances_test.go +++ b/aws/data_source_aws_instances_test.go @@ -160,7 +160,7 @@ resource "aws_instance" "test" { data "aws_instances" "test" { instance_tags { - Name = "*" + Name = "${aws_instance.test.0.tags["Name"]}" } instance_state_names = [ "pending", "running" ] diff --git a/website/docs/d/instances.html.markdown b/website/docs/d/instances.html.markdown index c67f990862d..dd16fe7a70e 100644 --- a/website/docs/d/instances.html.markdown +++ b/website/docs/d/instances.html.markdown @@ -47,7 +47,7 @@ resource "aws_eip" "test" { * `instance_tags` - (Optional) A mapping of tags, each pair of which must exactly match a pair on desired instances. -* `instance_state_names` - (Optional) A list of instance states that should be applicable to the desired instances. The permitted values are: `pending, running, shutting-down, stopped, stopping, terminated` +* `instance_state_names` - (Optional) A list of instance states that should be applicable to the desired instances. The permitted values are: `pending, running, shutting-down, stopped, stopping, terminated`. The default value is `running`. * `filter` - (Optional) One or more name/value pairs to use as filters. There are several valid keys, for a full reference, check out From d403191e3ff0940bd76c687707719b04a5c2fa2a Mon Sep 17 00:00:00 2001 From: Julien Duchesne Date: Tue, 3 Jul 2018 18:53:54 -0400 Subject: [PATCH 78/88] Remove JSON dependency according to PR comments --- aws/data_source_aws_pricing_product.go | 14 +- aws/data_source_aws_pricing_product_test.go | 50 +- vendor/github.com/tidwall/gjson/LICENSE | 20 - vendor/github.com/tidwall/gjson/README.md | 401 ---- vendor/github.com/tidwall/gjson/gjson.go | 2087 ----------------- vendor/github.com/tidwall/gjson/gjson_gae.go | 10 - vendor/github.com/tidwall/gjson/gjson_ngae.go | 73 - vendor/github.com/tidwall/gjson/logo.png | Bin 15936 -> 0 bytes vendor/github.com/tidwall/match/LICENSE | 20 - vendor/github.com/tidwall/match/README.md | 32 - vendor/github.com/tidwall/match/match.go | 192 -- vendor/vendor.json | 14 - website/docs/d/pricing_product.html.markdown | 7 +- 13 files changed, 17 insertions(+), 2903 deletions(-) delete mode 100644 vendor/github.com/tidwall/gjson/LICENSE delete mode 100644 vendor/github.com/tidwall/gjson/README.md delete mode 100644 vendor/github.com/tidwall/gjson/gjson.go delete mode 100644 vendor/github.com/tidwall/gjson/gjson_gae.go delete mode 100644 vendor/github.com/tidwall/gjson/gjson_ngae.go delete mode 100644 vendor/github.com/tidwall/gjson/logo.png delete mode 100644 vendor/github.com/tidwall/match/LICENSE delete mode 100644 vendor/github.com/tidwall/match/README.md delete mode 100644 vendor/github.com/tidwall/match/match.go diff --git a/aws/data_source_aws_pricing_product.go b/aws/data_source_aws_pricing_product.go index 43502b06688..79d26b9d350 100644 --- a/aws/data_source_aws_pricing_product.go +++ b/aws/data_source_aws_pricing_product.go @@ -9,7 +9,6 @@ import ( "github.com/aws/aws-sdk-go/service/pricing" "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/schema" - "github.com/tidwall/gjson" ) const ( @@ -41,11 +40,7 @@ func dataSourceAwsPricingProduct() *schema.Resource { }, }, }, - "json_query": { - Type: schema.TypeString, - Required: true, - }, - "query_result": { + "result": { Type: schema.TypeString, Computed: true, }, @@ -86,11 +81,8 @@ func dataSourceAwsPricingProductRead(d *schema.ResourceData, meta interface{}) e return fmt.Errorf("Invalid JSON value returned by AWS: %s", err) } - jsonQuery := d.Get("json_query").(string) - queryResult := gjson.Get(string(pricingResult), jsonQuery) - - d.SetId(fmt.Sprintf("%d-%d", hashcode.String(params.String()), hashcode.String(jsonQuery))) - d.Set("query_result", queryResult.String()) + d.SetId(fmt.Sprintf("%d", hashcode.String(params.String()))) + d.Set("result", string(pricingResult)) return nil } diff --git a/aws/data_source_aws_pricing_product_test.go b/aws/data_source_aws_pricing_product_test.go index 8f90c5224f3..ed9ee03c264 100644 --- a/aws/data_source_aws_pricing_product_test.go +++ b/aws/data_source_aws_pricing_product_test.go @@ -1,9 +1,9 @@ package aws import ( + "encoding/json" "fmt" "os" - "strconv" "testing" "github.com/hashicorp/terraform/helper/resource" @@ -17,13 +17,10 @@ func TestAccDataSourceAwsPricingProduct_ec2(t *testing.T) { Providers: testAccProviders, Steps: []resource.TestStep{ { - Config: testAccDataSourceAwsPricingProductConfigEc2("test1", "c5.large") + testAccDataSourceAwsPricingProductConfigEc2("test2", "c5.xlarge"), + Config: testAccDataSourceAwsPricingProductConfigEc2("test", "c5.large"), Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttrSet("data.aws_pricing_product.test1", "query_result"), - resource.TestCheckResourceAttrSet("data.aws_pricing_product.test2", "query_result"), - testAccPricingCheckValueIsFloat("data.aws_pricing_product.test1"), - testAccPricingCheckValueIsFloat("data.aws_pricing_product.test2"), - testAccPricingCheckGreaterValue("data.aws_pricing_product.test2", "data.aws_pricing_product.test1"), + resource.TestCheckResourceAttrSet("data.aws_pricing_product.test", "result"), + testAccPricingCheckValueIsJSON("data.aws_pricing_product.test"), ), }, }, @@ -39,8 +36,8 @@ func TestAccDataSourceAwsPricingProduct_redshift(t *testing.T) { { Config: testAccDataSourceAwsPricingProductConfigRedshift(), Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttrSet("data.aws_pricing_product.test", "query_result"), - testAccPricingCheckValueIsFloat("data.aws_pricing_product.test"), + resource.TestCheckResourceAttrSet("data.aws_pricing_product.test", "result"), + testAccPricingCheckValueIsJSON("data.aws_pricing_product.test"), ), }, }, @@ -77,8 +74,6 @@ func testAccDataSourceAwsPricingProductConfigEc2(dataName string, instanceType s value = "Shared" }, ] - - json_query = "terms.OnDemand.*.priceDimensions.*.pricePerUnit.USD" } `, dataName, instanceType) } @@ -97,13 +92,11 @@ func testAccDataSourceAwsPricingProductConfigRedshift() string { value = "US East (N. Virginia)" }, ] - - json_query = "terms.OnDemand.*.priceDimensions.*.pricePerUnit.USD" } `) } -func testAccPricingCheckValueIsFloat(data string) resource.TestCheckFunc { +func testAccPricingCheckValueIsJSON(data string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[data] @@ -111,32 +104,15 @@ func testAccPricingCheckValueIsFloat(data string) resource.TestCheckFunc { return fmt.Errorf("Can't find resource: %s", data) } - queryResult := rs.Primary.Attributes["query_result"] - if _, err := strconv.ParseFloat(queryResult, 32); err != nil { - return fmt.Errorf("%s query_result value (%s) is not a float: %s", data, queryResult, err) - } - - return nil - } -} + result := rs.Primary.Attributes["result"] + var objmap map[string]*json.RawMessage -func testAccPricingCheckGreaterValue(dataWithGreaterValue string, otherData string) resource.TestCheckFunc { - return func(s *terraform.State) error { - greaterResource, ok := s.RootModule().Resources[dataWithGreaterValue] - if !ok { - return fmt.Errorf("Can't find resource: %s", dataWithGreaterValue) - } - - lesserResource, ok := s.RootModule().Resources[otherData] - if !ok { - return fmt.Errorf("Can't find resource: %s", otherData) + if err := json.Unmarshal([]byte(result), &objmap); err != nil { + return fmt.Errorf("%s result value (%s) is not JSON: %s", data, result, err) } - greaterValue := greaterResource.Primary.Attributes["query_result"] - lesserValue := lesserResource.Primary.Attributes["query_result"] - - if greaterValue <= lesserValue { - return fmt.Errorf("%s (%s) has a greater value than %s (%s). Should have been the opposite", otherData, lesserValue, dataWithGreaterValue, greaterValue) + if len(objmap) == 0 { + return fmt.Errorf("%s result value (%s) unmarshalling resulted in an empty map", data, result) } return nil diff --git a/vendor/github.com/tidwall/gjson/LICENSE b/vendor/github.com/tidwall/gjson/LICENSE deleted file mode 100644 index 58f5819a438..00000000000 --- a/vendor/github.com/tidwall/gjson/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2016 Josh Baker - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/tidwall/gjson/README.md b/vendor/github.com/tidwall/gjson/README.md deleted file mode 100644 index 70240d99279..00000000000 --- a/vendor/github.com/tidwall/gjson/README.md +++ /dev/null @@ -1,401 +0,0 @@ -

-GJSON -
-Build Status -GoDoc -GJSON Playground -

- - - -

get json values quickly

- -GJSON is a Go package that provides a [fast](#performance) and [simple](#get-a-value) way to get values from a json document. -It has features such as [one line retrieval](#get-a-value), [dot notation paths](#path-syntax), [iteration](#iterate-through-an-object-or-array), and [parsing json lines](#json-lines). - -Also check out [SJSON](https://github.com/tidwall/sjson) for modifying json, and the [JJ](https://github.com/tidwall/jj) command line tool. - -Getting Started -=============== - -## Installing - -To start using GJSON, install Go and run `go get`: - -```sh -$ go get -u github.com/tidwall/gjson -``` - -This will retrieve the library. - -## Get a value -Get searches json for the specified path. A path is in dot syntax, such as "name.last" or "age". When the value is found it's returned immediately. - -```go -package main - -import "github.com/tidwall/gjson" - -const json = `{"name":{"first":"Janet","last":"Prichard"},"age":47}` - -func main() { - value := gjson.Get(json, "name.last") - println(value.String()) -} -``` - -This will print: - -``` -Prichard -``` -*There's also the [GetMany](#get-multiple-values-at-once) function to get multiple values at once, and [GetBytes](#working-with-bytes) for working with JSON byte slices.* - -## Path Syntax - -A path is a series of keys separated by a dot. -A key may contain special wildcard characters '\*' and '?'. -To access an array value use the index as the key. -To get the number of elements in an array or to access a child path, use the '#' character. -The dot and wildcard characters can be escaped with '\\'. - -```json -{ - "name": {"first": "Tom", "last": "Anderson"}, - "age":37, - "children": ["Sara","Alex","Jack"], - "fav.movie": "Deer Hunter", - "friends": [ - {"first": "Dale", "last": "Murphy", "age": 44}, - {"first": "Roger", "last": "Craig", "age": 68}, - {"first": "Jane", "last": "Murphy", "age": 47} - ] -} -``` -``` -"name.last" >> "Anderson" -"age" >> 37 -"children" >> ["Sara","Alex","Jack"] -"children.#" >> 3 -"children.1" >> "Alex" -"child*.2" >> "Jack" -"c?ildren.0" >> "Sara" -"fav\.movie" >> "Deer Hunter" -"friends.#.first" >> ["Dale","Roger","Jane"] -"friends.1.last" >> "Craig" -``` - -You can also query an array for the first match by using `#[...]`, or find all matches with `#[...]#`. -Queries support the `==`, `!=`, `<`, `<=`, `>`, `>=` comparison operators and the simple pattern matching `%` operator. - -``` -friends.#[last=="Murphy"].first >> "Dale" -friends.#[last=="Murphy"]#.first >> ["Dale","Jane"] -friends.#[age>45]#.last >> ["Craig","Murphy"] -friends.#[first%"D*"].last >> "Murphy" -``` - -## JSON Lines - -There's support for [JSON Lines](http://jsonlines.org/) using the `..` prefix, which treats a multilined document as an array. - -For example: - -``` -{"name": "Gilbert", "age": 61} -{"name": "Alexa", "age": 34} -{"name": "May", "age": 57} -{"name": "Deloise", "age": 44} -``` - -``` -..# >> 4 -..1 >> {"name": "Alexa", "age": 34} -..3 >> {"name": "Deloise", "age": 44} -..#.name >> ["Gilbert","Alexa","May","Deloise"] -..#[name="May"].age >> 57 -``` - -The `ForEachLines` function will iterate through JSON lines. - -```go -gjson.ForEachLine(json, func(line gjson.Result) bool{ - println(line.String()) - return true -}) -``` - -## Result Type - -GJSON supports the json types `string`, `number`, `bool`, and `null`. -Arrays and Objects are returned as their raw json types. - -The `Result` type holds one of these: - -``` -bool, for JSON booleans -float64, for JSON numbers -string, for JSON string literals -nil, for JSON null -``` - -To directly access the value: - -```go -result.Type // can be String, Number, True, False, Null, or JSON -result.Str // holds the string -result.Num // holds the float64 number -result.Raw // holds the raw json -result.Index // index of raw value in original json, zero means index unknown -``` - -There are a variety of handy functions that work on a result: - -```go -result.Exists() bool -result.Value() interface{} -result.Int() int64 -result.Uint() uint64 -result.Float() float64 -result.String() string -result.Bool() bool -result.Time() time.Time -result.Array() []gjson.Result -result.Map() map[string]gjson.Result -result.Get(path string) Result -result.ForEach(iterator func(key, value Result) bool) -result.Less(token Result, caseSensitive bool) bool -``` - -The `result.Value()` function returns an `interface{}` which requires type assertion and is one of the following Go types: - -The `result.Array()` function returns back an array of values. -If the result represents a non-existent value, then an empty array will be returned. -If the result is not a JSON array, the return value will be an array containing one result. - -```go -boolean >> bool -number >> float64 -string >> string -null >> nil -array >> []interface{} -object >> map[string]interface{} -``` - -### 64-bit integers - -The `result.Int()` and `result.Uint()` calls are capable of reading all 64 bits, allowing for large JSON integers. - -```go -result.Int() int64 // -9223372036854775808 to 9223372036854775807 -result.Uint() int64 // 0 to 18446744073709551615 -``` - -## Get nested array values - -Suppose you want all the last names from the following json: - -```json -{ - "programmers": [ - { - "firstName": "Janet", - "lastName": "McLaughlin", - }, { - "firstName": "Elliotte", - "lastName": "Hunter", - }, { - "firstName": "Jason", - "lastName": "Harold", - } - ] -} -``` - -You would use the path "programmers.#.lastName" like such: - -```go -result := gjson.Get(json, "programmers.#.lastName") -for _, name := range result.Array() { - println(name.String()) -} -``` - -You can also query an object inside an array: - -```go -name := gjson.Get(json, `programmers.#[lastName="Hunter"].firstName`) -println(name.String()) // prints "Elliotte" -``` - -## Iterate through an object or array - -The `ForEach` function allows for quickly iterating through an object or array. -The key and value are passed to the iterator function for objects. -Only the value is passed for arrays. -Returning `false` from an iterator will stop iteration. - -```go -result := gjson.Get(json, "programmers") -result.ForEach(func(key, value gjson.Result) bool { - println(value.String()) - return true // keep iterating -}) -``` - -## Simple Parse and Get - -There's a `Parse(json)` function that will do a simple parse, and `result.Get(path)` that will search a result. - -For example, all of these will return the same result: - -```go -gjson.Parse(json).Get("name").Get("last") -gjson.Get(json, "name").Get("last") -gjson.Get(json, "name.last") -``` - -## Check for the existence of a value - -Sometimes you just want to know if a value exists. - -```go -value := gjson.Get(json, "name.last") -if !value.Exists() { - println("no last name") -} else { - println(value.String()) -} - -// Or as one step -if gjson.Get(json, "name.last").Exists() { - println("has a last name") -} -``` - -## Validate JSON - -The `Get*` and `Parse*` functions expects that the json is well-formed. Bad json will not panic, but it may return back unexpected results. - -If you are consuming JSON from an unpredictable source then you may want to validate prior to using GJSON. - -```go -if !gjson.Valid(json) { - return errors.New("invalid json") -} -value := gjson.Get(json, "name.last") -``` - -## Unmarshal to a map - -To unmarshal to a `map[string]interface{}`: - -```go -m, ok := gjson.Parse(json).Value().(map[string]interface{}) -if !ok { - // not a map -} -``` - -## Working with Bytes - -If your JSON is contained in a `[]byte` slice, there's the [GetBytes](https://godoc.org/github.com/tidwall/gjson#GetBytes) function. This is preferred over `Get(string(data), path)`. - -```go -var json []byte = ... -result := gjson.GetBytes(json, path) -``` - -If you are using the `gjson.GetBytes(json, path)` function and you want to avoid converting `result.Raw` to a `[]byte`, then you can use this pattern: - -```go -var json []byte = ... -result := gjson.GetBytes(json, path) -var raw []byte -if result.Index > 0 { - raw = json[result.Index:result.Index+len(result.Raw)] -} else { - raw = []byte(result.Raw) -} -``` - -This is a best-effort no allocation sub slice of the original json. This method utilizes the `result.Index` field, which is the position of the raw data in the original json. It's possible that the value of `result.Index` equals zero, in which case the `result.Raw` is converted to a `[]byte`. - -## Get multiple values at once - -The `GetMany` function can be used to get multiple values at the same time. - -```go -results := gjson.GetMany(json, "name.first", "name.last", "age") -``` - -The return value is a `[]Result`, which will always contain exactly the same number of items as the input paths. - -## Performance - -Benchmarks of GJSON alongside [encoding/json](https://golang.org/pkg/encoding/json/), -[ffjson](https://github.com/pquerna/ffjson), -[EasyJSON](https://github.com/mailru/easyjson), -[jsonparser](https://github.com/buger/jsonparser), -and [json-iterator](https://github.com/json-iterator/go) - -``` -BenchmarkGJSONGet-8 3000000 372 ns/op 0 B/op 0 allocs/op -BenchmarkGJSONUnmarshalMap-8 900000 4154 ns/op 1920 B/op 26 allocs/op -BenchmarkJSONUnmarshalMap-8 600000 9019 ns/op 3048 B/op 69 allocs/op -BenchmarkJSONDecoder-8 300000 14120 ns/op 4224 B/op 184 allocs/op -BenchmarkFFJSONLexer-8 1500000 3111 ns/op 896 B/op 8 allocs/op -BenchmarkEasyJSONLexer-8 3000000 887 ns/op 613 B/op 6 allocs/op -BenchmarkJSONParserGet-8 3000000 499 ns/op 21 B/op 0 allocs/op -BenchmarkJSONIterator-8 3000000 812 ns/op 544 B/op 9 allocs/op -``` - -JSON document used: - -```json -{ - "widget": { - "debug": "on", - "window": { - "title": "Sample Konfabulator Widget", - "name": "main_window", - "width": 500, - "height": 500 - }, - "image": { - "src": "Images/Sun.png", - "hOffset": 250, - "vOffset": 250, - "alignment": "center" - }, - "text": { - "data": "Click Here", - "size": 36, - "style": "bold", - "vOffset": 100, - "alignment": "center", - "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" - } - } -} -``` - -Each operation was rotated though one of the following search paths: - -``` -widget.window.name -widget.image.hOffset -widget.text.onMouseUp -``` - -*These benchmarks were run on a MacBook Pro 15" 2.8 GHz Intel Core i7 using Go 1.8 and can be be found [here](https://github.com/tidwall/gjson-benchmarks).* - - -## Contact -Josh Baker [@tidwall](http://twitter.com/tidwall) - -## License - -GJSON source code is available under the MIT [License](/LICENSE). diff --git a/vendor/github.com/tidwall/gjson/gjson.go b/vendor/github.com/tidwall/gjson/gjson.go deleted file mode 100644 index 9ffd77dad5d..00000000000 --- a/vendor/github.com/tidwall/gjson/gjson.go +++ /dev/null @@ -1,2087 +0,0 @@ -// Package gjson provides searching for json strings. -package gjson - -import ( - "encoding/base64" - "encoding/json" - "errors" - "reflect" - "strconv" - "strings" - "sync" - "sync/atomic" - "time" - "unicode/utf16" - "unicode/utf8" - - "github.com/tidwall/match" -) - -// Type is Result type -type Type int - -const ( - // Null is a null json value - Null Type = iota - // False is a json false boolean - False - // Number is json number - Number - // String is a json string - String - // True is a json true boolean - True - // JSON is a raw block of JSON - JSON -) - -// String returns a string representation of the type. -func (t Type) String() string { - switch t { - default: - return "" - case Null: - return "Null" - case False: - return "False" - case Number: - return "Number" - case String: - return "String" - case True: - return "True" - case JSON: - return "JSON" - } -} - -// Result represents a json value that is returned from Get(). -type Result struct { - // Type is the json type - Type Type - // Raw is the raw json - Raw string - // Str is the json string - Str string - // Num is the json number - Num float64 - // Index of raw value in original json, zero means index unknown - Index int -} - -// String returns a string representation of the value. -func (t Result) String() string { - switch t.Type { - default: - return "" - case False: - return "false" - case Number: - return strconv.FormatFloat(t.Num, 'f', -1, 64) - case String: - return t.Str - case JSON: - return t.Raw - case True: - return "true" - } -} - -// Bool returns an boolean representation. -func (t Result) Bool() bool { - switch t.Type { - default: - return false - case True: - return true - case String: - return t.Str != "" && t.Str != "0" && t.Str != "false" - case Number: - return t.Num != 0 - } -} - -// Int returns an integer representation. -func (t Result) Int() int64 { - switch t.Type { - default: - return 0 - case True: - return 1 - case String: - n, _ := parseInt(t.Str) - return n - case Number: - // try to directly convert the float64 to int64 - n, ok := floatToInt(t.Num) - if !ok { - // now try to parse the raw string - n, ok = parseInt(t.Raw) - if !ok { - // fallback to a standard conversion - return int64(t.Num) - } - } - return n - } -} - -// Uint returns an unsigned integer representation. -func (t Result) Uint() uint64 { - switch t.Type { - default: - return 0 - case True: - return 1 - case String: - n, _ := parseUint(t.Str) - return n - case Number: - // try to directly convert the float64 to uint64 - n, ok := floatToUint(t.Num) - if !ok { - // now try to parse the raw string - n, ok = parseUint(t.Raw) - if !ok { - // fallback to a standard conversion - return uint64(t.Num) - } - } - return n - } -} - -// Float returns an float64 representation. -func (t Result) Float() float64 { - switch t.Type { - default: - return 0 - case True: - return 1 - case String: - n, _ := strconv.ParseFloat(t.Str, 64) - return n - case Number: - return t.Num - } -} - -// Time returns a time.Time representation. -func (t Result) Time() time.Time { - res, _ := time.Parse(time.RFC3339, t.String()) - return res -} - -// Array returns back an array of values. -// If the result represents a non-existent value, then an empty array will be returned. -// If the result is not a JSON array, the return value will be an array containing one result. -func (t Result) Array() []Result { - if t.Type == Null { - return []Result{} - } - if t.Type != JSON { - return []Result{t} - } - r := t.arrayOrMap('[', false) - return r.a -} - -// IsObject returns true if the result value is a JSON object. -func (t Result) IsObject() bool { - return t.Type == JSON && len(t.Raw) > 0 && t.Raw[0] == '{' -} - -// IsArray returns true if the result value is a JSON array. -func (t Result) IsArray() bool { - return t.Type == JSON && len(t.Raw) > 0 && t.Raw[0] == '[' -} - -// ForEach iterates through values. -// If the result represents a non-existent value, then no values will be iterated. -// If the result is an Object, the iterator will pass the key and value of each item. -// If the result is an Array, the iterator will only pass the value of each item. -// If the result is not a JSON array or object, the iterator will pass back one value equal to the result. -func (t Result) ForEach(iterator func(key, value Result) bool) { - if !t.Exists() { - return - } - if t.Type != JSON { - iterator(Result{}, t) - return - } - json := t.Raw - var keys bool - var i int - var key, value Result - for ; i < len(json); i++ { - if json[i] == '{' { - i++ - key.Type = String - keys = true - break - } else if json[i] == '[' { - i++ - break - } - if json[i] > ' ' { - return - } - } - var str string - var vesc bool - var ok bool - for ; i < len(json); i++ { - if keys { - if json[i] != '"' { - continue - } - s := i - i, str, vesc, ok = parseString(json, i+1) - if !ok { - return - } - if vesc { - key.Str = unescape(str[1 : len(str)-1]) - } else { - key.Str = str[1 : len(str)-1] - } - key.Raw = str - key.Index = s - } - for ; i < len(json); i++ { - if json[i] <= ' ' || json[i] == ',' || json[i] == ':' { - continue - } - break - } - s := i - i, value, ok = parseAny(json, i, true) - if !ok { - return - } - value.Index = s - if !iterator(key, value) { - return - } - } -} - -// Map returns back an map of values. The result should be a JSON array. -func (t Result) Map() map[string]Result { - if t.Type != JSON { - return map[string]Result{} - } - r := t.arrayOrMap('{', false) - return r.o -} - -// Get searches result for the specified path. -// The result should be a JSON array or object. -func (t Result) Get(path string) Result { - return Get(t.Raw, path) -} - -type arrayOrMapResult struct { - a []Result - ai []interface{} - o map[string]Result - oi map[string]interface{} - vc byte -} - -func (t Result) arrayOrMap(vc byte, valueize bool) (r arrayOrMapResult) { - var json = t.Raw - var i int - var value Result - var count int - var key Result - if vc == 0 { - for ; i < len(json); i++ { - if json[i] == '{' || json[i] == '[' { - r.vc = json[i] - i++ - break - } - if json[i] > ' ' { - goto end - } - } - } else { - for ; i < len(json); i++ { - if json[i] == vc { - i++ - break - } - if json[i] > ' ' { - goto end - } - } - r.vc = vc - } - if r.vc == '{' { - if valueize { - r.oi = make(map[string]interface{}) - } else { - r.o = make(map[string]Result) - } - } else { - if valueize { - r.ai = make([]interface{}, 0) - } else { - r.a = make([]Result, 0) - } - } - for ; i < len(json); i++ { - if json[i] <= ' ' { - continue - } - // get next value - if json[i] == ']' || json[i] == '}' { - break - } - switch json[i] { - default: - if (json[i] >= '0' && json[i] <= '9') || json[i] == '-' { - value.Type = Number - value.Raw, value.Num = tonum(json[i:]) - } else { - continue - } - case '{', '[': - value.Type = JSON - value.Raw = squash(json[i:]) - case 'n': - value.Type = Null - value.Raw = tolit(json[i:]) - case 't': - value.Type = True - value.Raw = tolit(json[i:]) - case 'f': - value.Type = False - value.Raw = tolit(json[i:]) - case '"': - value.Type = String - value.Raw, value.Str = tostr(json[i:]) - } - i += len(value.Raw) - 1 - - if r.vc == '{' { - if count%2 == 0 { - key = value - } else { - if valueize { - r.oi[key.Str] = value.Value() - } else { - r.o[key.Str] = value - } - } - count++ - } else { - if valueize { - r.ai = append(r.ai, value.Value()) - } else { - r.a = append(r.a, value) - } - } - } -end: - return -} - -// Parse parses the json and returns a result. -// -// This function expects that the json is well-formed, and does not validate. -// Invalid json will not panic, but it may return back unexpected results. -// If you are consuming JSON from an unpredictable source then you may want to -// use the Valid function first. -func Parse(json string) Result { - var value Result - for i := 0; i < len(json); i++ { - if json[i] == '{' || json[i] == '[' { - value.Type = JSON - value.Raw = json[i:] // just take the entire raw - break - } - if json[i] <= ' ' { - continue - } - switch json[i] { - default: - if (json[i] >= '0' && json[i] <= '9') || json[i] == '-' { - value.Type = Number - value.Raw, value.Num = tonum(json[i:]) - } else { - return Result{} - } - case 'n': - value.Type = Null - value.Raw = tolit(json[i:]) - case 't': - value.Type = True - value.Raw = tolit(json[i:]) - case 'f': - value.Type = False - value.Raw = tolit(json[i:]) - case '"': - value.Type = String - value.Raw, value.Str = tostr(json[i:]) - } - break - } - return value -} - -// ParseBytes parses the json and returns a result. -// If working with bytes, this method preferred over Parse(string(data)) -func ParseBytes(json []byte) Result { - return Parse(string(json)) -} - -func squash(json string) string { - // expects that the lead character is a '[' or '{' - // squash the value, ignoring all nested arrays and objects. - // the first '[' or '{' has already been read - depth := 1 - for i := 1; i < len(json); i++ { - if json[i] >= '"' && json[i] <= '}' { - switch json[i] { - case '"': - i++ - s2 := i - for ; i < len(json); i++ { - if json[i] > '\\' { - continue - } - if json[i] == '"' { - // look for an escaped slash - if json[i-1] == '\\' { - n := 0 - for j := i - 2; j > s2-1; j-- { - if json[j] != '\\' { - break - } - n++ - } - if n%2 == 0 { - continue - } - } - break - } - } - case '{', '[': - depth++ - case '}', ']': - depth-- - if depth == 0 { - return json[:i+1] - } - } - } - } - return json -} - -func tonum(json string) (raw string, num float64) { - for i := 1; i < len(json); i++ { - // less than dash might have valid characters - if json[i] <= '-' { - if json[i] <= ' ' || json[i] == ',' { - // break on whitespace and comma - raw = json[:i] - num, _ = strconv.ParseFloat(raw, 64) - return - } - // could be a '+' or '-'. let's assume so. - continue - } - if json[i] < ']' { - // probably a valid number - continue - } - if json[i] == 'e' || json[i] == 'E' { - // allow for exponential numbers - continue - } - // likely a ']' or '}' - raw = json[:i] - num, _ = strconv.ParseFloat(raw, 64) - return - } - raw = json - num, _ = strconv.ParseFloat(raw, 64) - return -} - -func tolit(json string) (raw string) { - for i := 1; i < len(json); i++ { - if json[i] < 'a' || json[i] > 'z' { - return json[:i] - } - } - return json -} - -func tostr(json string) (raw string, str string) { - // expects that the lead character is a '"' - for i := 1; i < len(json); i++ { - if json[i] > '\\' { - continue - } - if json[i] == '"' { - return json[:i+1], json[1:i] - } - if json[i] == '\\' { - i++ - for ; i < len(json); i++ { - if json[i] > '\\' { - continue - } - if json[i] == '"' { - // look for an escaped slash - if json[i-1] == '\\' { - n := 0 - for j := i - 2; j > 0; j-- { - if json[j] != '\\' { - break - } - n++ - } - if n%2 == 0 { - continue - } - } - break - } - } - var ret string - if i+1 < len(json) { - ret = json[:i+1] - } else { - ret = json[:i] - } - return ret, unescape(json[1:i]) - } - } - return json, json[1:] -} - -// Exists returns true if value exists. -// -// if gjson.Get(json, "name.last").Exists(){ -// println("value exists") -// } -func (t Result) Exists() bool { - return t.Type != Null || len(t.Raw) != 0 -} - -// Value returns one of these types: -// -// bool, for JSON booleans -// float64, for JSON numbers -// Number, for JSON numbers -// string, for JSON string literals -// nil, for JSON null -// map[string]interface{}, for JSON objects -// []interface{}, for JSON arrays -// -func (t Result) Value() interface{} { - if t.Type == String { - return t.Str - } - switch t.Type { - default: - return nil - case False: - return false - case Number: - return t.Num - case JSON: - r := t.arrayOrMap(0, true) - if r.vc == '{' { - return r.oi - } else if r.vc == '[' { - return r.ai - } - return nil - case True: - return true - } -} - -func parseString(json string, i int) (int, string, bool, bool) { - var s = i - for ; i < len(json); i++ { - if json[i] > '\\' { - continue - } - if json[i] == '"' { - return i + 1, json[s-1 : i+1], false, true - } - if json[i] == '\\' { - i++ - for ; i < len(json); i++ { - if json[i] > '\\' { - continue - } - if json[i] == '"' { - // look for an escaped slash - if json[i-1] == '\\' { - n := 0 - for j := i - 2; j > 0; j-- { - if json[j] != '\\' { - break - } - n++ - } - if n%2 == 0 { - continue - } - } - return i + 1, json[s-1 : i+1], true, true - } - } - break - } - } - return i, json[s-1:], false, false -} - -func parseNumber(json string, i int) (int, string) { - var s = i - i++ - for ; i < len(json); i++ { - if json[i] <= ' ' || json[i] == ',' || json[i] == ']' || json[i] == '}' { - return i, json[s:i] - } - } - return i, json[s:] -} - -func parseLiteral(json string, i int) (int, string) { - var s = i - i++ - for ; i < len(json); i++ { - if json[i] < 'a' || json[i] > 'z' { - return i, json[s:i] - } - } - return i, json[s:] -} - -type arrayPathResult struct { - part string - path string - more bool - alogok bool - arrch bool - alogkey string - query struct { - on bool - path string - op string - value string - all bool - } -} - -func parseArrayPath(path string) (r arrayPathResult) { - for i := 0; i < len(path); i++ { - if path[i] == '.' { - r.part = path[:i] - r.path = path[i+1:] - r.more = true - return - } - if path[i] == '#' { - r.arrch = true - if i == 0 && len(path) > 1 { - if path[1] == '.' { - r.alogok = true - r.alogkey = path[2:] - r.path = path[:1] - } else if path[1] == '[' { - r.query.on = true - // query - i += 2 - // whitespace - for ; i < len(path); i++ { - if path[i] > ' ' { - break - } - } - s := i - for ; i < len(path); i++ { - if path[i] <= ' ' || - path[i] == '!' || - path[i] == '=' || - path[i] == '<' || - path[i] == '>' || - path[i] == '%' || - path[i] == ']' { - break - } - } - r.query.path = path[s:i] - // whitespace - for ; i < len(path); i++ { - if path[i] > ' ' { - break - } - } - if i < len(path) { - s = i - if path[i] == '!' { - if i < len(path)-1 && path[i+1] == '=' { - i++ - } - } else if path[i] == '<' || path[i] == '>' { - if i < len(path)-1 && path[i+1] == '=' { - i++ - } - } else if path[i] == '=' { - if i < len(path)-1 && path[i+1] == '=' { - s++ - i++ - } - } - i++ - r.query.op = path[s:i] - // whitespace - for ; i < len(path); i++ { - if path[i] > ' ' { - break - } - } - s = i - for ; i < len(path); i++ { - if path[i] == '"' { - i++ - s2 := i - for ; i < len(path); i++ { - if path[i] > '\\' { - continue - } - if path[i] == '"' { - // look for an escaped slash - if path[i-1] == '\\' { - n := 0 - for j := i - 2; j > s2-1; j-- { - if path[j] != '\\' { - break - } - n++ - } - if n%2 == 0 { - continue - } - } - break - } - } - } else if path[i] == ']' { - if i+1 < len(path) && path[i+1] == '#' { - r.query.all = true - } - break - } - } - if i > len(path) { - i = len(path) - } - v := path[s:i] - for len(v) > 0 && v[len(v)-1] <= ' ' { - v = v[:len(v)-1] - } - r.query.value = v - } - } - } - continue - } - } - r.part = path - r.path = "" - return -} - -type objectPathResult struct { - part string - path string - wild bool - more bool -} - -func parseObjectPath(path string) (r objectPathResult) { - for i := 0; i < len(path); i++ { - if path[i] == '.' { - r.part = path[:i] - r.path = path[i+1:] - r.more = true - return - } - if path[i] == '*' || path[i] == '?' { - r.wild = true - continue - } - if path[i] == '\\' { - // go into escape mode. this is a slower path that - // strips off the escape character from the part. - epart := []byte(path[:i]) - i++ - if i < len(path) { - epart = append(epart, path[i]) - i++ - for ; i < len(path); i++ { - if path[i] == '\\' { - i++ - if i < len(path) { - epart = append(epart, path[i]) - } - continue - } else if path[i] == '.' { - r.part = string(epart) - r.path = path[i+1:] - r.more = true - return - } else if path[i] == '*' || path[i] == '?' { - r.wild = true - } - epart = append(epart, path[i]) - } - } - // append the last part - r.part = string(epart) - return - } - } - r.part = path - return -} - -func parseSquash(json string, i int) (int, string) { - // expects that the lead character is a '[' or '{' - // squash the value, ignoring all nested arrays and objects. - // the first '[' or '{' has already been read - s := i - i++ - depth := 1 - for ; i < len(json); i++ { - if json[i] >= '"' && json[i] <= '}' { - switch json[i] { - case '"': - i++ - s2 := i - for ; i < len(json); i++ { - if json[i] > '\\' { - continue - } - if json[i] == '"' { - // look for an escaped slash - if json[i-1] == '\\' { - n := 0 - for j := i - 2; j > s2-1; j-- { - if json[j] != '\\' { - break - } - n++ - } - if n%2 == 0 { - continue - } - } - break - } - } - case '{', '[': - depth++ - case '}', ']': - depth-- - if depth == 0 { - i++ - return i, json[s:i] - } - } - } - } - return i, json[s:] -} - -func parseObject(c *parseContext, i int, path string) (int, bool) { - var pmatch, kesc, vesc, ok, hit bool - var key, val string - rp := parseObjectPath(path) - for i < len(c.json) { - for ; i < len(c.json); i++ { - if c.json[i] == '"' { - // parse_key_string - // this is slightly different from getting s string value - // because we don't need the outer quotes. - i++ - var s = i - for ; i < len(c.json); i++ { - if c.json[i] > '\\' { - continue - } - if c.json[i] == '"' { - i, key, kesc, ok = i+1, c.json[s:i], false, true - goto parse_key_string_done - } - if c.json[i] == '\\' { - i++ - for ; i < len(c.json); i++ { - if c.json[i] > '\\' { - continue - } - if c.json[i] == '"' { - // look for an escaped slash - if c.json[i-1] == '\\' { - n := 0 - for j := i - 2; j > 0; j-- { - if c.json[j] != '\\' { - break - } - n++ - } - if n%2 == 0 { - continue - } - } - i, key, kesc, ok = i+1, c.json[s:i], true, true - goto parse_key_string_done - } - } - break - } - } - key, kesc, ok = c.json[s:], false, false - parse_key_string_done: - break - } - if c.json[i] == '}' { - return i + 1, false - } - } - if !ok { - return i, false - } - if rp.wild { - if kesc { - pmatch = match.Match(unescape(key), rp.part) - } else { - pmatch = match.Match(key, rp.part) - } - } else { - if kesc { - pmatch = rp.part == unescape(key) - } else { - pmatch = rp.part == key - } - } - hit = pmatch && !rp.more - for ; i < len(c.json); i++ { - switch c.json[i] { - default: - continue - case '"': - i++ - i, val, vesc, ok = parseString(c.json, i) - if !ok { - return i, false - } - if hit { - if vesc { - c.value.Str = unescape(val[1 : len(val)-1]) - } else { - c.value.Str = val[1 : len(val)-1] - } - c.value.Raw = val - c.value.Type = String - return i, true - } - case '{': - if pmatch && !hit { - i, hit = parseObject(c, i+1, rp.path) - if hit { - return i, true - } - } else { - i, val = parseSquash(c.json, i) - if hit { - c.value.Raw = val - c.value.Type = JSON - return i, true - } - } - case '[': - if pmatch && !hit { - i, hit = parseArray(c, i+1, rp.path) - if hit { - return i, true - } - } else { - i, val = parseSquash(c.json, i) - if hit { - c.value.Raw = val - c.value.Type = JSON - return i, true - } - } - case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - i, val = parseNumber(c.json, i) - if hit { - c.value.Raw = val - c.value.Type = Number - c.value.Num, _ = strconv.ParseFloat(val, 64) - return i, true - } - case 't', 'f', 'n': - vc := c.json[i] - i, val = parseLiteral(c.json, i) - if hit { - c.value.Raw = val - switch vc { - case 't': - c.value.Type = True - case 'f': - c.value.Type = False - } - return i, true - } - } - break - } - } - return i, false -} -func queryMatches(rp *arrayPathResult, value Result) bool { - rpv := rp.query.value - if len(rpv) > 2 && rpv[0] == '"' && rpv[len(rpv)-1] == '"' { - rpv = rpv[1 : len(rpv)-1] - } - switch value.Type { - case String: - switch rp.query.op { - case "=": - return value.Str == rpv - case "!=": - return value.Str != rpv - case "<": - return value.Str < rpv - case "<=": - return value.Str <= rpv - case ">": - return value.Str > rpv - case ">=": - return value.Str >= rpv - case "%": - return match.Match(value.Str, rpv) - } - case Number: - rpvn, _ := strconv.ParseFloat(rpv, 64) - switch rp.query.op { - case "=": - return value.Num == rpvn - case "!=": - return value.Num != rpvn - case "<": - return value.Num < rpvn - case "<=": - return value.Num <= rpvn - case ">": - return value.Num > rpvn - case ">=": - return value.Num >= rpvn - } - case True: - switch rp.query.op { - case "=": - return rpv == "true" - case "!=": - return rpv != "true" - case ">": - return rpv == "false" - case ">=": - return true - } - case False: - switch rp.query.op { - case "=": - return rpv == "false" - case "!=": - return rpv != "false" - case "<": - return rpv == "true" - case "<=": - return true - } - } - return false -} -func parseArray(c *parseContext, i int, path string) (int, bool) { - var pmatch, vesc, ok, hit bool - var val string - var h int - var alog []int - var partidx int - var multires []byte - rp := parseArrayPath(path) - if !rp.arrch { - n, ok := parseUint(rp.part) - if !ok { - partidx = -1 - } else { - partidx = int(n) - } - } - for i < len(c.json)+1 { - if !rp.arrch { - pmatch = partidx == h - hit = pmatch && !rp.more - } - h++ - if rp.alogok { - alog = append(alog, i) - } - for ; ; i++ { - var ch byte - if i > len(c.json) { - break - } else if i == len(c.json) { - ch = ']' - } else { - ch = c.json[i] - } - switch ch { - default: - continue - case '"': - i++ - i, val, vesc, ok = parseString(c.json, i) - if !ok { - return i, false - } - if hit { - if rp.alogok { - break - } - if vesc { - c.value.Str = unescape(val[1 : len(val)-1]) - } else { - c.value.Str = val[1 : len(val)-1] - } - c.value.Raw = val - c.value.Type = String - return i, true - } - case '{': - if pmatch && !hit { - i, hit = parseObject(c, i+1, rp.path) - if hit { - if rp.alogok { - break - } - return i, true - } - } else { - i, val = parseSquash(c.json, i) - if rp.query.on { - res := Get(val, rp.query.path) - if queryMatches(&rp, res) { - if rp.more { - res = Get(val, rp.path) - } else { - res = Result{Raw: val, Type: JSON} - } - if rp.query.all { - if len(multires) == 0 { - multires = append(multires, '[') - } else { - multires = append(multires, ',') - } - multires = append(multires, res.Raw...) - } else { - c.value = res - return i, true - } - } - } else if hit { - if rp.alogok { - break - } - c.value.Raw = val - c.value.Type = JSON - return i, true - } - } - case '[': - if pmatch && !hit { - i, hit = parseArray(c, i+1, rp.path) - if hit { - if rp.alogok { - break - } - return i, true - } - } else { - i, val = parseSquash(c.json, i) - if hit { - if rp.alogok { - break - } - c.value.Raw = val - c.value.Type = JSON - return i, true - } - } - case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - i, val = parseNumber(c.json, i) - if hit { - if rp.alogok { - break - } - c.value.Raw = val - c.value.Type = Number - c.value.Num, _ = strconv.ParseFloat(val, 64) - return i, true - } - case 't', 'f', 'n': - vc := c.json[i] - i, val = parseLiteral(c.json, i) - if hit { - if rp.alogok { - break - } - c.value.Raw = val - switch vc { - case 't': - c.value.Type = True - case 'f': - c.value.Type = False - } - return i, true - } - case ']': - if rp.arrch && rp.part == "#" { - if rp.alogok { - var jsons = make([]byte, 0, 64) - jsons = append(jsons, '[') - - for j, k := 0, 0; j < len(alog); j++ { - _, res, ok := parseAny(c.json, alog[j], true) - if ok { - res := res.Get(rp.alogkey) - if res.Exists() { - if k > 0 { - jsons = append(jsons, ',') - } - jsons = append(jsons, []byte(res.Raw)...) - k++ - } - } - } - jsons = append(jsons, ']') - c.value.Type = JSON - c.value.Raw = string(jsons) - return i + 1, true - } - if rp.alogok { - break - } - c.value.Raw = val - c.value.Type = Number - c.value.Num = float64(h - 1) - c.calcd = true - return i + 1, true - } - if len(multires) > 0 && !c.value.Exists() { - c.value = Result{ - Raw: string(append(multires, ']')), - Type: JSON, - } - } - return i + 1, false - } - break - } - } - return i, false -} - -// ForEachLine iterates through lines of JSON as specified by the JSON Lines -// format (http://jsonlines.org/). -// Each line is returned as a GJSON Result. -func ForEachLine(json string, iterator func(line Result) bool) { - var res Result - var i int - for { - i, res, _ = parseAny(json, i, true) - if !res.Exists() { - break - } - if !iterator(res) { - return - } - } -} - -type parseContext struct { - json string - value Result - calcd bool - lines bool -} - -// Get searches json for the specified path. -// A path is in dot syntax, such as "name.last" or "age". -// When the value is found it's returned immediately. -// -// A path is a series of keys searated by a dot. -// A key may contain special wildcard characters '*' and '?'. -// To access an array value use the index as the key. -// To get the number of elements in an array or to access a child path, use the '#' character. -// The dot and wildcard character can be escaped with '\'. -// -// { -// "name": {"first": "Tom", "last": "Anderson"}, -// "age":37, -// "children": ["Sara","Alex","Jack"], -// "friends": [ -// {"first": "James", "last": "Murphy"}, -// {"first": "Roger", "last": "Craig"} -// ] -// } -// "name.last" >> "Anderson" -// "age" >> 37 -// "children" >> ["Sara","Alex","Jack"] -// "children.#" >> 3 -// "children.1" >> "Alex" -// "child*.2" >> "Jack" -// "c?ildren.0" >> "Sara" -// "friends.#.first" >> ["James","Roger"] -// -// This function expects that the json is well-formed, and does not validate. -// Invalid json will not panic, but it may return back unexpected results. -// If you are consuming JSON from an unpredictable source then you may want to -// use the Valid function first. -func Get(json, path string) Result { - var i int - var c = &parseContext{json: json} - if len(path) >= 2 && path[0] == '.' && path[1] == '.' { - c.lines = true - parseArray(c, 0, path[2:]) - } else { - for ; i < len(c.json); i++ { - if c.json[i] == '{' { - i++ - parseObject(c, i, path) - break - } - if c.json[i] == '[' { - i++ - parseArray(c, i, path) - break - } - } - } - fillIndex(json, c) - return c.value -} - -// GetBytes searches json for the specified path. -// If working with bytes, this method preferred over Get(string(data), path) -func GetBytes(json []byte, path string) Result { - return getBytes(json, path) -} - -// runeit returns the rune from the the \uXXXX -func runeit(json string) rune { - n, _ := strconv.ParseUint(json[:4], 16, 64) - return rune(n) -} - -// unescape unescapes a string -func unescape(json string) string { //, error) { - var str = make([]byte, 0, len(json)) - for i := 0; i < len(json); i++ { - switch { - default: - str = append(str, json[i]) - case json[i] < ' ': - return string(str) - case json[i] == '\\': - i++ - if i >= len(json) { - return string(str) - } - switch json[i] { - default: - return string(str) - case '\\': - str = append(str, '\\') - case '/': - str = append(str, '/') - case 'b': - str = append(str, '\b') - case 'f': - str = append(str, '\f') - case 'n': - str = append(str, '\n') - case 'r': - str = append(str, '\r') - case 't': - str = append(str, '\t') - case '"': - str = append(str, '"') - case 'u': - if i+5 > len(json) { - return string(str) - } - r := runeit(json[i+1:]) - i += 5 - if utf16.IsSurrogate(r) { - // need another code - if len(json[i:]) >= 6 && json[i] == '\\' && json[i+1] == 'u' { - // we expect it to be correct so just consume it - r = utf16.DecodeRune(r, runeit(json[i+2:])) - i += 6 - } - } - // provide enough space to encode the largest utf8 possible - str = append(str, 0, 0, 0, 0, 0, 0, 0, 0) - n := utf8.EncodeRune(str[len(str)-8:], r) - str = str[:len(str)-8+n] - i-- // backtrack index by one - } - } - } - return string(str) -} - -// Less return true if a token is less than another token. -// The caseSensitive paramater is used when the tokens are Strings. -// The order when comparing two different type is: -// -// Null < False < Number < String < True < JSON -// -func (t Result) Less(token Result, caseSensitive bool) bool { - if t.Type < token.Type { - return true - } - if t.Type > token.Type { - return false - } - if t.Type == String { - if caseSensitive { - return t.Str < token.Str - } - return stringLessInsensitive(t.Str, token.Str) - } - if t.Type == Number { - return t.Num < token.Num - } - return t.Raw < token.Raw -} - -func stringLessInsensitive(a, b string) bool { - for i := 0; i < len(a) && i < len(b); i++ { - if a[i] >= 'A' && a[i] <= 'Z' { - if b[i] >= 'A' && b[i] <= 'Z' { - // both are uppercase, do nothing - if a[i] < b[i] { - return true - } else if a[i] > b[i] { - return false - } - } else { - // a is uppercase, convert a to lowercase - if a[i]+32 < b[i] { - return true - } else if a[i]+32 > b[i] { - return false - } - } - } else if b[i] >= 'A' && b[i] <= 'Z' { - // b is uppercase, convert b to lowercase - if a[i] < b[i]+32 { - return true - } else if a[i] > b[i]+32 { - return false - } - } else { - // neither are uppercase - if a[i] < b[i] { - return true - } else if a[i] > b[i] { - return false - } - } - } - return len(a) < len(b) -} - -// parseAny parses the next value from a json string. -// A Result is returned when the hit param is set. -// The return values are (i int, res Result, ok bool) -func parseAny(json string, i int, hit bool) (int, Result, bool) { - var res Result - var val string - for ; i < len(json); i++ { - if json[i] == '{' || json[i] == '[' { - i, val = parseSquash(json, i) - if hit { - res.Raw = val - res.Type = JSON - } - return i, res, true - } - if json[i] <= ' ' { - continue - } - switch json[i] { - case '"': - i++ - var vesc bool - var ok bool - i, val, vesc, ok = parseString(json, i) - if !ok { - return i, res, false - } - if hit { - res.Type = String - res.Raw = val - if vesc { - res.Str = unescape(val[1 : len(val)-1]) - } else { - res.Str = val[1 : len(val)-1] - } - } - return i, res, true - case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - i, val = parseNumber(json, i) - if hit { - res.Raw = val - res.Type = Number - res.Num, _ = strconv.ParseFloat(val, 64) - } - return i, res, true - case 't', 'f', 'n': - vc := json[i] - i, val = parseLiteral(json, i) - if hit { - res.Raw = val - switch vc { - case 't': - res.Type = True - case 'f': - res.Type = False - } - return i, res, true - } - } - } - return i, res, false -} - -var ( // used for testing - testWatchForFallback bool - testLastWasFallback bool -) - -// GetMany searches json for the multiple paths. -// The return value is a Result array where the number of items -// will be equal to the number of input paths. -func GetMany(json string, path ...string) []Result { - res := make([]Result, len(path)) - for i, path := range path { - res[i] = Get(json, path) - } - return res -} - -// GetManyBytes searches json for the multiple paths. -// The return value is a Result array where the number of items -// will be equal to the number of input paths. -func GetManyBytes(json []byte, path ...string) []Result { - return GetMany(string(json), path...) -} - -var fieldsmu sync.RWMutex -var fields = make(map[string]map[string]int) - -func assign(jsval Result, goval reflect.Value) { - if jsval.Type == Null { - return - } - switch goval.Kind() { - default: - case reflect.Ptr: - if !goval.IsNil() { - newval := reflect.New(goval.Elem().Type()) - assign(jsval, newval.Elem()) - goval.Elem().Set(newval.Elem()) - } else { - newval := reflect.New(goval.Type().Elem()) - assign(jsval, newval.Elem()) - goval.Set(newval) - } - case reflect.Struct: - fieldsmu.RLock() - sf := fields[goval.Type().String()] - fieldsmu.RUnlock() - if sf == nil { - fieldsmu.Lock() - sf = make(map[string]int) - for i := 0; i < goval.Type().NumField(); i++ { - f := goval.Type().Field(i) - tag := strings.Split(f.Tag.Get("json"), ",")[0] - if tag != "-" { - if tag != "" { - sf[tag] = i - sf[f.Name] = i - } else { - sf[f.Name] = i - } - } - } - fields[goval.Type().String()] = sf - fieldsmu.Unlock() - } - jsval.ForEach(func(key, value Result) bool { - if idx, ok := sf[key.Str]; ok { - f := goval.Field(idx) - if f.CanSet() { - assign(value, f) - } - } - return true - }) - case reflect.Slice: - if goval.Type().Elem().Kind() == reflect.Uint8 && jsval.Type == String { - data, _ := base64.StdEncoding.DecodeString(jsval.String()) - goval.Set(reflect.ValueOf(data)) - } else { - jsvals := jsval.Array() - slice := reflect.MakeSlice(goval.Type(), len(jsvals), len(jsvals)) - for i := 0; i < len(jsvals); i++ { - assign(jsvals[i], slice.Index(i)) - } - goval.Set(slice) - } - case reflect.Array: - i, n := 0, goval.Len() - jsval.ForEach(func(_, value Result) bool { - if i == n { - return false - } - assign(value, goval.Index(i)) - i++ - return true - }) - case reflect.Map: - if goval.Type().Key().Kind() == reflect.String && goval.Type().Elem().Kind() == reflect.Interface { - goval.Set(reflect.ValueOf(jsval.Value())) - } - case reflect.Interface: - goval.Set(reflect.ValueOf(jsval.Value())) - case reflect.Bool: - goval.SetBool(jsval.Bool()) - case reflect.Float32, reflect.Float64: - goval.SetFloat(jsval.Float()) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - goval.SetInt(jsval.Int()) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - goval.SetUint(jsval.Uint()) - case reflect.String: - goval.SetString(jsval.String()) - } - if len(goval.Type().PkgPath()) > 0 { - v := goval.Addr() - if v.Type().NumMethod() > 0 { - if u, ok := v.Interface().(json.Unmarshaler); ok { - u.UnmarshalJSON([]byte(jsval.Raw)) - } - } - } -} - -var validate uintptr = 1 - -// UnmarshalValidationEnabled provides the option to disable JSON validation -// during the Unmarshal routine. Validation is enabled by default. -// -// Deprecated: Use encoder/json.Unmarshal instead -func UnmarshalValidationEnabled(enabled bool) { - if enabled { - atomic.StoreUintptr(&validate, 1) - } else { - atomic.StoreUintptr(&validate, 0) - } -} - -// Unmarshal loads the JSON data into the value pointed to by v. -// -// This function works almost identically to json.Unmarshal except that -// gjson.Unmarshal will automatically attempt to convert JSON values to any Go -// type. For example, the JSON string "100" or the JSON number 100 can be equally -// assigned to Go string, int, byte, uint64, etc. This rule applies to all types. -// -// Deprecated: Use encoder/json.Unmarshal instead -func Unmarshal(data []byte, v interface{}) error { - if atomic.LoadUintptr(&validate) == 1 { - _, ok := validpayload(data, 0) - if !ok { - return errors.New("invalid json") - } - } - if v := reflect.ValueOf(v); v.Kind() == reflect.Ptr { - assign(ParseBytes(data), v) - } - return nil -} - -func validpayload(data []byte, i int) (outi int, ok bool) { - for ; i < len(data); i++ { - switch data[i] { - default: - i, ok = validany(data, i) - if !ok { - return i, false - } - for ; i < len(data); i++ { - switch data[i] { - default: - return i, false - case ' ', '\t', '\n', '\r': - continue - } - } - return i, true - case ' ', '\t', '\n', '\r': - continue - } - } - return i, false -} -func validany(data []byte, i int) (outi int, ok bool) { - for ; i < len(data); i++ { - switch data[i] { - default: - return i, false - case ' ', '\t', '\n', '\r': - continue - case '{': - return validobject(data, i+1) - case '[': - return validarray(data, i+1) - case '"': - return validstring(data, i+1) - case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - return validnumber(data, i+1) - case 't': - return validtrue(data, i+1) - case 'f': - return validfalse(data, i+1) - case 'n': - return validnull(data, i+1) - } - } - return i, false -} -func validobject(data []byte, i int) (outi int, ok bool) { - for ; i < len(data); i++ { - switch data[i] { - default: - return i, false - case ' ', '\t', '\n', '\r': - continue - case '}': - return i + 1, true - case '"': - key: - if i, ok = validstring(data, i+1); !ok { - return i, false - } - if i, ok = validcolon(data, i); !ok { - return i, false - } - if i, ok = validany(data, i); !ok { - return i, false - } - if i, ok = validcomma(data, i, '}'); !ok { - return i, false - } - if data[i] == '}' { - return i + 1, true - } - i++ - for ; i < len(data); i++ { - switch data[i] { - default: - return i, false - case ' ', '\t', '\n', '\r': - continue - case '"': - goto key - } - } - return i, false - } - } - return i, false -} -func validcolon(data []byte, i int) (outi int, ok bool) { - for ; i < len(data); i++ { - switch data[i] { - default: - return i, false - case ' ', '\t', '\n', '\r': - continue - case ':': - return i + 1, true - } - } - return i, false -} -func validcomma(data []byte, i int, end byte) (outi int, ok bool) { - for ; i < len(data); i++ { - switch data[i] { - default: - return i, false - case ' ', '\t', '\n', '\r': - continue - case ',': - return i, true - case end: - return i, true - } - } - return i, false -} -func validarray(data []byte, i int) (outi int, ok bool) { - for ; i < len(data); i++ { - switch data[i] { - default: - for ; i < len(data); i++ { - if i, ok = validany(data, i); !ok { - return i, false - } - if i, ok = validcomma(data, i, ']'); !ok { - return i, false - } - if data[i] == ']' { - return i + 1, true - } - } - case ' ', '\t', '\n', '\r': - continue - case ']': - return i + 1, true - } - } - return i, false -} -func validstring(data []byte, i int) (outi int, ok bool) { - for ; i < len(data); i++ { - if data[i] < ' ' { - return i, false - } else if data[i] == '\\' { - i++ - if i == len(data) { - return i, false - } - switch data[i] { - default: - return i, false - case '"', '\\', '/', 'b', 'f', 'n', 'r', 't': - case 'u': - for j := 0; j < 4; j++ { - i++ - if i >= len(data) { - return i, false - } - if !((data[i] >= '0' && data[i] <= '9') || - (data[i] >= 'a' && data[i] <= 'f') || - (data[i] >= 'A' && data[i] <= 'F')) { - return i, false - } - } - } - } else if data[i] == '"' { - return i + 1, true - } - } - return i, false -} -func validnumber(data []byte, i int) (outi int, ok bool) { - i-- - // sign - if data[i] == '-' { - i++ - } - // int - if i == len(data) { - return i, false - } - if data[i] == '0' { - i++ - } else { - for ; i < len(data); i++ { - if data[i] >= '0' && data[i] <= '9' { - continue - } - break - } - } - // frac - if i == len(data) { - return i, true - } - if data[i] == '.' { - i++ - if i == len(data) { - return i, false - } - if data[i] < '0' || data[i] > '9' { - return i, false - } - i++ - for ; i < len(data); i++ { - if data[i] >= '0' && data[i] <= '9' { - continue - } - break - } - } - // exp - if i == len(data) { - return i, true - } - if data[i] == 'e' || data[i] == 'E' { - i++ - if i == len(data) { - return i, false - } - if data[i] == '+' || data[i] == '-' { - i++ - } - if i == len(data) { - return i, false - } - if data[i] < '0' || data[i] > '9' { - return i, false - } - i++ - for ; i < len(data); i++ { - if data[i] >= '0' && data[i] <= '9' { - continue - } - break - } - } - return i, true -} - -func validtrue(data []byte, i int) (outi int, ok bool) { - if i+3 <= len(data) && data[i] == 'r' && data[i+1] == 'u' && data[i+2] == 'e' { - return i + 3, true - } - return i, false -} -func validfalse(data []byte, i int) (outi int, ok bool) { - if i+4 <= len(data) && data[i] == 'a' && data[i+1] == 'l' && data[i+2] == 's' && data[i+3] == 'e' { - return i + 4, true - } - return i, false -} -func validnull(data []byte, i int) (outi int, ok bool) { - if i+3 <= len(data) && data[i] == 'u' && data[i+1] == 'l' && data[i+2] == 'l' { - return i + 3, true - } - return i, false -} - -// Valid returns true if the input is valid json. -// -// if !gjson.Valid(json) { -// return errors.New("invalid json") -// } -// value := gjson.Get(json, "name.last") -// -func Valid(json string) bool { - _, ok := validpayload([]byte(json), 0) - return ok -} - -// ValidBytes returns true if the input is valid json. -// -// if !gjson.Valid(json) { -// return errors.New("invalid json") -// } -// value := gjson.Get(json, "name.last") -// -// If working with bytes, this method preferred over Valid(string(data)) -// -func ValidBytes(json []byte) bool { - _, ok := validpayload(json, 0) - return ok -} - -func parseUint(s string) (n uint64, ok bool) { - var i int - if i == len(s) { - return 0, false - } - for ; i < len(s); i++ { - if s[i] >= '0' && s[i] <= '9' { - n = n*10 + uint64(s[i]-'0') - } else { - return 0, false - } - } - return n, true -} - -func parseInt(s string) (n int64, ok bool) { - var i int - var sign bool - if len(s) > 0 && s[0] == '-' { - sign = true - i++ - } - if i == len(s) { - return 0, false - } - for ; i < len(s); i++ { - if s[i] >= '0' && s[i] <= '9' { - n = n*10 + int64(s[i]-'0') - } else { - return 0, false - } - } - if sign { - return n * -1, true - } - return n, true -} - -const minUint53 = 0 -const maxUint53 = 4503599627370495 -const minInt53 = -2251799813685248 -const maxInt53 = 2251799813685247 - -func floatToUint(f float64) (n uint64, ok bool) { - n = uint64(f) - if float64(n) == f && n >= minUint53 && n <= maxUint53 { - return n, true - } - return 0, false -} - -func floatToInt(f float64) (n int64, ok bool) { - n = int64(f) - if float64(n) == f && n >= minInt53 && n <= maxInt53 { - return n, true - } - return 0, false -} diff --git a/vendor/github.com/tidwall/gjson/gjson_gae.go b/vendor/github.com/tidwall/gjson/gjson_gae.go deleted file mode 100644 index cbe2ab420b7..00000000000 --- a/vendor/github.com/tidwall/gjson/gjson_gae.go +++ /dev/null @@ -1,10 +0,0 @@ -//+build appengine - -package gjson - -func getBytes(json []byte, path string) Result { - return Get(string(json), path) -} -func fillIndex(json string, c *parseContext) { - // noop. Use zero for the Index value. -} diff --git a/vendor/github.com/tidwall/gjson/gjson_ngae.go b/vendor/github.com/tidwall/gjson/gjson_ngae.go deleted file mode 100644 index ff313a78796..00000000000 --- a/vendor/github.com/tidwall/gjson/gjson_ngae.go +++ /dev/null @@ -1,73 +0,0 @@ -//+build !appengine - -package gjson - -import ( - "reflect" - "unsafe" -) - -// getBytes casts the input json bytes to a string and safely returns the -// results as uniquely allocated data. This operation is intended to minimize -// copies and allocations for the large json string->[]byte. -func getBytes(json []byte, path string) Result { - var result Result - if json != nil { - // unsafe cast to string - result = Get(*(*string)(unsafe.Pointer(&json)), path) - result = fromBytesGet(result) - } - return result -} - -func fromBytesGet(result Result) Result { - // safely get the string headers - rawhi := *(*reflect.StringHeader)(unsafe.Pointer(&result.Raw)) - strhi := *(*reflect.StringHeader)(unsafe.Pointer(&result.Str)) - // create byte slice headers - rawh := reflect.SliceHeader{Data: rawhi.Data, Len: rawhi.Len} - strh := reflect.SliceHeader{Data: strhi.Data, Len: strhi.Len} - if strh.Data == 0 { - // str is nil - if rawh.Data == 0 { - // raw is nil - result.Raw = "" - } else { - // raw has data, safely copy the slice header to a string - result.Raw = string(*(*[]byte)(unsafe.Pointer(&rawh))) - } - result.Str = "" - } else if rawh.Data == 0 { - // raw is nil - result.Raw = "" - // str has data, safely copy the slice header to a string - result.Str = string(*(*[]byte)(unsafe.Pointer(&strh))) - } else if strh.Data >= rawh.Data && - int(strh.Data)+strh.Len <= int(rawh.Data)+rawh.Len { - // Str is a substring of Raw. - start := int(strh.Data - rawh.Data) - // safely copy the raw slice header - result.Raw = string(*(*[]byte)(unsafe.Pointer(&rawh))) - // substring the raw - result.Str = result.Raw[start : start+strh.Len] - } else { - // safely copy both the raw and str slice headers to strings - result.Raw = string(*(*[]byte)(unsafe.Pointer(&rawh))) - result.Str = string(*(*[]byte)(unsafe.Pointer(&strh))) - } - return result -} - -// fillIndex finds the position of Raw data and assigns it to the Index field -// of the resulting value. If the position cannot be found then Index zero is -// used instead. -func fillIndex(json string, c *parseContext) { - if len(c.value.Raw) > 0 && !c.calcd { - jhdr := *(*reflect.StringHeader)(unsafe.Pointer(&json)) - rhdr := *(*reflect.StringHeader)(unsafe.Pointer(&(c.value.Raw))) - c.value.Index = int(rhdr.Data - jhdr.Data) - if c.value.Index < 0 || c.value.Index >= len(json) { - c.value.Index = 0 - } - } -} diff --git a/vendor/github.com/tidwall/gjson/logo.png b/vendor/github.com/tidwall/gjson/logo.png deleted file mode 100644 index 17a8bbe9d651e5d79cbd59a3645a152443440ad6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15936 zcmdseRa9I})9xU_0znfrFi3EBcXtB8-Q8tyw?MEE+&u)>;O-g-!QF!n?wrm0ecyln zi+`Pqb92@@b204MboZ|AuIj4isoIgsic)AO1SlX72u(&>{38ei4_w2jBEbT8g#0}> zfj{`J5}K~64(6^NM$Tp+5mN_aGq8-Ek%ieuGb2+^ry(I^6HUo1oax$2(u&{u+covw&X$WR|Y3j=W4v9v&Wy9&C&b&K688JUl#1%&bhTtPDU41{Y6zS0f06 zy$kt&Mi4i1F>$tXbhUD@2fvJHWbEMPDnJHE`mZV2IVvdp*TD8J|6V9y$(SHUj!Z0! z%uIH6FZ24RwTtUVv;Qr||Jd3^&C}70>7$v8gPXGnupj2+|LF{@-T(PPFAV`{c$J*3 zfK4&76?ZUkvoo`Il@S*p1OCHkYGukR$|buuylm3H z<}7aJY~^ldD(UQC2mWW3d9D5jDR|g;*tw0_nHkJkjZ7HWS$Vh_jJR2u8Q6HtIgQO& zSd4j$Sjqm~-}Jw&-oLaIxC6|@@jn9bnzJ+W7@M$}GZ-0hn=-JlaPctk7_+f5@NjXm zahsTP8Jln!kud=xGQB9ye^aFY+yb=o6ILj1BiHWiOsHjwSN(6-S#&IQQX(?!~oRWla6k zvj3m#lk>9cCv!wc6L6R*c~`T5NT-O!e%&k`LRLWCk5V)xyH@qD}#ba7*ern zD^KwwL26x@_H9n^_xJEwj%`LF z2_M&3l{$q7;~URr@StEw0z3u!#j3ZdE5%$S1w0iM^K2@_(3KW1T0xFBPH3#1nB2_f zvBMJo!z+(+E_6G$0(R-JL<$OXHd^e=iug&_x%->yfVBZnFV3sIHbvZbAFwXaW7AoKsJ9QG z>$+mQV_~P4&z8jBcSvrf!^$eZC-BSslJQJOm4Sir>t!xQeA0}meB*bk78Ll6JYotw zFN_tm1+Ok3dz)A2wN_Xinfj#Ee?*6SsMzd_g;n4n@*KNU9c5|uUDdVb5;09jU=BA8 z7Ip^CUs>Q(aGI&ybtyH$7K|L2U(LdFslcD0RJYaHH%xx{O^TZmsdabO$yfZSTX|)r z=-oR$toP)*A6N)!Ci&fYk#B|NMo}VC;kw_(;DJ=HK8MnXxCRjx zm+Ele5%M~`%RXZFhRaja zofW}k`izaw_BnN>(4NODngeC2ev#$a0Za@}FunO>tS9?CYq6*J5?4v-!SCmNEg}($ zsiIfoly7`UXRh?F`!)YcJa>S4yLz&L#;kL&6#~9Ni+?|0z}yLl+KUVh*{UN zQL82>H7J0_BwzsZbE2-owg@HT^}ncXoZ;dh4@Ag(^&lQPK)^E=LOJs3m1N!Uj9F54V7Fy*9;Hf!S| z2Vtfjj8{AZ6|UJ&-*wZR;=h8&K-WF?$U44F^rNysF*k#YfwM3ww(UIiz!K$Vl6g^; zpZSmDI41>YtUMi>*8?muaBUxB;C6#-g+)6l$2v@q$uZDbJ6wES8#l*s2D<1?VzXJ$ zNn3AE*NNnAtmKenlM+7=mM9>ZV6zb+`lI$2@hpIeP1DdcS*Cvz5A~9(XQ5ee8Zy?1 zV$H!Cd=InD(OPcd;^t`I|2d8dNC%ws6z&4#gegDv>rH+oz!8Nz>NP}eD-R;bVvA0S z5fJb?Ou@|fK(P*e**ICmfISbcs}Y$fZuREW@ZFBDNYmhXW7PA6V7+}jLHzU1y=p!n z^hyRvQ|hIqYYoin6oO1NuT}m&C#3Y93YZnNA2Tz$8cr96%FkEFIxLhO1c8xa?YS>1 zYfbRvnrv%W@wwZlMg$41zv!F1Uthy~PJ0n;XM;%WG#G1Z(D~^_heW34*YaC}4fwaY zQ_|5S2@Q6+L&grf$wpF2KXm2n`%skl-*5HsEQC3gz~7nJ8i!$efQc!-p`}FGdT|bp zjc+K291ok>nAU4-{|as^#|^q`l>3ommlA=!Yk*~f4lIlN?BhIKO<)tnThs?ySx%(Q zZ{BqMNZOg7QO=}w219Yn;z5ayrjclN12jx{->DqdA?>)@B*M55Z6*>9uOVG^g4+gB$$ z&!XEFAyF0vK@Hi45CHqg@K*IEb;v3e@Qc{QP%M+qSM^o+6flzV`6>&uh)6U4UOl>Y zUko)LSj?`l;=xRs21!m{!rvOi>)at<`A6r#TDg{HRsnKvk6eoC&S-x#Mq{7vf>j;i zpoU-IgJbwqGu*^D+T5dUG0a_I-j;HEIj*%e?#p&>hhO%ho;0*k&ga~7^2w21Ks!^N zJ88HQ;e-ECm0XE^;oQz-+JymauGTlg2o=jQD#D!&eneT>3#Y{`@=}~!gct3epErdK zU`nI?9GgnOgA%X>7A8>kKcDjv8zn1;!d*!BC&n1*8?K}4xYS*8J-uGJt;RUTVXir%{}C0=q5B02D<=xOiYxcn?4l1}Em4lV07IMeqs8t@G*e)leU z^LZ&bK;v9xiiAiYvcgAIY^ z{hsN<54t}2vf2`*A~p<)503a+JOop=cS87$R%0Y9b}n%u{HMx2QvUVcnksc$0lfh| zO+0575+Z)~?&@C8M99dsa?f$2%sd?hq|Ds&;G=9fK537ECE{4uO>1(q_^&|Vlh_Gr>&p4j#CTe1bR2DJ_0}1+G9&6IH~WZ{A=YBXE4q6-nx9bn^kCGD zKq`a|i1oH?5f(Dp2PUamK}6jL3(BY6_{%!xtDA#drfkJ=#M6{`HRjVF)bpUQq&Ef6 zyRCSNEr>MF%a_m1u9p-Py~pK8D7c5rSo^`&g9y&-@)_~;#h*R~7VEJ@5*>Y+Ig5{* z{KaPMCB3%XAcWYoc^46OK;ZSTx9HGO-}GxhBWm@2u4qc+2r6b_aBFo$7yfX_H3CEQ zaAx(~OTHSE^w9jPe|Db0^FFGfRAv|V-o14(YFFLKu2^KV)OYV$MilR}0%8L8mXrX# z?aXNsv{y&t%MTik%-A};(yQA&Eexy&Tel+t@?5=F>qE*mRdi^0cko1yYtB`K{ocK2 ze?^g1EeKeXFLrFjvhUiy$_GM26OFkX+qLLsXfuf_ zAiE6m$DGN>RDMk}<35n$|sHc(gYzBw5(l8USpk56G_tt5v zT2RPk>)0{R5nwf2>%09ZBb^t1O4UtbrXz%J=bQ7Aj>{^+T`g!YD&6-HAA&h~?oH{F z-*(;OvA=)%wE6M!TY|;d-~Kce;ebbI&U$_a!QI&6cp@;QC-Ynh;FOeB9wl}vAGWi_ zI6Eb)y%&;;oN5BIUcqEK(H~zuZ*l#_kGGu+l9nrvvTET;x{?m&Pz z)6(6l?`Nf+N{at87srvs@R$3YNC|1B5vn8BpnndfCUEtywUobq0}Eh3Y)W$a((OMt zeVTMUi_J2y7^X9|!b=N>O53xrkSYtG=HIjP+ixUv0p0}H|0JbRs^%hA!Jwk}>L&MM z80l=$8*p?p5nqzB2flUC+qS5co0cA{{~DgUCuuOfxzg5oVle|X6R|&z9HyjTBz7=W z6(hQ3!ou1{BT@o3I#0`zwh{7Z9D2aZ8IS0|LDbPB~{a9 zxp|-)J|}zIePx$CI@kZ{&VL_`9mP8HDU)6(_8QG5JF(05;fr7V+K5bA1Etfxgh#5W>6P@@ucg&Jzly%#@3VwSKV{@&j#^M( zTlz^^ZmfJ%6rHQ3%yEH?9(L!Qb(!Do<{dt5FB1GvxNV4LI{Pjr0R!MUrMb5A4pX>C zAxvpRy)idQ1E-@e{fUAX=xnu;V!H}&V4CM$%=yv*v=$M;MOYIcj@A-jG z?Rpip=$Wr@_3CPVC#tvr@>g2)Q4aUQ5cATrcEjnW2##{myh=>MLI$B&y6jSWY!swkxM01Od*SEg zL*Mh~ni#4$)U3y+i&vQhK|9qf!@Ie%77J-pfjx;IqN%|*VX(lO%{8>t0l{p^gB)j@ zJH57Z00N>gy2Swd*!!iFW0?|!a>O(rOZL2g&I>ppw~Od1Z5(JPKd=Q&^ICR7OvUvu z?=;QVzQ(dwYT=aA*Bg?3&P}yAThCa2|K}GvR-m-L*_!MseF=o;M zOc|!hnA_@||7VYZk>zZ=UNBMA*Vtu^yo#aiaXg2+Bh3A_ z*k(u*3VX`)Fpa0tfiD<{pqWjZHrb&h8UI8iP-1;S@Ctq-P$n^!{&NzMp(LvzzPbW5 z&{drM`nf{0E^A*AS;QZ4;qFHOENld#)$;q_vGmq_=O^r}ovg!Le`|uzGHczAnUBcG z`*F2&g&n(N8{#Biba+rq`+AT}7E##C*+mWAHcl~e8IF*BlD}ptBDDp7inFHgCu%CG zMQGj9<9umYwTdv9IK}7?I-Az9B4JJeXGNp&Y~9dg6EMByhM;=CJgzMOHPo|HyPwEF z>AJ~_8b(2SFF>9)iwy@AzPGmoydb?M{G9dKy6!c@N}i|Lt*7q!;Msy>FWlp16n&Oo zmA=5&{-W||a^yg79H=mpEMh=;B08fpy9ZhoVvt6ohvYk&9Dko*huy|_Vcj-#n-^%Y zs(?&;92eWq(+`)_??I^-a?v+g0KQKt*?l$Eba3dO7`iLIHz8VPfP?g+cWvw%=+?0$ zw8JC0o-q5rI(C{(+>M|Yd?#NqbO%~Z8MLGynQ3(_s2no9^BqZSUb>Vsq3+-&Z~pLd z_+I&t*zl@gEawwB70Gx!2LBP^FYe$`QL1{2+{*4>GM~87Lx9~C-tG~*?hRhhc=M|v zKz`3aEk0yz8Txxs?~Urdp+R0uG5?!T)2G;O-?IC#fm9y}er2kwt<)7;#|LG5_jh)$ z2Sc<@EHgtod#3K*7;KqZ%_)YTiJL+t8o!4XIkwUN@t`E$Yq7=AK*!U9H?3HzBPCK4 z1;{FwV^;P{uD18>%3tX3j%6R_MIMv&@f1V4k%w(DWcZccnI;9GVC^$jwAX_dysW1j zM`z~{y%DsP60N1h*GBx4LRqvFn|B3AG`4>4XHx@(`6tg$H8rQaVQ_b9%`q`{{4}r2 z=OP;`SSJVDA#p82i#aqb_VL+eJ|9?gaG4go0A~hZ1+MA=FDhbXUeMCt&#CDcTTP6} zxtZStJ{EiC|G-Fk6MeJpZ%|$9Rn@xfF(WyQBgyfy#vHNweKWz*P#EpEYl?H6KHpMe zvf?*tGh4Og7>Gv?^om*Qpu)Eyho@?PDF~@Ea^wN^&DqhE;aD3KU>Xh(01Gsi_yYOZ*mM@2DpdkOw<+vQpws53fo{ z&wajB`u(vzs_9!4?h&i8?CuR2u+G84+26IV*Kn`^r>fu|-veN(1Zn=_`3YN(XcZ|e1tk=THcWJbl zsYeS(c=;b95uXNv-?n*7v=S{~uMPFRje+fM2G~eIo<0k{7KhLSksr739qkp}2P;MF z#RHd6+;5fK#zzu#)US{5c4HuGKPuG*;1FXZM{@sop4IH+(rfX}ZIm^whc{X<>-WUx=Qlw6^^~L>&&#_wbjA%NM-BO- ziWfJ(=D?Q^MhL3 zhf&VlDz1)-0Zchw2k$O-icKDM^}Uif#Kk9m;R`tpuwOp5cG?~QR}6ZxbSN_q>1prP zakr4;NOzZ;Y^{A;uGpF=CV3`sPEq~4a=}w;)_$M_5ewbN6EZm-FnM6t)+yo$1Xzo8 zIpW+8HNz7 zlkMVkuwUHCF6C}Aw-E@>xDL3Tfbn_4oq*Rv>wJw&{jvDbvdHt}yMJ0Gb!csez+k{> zteQ9MH2^!E*)-B03O|-kOhidM>4!7jddvni6e+3Rhgmz{jFUl94}&j}0ME)Fp%mZ0 zXlE;DA$P!z`Ey7={LOdHkNp(zqOd+a5rWUY-&`B1w6#jF;9ooo#D?cdNf9r=4ZwBl zOS1FBam;Twsy90oaUIT{CrE+w=xr2`$no9-7C=scPgFEzAOBOZ<*$GeQ77bZwMSg@@7fXHj)VSZO3m{PYxnm7QPW=ydOs z{#chl`}vB`$gV*CX!a!p0D-WAVE#D;v=)%6LUt~fUM3C-2r&%X-h~3n92m=ymRYwXeipL(xBw46A4m}b zz`ipJfSi^h{{;9`Am@=3^@?4(vH)Plzr`-h3~f>NWL@{d&CLaZiG|xLcAC$!-`noB z4&_SEZ1pq&)UusTnZ1RoHFypZcN}#Eq#vv|MOIILF6sZa{{;$X363&PR_E3MC1@C^ zx;^*2YnYQiXAqu1qRWO2EB%sab?nau7YG4niID=(CE2JAH3TMJ{gmd4t!{42oMXA5 z=pHOdBnf^{06X{BZ;N29axO0saaETG~y-q@bv})p+406EHxk$8IMo93knK5R6iV?^+?4JEa=@BB{Wr;7$09%)WWpwdSJpc&gy?Xb#y=!13d4 znM6rJyf+X%lZo?o%SI9DF_A11@m+d`!3G!<29W9k_{MPczg;h)nTL@kmCpN_655)+ zV4#=PKa9~L!Z2&Ah&=5fCU1*LOJ_x$W27L$LOfiib%hP1*Z~zUp#+jQq7DbI-U%rP zk^T9Tl?*Eoupe)4-y&KT~+O##fDEImn4y>NutF;IVJ3=m*! z*rEW%TlVNiM?a>>U$u_$JuA>L6CZFXb!s@yP&g!R3gv~Bp}AgpK2=m0jV8VvIZv2C zmd^lRyVaiKaS$v6oYCt@$k#%)MsE3;z}yQ2L<8`{hpSucn4z6B`)k9mk!I(}zCa8Y zec8sli-a|~e-I8yodRg%#-Fe zUUq-YRYyjqA8u02I*uj{d;W>=)zk|nH^OPBH+T%!|4ND7zpZ*;!cs{XDZAc=)TpYy zygF?tJ_g{6^&8==X_&x=`hHuSa{O~;8JsY}g+Ry)97Lmf`m;+MhVx9uQw2LJB)YCN z@x%=aA>jGW@-K6kNsyeSxy?!*B;mpG3B8IhiJ`aL(iYA4>ejWW`Ba}KuFQ=qb|9UD zd1@S)Q6&PF^2(HexS@I93W4L)kM;M0t02}wjNHH_W_t5)TVGK6pzr>0Q^W#T7H~ar z!`O{mW@83Sl9!-P1c_I-0Gd=n(Al2Ah)MotJ8AbzM;HOuxA3PI2G5Xa4-KFnd(R<& zl~(O&-Jsc|!_LVQcPi|XD}`wzR1Xn(cG2(D_N3D~k*V(8k4i+i9K+ulxOuCy!l zG3U%zV9j+$hqsRH4K8qiX)Y%5kpNKe-@i7c^ly-X9EHpqEaj*3o>oZwj-~iM`20S0 zT+X3*f&BF+O!NkR{xf~GM<9^RlYs$K*d}c0boMm+LsFzfZDJ%7->?En1@nR69#5W9 zha7r+_<*>ozO^~mkB|Jz*z!F@=x~3DwK8pu~`vT=$gd#s-trjR06bA&FSRn zXosx!+Ar!=CCEZdm*mgGVygGr=a;bPp3`1$ojoLd5*z)h`E`i{b~Tk6I_M{*hrhdq z%km?-j{K^V=N^-HEYrs)!oparg}x|u^fEHypM^&WQ?T7d$N;k6=t`n8b5A;R7+fd< zweEqw_&I7h2Q`^|Nhl+xehysMZI!+Zv%IfOO*?!yw98BGB}51~t1J8V4~T$`Y~Rpg zC&EB-_kDWzSVdXeG7o$eVQN}aM@*<;RL@^rCgVF8kWxLDx|}q#y}Sgg z??KODbbqBH3$u%uWAZp`x1p_9+vh$RDq>q8DArehLiv#? zS7p(k_klZ2h^UNb09!*yqk7YKmjb|*xuZ{kcYnK_eZDQDgCKrv@~G^rCEkhh1KQ8t zT~&5d1+9jn{HLO`4hJ}XbT4W*8iQ!OH&R^30MdVf>goXR<52$G7-HwbKISxdG2?gX zUWamaU{hM>`{N6CBF`m|UO7f;#%00^HUQjv90sS3{=D}p=qL&|w9G5VQ=hdE?qpB* z8CVyn;Vhr1QP!CQcS~LOSrN!b4hgV%PU9lhY4qrPo;TID@g4v|;8auwD2G~SY|8TM zRd4Zg*tNyl*2c@A_Z{0+cYF_Pnwm+58d;%$qhs2q2XkwXMBxG|t)dcF{495+0LXAt z%=xkC)NYk>2z6<8b(io?P7{c7z!dMrujH=okhn5qE$`KV2r?QH33y;KVw{~_a|@d1 zgc0Z`b1>*sRl_Co)qBcExZwdxbAVy&napFK=rMOLMPFe2WYbbO%19sPa*w$o;yi)Q z){i?j*$ujpB;L{(m!Q9Sdnfg#tTM-#YfR{CBw}t`;qUq-!x&bmVfcN}-Po_I4}X}_ z^t5y1VNMmk9z)``Ic6iQsFY7DwU8AJM3TN>l5#qSx#b5zyk}gA%k0LtM)YSep6s(K^!|HPUZnNmZx2o z56)-Om(G2o{0FL>zz=R>3h!{P^*+b!qaH#4@l(IQkuiEnE!ScPnFiaVAO&1gyai|l zAdJrT+kK3Se%<(USIy}n=jk}2Ht2IX#e*edEZ~-uUSJSqWC~k*1T5@YJrKyPnw|X< z+W3-Y{WKrQdHr~R3#5&-TZ3zM;w6TqCbhn|Rj2*Z?S*6e-R?XXaRSs|oId=TVXLu{ zj|URNRPe7+kR)4)8|nkVLsFM7<=Vmf~HeV0cp1|XXS5L@wjNj5yKie+KA z%09g+z^n`}M7P_vg>2)FYXMz=YLgchlQ~%ARY;7G(ys;3HB-ed^wLOTkupT@Nfk&o zqI^Bl$76XEwk1?h=qO6mkigTz!Fhq&Y~rX|9n!A;SBB16W?nFjd=|4(?*6`uT>Om4 zq!ivu+WiqM6jPn_bYNMC+&k!sajqnXW6Bh6mHZwrOTOLYAspD>VRI&w0>>Ym%QSk7jV!gm~4K#@3Ekv@C& z1ZW^9I}1pIxbnRqUm@~9k$hFsgO$SclmV)dsY(@axX-x;K@08<7z*M;!)D+)GnB-h_pX#E4&BdOm}hnZK05O34%JkmnSPvltCp%pfh1YkKaB; z3r{eK1y}F0zteZ!VbKxN(mGE+RBO2}il&HvJsmW5)9f&i7~9pNsoJ;9UuU{fdVZPdaTpl3xn;a*6FGldFooO)ID5!f~o{A(@Q|Z#mgd97e)$p~ZNmCyO%SVP)0qcao?}@xnBznLG_-jRz&F1t?k^&(O-9@Xe66ob6E_pADB_QBgqLn-gy;LfXQBSJbXm4Qa z)@0fUbjL-DMg$ghS|B?CBkpf7|ESPv)cUf`>w<=*s{H62A8V`IiK$3LELwT3jzOb8 zl!S`+Tx&GX2;@<}_wVf9BLV{P^(7He508+NcJ@gXb$|bH_KiMExuqD$AfHUdt4cDv znhtuostMM!A9W3zyP;)%a8-~&aywa>^w*%F9kkeh*?k3sl)5}_ql5OAK9{{G;i=&{ zUtHfQwYq)Mv2b#<<*l>m4R5ck-D}D4z}ASJ5n|#5oacfBD7c8Ej7x(YE2s{iT$}G8 zwe%kv{OB)T?3TF&{@pgAVDqA%#pj(8sb{K9MGKWIs8|2FC9!(el>JYu3twNt=QY>E zEfe)z$ve+s#4HImOHv>x>;K~XCmW++46hoJ3_n6i`N8IeHpqE;laOh)fhBpsdn`Zm z=#=<=nmx{M{f?01b_7r#Sw*A=7N%8-I`SyqirAZn#cfO6?4!h|ub2b6?Zr>?Gx+w7 z<{S4WRHS>hqD7{`zLA&m^E6s)FM4@tcOl;dBnnQWimCjB_^)p8k672RtIbfAuFEw6 zgYuq`4j>h|oy0tO_clJuOF6%_;JJXGg4^VC^xm%7&7Hy10VSjE=3!N?H7J&iqdP}I zNOMAsr-?az+$AOgF>TROKSqm>+JU2`&iM{Ko72ZNnN>6ZNGPvfr7rwp!%~yBJSI?n zB3R6?SX`*e^MKP=??Xlg7}vTlQ%RIYvO<*`dtIXjAi@O&*XGS5Uh^+>(R3{UOg9!# zeFU6}>N+X$p5K_^4*d4ma(tvz--++&_^^mulRJJ-8!^^{Pd@#l&?DVnNZmyNE?sJo z1v4i@CO?oX!Dg1Lqv3TWiOw2-FS)k+Ljd}Ilpuew^DX&cs7!n~Bu5*_ zbNkTWkOG>zvpx~^d~foOLY<`#J9-4ss8Adupv(u66p=MohfRAYxhNA>HSU|gUFPg!w!n|-*n&2dvMVX3i(q* z9avlqEO5CvT+~(A1xN=y58K~En@L`vMnKT5ufo5MXk%ca7vW2=31I*Km~>+KC%76EmzQ{Epl*zpBy66^=k| zgxepQc7MxTEKkj~;bY@6Gd!n`K!v>&``bhrGUsu ziUsg|6^14}8h@IKz!**2yt0@~gdZ8TQ6A456}yw%F8pOXchJfG;IsFRiK@Z=(_=hNf1EVaQxVt%sqi_6s+|aA2Hs^Z1^jkF} z)1c0f3(n&QqBF4M>5l>N+2Cviy}C>Nvp)=5PwiM9D`yM8h&eBJ^iH_7m4)X9Q%5MW z!_c~><|XVWOt+Ve`j5xtKb#eCe342+D}=)aV%}@?*l_nK;*CILu4J}tqmJH`|Q{N79k>Ago$ zw+kbnqFbA;2Yd~D;IrHI?7OkQTl}|AhyXHl!kF?*%64_xllGp7XvzmFLZx)x^DQ#( z8yw9_Kg)1_V%`v*<7w+#aJ1^1V0 zi}6B_vKX`{IOQ9xZOX1JvESZ!V(|c{SDSNfEs(d|CL$lQ?eu!R|NTpI?eS+Kf7vNl z%!IbKTC}f)@d@}t?+klTHUjTrMxbJXOeb(+XezwY*G|WOOma4Pdp;kk0Fl~k&%h$# zkNqIcqQ*;D3DT7}0g$zkqn+g9IR#X2V^fthIf z6xut?2mox2Q$mF~UZ_v*={bqjQ*QF8smcNS^TPHE8vGd1^gz*sl^2S)?%;8oMxCX* z0SKz`wGxRnWD$dRPEJ3QEZIK37gU^>f=z&GmJiDQqp92G7HAK&!?*nn%c^fgy_Rc6 z2i%y;23Y}W_x9$Q$t)tiX=};p&i0?ga{!QL{KM0y9!)RXqLg$ix6$n3zDrBF#amXMJ8=viyxAutbQYXsIV&Y%NuDPMK&4 z49)cj0zMhnla%IgMc=|Vc20Hv6p#_`b$l?h_yQC$DIz&u_>1yOOYIZ}$7ErKy`9Iw zO&^zH_d%1a{z{drpduoOO9>4K0hs_Wqej8OE3;~Jz;l|XDy($&>P*2><8H_*uLF

Bg_)L$48&o}ack zt!HAQladuqH6nxcdZjxF6vTK*7%AV0IOX}JZ^u_OkMumMel#gIzv1VJUb|?$j71?Q zE%B-a&vFk0&Z20HLn$L5hH9dQ4Ef5bKHOX_RfA5_`gJvR%bW4}0DCGg0Va&YC>upU z1&8GC>xLY}LkxejF;yq2sOY~5Cqo4UPpDVfIk~O=P(Gb%k`up^XsPRv9)fXL9y}y5 z_nYE{jUJUly7pJ?$r_eiajL0B@N?t6efF1pS9lk2UGDMWIj>@eS3OH*kme)>qMs4c zXm_q!8hu`I8Dhv5J1?UvlYLJ*X6D(~=y^sb&mT@}>JxIp-$sTvzSdP`p2 zJSTpI%GX_eG4+h<`8LXOC{XRWB^Zd0<_&>b2eoF`!(!~j1?f~Xrey`D;63!OSo@1j zhVkNITfkbp3}EkdgcmMq1ZjCrPrG8@X$!y3nZFDG<8?k82gdmVJ<<=koN(R0uQJ&q&;Hm9we)+ z>EiW_<2L!S=W{bXQR{t-Xk&^=i3!xJ1GH*$ft^*`kFY2U5$7}?R(xpv1OE!wXhM-U z{|-m&g!{Wci5J~AR7xhAcz2%T1X}X;Lm5kqn$n>CdZG%!jlgyEgu}8cSE=Tna>KaW zE{i{1$MRasX61aWzh$Kz+gg_XXfmLI)gJjN8%-etjvKrZa0TIVu@FNzi_DBbgG}?X zc%`<^VRHLdaERnl?k6tkmy~V-6wDDz--2n{wg8GA9pEX|*>WDA)uFS%@&MN%-fruuv;9cz5hkb^M5ny{e|a>hsQrwX-D^ zNBO*f*^1b~?2NHQ2g~@Hiq$_V;Z8|Q@sz(OJ-_;@xOeF3^57a-&;bjXTR0^(9&O1`Qs5mDXKaZ{gYh~^a4>N0el@Q8ji@;G0VwtnJC=gh zkvhS5%hY0~-FeS=8*`P?hYjBB(TJ(h3vvLJ;q{z?7Jf~hQZOlh z8(M!wZ6}#fur(A{mkQvWp^(z{kJ9Qcf&hRH6HK!c5b1?W28wck^aQ{Ju^0{PD&(m< zLbu{`I17`!l>tz)uDi~j;Q*7#M!X;*rP#E#K`0MHVO=C_Y>Kpq3I%tz)KS21z(70w zu+S+efLBlwQ<3knLW3-aM&dW%uuv1r?kQqDWsn|wHc35K!jBl%t~9oLn(8w!(iF2Y zj)Ca3zC|-dT69O*c44Drzb zTWRcGB4pjuzq;tNLKJ7H7&3CTH`^5&fs^&Csh+4TdNT~n>o18P123FK1<6Ap7>9OE zH?~wjjt8TicxY-E;Ld@{^P%j9BpeC^HJlw8iW2I<*Vc#1VH5Y&k@$T23>riN5a^fE z1xtK3We+L~pTm`C@mKj&g@flbKtRjuc?i2+3nW^d516YT2Qx*|&}0%IIZ{BK)n@-A z7W$f>T8g%2qR?15Ezb7#Al1jSng=Um;<;^4CZ)UF4*B#s16}WxXDR&e;IFDemyJBP zQD>pCTMx2W)Iwg$ckc4ElkYeRp?hVt(&_O^6u-=WO4y!T>T70vx)M)1f17nmsqw{$ zZhYP=C<2j}m^Lg-ex&8RKf~0WrfrL!~NLQ^Xk?)No>Z1xiNn~MBa)Y#@)-369lm6 zDH#U3Pv&kw{mt3+_t3uW8=*{`^%?Rl0~ff@XZrjZDZCjasI5PtLP6)zBrRO$pYlfV z4DCdS&(8)uR*23AuP?jhTi@#6yL=G*b~7!5d3?2d_2DzzR3V+SVCZe@ULo|*Qz`CV z*j}+=>D|^uy$<{s0tgCLYba;t*4jWsj}CfRI?T4cS90us!fy8Lz;(qp7V}H&`+loKEpPZr6wt`2EqWMWj z6-EjjNWU@D@IQdNmlvT5>qUq{`9mBuild Status -GoDoc - -Match is a very simple pattern matcher where '*' matches on any -number characters and '?' matches on any one character. - -Installing ----------- - -``` -go get -u github.com/tidwall/match -``` - -Example -------- - -```go -match.Match("hello", "*llo") -match.Match("jello", "?ello") -match.Match("hello", "h*o") -``` - - -Contact -------- -Josh Baker [@tidwall](http://twitter.com/tidwall) - -License -------- -Redcon source code is available under the MIT [License](/LICENSE). diff --git a/vendor/github.com/tidwall/match/match.go b/vendor/github.com/tidwall/match/match.go deleted file mode 100644 index 8885add63c8..00000000000 --- a/vendor/github.com/tidwall/match/match.go +++ /dev/null @@ -1,192 +0,0 @@ -// Match provides a simple pattern matcher with unicode support. -package match - -import "unicode/utf8" - -// Match returns true if str matches pattern. This is a very -// simple wildcard match where '*' matches on any number characters -// and '?' matches on any one character. - -// pattern: -// { term } -// term: -// '*' matches any sequence of non-Separator characters -// '?' matches any single non-Separator character -// c matches character c (c != '*', '?', '\\') -// '\\' c matches character c -// -func Match(str, pattern string) bool { - if pattern == "*" { - return true - } - return deepMatch(str, pattern) -} -func deepMatch(str, pattern string) bool { - for len(pattern) > 0 { - if pattern[0] > 0x7f { - return deepMatchRune(str, pattern) - } - switch pattern[0] { - default: - if len(str) == 0 { - return false - } - if str[0] > 0x7f { - return deepMatchRune(str, pattern) - } - if str[0] != pattern[0] { - return false - } - case '?': - if len(str) == 0 { - return false - } - case '*': - return deepMatch(str, pattern[1:]) || - (len(str) > 0 && deepMatch(str[1:], pattern)) - } - str = str[1:] - pattern = pattern[1:] - } - return len(str) == 0 && len(pattern) == 0 -} - -func deepMatchRune(str, pattern string) bool { - var sr, pr rune - var srsz, prsz int - - // read the first rune ahead of time - if len(str) > 0 { - if str[0] > 0x7f { - sr, srsz = utf8.DecodeRuneInString(str) - } else { - sr, srsz = rune(str[0]), 1 - } - } else { - sr, srsz = utf8.RuneError, 0 - } - if len(pattern) > 0 { - if pattern[0] > 0x7f { - pr, prsz = utf8.DecodeRuneInString(pattern) - } else { - pr, prsz = rune(pattern[0]), 1 - } - } else { - pr, prsz = utf8.RuneError, 0 - } - // done reading - for pr != utf8.RuneError { - switch pr { - default: - if srsz == utf8.RuneError { - return false - } - if sr != pr { - return false - } - case '?': - if srsz == utf8.RuneError { - return false - } - case '*': - return deepMatchRune(str, pattern[prsz:]) || - (srsz > 0 && deepMatchRune(str[srsz:], pattern)) - } - str = str[srsz:] - pattern = pattern[prsz:] - // read the next runes - if len(str) > 0 { - if str[0] > 0x7f { - sr, srsz = utf8.DecodeRuneInString(str) - } else { - sr, srsz = rune(str[0]), 1 - } - } else { - sr, srsz = utf8.RuneError, 0 - } - if len(pattern) > 0 { - if pattern[0] > 0x7f { - pr, prsz = utf8.DecodeRuneInString(pattern) - } else { - pr, prsz = rune(pattern[0]), 1 - } - } else { - pr, prsz = utf8.RuneError, 0 - } - // done reading - } - - return srsz == 0 && prsz == 0 -} - -var maxRuneBytes = func() []byte { - b := make([]byte, 4) - if utf8.EncodeRune(b, '\U0010FFFF') != 4 { - panic("invalid rune encoding") - } - return b -}() - -// Allowable parses the pattern and determines the minimum and maximum allowable -// values that the pattern can represent. -// When the max cannot be determined, 'true' will be returned -// for infinite. -func Allowable(pattern string) (min, max string) { - if pattern == "" || pattern[0] == '*' { - return "", "" - } - - minb := make([]byte, 0, len(pattern)) - maxb := make([]byte, 0, len(pattern)) - var wild bool - for i := 0; i < len(pattern); i++ { - if pattern[i] == '*' { - wild = true - break - } - if pattern[i] == '?' { - minb = append(minb, 0) - maxb = append(maxb, maxRuneBytes...) - } else { - minb = append(minb, pattern[i]) - maxb = append(maxb, pattern[i]) - } - } - if wild { - r, n := utf8.DecodeLastRune(maxb) - if r != utf8.RuneError { - if r < utf8.MaxRune { - r++ - if r > 0x7f { - b := make([]byte, 4) - nn := utf8.EncodeRune(b, r) - maxb = append(maxb[:len(maxb)-n], b[:nn]...) - } else { - maxb = append(maxb[:len(maxb)-n], byte(r)) - } - } - } - } - return string(minb), string(maxb) - /* - return - if wild { - r, n := utf8.DecodeLastRune(maxb) - if r != utf8.RuneError { - if r < utf8.MaxRune { - infinite = true - } else { - r++ - if r > 0x7f { - b := make([]byte, 4) - nn := utf8.EncodeRune(b, r) - maxb = append(maxb[:len(maxb)-n], b[:nn]...) - } else { - maxb = append(maxb[:len(maxb)-n], byte(r)) - } - } - } - } - return string(minb), string(maxb), infinite - */ -} diff --git a/vendor/vendor.json b/vendor/vendor.json index 18a8bfd6b5b..a38b8f0b93a 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -1717,20 +1717,6 @@ "version": "v0.1.0", "versionExact": "v0.1.0" }, - { - "checksumSHA1": "d+4iuDj/qQ+2LiWEbQ+AWjxEJOc=", - "path": "github.com/tidwall/gjson", - "revision": "f123b340873a0084cb27267eddd8ff615115fbff", - "revisionTime": "2018-06-21T18:09:58Z", - "version": "v1.1.2", - "versionExact": "v1.1.2" - }, - { - "checksumSHA1": "Tk+aXpWRuTQhURlO79bd7YogcU0=", - "path": "github.com/tidwall/match", - "revision": "1731857f09b1f38450e2c12409748407822dc6be", - "revisionTime": "2017-10-02T07:59:45Z" - }, { "checksumSHA1": "qgMa75aMGbkFY0jIqqqgVnCUoNA=", "path": "github.com/ulikunitz/xz", diff --git a/website/docs/d/pricing_product.html.markdown b/website/docs/d/pricing_product.html.markdown index 1233f314515..2379ce5419e 100644 --- a/website/docs/d/pricing_product.html.markdown +++ b/website/docs/d/pricing_product.html.markdown @@ -43,8 +43,6 @@ data "aws_pricing_product" "test1" { value = "Shared" }, ] - - json_query = "terms.OnDemand.*.priceDimensions.*.pricePerUnit.USD" } data "aws_pricing_product" "test2" { @@ -60,8 +58,6 @@ data "aws_pricing_product" "test2" { value = "US East (N. Virginia)" }, ] - - json_query = "terms.OnDemand.*.priceDimensions.*.pricePerUnit.USD" } ``` @@ -69,8 +65,7 @@ data "aws_pricing_product" "test2" { * `service_code` - (Required) The code of the service. Available service codes can be fetched using the DescribeServices pricing API call. * `filters` - (Required) A list of filters. Passed directly to the API (see GetProducts API reference). These filters must describe a single product, this resource will fail if more than one product is returned by the API. - * `json_query` - (Required) The JSON query used to fetch the wanted value. In a [GJSON format](https://github.com/tidwall/gjson). ## Attributes Reference - * `query_result` - Set to the result of the JSON query applied on the product returned from the API. + * `result` - Set to the product returned from the API. From e3685af6a7ff24563a302048c13ed990dfd94ce8 Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Tue, 3 Jul 2018 19:57:42 -0400 Subject: [PATCH 79/88] resource/aws_lambda_event_source_mapping: Remove panic() and update batch_size default documentation --- aws/resource_aws_lambda_event_source_mapping.go | 2 -- website/docs/r/lambda_event_source_mapping.html.markdown | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/aws/resource_aws_lambda_event_source_mapping.go b/aws/resource_aws_lambda_event_source_mapping.go index 8dc51aaecf9..8b20bb94997 100644 --- a/aws/resource_aws_lambda_event_source_mapping.go +++ b/aws/resource_aws_lambda_event_source_mapping.go @@ -68,8 +68,6 @@ func resourceAwsLambdaEventSourceMapping() *schema.Resource { if old == "10" { return true } - default: - panic(eventSourceARN.Service) } return false }, diff --git a/website/docs/r/lambda_event_source_mapping.html.markdown b/website/docs/r/lambda_event_source_mapping.html.markdown index d00effbe38e..a4aad55cfdb 100644 --- a/website/docs/r/lambda_event_source_mapping.html.markdown +++ b/website/docs/r/lambda_event_source_mapping.html.markdown @@ -27,7 +27,7 @@ resource "aws_lambda_event_source_mapping" "event_source_mapping" { ## Argument Reference -* `batch_size` - (Optional) The largest number of records that Lambda will retrieve from your event source at the time of invocation. Defaults to `100`. +* `batch_size` - (Optional) The largest number of records that Lambda will retrieve from your event source at the time of invocation. Defaults to `100` for DynamoDB and Kinesis, `10` for SQS. * `event_source_arn` - (Required) The event source ARN - can either be a Kinesis or DynamoDB stream. * `enabled` - (Optional) Determines if the mapping will be enabled on creation. Defaults to `true`. * `function_name` - (Required) The name or the ARN of the Lambda function that will be subscribing to events. From 5247217ecc45b3867eb8ca4cd9ae934054a339de Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Tue, 3 Jul 2018 20:03:20 -0400 Subject: [PATCH 80/88] Update CHANGELOG for #5024 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6d31bcfd6a..7af64dd7e54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ ENHANCEMENTS: * data-source/aws_subnet_ids: Add `filter` argument [GH-5038] * resource/aws_eip_association: Support resource import [GH-5006] * resource/aws_lambda_alias: Add `routing_config` argument (support traffic shifting) [GH-3316] +* resource/aws_lambda_event_source_mapping: Make `starting_position` optional and allow `batch_size` to support default of 10 for SQS [GH-5024] * resource/aws_network_acl_rule: Add plan time conflict validation with `cidr_block` and `ipv6_cidr_block` [GH-3951] * resource/aws_spot_fleet_request: Add `fleet_type` argument [GH-5032] * resource/aws_ssm_document: Add `tags` argument (support tagging) [GH-5020] From fba6e5daeb9046eb439ff94ba15bcc7091b6aeb1 Mon Sep 17 00:00:00 2001 From: Tim Malone Date: Wed, 4 Jul 2018 10:13:47 +1000 Subject: [PATCH 81/88] db_event_subscription source_type doc expansion - Adds valid options for `source_type` argument (sourced from running `aws rds describe-event-categories`) - Fixes double slash in URL given for `event_categories` argument - Adds AWS CLI command for describing valid options for `event_categories` argument --- website/docs/r/db_event_subscription.html.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/r/db_event_subscription.html.markdown b/website/docs/r/db_event_subscription.html.markdown index 7f043235d5f..aa5d73640fd 100644 --- a/website/docs/r/db_event_subscription.html.markdown +++ b/website/docs/r/db_event_subscription.html.markdown @@ -59,8 +59,8 @@ The following arguments are supported: * `name_prefix` - (Optional) The name of the DB event subscription. Conflicts with `name`. * `sns_topic` - (Required) The SNS topic to send events to. * `source_ids` - (Optional) A list of identifiers of the event sources for which events will be returned. If not specified, then all sources are included in the response. If specified, a source_type must also be specified. -* `source_type` - (Optional) The type of source that will be generating the events. -* `event_categories` - (Optional) A list of event categories for a SourceType that you want to subscribe to. See http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide//USER_Events.html +* `source_type` - (Optional) The type of source that will be generating the events. Valid options are `db-instance`, `db-security-group`, `db-parameter-group`, `db-cluster` or `db-cluster-snapshot`. If not set, all sources will be subscribed to. +* `event_categories` - (Optional) A list of event categories for a SourceType that you want to subscribe to. See http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_Events.html or run `aws rds describe-event-categories`. * `enabled` - (Optional) A boolean flag to enable/disable the subscription. Defaults to true. * `tags` - (Optional) A mapping of tags to assign to the resource. From 1590956ae2f6d042135480b018cd06088e984cd3 Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Tue, 3 Jul 2018 22:13:01 -0400 Subject: [PATCH 82/88] Update CHANGELOG for #4950 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7af64dd7e54..7f699da3a04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ FEATURES: ENHANCEMENTS: +* data-source/aws_instances: Add `instance_state_names` argument (support non-`running` instances) [GH-4950] * data-source/aws_route_tables: Add `filter` argument [GH-5035] * data-source/aws_subnet_ids: Add `filter` argument [GH-5038] * resource/aws_eip_association: Support resource import [GH-5006] From 1d729c1337740193e0bab1cbca8c43f7826047f1 Mon Sep 17 00:00:00 2001 From: Julien Duchesne Date: Tue, 3 Jul 2018 22:12:57 -0400 Subject: [PATCH 83/88] Fix comments from PR #5057 (pricing_product) --- aws/data_source_aws_pricing_product.go | 34 +++++++------------- aws/data_source_aws_pricing_product_test.go | 4 +++ website/aws.erb | 3 ++ website/docs/d/pricing_product.html.markdown | 11 +++++-- 4 files changed, 28 insertions(+), 24 deletions(-) diff --git a/aws/data_source_aws_pricing_product.go b/aws/data_source_aws_pricing_product.go index 79d26b9d350..393acef12a6 100644 --- a/aws/data_source_aws_pricing_product.go +++ b/aws/data_source_aws_pricing_product.go @@ -5,16 +5,13 @@ import ( "encoding/json" "fmt" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/pricing" "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/schema" ) -const ( - awsPricingTermMatch = "TERM_MATCH" -) - func dataSourceAwsPricingProduct() *schema.Resource { return &schema.Resource{ Read: dataSourceAwsPricingProductRead, @@ -62,7 +59,7 @@ func dataSourceAwsPricingProductRead(d *schema.ResourceData, meta interface{}) e params.Filters = append(params.Filters, &pricing.Filter{ Field: aws.String(m["field"].(string)), Value: aws.String(m["value"].(string)), - Type: aws.String(awsPricingTermMatch), + Type: aws.String(pricing.FilterTypeTermMatch), }) } @@ -72,31 +69,24 @@ func dataSourceAwsPricingProductRead(d *schema.ResourceData, meta interface{}) e return fmt.Errorf("Error reading pricing of products: %s", err) } - if err = verifyProductsPriceListLength(resp.PriceList); err != nil { - return err - } - - pricingResult, err := json.Marshal(resp.PriceList[0]) - if err != nil { - return fmt.Errorf("Invalid JSON value returned by AWS: %s", err) - } - - d.SetId(fmt.Sprintf("%d", hashcode.String(params.String()))) - d.Set("result", string(pricingResult)) - return nil -} - -func verifyProductsPriceListLength(priceList []aws.JSONValue) error { - numberOfElements := len(priceList) + numberOfElements := len(resp.PriceList) if numberOfElements == 0 { return fmt.Errorf("Pricing product query did not return any elements") } else if numberOfElements > 1 { - priceListBytes, err := json.Marshal(priceList) + priceListBytes, err := json.Marshal(resp.PriceList) priceListString := string(priceListBytes) if err != nil { priceListString = err.Error() } return fmt.Errorf("Pricing product query not precise enough. Returned more than one element: %s", priceListString) } + + pricingResult, err := json.Marshal(resp.PriceList[0]) + if err != nil { + return fmt.Errorf("Invalid JSON value returned by AWS: %s", err) + } + + d.SetId(fmt.Sprintf("%d", hashcode.String(params.String()))) + d.Set("result", string(pricingResult)) return nil } diff --git a/aws/data_source_aws_pricing_product_test.go b/aws/data_source_aws_pricing_product_test.go index ed9ee03c264..17e9d278a65 100644 --- a/aws/data_source_aws_pricing_product_test.go +++ b/aws/data_source_aws_pricing_product_test.go @@ -11,7 +11,9 @@ import ( ) func TestAccDataSourceAwsPricingProduct_ec2(t *testing.T) { + oldRegion := os.Getenv("AWS_DEFAULT_REGION") os.Setenv("AWS_DEFAULT_REGION", "us-east-1") + defer os.Setenv("AWS_DEFAULT_REGION", oldRegion) resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, @@ -28,7 +30,9 @@ func TestAccDataSourceAwsPricingProduct_ec2(t *testing.T) { } func TestAccDataSourceAwsPricingProduct_redshift(t *testing.T) { + oldRegion := os.Getenv("AWS_DEFAULT_REGION") os.Setenv("AWS_DEFAULT_REGION", "us-east-1") + defer os.Setenv("AWS_DEFAULT_REGION", oldRegion) resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, diff --git a/website/aws.erb b/website/aws.erb index f4d36921980..43b5197ba53 100644 --- a/website/aws.erb +++ b/website/aws.erb @@ -274,6 +274,9 @@ > aws_prefix_list + > + aws_pricing_product + > aws_rds_cluster diff --git a/website/docs/d/pricing_product.html.markdown b/website/docs/d/pricing_product.html.markdown index 2379ce5419e..b60952a0936 100644 --- a/website/docs/d/pricing_product.html.markdown +++ b/website/docs/d/pricing_product.html.markdown @@ -14,7 +14,7 @@ This data source is only available in a us-east-1 or ap-south-1 provider. ## Example Usage ```hcl -data "aws_pricing_product" "test1" { +data "aws_pricing_product" "example" { service_code = "AmazonEC2" filters = [ @@ -44,8 +44,10 @@ data "aws_pricing_product" "test1" { }, ] } +``` -data "aws_pricing_product" "test2" { +```hcl +data "aws_pricing_product" "example" { service_code = "AmazonRedshift" filters = [ @@ -66,6 +68,11 @@ data "aws_pricing_product" "test2" { * `service_code` - (Required) The code of the service. Available service codes can be fetched using the DescribeServices pricing API call. * `filters` - (Required) A list of filters. Passed directly to the API (see GetProducts API reference). These filters must describe a single product, this resource will fail if more than one product is returned by the API. +### `filters` + + * `field` (Required) The product attribute name that you want to filter on. + * `value` (Required) The product attribute value that you want to filter on. + ## Attributes Reference * `result` - Set to the product returned from the API. From 35c0728d588eca6fdcb94b516eae4c40401125e1 Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Tue, 3 Jul 2018 22:22:13 -0400 Subject: [PATCH 84/88] Update CHANGELOG for #5057 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f699da3a04..f54b67950ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ FEATURES: * **New Data Source:** `aws_launch_configuration` [GH-3624] +* **New Data Source:** `aws_pricing_product` [GH-5057] * **New Resource:** `aws_s3_bucket_inventory` [GH-5019] * **New Resource:** `aws_vpc_ipv4_cidr_block_association` [GH-3723] From 4bd7c4aaf208b89740d219d6f65f6696bb2f3256 Mon Sep 17 00:00:00 2001 From: Tim Malone Date: Wed, 4 Jul 2018 12:53:30 +1000 Subject: [PATCH 85/88] db_event_subscription add missing db-snapshot type --- website/docs/r/db_event_subscription.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/db_event_subscription.html.markdown b/website/docs/r/db_event_subscription.html.markdown index aa5d73640fd..e8e0afe9d63 100644 --- a/website/docs/r/db_event_subscription.html.markdown +++ b/website/docs/r/db_event_subscription.html.markdown @@ -59,7 +59,7 @@ The following arguments are supported: * `name_prefix` - (Optional) The name of the DB event subscription. Conflicts with `name`. * `sns_topic` - (Required) The SNS topic to send events to. * `source_ids` - (Optional) A list of identifiers of the event sources for which events will be returned. If not specified, then all sources are included in the response. If specified, a source_type must also be specified. -* `source_type` - (Optional) The type of source that will be generating the events. Valid options are `db-instance`, `db-security-group`, `db-parameter-group`, `db-cluster` or `db-cluster-snapshot`. If not set, all sources will be subscribed to. +* `source_type` - (Optional) The type of source that will be generating the events. Valid options are `db-instance`, `db-security-group`, `db-parameter-group`, `db-snapshot`, `db-cluster` or `db-cluster-snapshot`. If not set, all sources will be subscribed to. * `event_categories` - (Optional) A list of event categories for a SourceType that you want to subscribe to. See http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_Events.html or run `aws rds describe-event-categories`. * `enabled` - (Optional) A boolean flag to enable/disable the subscription. Defaults to true. * `tags` - (Optional) A mapping of tags to assign to the resource. From 43634bb6a9aa9b81e80a48ac3290dc704aed6798 Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Tue, 3 Jul 2018 23:32:01 -0400 Subject: [PATCH 86/88] Address #5056 PR feedback --- aws/data_source_aws_elasticache_replication_group.go | 4 +++- aws/data_source_aws_elasticache_replication_group_test.go | 4 +--- aws/resource_aws_elasticache_replication_group.go | 4 +++- aws/resource_aws_elasticache_replication_group_test.go | 4 ++-- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/aws/data_source_aws_elasticache_replication_group.go b/aws/data_source_aws_elasticache_replication_group.go index 77ec2e8780c..7c3ee5e2ca5 100644 --- a/aws/data_source_aws_elasticache_replication_group.go +++ b/aws/data_source_aws_elasticache_replication_group.go @@ -112,7 +112,9 @@ func dataSourceAwsElasticacheReplicationGroupRead(d *schema.ResourceData, meta i d.Set("primary_endpoint_address", rg.NodeGroups[0].PrimaryEndpoint.Address) } d.Set("number_cache_clusters", len(rg.MemberClusters)) - d.Set("member_clusters", flattenStringList(rg.MemberClusters)) + if err := d.Set("member_clusters", flattenStringList(rg.MemberClusters)); err != nil { + return fmt.Errorf("error setting member_clusters: %s", err) + } d.Set("node_type", rg.CacheNodeType) d.Set("snapshot_window", rg.SnapshotWindow) d.Set("snapshot_retention_limit", rg.SnapshotRetentionLimit) diff --git a/aws/data_source_aws_elasticache_replication_group_test.go b/aws/data_source_aws_elasticache_replication_group_test.go index 00e361bbfcf..409b2fd4259 100644 --- a/aws/data_source_aws_elasticache_replication_group_test.go +++ b/aws/data_source_aws_elasticache_replication_group_test.go @@ -24,7 +24,7 @@ func TestAccDataSourceAwsElasticacheReplicationGroup_basic(t *testing.T) { resource.TestCheckResourceAttr("data.aws_elasticache_replication_group.bar", "port", "6379"), resource.TestCheckResourceAttrSet("data.aws_elasticache_replication_group.bar", "primary_endpoint_address"), resource.TestCheckResourceAttr("data.aws_elasticache_replication_group.bar", "number_cache_clusters", "2"), - resource.TestCheckResourceAttrSet("data.aws_elasticache_replication_group.bar", "member_clusters"), + resource.TestCheckResourceAttr("data.aws_elasticache_replication_group.bar", "member_clusters.#", "2"), resource.TestCheckResourceAttr("data.aws_elasticache_replication_group.bar", "node_type", "cache.m1.small"), resource.TestCheckResourceAttr("data.aws_elasticache_replication_group.bar", "snapshot_window", "01:00-02:00"), ), @@ -65,7 +65,6 @@ resource "aws_elasticache_replication_group" "bar" { node_type = "cache.m1.small" number_cache_clusters = 2 port = 6379 - parameter_group_name = "default.redis3.2" availability_zones = ["${data.aws_availability_zones.available.names[0]}", "${data.aws_availability_zones.available.names[1]}"] automatic_failover_enabled = true snapshot_window = "01:00-02:00" @@ -83,7 +82,6 @@ resource "aws_elasticache_replication_group" "cluster" { replication_group_description = "test description" node_type = "cache.m1.small" port = 6379 - parameter_group_name = "default.redis3.2.cluster.on" automatic_failover_enabled = true cluster_mode { replicas_per_node_group = 1 diff --git a/aws/resource_aws_elasticache_replication_group.go b/aws/resource_aws_elasticache_replication_group.go index db52c9eb2cc..af8bee44722 100644 --- a/aws/resource_aws_elasticache_replication_group.go +++ b/aws/resource_aws_elasticache_replication_group.go @@ -320,7 +320,9 @@ func resourceAwsElasticacheReplicationGroupRead(d *schema.ResourceData, meta int d.Set("replication_group_description", rgp.Description) d.Set("number_cache_clusters", len(rgp.MemberClusters)) - d.Set("member_clusters", flattenStringList(rgp.MemberClusters)) + if err := d.Set("member_clusters", flattenStringList(rgp.MemberClusters)); err != nil { + return fmt.Errorf("error setting member_clusters: %s", err) + } if err := d.Set("cluster_mode", flattenElasticacheNodeGroupsToClusterMode(aws.BoolValue(rgp.ClusterEnabled), rgp.NodeGroups)); err != nil { return fmt.Errorf("error setting cluster_mode attribute: %s", err) } diff --git a/aws/resource_aws_elasticache_replication_group_test.go b/aws/resource_aws_elasticache_replication_group_test.go index 03425bf836e..32e66ba52dc 100644 --- a/aws/resource_aws_elasticache_replication_group_test.go +++ b/aws/resource_aws_elasticache_replication_group_test.go @@ -89,8 +89,8 @@ func TestAccAWSElasticacheReplicationGroup_basic(t *testing.T) { "aws_elasticache_replication_group.bar", "cluster_mode.#", "0"), resource.TestCheckResourceAttr( "aws_elasticache_replication_group.bar", "number_cache_clusters", "2"), - resource.TestCheckResourceAttrSet( - "aws_elasticache_replication_group.bar", "member_clusters"), + resource.TestCheckResourceAttr( + "aws_elasticache_replication_group.bar", "member_clusters.#", "2"), resource.TestCheckResourceAttr( "aws_elasticache_replication_group.bar", "auto_minor_version_upgrade", "false"), ), From dc4b26f52d16ca0b8269473ff4339a03b65c909e Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Tue, 3 Jul 2018 23:33:47 -0400 Subject: [PATCH 87/88] Update CHANGELOG for #5056 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f54b67950ec..50b9a09e848 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,10 +9,12 @@ FEATURES: ENHANCEMENTS: +* data-source/aws_elasticache_replication_group: Add `member_clusters` attribute [GH-5056] * data-source/aws_instances: Add `instance_state_names` argument (support non-`running` instances) [GH-4950] * data-source/aws_route_tables: Add `filter` argument [GH-5035] * data-source/aws_subnet_ids: Add `filter` argument [GH-5038] * resource/aws_eip_association: Support resource import [GH-5006] +* resource/aws_elasticache_replication_group: Add `member_clusters` attribute [GH-5056] * resource/aws_lambda_alias: Add `routing_config` argument (support traffic shifting) [GH-3316] * resource/aws_lambda_event_source_mapping: Make `starting_position` optional and allow `batch_size` to support default of 10 for SQS [GH-5024] * resource/aws_network_acl_rule: Add plan time conflict validation with `cidr_block` and `ipv6_cidr_block` [GH-3951] From 7b7d817c4fdb15ee41e41e69e7e5ca0fc459119d Mon Sep 17 00:00:00 2001 From: tf-release-bot Date: Wed, 4 Jul 2018 10:13:57 +0000 Subject: [PATCH 88/88] v1.26.0 --- CHANGELOG.md | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 50b9a09e848..8d6ad6fb9b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,30 +1,30 @@ -## 1.26.0 (Unreleased) +## 1.26.0 (July 04, 2018) FEATURES: -* **New Data Source:** `aws_launch_configuration` [GH-3624] -* **New Data Source:** `aws_pricing_product` [GH-5057] -* **New Resource:** `aws_s3_bucket_inventory` [GH-5019] -* **New Resource:** `aws_vpc_ipv4_cidr_block_association` [GH-3723] +* **New Data Source:** `aws_launch_configuration` ([#3624](https://github.com/terraform-providers/terraform-provider-aws/issues/3624)) +* **New Data Source:** `aws_pricing_product` ([#5057](https://github.com/terraform-providers/terraform-provider-aws/issues/5057)) +* **New Resource:** `aws_s3_bucket_inventory` ([#5019](https://github.com/terraform-providers/terraform-provider-aws/issues/5019)) +* **New Resource:** `aws_vpc_ipv4_cidr_block_association` ([#3723](https://github.com/terraform-providers/terraform-provider-aws/issues/3723)) ENHANCEMENTS: -* data-source/aws_elasticache_replication_group: Add `member_clusters` attribute [GH-5056] -* data-source/aws_instances: Add `instance_state_names` argument (support non-`running` instances) [GH-4950] -* data-source/aws_route_tables: Add `filter` argument [GH-5035] -* data-source/aws_subnet_ids: Add `filter` argument [GH-5038] -* resource/aws_eip_association: Support resource import [GH-5006] -* resource/aws_elasticache_replication_group: Add `member_clusters` attribute [GH-5056] -* resource/aws_lambda_alias: Add `routing_config` argument (support traffic shifting) [GH-3316] -* resource/aws_lambda_event_source_mapping: Make `starting_position` optional and allow `batch_size` to support default of 10 for SQS [GH-5024] -* resource/aws_network_acl_rule: Add plan time conflict validation with `cidr_block` and `ipv6_cidr_block` [GH-3951] -* resource/aws_spot_fleet_request: Add `fleet_type` argument [GH-5032] -* resource/aws_ssm_document: Add `tags` argument (support tagging) [GH-5020] +* data-source/aws_elasticache_replication_group: Add `member_clusters` attribute ([#5056](https://github.com/terraform-providers/terraform-provider-aws/issues/5056)) +* data-source/aws_instances: Add `instance_state_names` argument (support non-`running` instances) ([#4950](https://github.com/terraform-providers/terraform-provider-aws/issues/4950)) +* data-source/aws_route_tables: Add `filter` argument ([#5035](https://github.com/terraform-providers/terraform-provider-aws/issues/5035)) +* data-source/aws_subnet_ids: Add `filter` argument ([#5038](https://github.com/terraform-providers/terraform-provider-aws/issues/5038)) +* resource/aws_eip_association: Support resource import ([#5006](https://github.com/terraform-providers/terraform-provider-aws/issues/5006)) +* resource/aws_elasticache_replication_group: Add `member_clusters` attribute ([#5056](https://github.com/terraform-providers/terraform-provider-aws/issues/5056)) +* resource/aws_lambda_alias: Add `routing_config` argument (support traffic shifting) ([#3316](https://github.com/terraform-providers/terraform-provider-aws/issues/3316)) +* resource/aws_lambda_event_source_mapping: Make `starting_position` optional and allow `batch_size` to support default of 10 for SQS ([#5024](https://github.com/terraform-providers/terraform-provider-aws/issues/5024)) +* resource/aws_network_acl_rule: Add plan time conflict validation with `cidr_block` and `ipv6_cidr_block` ([#3951](https://github.com/terraform-providers/terraform-provider-aws/issues/3951)) +* resource/aws_spot_fleet_request: Add `fleet_type` argument ([#5032](https://github.com/terraform-providers/terraform-provider-aws/issues/5032)) +* resource/aws_ssm_document: Add `tags` argument (support tagging) ([#5020](https://github.com/terraform-providers/terraform-provider-aws/issues/5020)) BUG FIXES: -* resource/aws_codebuild_project: Prevent panic with missing environment variable type [GH-5052] -* resource/aws_kms_alias: Fix perpetual plan when `target_key_id` is ARN [GH-4010] +* resource/aws_codebuild_project: Prevent panic with missing environment variable type ([#5052](https://github.com/terraform-providers/terraform-provider-aws/issues/5052)) +* resource/aws_kms_alias: Fix perpetual plan when `target_key_id` is ARN ([#4010](https://github.com/terraform-providers/terraform-provider-aws/issues/4010)) ## 1.25.0 (June 27, 2018)