provider/aws: Added API Gateway Usage Plan (#12542)

* Added api_gateway_usage_plan

* Updated documentation

* Fixed AWS usage plan review points
This commit is contained in:
Gauthier Wallet 2017-03-18 15:18:19 +01:00 committed by Paul Stack
parent 2701586b06
commit 323f646b42
9 changed files with 1391 additions and 1 deletions

View File

@ -0,0 +1,30 @@
package aws
import (
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
)
func TestAccAWSAPIGatewayUsagePlan_importBasic(t *testing.T) {
resourceName := "aws_api_gateway_usage_plan.main"
rName := acctest.RandString(10)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSAPIGatewayUsagePlanDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSApiGatewayUsagePlanBasicConfig(rName),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

View File

@ -219,6 +219,7 @@ func Provider() terraform.ResourceProvider {
"aws_api_gateway_model": resourceAwsApiGatewayModel(),
"aws_api_gateway_resource": resourceAwsApiGatewayResource(),
"aws_api_gateway_rest_api": resourceAwsApiGatewayRestApi(),
"aws_api_gateway_usage_plan": resourceAwsApiGatewayUsagePlan(),
"aws_app_cookie_stickiness_policy": resourceAwsAppCookieStickinessPolicy(),
"aws_appautoscaling_target": resourceAwsAppautoscalingTarget(),
"aws_appautoscaling_policy": resourceAwsAppautoscalingPolicy(),

View File

@ -0,0 +1,499 @@
package aws
import (
"fmt"
"log"
"strconv"
"time"
"errors"
"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 resourceAwsApiGatewayUsagePlan() *schema.Resource {
return &schema.Resource{
Create: resourceAwsApiGatewayUsagePlanCreate,
Read: resourceAwsApiGatewayUsagePlanRead,
Update: resourceAwsApiGatewayUsagePlanUpdate,
Delete: resourceAwsApiGatewayUsagePlanDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true, // Required since not addable nor removable afterwards
},
"description": {
Type: schema.TypeString,
Optional: true,
},
"api_stages": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"api_id": {
Type: schema.TypeString,
Required: true,
},
"stage": {
Type: schema.TypeString,
Required: true,
},
},
},
},
"quota_settings": {
Type: schema.TypeSet,
MaxItems: 1,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"limit": {
Type: schema.TypeInt,
Required: true, // Required as not removable singularly
},
"offset": {
Type: schema.TypeInt,
Default: 0,
Optional: true,
},
"period": {
Type: schema.TypeString,
Required: true, // Required as not removable
ValidateFunc: validateApiGatewayUsagePlanQuotaSettingsPeriod,
},
},
},
},
"throttle_settings": {
Type: schema.TypeSet,
MaxItems: 1,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"burst_limit": {
Type: schema.TypeInt,
Default: 0,
Optional: true,
},
"rate_limit": {
Type: schema.TypeInt,
Default: 0,
Optional: true,
},
},
},
},
"product_code": {
Type: schema.TypeString,
Optional: true,
},
},
}
}
func resourceAwsApiGatewayUsagePlanCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).apigateway
log.Print("[DEBUG] Creating API Gateway Usage Plan")
params := &apigateway.CreateUsagePlanInput{
Name: aws.String(d.Get("name").(string)),
}
if v, ok := d.GetOk("description"); ok {
params.Description = aws.String(v.(string))
}
if s, ok := d.GetOk("api_stages"); ok {
stages := s.([]interface{})
as := make([]*apigateway.ApiStage, 0)
for _, v := range stages {
sv := v.(map[string]interface{})
stage := &apigateway.ApiStage{}
if v, ok := sv["api_id"].(string); ok && v != "" {
stage.ApiId = aws.String(v)
}
if v, ok := sv["stage"].(string); ok && v != "" {
stage.Stage = aws.String(v)
}
as = append(as, stage)
}
if len(as) > 0 {
params.ApiStages = as
}
}
if v, ok := d.GetOk("quota_settings"); ok {
settings := v.(*schema.Set).List()
q, ok := settings[0].(map[string]interface{})
if errors := validateApiGatewayUsagePlanQuotaSettings(q); len(errors) > 0 {
return fmt.Errorf("Error validating the quota settings: %v", errors)
}
if !ok {
return errors.New("At least one field is expected inside quota_settings")
}
qs := &apigateway.QuotaSettings{}
if sv, ok := q["limit"].(int); ok {
qs.Limit = aws.Int64(int64(sv))
}
if sv, ok := q["offset"].(int); ok {
qs.Offset = aws.Int64(int64(sv))
}
if sv, ok := q["period"].(string); ok && sv != "" {
qs.Period = aws.String(sv)
}
params.Quota = qs
}
if v, ok := d.GetOk("throttle_settings"); ok {
settings := v.(*schema.Set).List()
q, ok := settings[0].(map[string]interface{})
if !ok {
return errors.New("At least one field is expected inside throttle_settings")
}
ts := &apigateway.ThrottleSettings{}
if sv, ok := q["burst_limit"].(int); ok {
ts.BurstLimit = aws.Int64(int64(sv))
}
if sv, ok := q["rate_limit"].(float64); ok {
ts.RateLimit = aws.Float64(float64(sv))
}
params.Throttle = ts
}
up, err := conn.CreateUsagePlan(params)
if err != nil {
return fmt.Errorf("Error creating API Gateway Usage Plan: %s", err)
}
d.SetId(*up.Id)
// Handle case of adding the product code since not addable when
// creating the Usage Plan initially.
if v, ok := d.GetOk("product_code"); ok {
updateParameters := &apigateway.UpdateUsagePlanInput{
UsagePlanId: aws.String(d.Id()),
PatchOperations: []*apigateway.PatchOperation{
{
Op: aws.String("add"),
Path: aws.String("/productCode"),
Value: aws.String(v.(string)),
},
},
}
up, err = conn.UpdateUsagePlan(updateParameters)
if err != nil {
return fmt.Errorf("Error creating the API Gateway Usage Plan product code: %s", err)
}
}
return resourceAwsApiGatewayUsagePlanRead(d, meta)
}
func resourceAwsApiGatewayUsagePlanRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).apigateway
log.Printf("[DEBUG] Reading API Gateway Usage Plan: %s", d.Id())
up, err := conn.GetUsagePlan(&apigateway.GetUsagePlanInput{
UsagePlanId: aws.String(d.Id()),
})
if err != nil {
if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NotFoundException" {
d.SetId("")
return nil
}
return err
}
d.Set("name", up.Name)
d.Set("description", up.Description)
d.Set("product_code", up.ProductCode)
if up.ApiStages != nil {
if err := d.Set("api_stages", flattenApiGatewayUsageApiStages(up.ApiStages)); err != nil {
return fmt.Errorf("[DEBUG] Error setting api_stages error: %#v", err)
}
}
if up.Throttle != nil {
if err := d.Set("throttle_settings", flattenApiGatewayUsagePlanThrottling(up.Throttle)); err != nil {
return fmt.Errorf("[DEBUG] Error setting throttle_settings error: %#v", err)
}
}
if up.Quota != nil {
if err := d.Set("quota_settings", flattenApiGatewayUsagePlanQuota(up.Quota)); err != nil {
return fmt.Errorf("[DEBUG] Error setting quota_settings error: %#v", err)
}
}
return nil
}
func resourceAwsApiGatewayUsagePlanUpdate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).apigateway
log.Print("[DEBUG] Updating API Gateway Usage Plan")
operations := make([]*apigateway.PatchOperation, 0)
if d.HasChange("name") {
operations = append(operations, &apigateway.PatchOperation{
Op: aws.String("replace"),
Path: aws.String("/name"),
Value: aws.String(d.Get("name").(string)),
})
}
if d.HasChange("description") {
operations = append(operations, &apigateway.PatchOperation{
Op: aws.String("replace"),
Path: aws.String("/description"),
Value: aws.String(d.Get("description").(string)),
})
}
if d.HasChange("product_code") {
v, ok := d.GetOk("product_code")
if ok {
operations = append(operations, &apigateway.PatchOperation{
Op: aws.String("replace"),
Path: aws.String("/productCode"),
Value: aws.String(v.(string)),
})
} else {
operations = append(operations, &apigateway.PatchOperation{
Op: aws.String("remove"),
Path: aws.String("/productCode"),
})
}
}
if d.HasChange("api_stages") {
o, n := d.GetChange("api_stages")
old := o.([]interface{})
new := n.([]interface{})
// Remove every stages associated. Simpler to remove and add new ones,
// since there are no replacings.
for _, v := range old {
m := v.(map[string]interface{})
operations = append(operations, &apigateway.PatchOperation{
Op: aws.String("remove"),
Path: aws.String("/apiStages"),
Value: aws.String(fmt.Sprintf("%s:%s", m["api_id"].(string), m["stage"].(string))),
})
}
// Handle additions
if len(new) > 0 {
for _, v := range new {
m := v.(map[string]interface{})
operations = append(operations, &apigateway.PatchOperation{
Op: aws.String("add"),
Path: aws.String("/apiStages"),
Value: aws.String(fmt.Sprintf("%s:%s", m["api_id"].(string), m["stage"].(string))),
})
}
}
}
if d.HasChange("throttle_settings") {
o, n := d.GetChange("throttle_settings")
os := o.(*schema.Set)
ns := n.(*schema.Set)
diff := ns.Difference(os).List()
// Handle Removal
if len(diff) == 0 {
operations = append(operations, &apigateway.PatchOperation{
Op: aws.String("remove"),
Path: aws.String("/throttle"),
})
}
if len(diff) > 0 {
d := diff[0].(map[string]interface{})
// Handle Replaces
if o != nil && n != nil {
operations = append(operations, &apigateway.PatchOperation{
Op: aws.String("replace"),
Path: aws.String("/throttle/rateLimit"),
Value: aws.String(strconv.Itoa(d["rate_limit"].(int))),
})
operations = append(operations, &apigateway.PatchOperation{
Op: aws.String("replace"),
Path: aws.String("/throttle/burstLimit"),
Value: aws.String(strconv.Itoa(d["burst_limit"].(int))),
})
}
// Handle Additions
if o == nil && n != nil {
operations = append(operations, &apigateway.PatchOperation{
Op: aws.String("add"),
Path: aws.String("/throttle/rateLimit"),
Value: aws.String(strconv.Itoa(d["rate_limit"].(int))),
})
operations = append(operations, &apigateway.PatchOperation{
Op: aws.String("add"),
Path: aws.String("/throttle/burstLimit"),
Value: aws.String(strconv.Itoa(d["burst_limit"].(int))),
})
}
}
}
if d.HasChange("quota_settings") {
o, n := d.GetChange("quota_settings")
os := o.(*schema.Set)
ns := n.(*schema.Set)
diff := ns.Difference(os).List()
// Handle Removal
if len(diff) == 0 {
operations = append(operations, &apigateway.PatchOperation{
Op: aws.String("remove"),
Path: aws.String("/quota"),
})
}
if len(diff) > 0 {
d := diff[0].(map[string]interface{})
if errors := validateApiGatewayUsagePlanQuotaSettings(d); len(errors) > 0 {
return fmt.Errorf("Error validating the quota settings: %v", errors)
}
// Handle Replaces
if o != nil && n != nil {
operations = append(operations, &apigateway.PatchOperation{
Op: aws.String("replace"),
Path: aws.String("/quota/limit"),
Value: aws.String(strconv.Itoa(d["limit"].(int))),
})
operations = append(operations, &apigateway.PatchOperation{
Op: aws.String("replace"),
Path: aws.String("/quota/offset"),
Value: aws.String(strconv.Itoa(d["offset"].(int))),
})
operations = append(operations, &apigateway.PatchOperation{
Op: aws.String("replace"),
Path: aws.String("/quota/period"),
Value: aws.String(d["period"].(string)),
})
}
// Handle Additions
if o == nil && n != nil {
operations = append(operations, &apigateway.PatchOperation{
Op: aws.String("add"),
Path: aws.String("/quota/limit"),
Value: aws.String(strconv.Itoa(d["limit"].(int))),
})
operations = append(operations, &apigateway.PatchOperation{
Op: aws.String("add"),
Path: aws.String("/quota/offset"),
Value: aws.String(strconv.Itoa(d["offset"].(int))),
})
operations = append(operations, &apigateway.PatchOperation{
Op: aws.String("add"),
Path: aws.String("/quota/period"),
Value: aws.String(d["period"].(string)),
})
}
}
}
params := &apigateway.UpdateUsagePlanInput{
UsagePlanId: aws.String(d.Id()),
PatchOperations: operations,
}
_, err := conn.UpdateUsagePlan(params)
if err != nil {
return fmt.Errorf("Error updating API Gateway Usage Plan: %s", err)
}
return resourceAwsApiGatewayUsagePlanRead(d, meta)
}
func resourceAwsApiGatewayUsagePlanDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).apigateway
// Removing existing api stages associated
if apistages, ok := d.GetOk("api_stages"); ok {
log.Printf("[DEBUG] Deleting API Stages associated with Usage Plan: %s", d.Id())
stages := apistages.([]interface{})
operations := []*apigateway.PatchOperation{}
for _, v := range stages {
sv := v.(map[string]interface{})
operations = append(operations, &apigateway.PatchOperation{
Op: aws.String("remove"),
Path: aws.String("/apiStages"),
Value: aws.String(fmt.Sprintf("%s:%s", sv["api_id"].(string), sv["stage"].(string))),
})
}
_, err := conn.UpdateUsagePlan(&apigateway.UpdateUsagePlanInput{
UsagePlanId: aws.String(d.Id()),
PatchOperations: operations,
})
if err != nil {
return fmt.Errorf("Error removing API Stages associated with Usage Plan: %s", err)
}
}
log.Printf("[DEBUG] Deleting API Gateway Usage Plan: %s", d.Id())
return resource.Retry(5*time.Minute, func() *resource.RetryError {
_, err := conn.DeleteUsagePlan(&apigateway.DeleteUsagePlanInput{
UsagePlanId: aws.String(d.Id()),
})
if err == nil {
return nil
}
return resource.NonRetryableError(err)
})
}

View File

@ -0,0 +1,557 @@
package aws
import (
"fmt"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/apigateway"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccAWSAPIGatewayUsagePlan_basic(t *testing.T) {
var conf apigateway.UsagePlan
name := acctest.RandString(10)
updatedName := acctest.RandString(10)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSAPIGatewayUsagePlanDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSApiGatewayUsagePlanBasicConfig(name),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayUsagePlanExists("aws_api_gateway_usage_plan.main", &conf),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "name", name),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "description", ""),
),
},
{
Config: testAccAWSApiGatewayUsagePlanBasicUpdatedConfig(updatedName),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "name", updatedName),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "description", ""),
),
},
{
Config: testAccAWSApiGatewayUsagePlanBasicConfig(name),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayUsagePlanExists("aws_api_gateway_usage_plan.main", &conf),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "name", name),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "description", ""),
),
},
},
})
}
func TestAccAWSAPIGatewayUsagePlan_description(t *testing.T) {
var conf apigateway.UsagePlan
name := acctest.RandString(10)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSAPIGatewayUsagePlanDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSApiGatewayUsagePlanBasicConfig(name),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayUsagePlanExists("aws_api_gateway_usage_plan.main", &conf),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "description", ""),
),
},
{
Config: testAccAWSApiGatewayUsagePlanDescriptionConfig(name),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayUsagePlanExists("aws_api_gateway_usage_plan.main", &conf),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "description", "This is a description"),
),
},
{
Config: testAccAWSApiGatewayUsagePlanDescriptionUpdatedConfig(name),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "description", "This is a new description"),
),
},
{
Config: testAccAWSApiGatewayUsagePlanDescriptionConfig(name),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayUsagePlanExists("aws_api_gateway_usage_plan.main", &conf),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "description", "This is a description"),
),
},
{
Config: testAccAWSApiGatewayUsagePlanBasicConfig(name),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayUsagePlanExists("aws_api_gateway_usage_plan.main", &conf),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "description", ""),
),
},
},
})
}
func TestAccAWSAPIGatewayUsagePlan_productCode(t *testing.T) {
var conf apigateway.UsagePlan
name := acctest.RandString(10)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSAPIGatewayUsagePlanDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSApiGatewayUsagePlanBasicConfig(name),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayUsagePlanExists("aws_api_gateway_usage_plan.main", &conf),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "product_code", ""),
),
},
{
Config: testAccAWSApiGatewayUsagePlanProductCodeConfig(name),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayUsagePlanExists("aws_api_gateway_usage_plan.main", &conf),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "product_code", "MYCODE"),
),
},
{
Config: testAccAWSApiGatewayUsagePlanProductCodeUpdatedConfig(name),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "product_code", "MYCODE2"),
),
},
{
Config: testAccAWSApiGatewayUsagePlanProductCodeConfig(name),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayUsagePlanExists("aws_api_gateway_usage_plan.main", &conf),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "product_code", "MYCODE"),
),
},
{
Config: testAccAWSApiGatewayUsagePlanBasicConfig(name),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayUsagePlanExists("aws_api_gateway_usage_plan.main", &conf),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "product_code", ""),
),
},
},
})
}
func TestAccAWSAPIGatewayUsagePlan_throttling(t *testing.T) {
var conf apigateway.UsagePlan
name := acctest.RandString(10)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSAPIGatewayUsagePlanDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSApiGatewayUsagePlanBasicConfig(name),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayUsagePlanExists("aws_api_gateway_usage_plan.main", &conf),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "name", name),
resource.TestCheckNoResourceAttr("aws_api_gateway_usage_plan.main", "throttle_settings"),
),
},
{
Config: testAccAWSApiGatewayUsagePlanThrottlingConfig(name),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayUsagePlanExists("aws_api_gateway_usage_plan.main", &conf),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "name", name),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "throttle_settings.4173790118.burst_limit", "2"),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "throttle_settings.4173790118.rate_limit", "5"),
),
},
{
Config: testAccAWSApiGatewayUsagePlanThrottlingModifiedConfig(name),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayUsagePlanExists("aws_api_gateway_usage_plan.main", &conf),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "name", name),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "throttle_settings.1779463053.burst_limit", "3"),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "throttle_settings.1779463053.rate_limit", "6"),
),
},
{
Config: testAccAWSApiGatewayUsagePlanBasicConfig(name),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayUsagePlanExists("aws_api_gateway_usage_plan.main", &conf),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "name", name),
resource.TestCheckNoResourceAttr("aws_api_gateway_usage_plan.main", "throttle_settings"),
),
},
},
})
}
func TestAccAWSAPIGatewayUsagePlan_quota(t *testing.T) {
var conf apigateway.UsagePlan
name := acctest.RandString(10)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSAPIGatewayUsagePlanDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSApiGatewayUsagePlanBasicConfig(name),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayUsagePlanExists("aws_api_gateway_usage_plan.main", &conf),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "name", name),
resource.TestCheckNoResourceAttr("aws_api_gateway_usage_plan.main", "quota_settings"),
),
},
{
Config: testAccAWSApiGatewayUsagePlanQuotaConfig(name),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayUsagePlanExists("aws_api_gateway_usage_plan.main", &conf),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "name", name),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "quota_settings.1956747625.limit", "100"),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "quota_settings.1956747625.offset", "6"),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "quota_settings.1956747625.period", "WEEK"),
),
},
{
Config: testAccAWSApiGatewayUsagePlanQuotaModifiedConfig(name),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayUsagePlanExists("aws_api_gateway_usage_plan.main", &conf),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "name", name),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "quota_settings.3909168194.limit", "200"),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "quota_settings.3909168194.offset", "20"),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "quota_settings.3909168194.period", "MONTH"),
),
},
{
Config: testAccAWSApiGatewayUsagePlanBasicConfig(name),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayUsagePlanExists("aws_api_gateway_usage_plan.main", &conf),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "name", name),
resource.TestCheckNoResourceAttr("aws_api_gateway_usage_plan.main", "quota_settings"),
),
},
},
})
}
func TestAccAWSAPIGatewayUsagePlan_apiStages(t *testing.T) {
var conf apigateway.UsagePlan
name := acctest.RandString(10)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSAPIGatewayUsagePlanDestroy,
Steps: []resource.TestStep{
// Create UsagePlan WITH Stages as the API calls are different
// when creating or updating.
{
Config: testAccAWSApiGatewayUsagePlanApiStagesConfig(name),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayUsagePlanExists("aws_api_gateway_usage_plan.main", &conf),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "name", name),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "api_stages.0.stage", "test"),
),
},
// Handle api stages removal
{
Config: testAccAWSApiGatewayUsagePlanBasicConfig(name),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayUsagePlanExists("aws_api_gateway_usage_plan.main", &conf),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "name", name),
resource.TestCheckNoResourceAttr("aws_api_gateway_usage_plan.main", "api_stages"),
),
},
// Handle api stages additions
{
Config: testAccAWSApiGatewayUsagePlanApiStagesConfig(name),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayUsagePlanExists("aws_api_gateway_usage_plan.main", &conf),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "name", name),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "api_stages.0.stage", "test"),
),
},
// Handle api stages updates
{
Config: testAccAWSApiGatewayUsagePlanApiStagesModifiedConfig(name),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayUsagePlanExists("aws_api_gateway_usage_plan.main", &conf),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "name", name),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "api_stages.0.stage", "foo"),
),
},
{
Config: testAccAWSApiGatewayUsagePlanBasicConfig(name),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayUsagePlanExists("aws_api_gateway_usage_plan.main", &conf),
resource.TestCheckResourceAttr("aws_api_gateway_usage_plan.main", "name", name),
resource.TestCheckNoResourceAttr("aws_api_gateway_usage_plan.main", "api_stages"),
),
},
},
})
}
func testAccCheckAWSAPIGatewayUsagePlanExists(n string, res *apigateway.UsagePlan) 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 Usage Plan ID is set")
}
conn := testAccProvider.Meta().(*AWSClient).apigateway
req := &apigateway.GetUsagePlanInput{
UsagePlanId: aws.String(rs.Primary.ID),
}
up, err := conn.GetUsagePlan(req)
if err != nil {
return err
}
if *up.Id != rs.Primary.ID {
return fmt.Errorf("APIGateway Usage Plan not found")
}
*res = *up
return nil
}
}
func testAccCheckAWSAPIGatewayUsagePlanDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).apigateway
for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_api_gateway_usage_plan" {
continue
}
req := &apigateway.GetUsagePlanInput{
UsagePlanId: aws.String(s.RootModule().Resources["aws_api_gateway_rest_api.test"].Primary.ID),
}
describe, err := conn.GetUsagePlan(req)
if err == nil {
if describe.Id != nil && *describe.Id == rs.Primary.ID {
return fmt.Errorf("API Gateway Usage Plan still exists")
}
}
aws2err, ok := err.(awserr.Error)
if !ok {
return err
}
if aws2err.Code() != "NotFoundException" {
return err
}
return nil
}
return nil
}
const testAccAWSAPIGatewayUsagePlanConfig = `
resource "aws_api_gateway_rest_api" "test" {
name = "test"
}
resource "aws_api_gateway_resource" "test" {
rest_api_id = "${aws_api_gateway_rest_api.test.id}"
parent_id = "${aws_api_gateway_rest_api.test.root_resource_id}"
path_part = "test"
}
resource "aws_api_gateway_method" "test" {
rest_api_id = "${aws_api_gateway_rest_api.test.id}"
resource_id = "${aws_api_gateway_resource.test.id}"
http_method = "GET"
authorization = "NONE"
}
resource "aws_api_gateway_method_response" "error" {
rest_api_id = "${aws_api_gateway_rest_api.test.id}"
resource_id = "${aws_api_gateway_resource.test.id}"
http_method = "${aws_api_gateway_method.test.http_method}"
status_code = "400"
}
resource "aws_api_gateway_integration" "test" {
rest_api_id = "${aws_api_gateway_rest_api.test.id}"
resource_id = "${aws_api_gateway_resource.test.id}"
http_method = "${aws_api_gateway_method.test.http_method}"
type = "HTTP"
uri = "https://www.google.de"
integration_http_method = "GET"
}
resource "aws_api_gateway_integration_response" "test" {
rest_api_id = "${aws_api_gateway_rest_api.test.id}"
resource_id = "${aws_api_gateway_resource.test.id}"
http_method = "${aws_api_gateway_integration.test.http_method}"
status_code = "${aws_api_gateway_method_response.error.status_code}"
}
resource "aws_api_gateway_deployment" "test" {
depends_on = ["aws_api_gateway_integration.test"]
rest_api_id = "${aws_api_gateway_rest_api.test.id}"
stage_name = "test"
description = "This is a test"
variables = {
"a" = "2"
}
}
resource "aws_api_gateway_deployment" "foo" {
depends_on = ["aws_api_gateway_integration.test"]
rest_api_id = "${aws_api_gateway_rest_api.test.id}"
stage_name = "foo"
description = "This is a prod stage"
}
`
func testAccAWSApiGatewayUsagePlanBasicConfig(rName string) string {
return fmt.Sprintf(testAccAWSAPIGatewayUsagePlanConfig+`
resource "aws_api_gateway_usage_plan" "main" {
name = "%s"
}
`, rName)
}
func testAccAWSApiGatewayUsagePlanDescriptionConfig(rName string) string {
return fmt.Sprintf(testAccAWSAPIGatewayUsagePlanConfig+`
resource "aws_api_gateway_usage_plan" "main" {
name = "%s"
description = "This is a description"
}
`, rName)
}
func testAccAWSApiGatewayUsagePlanDescriptionUpdatedConfig(rName string) string {
return fmt.Sprintf(testAccAWSAPIGatewayUsagePlanConfig+`
resource "aws_api_gateway_usage_plan" "main" {
name = "%s"
description = "This is a new description"
}
`, rName)
}
func testAccAWSApiGatewayUsagePlanProductCodeConfig(rName string) string {
return fmt.Sprintf(testAccAWSAPIGatewayUsagePlanConfig+`
resource "aws_api_gateway_usage_plan" "main" {
name = "%s"
product_code = "MYCODE"
}
`, rName)
}
func testAccAWSApiGatewayUsagePlanProductCodeUpdatedConfig(rName string) string {
return fmt.Sprintf(testAccAWSAPIGatewayUsagePlanConfig+`
resource "aws_api_gateway_usage_plan" "main" {
name = "%s"
product_code = "MYCODE2"
}
`, rName)
}
func testAccAWSApiGatewayUsagePlanBasicUpdatedConfig(rName string) string {
return fmt.Sprintf(testAccAWSAPIGatewayUsagePlanConfig+`
resource "aws_api_gateway_usage_plan" "main" {
name = "%s"
}
`, rName)
}
func testAccAWSApiGatewayUsagePlanThrottlingConfig(rName string) string {
return fmt.Sprintf(testAccAWSAPIGatewayUsagePlanConfig+`
resource "aws_api_gateway_usage_plan" "main" {
name = "%s"
throttle_settings {
burst_limit = 2
rate_limit = 5
}
}
`, rName)
}
func testAccAWSApiGatewayUsagePlanThrottlingModifiedConfig(rName string) string {
return fmt.Sprintf(testAccAWSAPIGatewayUsagePlanConfig+`
resource "aws_api_gateway_usage_plan" "main" {
name = "%s"
throttle_settings {
burst_limit = 3
rate_limit = 6
}
}
`, rName)
}
func testAccAWSApiGatewayUsagePlanQuotaConfig(rName string) string {
return fmt.Sprintf(testAccAWSAPIGatewayUsagePlanConfig+`
resource "aws_api_gateway_usage_plan" "main" {
name = "%s"
quota_settings {
limit = 100
offset = 6
period = "WEEK"
}
}
`, rName)
}
func testAccAWSApiGatewayUsagePlanQuotaModifiedConfig(rName string) string {
return fmt.Sprintf(testAccAWSAPIGatewayUsagePlanConfig+`
resource "aws_api_gateway_usage_plan" "main" {
name = "%s"
quota_settings {
limit = 200
offset = 20
period = "MONTH"
}
}
`, rName)
}
func testAccAWSApiGatewayUsagePlanApiStagesConfig(rName string) string {
return fmt.Sprintf(testAccAWSAPIGatewayUsagePlanConfig+`
resource "aws_api_gateway_usage_plan" "main" {
name = "%s"
api_stages {
api_id = "${aws_api_gateway_rest_api.test.id}"
stage = "${aws_api_gateway_deployment.test.stage_name}"
}
}
`, rName)
}
func testAccAWSApiGatewayUsagePlanApiStagesModifiedConfig(rName string) string {
return fmt.Sprintf(testAccAWSAPIGatewayUsagePlanConfig+`
resource "aws_api_gateway_usage_plan" "main" {
name = "%s"
api_stages {
api_id = "${aws_api_gateway_rest_api.test.id}"
stage = "${aws_api_gateway_deployment.foo.stage_name}"
}
}
`, rName)
}

View File

@ -1849,3 +1849,63 @@ func flattenInspectorTags(cfTags []*cloudformation.Tag) map[string]string {
}
return tags
}
func flattenApiGatewayUsageApiStages(s []*apigateway.ApiStage) []map[string]interface{} {
stages := make([]map[string]interface{}, 0)
for _, bd := range s {
if bd.ApiId != nil && bd.Stage != nil {
stage := make(map[string]interface{})
stage["api_id"] = *bd.ApiId
stage["stage"] = *bd.Stage
stages = append(stages, stage)
}
}
if len(stages) > 0 {
return stages
}
return nil
}
func flattenApiGatewayUsagePlanThrottling(s *apigateway.ThrottleSettings) []map[string]interface{} {
settings := make(map[string]interface{}, 0)
if s == nil {
return nil
}
if s.BurstLimit != nil {
settings["burst_limit"] = *s.BurstLimit
}
if s.RateLimit != nil {
settings["rate_limit"] = *s.RateLimit
}
return []map[string]interface{}{settings}
}
func flattenApiGatewayUsagePlanQuota(s *apigateway.QuotaSettings) []map[string]interface{} {
settings := make(map[string]interface{}, 0)
if s == nil {
return nil
}
if s.Limit != nil {
settings["limit"] = *s.Limit
}
if s.Offset != nil {
settings["offset"] = *s.Offset
}
if s.Period != nil {
settings["period"] = *s.Period
}
return []map[string]interface{}{settings}
}

View File

@ -7,6 +7,7 @@ import (
"strings"
"time"
"github.com/aws/aws-sdk-go/service/apigateway"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/hashicorp/terraform/helper/schema"
)
@ -926,7 +927,7 @@ func validateConfigExecutionFrequency(v interface{}, k string) (ws []string, err
}
}
errors = append(errors, fmt.Errorf(
"%q contains an invalid freqency %q. Valid frequencies are %q.",
"%q contains an invalid frequency %q. Valid frequencies are %q.",
k, frequency, validFrequencies))
return
}
@ -974,3 +975,40 @@ func validateIamRolePolicyNamePrefix(v interface{}, k string) (ws []string, erro
}
return
}
func validateApiGatewayUsagePlanQuotaSettingsPeriod(v interface{}, k string) (ws []string, errors []error) {
validPeriods := []string{
apigateway.QuotaPeriodTypeDay,
apigateway.QuotaPeriodTypeWeek,
apigateway.QuotaPeriodTypeMonth,
}
period := v.(string)
for _, f := range validPeriods {
if period == f {
return
}
}
errors = append(errors, fmt.Errorf(
"%q contains an invalid period %q. Valid period are %q.",
k, period, validPeriods))
return
}
func validateApiGatewayUsagePlanQuotaSettings(v map[string]interface{}) (errors []error) {
period := v["period"].(string)
offset := v["offset"].(int)
if period == apigateway.QuotaPeriodTypeDay && offset != 0 {
errors = append(errors, fmt.Errorf("Usage Plan quota offset must be zero in the DAY period"))
}
if period == apigateway.QuotaPeriodTypeWeek && (offset < 0 || offset > 6) {
errors = append(errors, fmt.Errorf("Usage Plan quota offset must be between 0 and 6 inclusive in the WEEK period"))
}
if period == apigateway.QuotaPeriodTypeMonth && (offset < 0 || offset > 27) {
errors = append(errors, fmt.Errorf("Usage Plan quota offset must be between 0 and 27 inclusive in the MONTH period"))
}
return
}

View File

@ -1629,3 +1629,106 @@ func TestValidateIamRoleProfileNamePrefix(t *testing.T) {
}
}
}
func TestValidateApiGatewayUsagePlanQuotaSettingsPeriod(t *testing.T) {
validEntries := []string{
"DAY",
"WEEK",
"MONTH",
}
invalidEntries := []string{
"fooBAR",
"foobar45Baz",
"foobar45Baz@!",
}
for _, v := range validEntries {
_, errors := validateApiGatewayUsagePlanQuotaSettingsPeriod(v, "name")
if len(errors) != 0 {
t.Fatalf("%q should be a valid API Gateway Quota Settings Period: %v", v, errors)
}
}
for _, v := range invalidEntries {
_, errors := validateApiGatewayUsagePlanQuotaSettingsPeriod(v, "name")
if len(errors) == 0 {
t.Fatalf("%q should not be a API Gateway Quota Settings Period", v)
}
}
}
func TestValidateApiGatewayUsagePlanQuotaSettings(t *testing.T) {
cases := []struct {
Offset int
Period string
ErrCount int
}{
{
Offset: 0,
Period: "DAY",
ErrCount: 0,
},
{
Offset: -1,
Period: "DAY",
ErrCount: 1,
},
{
Offset: 1,
Period: "DAY",
ErrCount: 1,
},
{
Offset: 0,
Period: "WEEK",
ErrCount: 0,
},
{
Offset: 6,
Period: "WEEK",
ErrCount: 0,
},
{
Offset: -1,
Period: "WEEK",
ErrCount: 1,
},
{
Offset: 7,
Period: "WEEK",
ErrCount: 1,
},
{
Offset: 0,
Period: "MONTH",
ErrCount: 0,
},
{
Offset: 27,
Period: "MONTH",
ErrCount: 0,
},
{
Offset: -1,
Period: "MONTH",
ErrCount: 1,
},
{
Offset: 28,
Period: "MONTH",
ErrCount: 1,
},
}
for _, tc := range cases {
m := make(map[string]interface{})
m["offset"] = tc.Offset
m["period"] = tc.Period
errors := validateApiGatewayUsagePlanQuotaSettings(m)
if len(errors) != tc.ErrCount {
t.Fatalf("API Gateway Usage Plan Quota Settings validation failed: %v", errors)
}
}
}

