terraform/builtin/providers/aws/resource_aws_api_gateway_us...

500 lines
13 KiB
Go

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)
})
}