github.com/nathanielks/terraform@v0.6.1-0.20170509030759-13e1a62319dc/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  }