View File

@ -0,0 +1,99 @@
---
layout: "aws"
page_title: "AWS: aws_api_gateway_usage_plan"
sidebar_current: "docs-aws-resource-api-gateway-usage-plan"
description: |-
Provides an API Gateway Usage Plan.
---
# aws\_api\_usage\_plan
Provides an API Gateway Usage Plan.
## Example Usage
```
resource "aws_api_gateway_rest_api" "myapi" {
name = "MyDemoAPI"
}
...
resource "aws_api_gateway_deployment" "dev" {
rest_api_id = "${aws_api_gateway_rest_api.myapi.id}"
stage_name = "dev"
}
resource "aws_api_gateway_deployment" "prod" {
rest_api_id = "${aws_api_gateway_rest_api.myapi.id}"
stage_name = "prod"
}
resource "aws_api_gateway_usage_plan" "MyUsagePlan" {
name = "my-usage-plan"
description = "my description"
product_code = "MYCODE"
api_stages {
api_id = "${aws_api_gateway_rest_api.myapi.id}"
stage = "${aws_api_gateway_deployment.dev.stage_name}"
}
api_stages {
api_id = "${aws_api_gateway_rest_api.myapi.id}"
stage = "${aws_api_gateway_deployment.prod.stage_name}"
}
quota_settings {
limit = 20
offset = 2
period = "WEEK"
}
throttle_settings {
burst_limit = 5
rate_limit = 10
}
}
```
## Argument Reference
The API Gateway Usage Plan argument layout is a structure composed of several sub-resources - these resources are laid out below.
### Top-Level Arguments
* `name` - (Required) The name of the usage plan.
* `description` - (Required) The description of a usage plan.
* `api_stages` - (Optional) The associated [API stages](#api-stages-arguments) of the usage plan.
* `quota_settings` - (Optional) The [quota settings](#quota-settings-arguments) of the usage plan.
* `throttle_settings` - (Optional) The [throttling limits](#throttling-settings-arguments) of the usage plan.
* `product_code` - (Optional) The AWS Markeplace product identifier to associate with the usage plan as a SaaS product on AWS Marketplace.
#### Api Stages arguments
* `api_id` (Optional) - API Id of the associated API stage in a usage plan.
* `stage` (Optional) - API stage name of the associated API stage in a usage plan.
#### Quota Settings Arguments
* `limit` (Optional) - The maximum number of requests that can be made in a given time period.
* `offset` (Optional) - The number of requests subtracted from the given limit in the initial time period.
* `period` (Optional) - The time period in which the limit applies. Valid values are "DAY", "WEEK" or "MONTH".
#### Throttling Settings Arguments
* `burst_limit` (Optional) - The API request burst limit, the maximum rate limit over a time ranging from one to a few seconds, depending upon whether the underlying token bucket is at its full capacity.
* `rate_limit` (Optional) - The API request steady-state rate limit.
## Attributes Reference
The following attributes are exported:
* `id` - The ID of the API resource
* `name` - The name of the usage plan.
* `description` - The description of a usage plan.
* `api_stages` - The associated API stages of the usage plan.
* `quota_settings` - The quota of the usage plan.
* `throttle_settings` - The throttling limits of the usage plan.
* `product_code` - The AWS Markeplace product identifier to associate with the usage plan as a SaaS product on AWS Marketplace.

View File

@ -179,6 +179,9 @@
<li<%= sidebar_current("docs-aws-resource-api-gateway-rest-api") %>>
<a href="/docs/providers/aws/r/api_gateway_rest_api.html">aws_api_gateway_rest_api</a>
</li>
<li<%= sidebar_current("docs-aws-resource-api-gateway-usage-plan") %>>
<a href="/docs/providers/aws/r/api_gateway_usage_plan.html">aws_api_gateway_usage_plan</a>
</li>
</ul>
</li>