forked from hashicorp/terraform-provider-vault
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add
vault_quota_rate_limit
resource (hashicorp#825)
* Add `vault_quota_rate_limit` resource * Fix tab * Fix documentation * Remove unused functionemove unused function * Add validation function * Make `path` `ForceNew`
- Loading branch information
Showing
5 changed files
with
307 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
package vault | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
|
||
"github.com/hashicorp/terraform-plugin-sdk/helper/schema" | ||
"github.com/hashicorp/terraform-plugin-sdk/helper/validation" | ||
"github.com/hashicorp/vault/api" | ||
) | ||
|
||
func quotaRateLimitPath(name string) string { | ||
return "sys/quotas/rate-limit/" + name | ||
} | ||
|
||
func quotaRateLimitResource() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: quotaRateLimitCreate, | ||
Read: quotaRateLimitRead, | ||
Update: quotaRateLimitUpdate, | ||
Delete: quotaRateLimitDelete, | ||
Exists: quotaRateLimitExists, | ||
Importer: &schema.ResourceImporter{ | ||
State: schema.ImportStatePassthrough, | ||
}, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"name": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
Description: "The name of the quota.", | ||
ForceNew: true, | ||
}, | ||
"path": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
ForceNew: true, | ||
Description: "Path of the mount or namespace to apply the quota. A blank path configures a global rate limit quota.", | ||
}, | ||
"rate": { | ||
Type: schema.TypeFloat, | ||
Required: true, | ||
Description: "The maximum number of requests at any given second to be allowed by the quota rule. The rate must be positive.", | ||
ValidateFunc: validation.FloatAtLeast(0.0), | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func quotaRateLimitCreate(d *schema.ResourceData, meta interface{}) error { | ||
client := meta.(*api.Client) | ||
|
||
name := d.Get("name").(string) | ||
path := quotaRateLimitPath(name) | ||
d.SetId(name) | ||
|
||
log.Printf("[DEBUG] Creating Resource Rate Limit Quota %s", name) | ||
|
||
data := map[string]interface{}{} | ||
data["path"] = d.Get("path").(string) | ||
data["rate"] = d.Get("rate").(float64) | ||
|
||
_, err := client.Logical().Write(path, data) | ||
if err != nil { | ||
d.SetId("") | ||
return fmt.Errorf("Error creating Resource Rate Limit Quota %s: %s", name, err) | ||
} | ||
log.Printf("[DEBUG] Created Resource Rate Limit Quota %s", name) | ||
|
||
return quotaRateLimitRead(d, meta) | ||
} | ||
|
||
func quotaRateLimitRead(d *schema.ResourceData, meta interface{}) error { | ||
client := meta.(*api.Client) | ||
|
||
name := d.Id() | ||
path := quotaRateLimitPath(name) | ||
|
||
log.Printf("[DEBUG] Reading Resource Rate Limit Quota %s", name) | ||
resp, err := client.Logical().Read(path) | ||
if err != nil { | ||
return fmt.Errorf("error reading Resource Rate Limit Quota %s: %s", name, err) | ||
} | ||
|
||
if resp == nil { | ||
log.Printf("[WARN] Resource Rate Limit Quota %s not found, removing from state", name) | ||
d.SetId("") | ||
return nil | ||
} | ||
|
||
for _, k := range []string{"path", "rate"} { | ||
v, ok := resp.Data[k] | ||
if ok { | ||
if err := d.Set(k, v); err != nil { | ||
return fmt.Errorf("error setting %s for Resource Rate Limit Quota %s: %q", k, name, err) | ||
} | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func quotaRateLimitUpdate(d *schema.ResourceData, meta interface{}) error { | ||
client := meta.(*api.Client) | ||
|
||
name := d.Id() | ||
path := quotaRateLimitPath(name) | ||
|
||
log.Printf("[DEBUG] Updating Resource Rate Limit Quota %s", name) | ||
|
||
data := map[string]interface{}{} | ||
data["path"] = d.Get("path").(string) | ||
data["rate"] = d.Get("rate").(float64) | ||
|
||
_, err := client.Logical().Write(path, data) | ||
if err != nil { | ||
d.SetId("") | ||
return fmt.Errorf("Error updating Resource Rate Limit Quota %s: %s", name, err) | ||
} | ||
log.Printf("[DEBUG] Updated Resource Rate Limit Quota %s", name) | ||
|
||
return quotaRateLimitRead(d, meta) | ||
} | ||
|
||
func quotaRateLimitDelete(d *schema.ResourceData, meta interface{}) error { | ||
client := meta.(*api.Client) | ||
|
||
name := d.Id() | ||
path := quotaRateLimitPath(name) | ||
|
||
log.Printf("[DEBUG] Deleting Resource Rate Limit Quota %s", name) | ||
_, err := client.Logical().Delete(path) | ||
if err != nil { | ||
return fmt.Errorf("Error deleting Resource Rate Limit Quota %s", name) | ||
} | ||
log.Printf("[DEBUG] Deleted Resource Rate Limit Quota %s", name) | ||
|
||
return nil | ||
} | ||
|
||
func quotaRateLimitExists(d *schema.ResourceData, meta interface{}) (bool, error) { | ||
client := meta.(*api.Client) | ||
|
||
name := d.Id() | ||
path := quotaRateLimitPath(name) | ||
|
||
log.Printf("[DEBUG] Checking if Resource Rate Limit Quota %s exists", name) | ||
|
||
secret, err := client.Logical().Read(path) | ||
if err != nil { | ||
return true, fmt.Errorf("error checking if Resource Rate Limit Quota %s exists: %s", name, err) | ||
} | ||
|
||
log.Printf("[DEBUG] Checked if Resource Rate Limit Quota %s exists", name) | ||
return secret != nil, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
package vault | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform-plugin-sdk/helper/acctest" | ||
"github.com/hashicorp/terraform-plugin-sdk/helper/resource" | ||
"github.com/hashicorp/terraform-plugin-sdk/terraform" | ||
"github.com/hashicorp/vault/api" | ||
) | ||
|
||
func randomQuotaRateString() string { | ||
whole := float64(acctest.RandIntRange(1000, 2000)) | ||
decimal := float64(acctest.RandIntRange(0, 100)) / 100 | ||
|
||
rateLimt := fmt.Sprintf("%.1f", whole+decimal) | ||
// Vault retuns floats with trailing zeros trimmed | ||
return strings.TrimRight(strings.TrimRight(rateLimt, "0"), ".") | ||
} | ||
|
||
func TestQuotaRateLimit(t *testing.T) { | ||
name := acctest.RandomWithPrefix("tf-test") | ||
rateLimit := randomQuotaRateString() | ||
newRateLimit := randomQuotaRateString() | ||
resource.Test(t, resource.TestCase{ | ||
Providers: testProviders, | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
CheckDestroy: testQuotaRateLimitCheckDestroy([]string{rateLimit, newRateLimit}), | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testQuotaRateLimit_Config(name, "", rateLimit), | ||
Check: resource.ComposeTestCheckFunc( | ||
resource.TestCheckResourceAttr("vault_quota_rate_limit.foobar", "name", name), | ||
resource.TestCheckResourceAttr("vault_quota_rate_limit.foobar", "path", ""), | ||
resource.TestCheckResourceAttr("vault_quota_rate_limit.foobar", "rate", rateLimit), | ||
), | ||
}, | ||
{ | ||
Config: testQuotaRateLimit_Config(name, "", newRateLimit), | ||
Check: resource.ComposeTestCheckFunc( | ||
resource.TestCheckResourceAttr("vault_quota_rate_limit.foobar", "name", name), | ||
resource.TestCheckResourceAttr("vault_quota_rate_limit.foobar", "path", ""), | ||
resource.TestCheckResourceAttr("vault_quota_rate_limit.foobar", "rate", newRateLimit), | ||
), | ||
}, | ||
{ | ||
Config: testQuotaRateLimit_Config(name, "sys/", newRateLimit), | ||
Check: resource.ComposeTestCheckFunc( | ||
resource.TestCheckResourceAttr("vault_quota_rate_limit.foobar", "name", name), | ||
resource.TestCheckResourceAttr("vault_quota_rate_limit.foobar", "path", "sys/"), | ||
resource.TestCheckResourceAttr("vault_quota_rate_limit.foobar", "rate", newRateLimit), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testQuotaRateLimitCheckDestroy(rateLimits []string) resource.TestCheckFunc { | ||
return func(s *terraform.State) error { | ||
client := testProvider.Meta().(*api.Client) | ||
|
||
for _, name := range rateLimits { | ||
resp, err := client.Logical().Read(quotaRateLimitPath(name)) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
if resp != nil { | ||
return fmt.Errorf("Resource Quota Rate Limit %s still exists", name) | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
} | ||
|
||
// Caution: Don't set test rate values too low or other tests running concurrently might fail | ||
func testQuotaRateLimit_Config(name, path, rate string) string { | ||
return fmt.Sprintf(` | ||
resource "vault_quota_rate_limit" "foobar" { | ||
name = "%s" | ||
path = "%s" | ||
rate = %s | ||
} | ||
`, name, path, rate) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
--- | ||
layout: "vault" | ||
page_title: "Vault: vault_quota_rate_limit resource" | ||
sidebar_current: "docs-vault-quota-rate-limit" | ||
description: |- | ||
Manage Rate Limit Quota | ||
--- | ||
|
||
# vault\_quota\_rate\_limit | ||
|
||
Manage rate limit quotas which enforce API rate limiting using a token bucket algorithm. | ||
A rate limit quota can be created at the root level or defined on a namespace or mount by | ||
specifying a path when creating the quota. | ||
|
||
See [Vault's Documentation](https://www.vaultproject.io/docs/concepts/resource-quotas) for more | ||
information. | ||
|
||
## Example Usage | ||
|
||
```hcl | ||
resource "vault_quota_rate_limit" "global" { | ||
name = "global" | ||
path = "" | ||
rate = 100 | ||
} | ||
``` | ||
|
||
## Argument Reference | ||
|
||
The following arguments are supported: | ||
|
||
* `name` - (Required) Name of the rate limit quota | ||
|
||
* `path` - (Optional) Path of the mount or namespace to apply the quota. A blank path configures a | ||
global rate limit quota. For example `namespace1/` adds a quota to a full namespace, | ||
`namespace1/auth/userpass` adds a `quota` to `userpass` in `namespace1`. | ||
Updating this field on an existing quota can have "moving" effects. For example, updating | ||
`auth/userpass` to `namespace1/auth/userpass` moves this quota from being a global mount quota to | ||
a namespace specific mount quota. **Note, namespaces are supported in Enterprise only.** | ||
|
||
* `rate` - (Required) The maximum number of requests at any given second to be allowed by the quota | ||
rule. The `rate` must be positive. | ||
|
||
## Attributes Reference | ||
|
||
No additional attributes are exported by this resource. | ||
|
||
## Import | ||
|
||
Rate limit quotas can be imported using their names | ||
|
||
``` | ||
$ terraform import vault_quota_rate_limit.global global | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters