Skip to content

Commit

Permalink
provider/aws: Add support for api_gateway_account (#6321)
Browse files Browse the repository at this point in the history
  • Loading branch information
radeksimko authored and stack72 committed Apr 27, 2016
1 parent 0194cfd commit e3ade6a
Show file tree
Hide file tree
Showing 7 changed files with 476 additions and 0 deletions.
1 change: 1 addition & 0 deletions builtin/providers/aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ func Provider() terraform.ResourceProvider {
"aws_ami": resourceAwsAmi(),
"aws_ami_copy": resourceAwsAmiCopy(),
"aws_ami_from_instance": resourceAwsAmiFromInstance(),
"aws_api_gateway_account": resourceAwsApiGatewayAccount(),
"aws_api_gateway_api_key": resourceAwsApiGatewayApiKey(),
"aws_api_gateway_authorizer": resourceAwsApiGatewayAuthorizer(),
"aws_api_gateway_deployment": resourceAwsApiGatewayDeployment(),
Expand Down
124 changes: 124 additions & 0 deletions builtin/providers/aws/resource_aws_api_gateway_account.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package aws

import (
"fmt"
"log"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/apigateway"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsApiGatewayAccount() *schema.Resource {
return &schema.Resource{
Create: resourceAwsApiGatewayAccountUpdate,
Read: resourceAwsApiGatewayAccountRead,
Update: resourceAwsApiGatewayAccountUpdate,
Delete: resourceAwsApiGatewayAccountDelete,

Schema: map[string]*schema.Schema{
"cloudwatch_role_arn": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"throttle_settings": &schema.Schema{
Type: schema.TypeList,
Computed: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"burst_limit": &schema.Schema{
Type: schema.TypeInt,
Computed: true,
},
"rate_limit": &schema.Schema{
Type: schema.TypeFloat,
Computed: true,
},
},
},
},
},
}
}

func resourceAwsApiGatewayAccountRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).apigateway

log.Printf("[INFO] Reading API Gateway Account %s", d.Id())
account, err := conn.GetAccount(&apigateway.GetAccountInput{})
if err != nil {
return err
}

log.Printf("[DEBUG] Received API Gateway Account: %s", account)

if _, ok := d.GetOk("cloudwatch_role_arn"); ok {
// CloudwatchRoleArn cannot be empty nor made empty via API
// This resource can however be useful w/out defining cloudwatch_role_arn
// (e.g. for referencing throttle_settings)
d.Set("cloudwatch_role_arn", account.CloudwatchRoleArn)
}
d.Set("throttle_settings", flattenApiGatewayThrottleSettings(account.ThrottleSettings))

return nil
}

func resourceAwsApiGatewayAccountUpdate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).apigateway

input := apigateway.UpdateAccountInput{}
operations := make([]*apigateway.PatchOperation, 0)

if d.HasChange("cloudwatch_role_arn") {
arn := d.Get("cloudwatch_role_arn").(string)
if len(arn) > 0 {
// Unfortunately AWS API doesn't allow empty ARNs,
// even though that's default settings for new AWS accounts
// BadRequestException: The role ARN is not well formed
operations = append(operations, &apigateway.PatchOperation{
Op: aws.String("replace"),
Path: aws.String("/cloudwatchRoleArn"),
Value: aws.String(arn),
})
}
}
input.PatchOperations = operations

log.Printf("[INFO] Updating API Gateway Account: %s", input)

// Retry due to eventual consistency of IAM
expectedErrMsg := "The role ARN does not have required permissions set to API Gateway"
var out *apigateway.Account
var err error
err = resource.Retry(2*time.Minute, func() *resource.RetryError {
out, err = conn.UpdateAccount(&input)

if err != nil {
if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "BadRequestException" &&
awsErr.Message() == expectedErrMsg {
log.Printf("[DEBUG] Retrying API Gateway Account update: %s", awsErr)
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}

return nil
})
if err != nil {
return fmt.Errorf("Updating API Gateway Account failed: %s", err)
}
log.Printf("[DEBUG] API Gateway Account updated: %s", out)

d.SetId("api-gateway-account")
return resourceAwsApiGatewayAccountRead(d, meta)
}

func resourceAwsApiGatewayAccountDelete(d *schema.ResourceData, meta interface{}) error {
// There is no API for "deleting" account or resetting it to "default" settings
d.SetId("")
return nil
}
205 changes: 205 additions & 0 deletions builtin/providers/aws/resource_aws_api_gateway_account_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
package aws

import (
"fmt"
"regexp"
"testing"

"github.com/aws/aws-sdk-go/service/apigateway"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)

