Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

r/aws_api_gateway_stage: implement access logging #4369

Merged
merged 1 commit into from
Apr 27, 2018
Merged

r/aws_api_gateway_stage: implement access logging #4369

merged 1 commit into from
Apr 27, 2018

Conversation

appilon
Copy link
Contributor

@appilon appilon commented Apr 26, 2018

Fix #2406

implement access logging for apigw stage
add acceptance test with different accepted log formats
make note of sanitization performed on destination_arn

TF_ACC=1 go test ./aws -v -run=TestAccAWSAPIGatewayStage_accessLogSettings -timeout 120m
=== RUN   TestAccAWSAPIGatewayStage_accessLogSettings
--- PASS: TestAccAWSAPIGatewayStage_accessLogSettings (331.21s)
PASS
ok  	github.com/terraform-providers/terraform-provider-aws/aws	331.260s

@ghost ghost added size/L Managed by automation to categorize the size of a PR. labels Apr 26, 2018
@appilon appilon requested a review from a team April 26, 2018 17:45
@appilon appilon added service/apigateway Issues and PRs that pertain to the apigateway service. enhancement Requests to existing resources that expand the functionality or scope. labels Apr 26, 2018
@ghost ghost added size/L Managed by automation to categorize the size of a PR. labels Apr 26, 2018
Copy link
Contributor

@bflad bflad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good @appilon! Left some comments below -- please let me know if you have any questions 😄

@@ -177,6 +202,8 @@ func resourceAwsApiGatewayStageRead(d *schema.ResourceData, meta interface{}) er
}
log.Printf("[DEBUG] Received API Gateway Stage: %s", stage)

d.Set("access_log_settings", flattenApiGatewayStageAccessLogSettings(stage.AccessLogSettings))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should do error checking when setting non-scalar attributes 👍 e.g.

if err := d.Set("access_log_settings", flattenApiGatewayStageAccessLogSettings(stage.AccessLogSettings)); err != nil {
  return fmt.Errorf("error setting access_log_settings: %s", err)
}

