github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/aws/resource_aws_api_gateway_usage_plan.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "log" 6 "strconv" 7 "time" 8 9 "errors" 10 "github.com/aws/aws-sdk-go/aws" 11 "github.com/aws/aws-sdk-go/aws/awserr" 12 "github.com/aws/aws-sdk-go/service/apigateway" 13 "github.com/hashicorp/terraform/helper/resource" 14 "github.com/hashicorp/terraform/helper/schema" 15 ) 16 17 func resourceAwsApiGatewayUsagePlan() *schema.Resource { 18 return &schema.Resource{ 19 Create: resourceAwsApiGatewayUsagePlanCreate, 20 Read: resourceAwsApiGatewayUsagePlanRead, 21 Update: resourceAwsApiGatewayUsagePlanUpdate, 22 Delete: resourceAwsApiGatewayUsagePlanDelete, 23 Importer: &schema.ResourceImporter{ 24 State: schema.ImportStatePassthrough, 25 }, 26 27 Schema: map[string]*schema.Schema{ 28 "name": { 29 Type: schema.TypeString, 30 Required: true, // Required since not addable nor removable afterwards 31 }, 32 33 "description": { 34 Type: schema.TypeString, 35 Optional: true, 36 }, 37 38 "api_stages": { 39 Type: schema.TypeList, 40 Optional: true, 41 Elem: &schema.Resource{ 42 Schema: map[string]*schema.Schema{ 43 "api_id": { 44 Type: schema.TypeString, 45 Required: true, 46 }, 47 48 "stage": { 49 Type: schema.TypeString, 50 Required: true, 51 }, 52 }, 53 }, 54 }, 55 56 "quota_settings": { 57 Type: schema.TypeSet, 58 MaxItems: 1, 59 Optional: true, 60 Elem: &schema.Resource{ 61 Schema: map[string]*schema.Schema{ 62 "limit": { 63 Type: schema.TypeInt, 64 Required: true, // Required as not removable singularly 65 }, 66 67 "offset": { 68 Type: schema.TypeInt, 69 Default: 0, 70 Optional: true, 71 }, 72 73 "period": { 74 Type: schema.TypeString, 75 Required: true, // Required as not removable 76 ValidateFunc: validateApiGatewayUsagePlanQuotaSettingsPeriod, 77 }, 78 }, 79 }, 80 }, 81 82 "throttle_settings": { 83 Type: schema.TypeSet, 84 MaxItems: 1, 85 Optional: true, 86 Elem: &schema.Resource{ 87 Schema: map[string]*schema.Schema{ 88 "burst_limit": { 89 Type: schema.TypeInt, 90 Default: 0, 91 Optional: true, 92 }, 93 94 "rate_limit": { 95 Type: schema.TypeInt, 96 Default: 0, 97 Optional: true, 98 }, 99 }, 100 }, 101 }, 102 103 "product_code": { 104 Type: schema.TypeString, 105 Optional: true, 106 }, 107 }, 108 } 109 } 110 111 func resourceAwsApiGatewayUsagePlanCreate(d *schema.ResourceData, meta interface{}) error { 112 conn := meta.(*AWSClient).apigateway 113 log.Print("[DEBUG] Creating API Gateway Usage Plan") 114 115 params := &apigateway.CreateUsagePlanInput{ 116 Name: aws.String(d.Get("name").(string)), 117 } 118 119 if v, ok := d.GetOk("description"); ok { 120 params.Description = aws.String(v.(string)) 121 } 122 123 if s, ok := d.GetOk("api_stages"); ok { 124 stages := s.([]interface{}) 125 as := make([]*apigateway.ApiStage, 0) 126 127 for _, v := range stages { 128 sv := v.(map[string]interface{}) 129 stage := &apigateway.ApiStage{} 130 131 if v, ok := sv["api_id"].(string); ok && v != "" { 132 stage.ApiId = aws.String(v) 133 } 134 135 if v, ok := sv["stage"].(string); ok && v != "" { 136 stage.Stage = aws.String(v) 137 } 138 139 as = append(as, stage) 140 } 141 142 if len(as) > 0 { 143 params.ApiStages = as 144 } 145 } 146 147 if v, ok := d.GetOk("quota_settings"); ok { 148 settings := v.(*schema.Set).List() 149 q, ok := settings[0].(map[string]interface{}) 150 151 if errors := validateApiGatewayUsagePlanQuotaSettings(q); len(errors) > 0 { 152 return fmt.Errorf("Error validating the quota settings: %v", errors) 153 } 154 155 if !ok { 156 return errors.New("At least one field is expected inside quota_settings") 157 } 158 159 qs := &apigateway.QuotaSettings{} 160 161 if sv, ok := q["limit"].(int); ok { 162 qs.Limit = aws.Int64(int64(sv)) 163 } 164 165 if sv, ok := q["offset"].(int); ok { 166 qs.Offset = aws.Int64(int64(sv)) 167 } 168 169 if sv, ok := q["period"].(string); ok && sv != "" { 170 qs.Period = aws.String(sv) 171 } 172 173 params.Quota = qs 174 } 175 176 if v, ok := d.GetOk("throttle_settings"); ok { 177 settings := v.(*schema.Set).List() 178 q, ok := settings[0].(map[string]interface{}) 179 180 if !ok { 181 return errors.New("At least one field is expected inside throttle_settings") 182 } 183 184 ts := &apigateway.ThrottleSettings{} 185 186 if sv, ok := q["burst_limit"].(int); ok { 187 ts.BurstLimit = aws.Int64(int64(sv)) 188 } 189 190 if sv, ok := q["rate_limit"].(float64); ok { 191 ts.RateLimit = aws.Float64(float64(sv)) 192 } 193 194 params.Throttle = ts 195 } 196 197 up, err := conn.CreateUsagePlan(params) 198 if err != nil { 199 return fmt.Errorf("Error creating API Gateway Usage Plan: %s", err) 200 } 201 202 d.SetId(*up.Id) 203 204 // Handle case of adding the product code since not addable when 205 // creating the Usage Plan initially. 206 if v, ok := d.GetOk("product_code"); ok { 207 updateParameters := &apigateway.UpdateUsagePlanInput{ 208 UsagePlanId: aws.String(d.Id()), 209 PatchOperations: []*apigateway.PatchOperation{ 210 { 211 Op: aws.String("add"), 212 Path: aws.String("/productCode"), 213 Value: aws.String(v.(string)), 214 }, 215 }, 216 } 217 218 up, err = conn.UpdateUsagePlan(updateParameters) 219 if err != nil { 220 return fmt.Errorf("Error creating the API Gateway Usage Plan product code: %s", err) 221 } 222 } 223 224 return resourceAwsApiGatewayUsagePlanRead(d, meta) 225 } 226 227 func resourceAwsApiGatewayUsagePlanRead(d *schema.ResourceData, meta interface{}) error { 228 conn := meta.(*AWSClient).apigateway 229 log.Printf("[DEBUG] Reading API Gateway Usage Plan: %s", d.Id()) 230 231 up, err := conn.GetUsagePlan(&apigateway.GetUsagePlanInput{ 232 UsagePlanId: aws.String(d.Id()), 233 }) 234 if err != nil { 235 if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NotFoundException" { 236 d.SetId("") 237 return nil 238 } 239 return err 240 } 241 242 d.Set("name", up.Name) 243 d.Set("description", up.Description) 244 d.Set("product_code", up.ProductCode) 245 246 if up.ApiStages != nil { 247 if err := d.Set("api_stages", flattenApiGatewayUsageApiStages(up.ApiStages)); err != nil { 248 return fmt.Errorf("[DEBUG] Error setting api_stages error: %#v", err) 249 } 250 } 251 252 if up.Throttle != nil { 253 if err := d.Set("throttle_settings", flattenApiGatewayUsagePlanThrottling(up.Throttle)); err != nil { 254 return fmt.Errorf("[DEBUG] Error setting throttle_settings error: %#v", err) 255 } 256 } 257 258 if up.Quota != nil { 259 if err := d.Set("quota_settings", flattenApiGatewayUsagePlanQuota(up.Quota)); err != nil { 260 return fmt.Errorf("[DEBUG] Error setting quota_settings error: %#v", err) 261 } 262 } 263 264 return nil 265 } 266 267 func resourceAwsApiGatewayUsagePlanUpdate(d *schema.ResourceData, meta interface{}) error { 268 conn := meta.(*AWSClient).apigateway 269 log.Print("[DEBUG] Updating API Gateway Usage Plan") 270 271 operations := make([]*apigateway.PatchOperation, 0) 272 273 if d.HasChange("name") { 274 operations = append(operations, &apigateway.PatchOperation{ 275 Op: aws.String("replace"), 276 Path: aws.String("/name"), 277 Value: aws.String(d.Get("name").(string)), 278 }) 279 } 280 281 if d.HasChange("description") { 282 operations = append(operations, &apigateway.PatchOperation{ 283 Op: aws.String("replace"), 284 Path: aws.String("/description"), 285 Value: aws.String(d.Get("description").(string)), 286 }) 287 } 288 289 if d.HasChange("product_code") { 290 v, ok := d.GetOk("product_code") 291 292 if ok { 293 operations = append(operations, &apigateway.PatchOperation{ 294 Op: aws.String("replace"), 295 Path: aws.String("/productCode"), 296 Value: aws.String(v.(string)), 297 }) 298 } else { 299 operations = append(operations, &apigateway.PatchOperation{ 300 Op: aws.String("remove"), 301 Path: aws.String("/productCode"), 302 }) 303 } 304 } 305 306 if d.HasChange("api_stages") { 307 o, n := d.GetChange("api_stages") 308 old := o.([]interface{}) 309 new := n.([]interface{}) 310 311 // Remove every stages associated. Simpler to remove and add new ones, 312 // since there are no replacings. 313 for _, v := range old { 314 m := v.(map[string]interface{}) 315 operations = append(operations, &apigateway.PatchOperation{ 316 Op: aws.String("remove"), 317 Path: aws.String("/apiStages"), 318 Value: aws.String(fmt.Sprintf("%s:%s", m["api_id"].(string), m["stage"].(string))), 319 }) 320 } 321 322 // Handle additions 323 if len(new) > 0 { 324 for _, v := range new { 325 m := v.(map[string]interface{}) 326 operations = append(operations, &apigateway.PatchOperation{ 327 Op: aws.String("add"), 328 Path: aws.String("/apiStages"), 329 Value: aws.String(fmt.Sprintf("%s:%s", m["api_id"].(string), m["stage"].(string))), 330 }) 331 } 332 } 333 } 334 335 if d.HasChange("throttle_settings") { 336 o, n := d.GetChange("throttle_settings") 337 338 os := o.(*schema.Set) 339 ns := n.(*schema.Set) 340 diff := ns.Difference(os).List() 341 342 // Handle Removal 343 if len(diff) == 0 { 344 operations = append(operations, &apigateway.PatchOperation{ 345 Op: aws.String("remove"), 346 Path: aws.String("/throttle"), 347 }) 348 } 349 350 if len(diff) > 0 { 351 d := diff[0].(map[string]interface{}) 352 353 // Handle Replaces 354 if o != nil && n != nil { 355 operations = append(operations, &apigateway.PatchOperation{ 356 Op: aws.String("replace"), 357 Path: aws.String("/throttle/rateLimit"), 358 Value: aws.String(strconv.Itoa(d["rate_limit"].(int))), 359 }) 360 operations = append(operations, &apigateway.PatchOperation{ 361 Op: aws.String("replace"), 362 Path: aws.String("/throttle/burstLimit"), 363 Value: aws.String(strconv.Itoa(d["burst_limit"].(int))), 364 }) 365 } 366 367 // Handle Additions 368 if o == nil && n != nil { 369 operations = append(operations, &apigateway.PatchOperation{ 370 Op: aws.String("add"), 371 Path: aws.String("/throttle/rateLimit"), 372 Value: aws.String(strconv.Itoa(d["rate_limit"].(int))), 373 }) 374 operations = append(operations, &apigateway.PatchOperation{ 375 Op: aws.String("add"), 376 Path: aws.String("/throttle/burstLimit"), 377 Value: aws.String(strconv.Itoa(d["burst_limit"].(int))), 378 }) 379 } 380 } 381 } 382 383 if d.HasChange("quota_settings") { 384 o, n := d.GetChange("quota_settings") 385 386 os := o.(*schema.Set) 387 ns := n.(*schema.Set) 388 diff := ns.Difference(os).List() 389 390 // Handle Removal 391 if len(diff) == 0 { 392 operations = append(operations, &apigateway.PatchOperation{ 393 Op: aws.String("remove"), 394 Path: aws.String("/quota"), 395 }) 396 } 397 398 if len(diff) > 0 { 399 d := diff[0].(map[string]interface{}) 400 401 if errors := validateApiGatewayUsagePlanQuotaSettings(d); len(errors) > 0 { 402 return fmt.Errorf("Error validating the quota settings: %v", errors) 403 } 404 405 // Handle Replaces 406 if o != nil && n != nil { 407 operations = append(operations, &apigateway.PatchOperation{ 408 Op: aws.String("replace"), 409 Path: aws.String("/quota/limit"), 410 Value: aws.String(strconv.Itoa(d["limit"].(int))), 411 }) 412 operations = append(operations, &apigateway.PatchOperation{ 413 Op: aws.String("replace"), 414 Path: aws.String("/quota/offset"), 415 Value: aws.String(strconv.Itoa(d["offset"].(int))), 416 }) 417 operations = append(operations, &apigateway.PatchOperation{ 418 Op: aws.String("replace"), 419 Path: aws.String("/quota/period"), 420 Value: aws.String(d["period"].(string)), 421 }) 422 } 423 424 // Handle Additions 425 if o == nil && n != nil { 426 operations = append(operations, &apigateway.PatchOperation{ 427 Op: aws.String("add"), 428 Path: aws.String("/quota/limit"), 429 Value: aws.String(strconv.Itoa(d["limit"].(int))), 430 }) 431 operations = append(operations, &apigateway.PatchOperation{ 432 Op: aws.String("add"), 433 Path: aws.String("/quota/offset"), 434 Value: aws.String(strconv.Itoa(d["offset"].(int))), 435 }) 436 operations = append(operations, &apigateway.PatchOperation{ 437 Op: aws.String("add"), 438 Path: aws.String("/quota/period"), 439 Value: aws.String(d["period"].(string)), 440 }) 441 } 442 } 443 } 444 445 params := &apigateway.UpdateUsagePlanInput{ 446 UsagePlanId: aws.String(d.Id()), 447 PatchOperations: operations, 448 } 449 450 _, err := conn.UpdateUsagePlan(params) 451 if err != nil { 452 return fmt.Errorf("Error updating API Gateway Usage Plan: %s", err) 453 } 454 455 return resourceAwsApiGatewayUsagePlanRead(d, meta) 456 } 457 458 func resourceAwsApiGatewayUsagePlanDelete(d *schema.ResourceData, meta interface{}) error { 459 conn := meta.(*AWSClient).apigateway 460 461 // Removing existing api stages associated 462 if apistages, ok := d.GetOk("api_stages"); ok { 463 log.Printf("[DEBUG] Deleting API Stages associated with Usage Plan: %s", d.Id()) 464 stages := apistages.([]interface{}) 465 operations := []*apigateway.PatchOperation{} 466 467 for _, v := range stages { 468 sv := v.(map[string]interface{}) 469 470 operations = append(operations, &apigateway.PatchOperation{ 471 Op: aws.String("remove"), 472 Path: aws.String("/apiStages"), 473 Value: aws.String(fmt.Sprintf("%s:%s", sv["api_id"].(string), sv["stage"].(string))), 474 }) 475 } 476 477 _, err := conn.UpdateUsagePlan(&apigateway.UpdateUsagePlanInput{ 478 UsagePlanId: aws.String(d.Id()), 479 PatchOperations: operations, 480 }) 481 if err != nil { 482 return fmt.Errorf("Error removing API Stages associated with Usage Plan: %s", err) 483 } 484 } 485 486 log.Printf("[DEBUG] Deleting API Gateway Usage Plan: %s", d.Id()) 487 488 return resource.Retry(5*time.Minute, func() *resource.RetryError { 489 _, err := conn.DeleteUsagePlan(&apigateway.DeleteUsagePlanInput{ 490 UsagePlanId: aws.String(d.Id()), 491 }) 492 493 if err == nil { 494 return nil 495 } 496 497 return resource.NonRetryableError(err) 498 }) 499 }