func TestAccAWSAPIGatewayAccount_basic(t *testing.T) {
var conf apigateway.Account

expectedRoleArn_first := regexp.MustCompile("[0-9]+")
expectedRoleArn_second := regexp.MustCompile("[0-9]+")

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSAPIGatewayAccountDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSAPIGatewayAccountConfig_updated,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayAccountExists("aws_api_gateway_account.test", &conf),
testAccCheckAWSAPIGatewayAccountCloudwatchRoleArn(&conf, expectedRoleArn_first),
resource.TestMatchResourceAttr("aws_api_gateway_account.test", "cloudwatch_role_arn", expectedRoleArn_first),
),
},
resource.TestStep{
Config: testAccAWSAPIGatewayAccountConfig_updated2,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayAccountExists("aws_api_gateway_account.test", &conf),
testAccCheckAWSAPIGatewayAccountCloudwatchRoleArn(&conf, expectedRoleArn_second),
resource.TestMatchResourceAttr("aws_api_gateway_account.test", "cloudwatch_role_arn", expectedRoleArn_second),
),
},
resource.TestStep{
Config: testAccAWSAPIGatewayAccountConfig_empty,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayAccountExists("aws_api_gateway_account.test", &conf),
testAccCheckAWSAPIGatewayAccountCloudwatchRoleArn(&conf, expectedRoleArn_second),
),
},
},
})
}

func testAccCheckAWSAPIGatewayAccountCloudwatchRoleArn(conf *apigateway.Account, expectedArn *regexp.Regexp) resource.TestCheckFunc {
return func(s *terraform.State) error {
if expectedArn == nil && conf.CloudwatchRoleArn == nil {
return nil
}
if expectedArn == nil && conf.CloudwatchRoleArn != nil {
return fmt.Errorf("Expected empty CloudwatchRoleArn, given: %q", *conf.CloudwatchRoleArn)
}
if expectedArn != nil && conf.CloudwatchRoleArn == nil {
return fmt.Errorf("Empty CloudwatchRoleArn, expected: %q", expectedArn)
}
if !expectedArn.MatchString(*conf.CloudwatchRoleArn) {
return fmt.Errorf("CloudwatchRoleArn didn't match. Expected: %q, Given: %q", expectedArn, *conf.CloudwatchRoleArn)
}
return nil
}
}

func testAccCheckAWSAPIGatewayAccountExists(n string, res *apigateway.Account) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}

if rs.Primary.ID == "" {
return fmt.Errorf("No API Gateway Account ID is set")
}

conn := testAccProvider.Meta().(*AWSClient).apigateway

req := &apigateway.GetAccountInput{}
describe, err := conn.GetAccount(req)
if err != nil {
return err
}
if describe == nil {
return fmt.Errorf("Got nil account ?!")
}

*res = *describe

return nil
}
}

func testAccCheckAWSAPIGatewayAccountDestroy(s *terraform.State) error {
// Intentionally noop
// as there is no API method for deleting or resetting account settings
return nil
}

const testAccAWSAPIGatewayAccountConfig_empty = `
resource "aws_api_gateway_account" "test" {
}
`

const testAccAWSAPIGatewayAccountConfig_updated = `
resource "aws_api_gateway_account" "test" {
cloudwatch_role_arn = "${aws_iam_role.cloudwatch.arn}"
}
resource "aws_iam_role" "cloudwatch" {
name = "api_gateway_cloudwatch_global"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": "apigateway.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}
resource "aws_iam_role_policy" "cloudwatch" {
name = "default"
role = "${aws_iam_role.cloudwatch.id}"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:DescribeLogGroups",
"logs:DescribeLogStreams",
"logs:PutLogEvents",
"logs:GetLogEvents",
"logs:FilterLogEvents"
],
"Resource": "*"
}
]
}
EOF
}
`
const testAccAWSAPIGatewayAccountConfig_updated2 = `
resource "aws_api_gateway_account" "test" {
cloudwatch_role_arn = "${aws_iam_role.second.arn}"
}
resource "aws_iam_role" "second" {
name = "api_gateway_cloudwatch_global_modified"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": "apigateway.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}
resource "aws_iam_role_policy" "cloudwatch" {
name = "default"
role = "${aws_iam_role.second.id}"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:DescribeLogGroups",
"logs:DescribeLogStreams",
"logs:PutLogEvents",
"logs:GetLogEvents",
"logs:FilterLogEvents"
],
"Resource": "*"
}
]
}
EOF
}
`
19 changes: 19 additions & 0 deletions builtin/providers/aws/structure.go
Original file line number Diff line number Diff line change
Expand Up @@ -1097,3 +1097,22 @@ func sortInterfaceSlice(in []interface{}) []interface{} {

return b
}

func flattenApiGatewayThrottleSettings(settings *apigateway.ThrottleSettings) []map[string]interface{} {
result := make([]map[string]interface{}, 0, 1)

if settings != nil {
r := make(map[string]interface{})
if settings.BurstLimit != nil {
r["burst_limit"] = *settings.BurstLimit
}

if settings.RateLimit != nil {
r["rate_limit"] = *settings.RateLimit
}

result = append(result, r)
}

return result
}
Loading

0 comments on commit e3ade6a

Please sign in to comment.