Config: testAccAWSAPIGatewayStageConfig_basic(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayStageExists("aws_api_gateway_stage.test", &conf),
testAccCheckAWSAPIGatewayStageAccessLogSettingsIsNil(&conf),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can combine both this check and the below with resource.TestCheckResourceAttr("aws_api_gateway_stage.test", "access_logs_settings.#", "0")

Copy link
Contributor Author

@appilon appilon Apr 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because my flattener returns nil if the sdk struct is nil, access_logs_settings.# doesn't even exist in the state, should I just check that access_logs_settings is not set? Alternatively I could change my flattener to return an empty array. It boils down to what do we prefer for the 'zero' value on a nested block, nil or empty []interface{}?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tend to lean towards returning empy list/set/map so it explicitly gets set as a zero value in the state. I believe the reasoning is that it helps with writing conditionals like ${length(aws_api_gateway_stage.example.access_log_settings)} (where if its not set it'll return an error about the attribute not existing instead of returning 0).

Providers: testAccProviders,
CheckDestroy: testAccCheckAWSAPIGatewayStageDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor nitpick: The resource.TestStep declarations here are extraneous as its already defined by []resource.TestStep{ above

Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayStageExists("aws_api_gateway_stage.test", &conf),
resource.TestCheckResourceAttr("aws_api_gateway_stage.test", "access_log_settings.#", "1"),
testAccCheckAWSAPIGatewayStageAccessLogSettingsDestinationArnIsValid("aws_api_gateway_stage.test"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something for consideration instead of writing a new function:

resource.TestMatchResourceAttr("aws_api_gateway_stage.test", "access_log_settings.0.destination_arn", regexp.MustCompile(fmt.Sprintf("^arn:[^:]+:logs:[^:]+:[^:]+:foo-bar-%s$", rName))),

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

your regex skills are lit 🔥

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sadly that rule didn't work I was inspired by you though and tweaked it let me know if you think it's robust enough.

logGroupArnRegex := regexp.MustCompile(fmt.Sprintf("^(arn:aws:logs:)(.*)(log-group:foo-bar-%s)$", rName))

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like it just needed log-group: before foo-bar-%s:

resource.TestMatchResourceAttr("aws_api_gateway_stage.test", "access_log_settings.0.destination_arn", regexp.MustCompile(fmt.Sprintf("^arn:[^:]+:logs:[^:]+:[^:]+:log-group:foo-bar-%s$", rName))),

Whatever you think is best. I try to avoid wildcards.

I will say we should to try to not hardcode arn:aws at the beginning because at some point these tests will be running in US GovCloud (aws-us-gov) and maybe China partitions (aws-cn).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does bring up a valid point that maybe we should have a simple ARN matching TestCheckFunc instead of handcrafting these.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 to arn matcher and to your points on harcoded arn:aws I didn't even think of that!

@@ -69,6 +69,7 @@ The following arguments are supported:
* `rest_api_id` - (Required) The ID of the associated REST API
* `stage_name` - (Required) The name of the stage
* `deployment_id` - (Required) The ID of the deployment that the stage points to
* `access_log_settings` - (Optional) enables access logs for the api stage
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably mention: ...API stage. Detailed below.

#### `access_log_settings`

* `destination_arn` - (Required) ARN of the log group to send the logs to.
~> **Note:** Referencing a log group arn from a Terraform resource will normally include a `:*` suffix which is not valid for the destination arn, Terraform automatically strips this suffix when configuring this argument.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need this bold callout that makes it sound like Terraform is doing something wrong or this is Terraform specific behavior (as the ARN value with :* coming from the CloudWatch API). I think a simple Automatically removes trailing `:*` if present. in the line above would suffice. 👍

return nil
}
item := make(map[string]interface{})
if accessLogSettings.DestinationArn != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something to consider when working in the AWS provider is that the SDK provides helper functions so you don't need these wrapping nil checks (in most cases anyways): item["destination_arn"] = aws.StringValue(accessLogSettings.DestinationArn)

@ghost ghost added the size/L Managed by automation to categorize the size of a PR. label Apr 26, 2018
@@ -276,6 +305,27 @@ func resourceAwsApiGatewayStageUpdate(d *schema.ResourceData, meta interface{})
newV := n.(map[string]interface{})
operations = append(operations, diffVariablesOps("/variables/", oldV, newV)...)
}
if d.HasChange("access_log_settings") {
accessLogSettings := d.Get("access_log_settings").([]interface{})
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bflad if the user deletes the nested block, is this code risky? My acceptance tests pass but I'm concerned len(accessLogSettings) might gag... (I've admittedly never used much interface{} or type casts until TF)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I know helper/schema.Schema should return the appropriate Go type []interface{} here with either 0 or 1 elements: https://godoc.org/github.com/hashicorp/terraform/helper/schema#Schema

@ghost ghost added the size/L Managed by automation to categorize the size of a PR. label Apr 27, 2018
add acceptance test with different accepted log formats
make note of sanitization performed on destinatation_arn
@ghost ghost added the size/L Managed by automation to categorize the size of a PR. label Apr 27, 2018
@appilon
Copy link
Contributor Author

appilon commented Apr 27, 2018

@bflad I think I've addressed all the comments!

Copy link
Contributor

@bflad bflad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great @appilon, thanks! 🚀

2 tests passed (all tests)
=== RUN   TestAccAWSAPIGatewayStage_accessLogSettings
--- PASS: TestAccAWSAPIGatewayStage_accessLogSettings (392.52s)
=== RUN   TestAccAWSAPIGatewayStage_basic
--- PASS: TestAccAWSAPIGatewayStage_basic (519.82s)

@bflad bflad merged commit 2438532 into hashicorp:master Apr 27, 2018
@bflad bflad added this to the v1.17.0 milestone Apr 27, 2018
bflad added a commit that referenced this pull request Apr 27, 2018
@bflad
Copy link
Contributor

bflad commented May 2, 2018

This has been released in version 1.17.0 of the AWS provider. Please see the Terraform documentation on provider versioning or reach out if you need any assistance upgrading.

@ghost
Copy link

ghost commented Apr 6, 2020

I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues.

If you feel this issue should be reopened, we encourage creating a new issue linking back to this one for added context. Thanks!

@ghost ghost locked and limited conversation to collaborators Apr 6, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
enhancement Requests to existing resources that expand the functionality or scope. service/apigateway Issues and PRs that pertain to the apigateway service. size/L Managed by automation to categorize the size of a PR.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Amazon API Gateway Access Logging Support
2 participants