mirror of
https://github.com/opentofu/opentofu.git
synced 2025-01-08 15:13:56 -06:00
323f646b42
* Added api_gateway_usage_plan * Updated documentation * Fixed AWS usage plan review points
500 lines
13 KiB
Go
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)
|
|
})
|
|
}
|