Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support inheritable resource quotas #2133

Merged
merged 8 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

FEATURES:
* Add support for `iam_tags` in `vault_aws_secret_backend_role` ([#2231](https://github.com/hashicorp/terraform-provider-vault/pull/2231)).
* Add support for `inheritable` on `vault_quota_rate_limit` and `vault_quota_lease_count`. Requires Vault 1.15+.: ([#2133](https://github.com/hashicorp/terraform-provider-vault/pull/2133)).

IMPROVEMENTS:
* return a useful error when delete fails for the `vault_jwt_auth_backend_role` resource: ([#2232](https://github.com/hashicorp/terraform-provider-vault/pull/2232))
Expand Down
23 changes: 23 additions & 0 deletions vault/resource_quota_lease_count.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ func quotaLeaseCountResource() *schema.Resource {
Optional: true,
Description: "If set on a quota where path is set to an auth mount with a concept of roles (such as /auth/approle/), this will make the quota restrict login requests to that mount that are made with the specified role.",
},
"inheritable": {
Type: schema.TypeBool,
Optional: true,
Description: "If set to true on a quota where path is set to a namespace, the same quota will be cumulatively applied to all child namespace. The inheritable parameter cannot be set to true if the path does not specify a namespace. Only the quotas associated with the root namespace are inheritable by default.",
},
},
}
}
Expand All @@ -74,6 +79,12 @@ func quotaLeaseCountCreate(d *schema.ResourceData, meta interface{}) error {
data["path"] = d.Get("path").(string)
data["max_leases"] = d.Get("max_leases").(int)

if provider.IsAPISupported(meta, provider.VaultVersion115) {
if v, ok := d.GetOkExists("inheritable"); ok {
data["inheritable"] = v.(bool)
}
}

if provider.IsAPISupported(meta, provider.VaultVersion112) {
if v, ok := d.GetOk(consts.FieldRole); ok {
data[consts.FieldRole] = v
Expand Down Expand Up @@ -116,6 +127,12 @@ func quotaLeaseCountRead(d *schema.ResourceData, meta interface{}) error {
fields = append(fields, consts.FieldRole)
}

if provider.IsAPISupported(meta, provider.VaultVersion115) {
if _, ok := d.GetOkExists("inheritable"); ok {
fields = append(fields, "inheritable")
}
}

for _, k := range fields {
v, ok := resp.Data[k]
if ok {
Expand Down Expand Up @@ -149,6 +166,12 @@ func quotaLeaseCountUpdate(d *schema.ResourceData, meta interface{}) error {
}
}

if provider.IsAPISupported(meta, provider.VaultVersion115) {
if v, ok := d.GetOkExists("inheritable"); ok {
data["inheritable"] = v.(bool)
}
}

_, err := client.Logical().Write(path, data)
if err != nil {
d.SetId("")
Expand Down
209 changes: 202 additions & 7 deletions vault/resource_quota_lease_count_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,35 @@ func TestQuotaLeaseCount(t *testing.T) {
})
}

func TestQuotaLeaseCountRoot(t *testing.T) {
name := acctest.RandomWithPrefix("tf-test")
leaseCount := "1001"
newLeaseCount := "2001"
resourceName := "vault_quota_lease_count.foobar"

resource.Test(t, resource.TestCase{
ProviderFactories: providerFactories,
PreCheck: func() {
testutil.TestEntPreCheck(t)
SkipIfAPIVersionGTE(t, testProvider.Meta(), provider.VaultVersion116)
},
CheckDestroy: testQuotaLeaseCountCheckDestroy([]string{leaseCount, newLeaseCount}),
Steps: []resource.TestStep{
{
Config: testQuotaLeaseCountConfigRootPath(name, "", newLeaseCount),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "name", name),
resource.TestCheckResourceAttr(resourceName, "path", ""),
resource.TestCheckResourceAttr(resourceName, "max_leases", newLeaseCount),
),
},
},
})
}

func TestQuotaLeaseCountWithRole(t *testing.T) {
name := acctest.RandomWithPrefix("lease-count")
ns := "ns-" + name
backend := acctest.RandomWithPrefix("approle")
role := acctest.RandomWithPrefix("test-role")
leaseCount := "1001"
Expand All @@ -76,19 +103,19 @@ func TestQuotaLeaseCountWithRole(t *testing.T) {
),
Steps: []resource.TestStep{
{
Config: testQuotaLeaseCountWithRoleConfig(backend, role, name, leaseCount),
Config: testQuotaLeaseCountWithRoleConfig(ns, backend, role, name, leaseCount),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "name", name),
resource.TestCheckResourceAttr(resourceName, consts.FieldPath, fmt.Sprintf("auth/%s/", backend)),
resource.TestCheckResourceAttr(resourceName, consts.FieldPath, fmt.Sprintf("%s/auth/%s/", ns, backend)),
resource.TestCheckResourceAttr(resourceName, "max_leases", leaseCount),
resource.TestCheckResourceAttr(resourceName, consts.FieldRole, role),
),
},
{
Config: testQuotaLeaseCountWithRoleConfig(backend, role, name, newLeaseCount),
Config: testQuotaLeaseCountWithRoleConfig(ns, backend, role, name, newLeaseCount),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "name", name),
resource.TestCheckResourceAttr(resourceName, consts.FieldPath, fmt.Sprintf("auth/%s/", backend)),
resource.TestCheckResourceAttr(resourceName, consts.FieldPath, fmt.Sprintf("%s/auth/%s/", ns, backend)),
resource.TestCheckResourceAttr(resourceName, "max_leases", newLeaseCount),
resource.TestCheckResourceAttr(resourceName, consts.FieldRole, role),
),
Expand All @@ -98,6 +125,113 @@ func TestQuotaLeaseCountWithRole(t *testing.T) {
})
}

func TestQuotaLeaseCountInheritable(t *testing.T) {
name := acctest.RandomWithPrefix("tf-test")
ns := "ns-" + name
leaseCount := "1001"
newLeaseCount := "2001"
inheritable := false
resourceName := "vault_quota_lease_count.foobar"

resource.Test(t, resource.TestCase{
ProviderFactories: providerFactories,
PreCheck: func() {
testutil.TestEntPreCheck(t)
SkipIfAPIVersionLT(t, testProvider.Meta(), provider.VaultVersion115)
},
CheckDestroy: testQuotaLeaseCountCheckDestroy([]string{leaseCount, newLeaseCount}),
Steps: []resource.TestStep{
{
Config: testQuotaLeaseCountConfigInheritable(ns, name, "", leaseCount, inheritable),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "name", name),
resource.TestCheckResourceAttr(resourceName, consts.FieldPath, ns+"/"),
resource.TestCheckResourceAttr(resourceName, "max_leases", leaseCount),
resource.TestCheckResourceAttr(resourceName, "inheritable", fmt.Sprintf("%t", inheritable)),
),
},
{
Config: testQuotaLeaseCountConfigInheritable(ns, name, "", newLeaseCount, inheritable),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "name", name),
resource.TestCheckResourceAttr(resourceName, consts.FieldPath, ns+"/"),
resource.TestCheckResourceAttr(resourceName, "max_leases", newLeaseCount),
resource.TestCheckResourceAttr(resourceName, "inheritable", fmt.Sprintf("%t", inheritable)),
),
},
{
Config: testQuotaLeaseCountConfigInheritable(ns, name, "sys/", newLeaseCount, inheritable),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "name", name),
resource.TestCheckResourceAttr(resourceName, consts.FieldPath, ns+"/sys/"),
resource.TestCheckResourceAttr(resourceName, "max_leases", newLeaseCount),
resource.TestCheckResourceAttr(resourceName, "inheritable", fmt.Sprintf("%t", inheritable)),
),
},
{
Config: testQuotaLeaseCountConfigInheritable(ns, name, "", newLeaseCount, true),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "name", name),
resource.TestCheckResourceAttr(resourceName, consts.FieldPath, ns+"/"),
resource.TestCheckResourceAttr(resourceName, "max_leases", newLeaseCount),
resource.TestCheckResourceAttr(resourceName, "inheritable", "true"),
),
},
},
})
}

func TestQuotaLeaseCountWithRoleInheritable(t *testing.T) {
name := acctest.RandomWithPrefix("lease-count")
ns := "ns-" + name
backend := acctest.RandomWithPrefix("approle")
role := acctest.RandomWithPrefix("test-role")
leaseCount := "1001"
newLeaseCount := "2001"
inheritable := false
resourceName := "vault_quota_lease_count.foobar"

resource.Test(t, resource.TestCase{
ProviderFactories: providerFactories,
PreCheck: func() {
testutil.TestEntPreCheck(t)
SkipIfAPIVersionLT(t, testProvider.Meta(), provider.VaultVersion115)
},
CheckDestroy: resource.ComposeTestCheckFunc(
testQuotaLeaseCountCheckDestroy([]string{leaseCount, newLeaseCount}),
testAccCheckAppRoleAuthBackendRoleDestroy,
),
Steps: []resource.TestStep{
{
Config: testQuotaLeaseCountWithRoleConfigInheritable(ns, backend, role, name, leaseCount, inheritable),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "name", name),
resource.TestCheckResourceAttr(resourceName, consts.FieldPath, fmt.Sprintf("%s/auth/%s/", ns, backend)),
resource.TestCheckResourceAttr(resourceName, "max_leases", leaseCount),
resource.TestCheckResourceAttr(resourceName, consts.FieldRole, role),
resource.TestCheckResourceAttr(resourceName, "inheritable", fmt.Sprintf("%t", inheritable)),
),
},
{
Config: testQuotaLeaseCountWithRoleConfigInheritable(ns, backend, role, name, newLeaseCount, inheritable),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "name", name),
resource.TestCheckResourceAttr(resourceName, consts.FieldPath, fmt.Sprintf("%s/auth/%s/", ns, backend)),
resource.TestCheckResourceAttr(resourceName, "max_leases", newLeaseCount),
resource.TestCheckResourceAttr(resourceName, consts.FieldRole, role),
resource.TestCheckResourceAttr(resourceName, "inheritable", fmt.Sprintf("%t", inheritable)),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"inheritable"},
},
},
})
}

func testQuotaLeaseCountCheckDestroy(leaseCounts []string) resource.TestCheckFunc {
return func(s *terraform.State) error {
client := testProvider.Meta().(*provider.ProviderMeta).MustGetClient()
Expand Down Expand Up @@ -132,24 +266,85 @@ resource "vault_quota_lease_count" "foobar" {
`, ns, name, path, maxLeases)
}

func testQuotaLeaseCountWithRoleConfig(backend, role, name, maxLeases string) string {
func testQuotaLeaseCountConfigRootPath(name, path, maxLeases string) string {
return fmt.Sprintf(`
resource "vault_quota_lease_count" "foobar" {
name = "%s"
path = "%s"
max_leases = %s
}
`, name, path, maxLeases)
}

func testQuotaLeaseCountWithRoleConfig(ns, backend, role, name, maxLeases string) string {
return fmt.Sprintf(`
resource "vault_namespace" "test" {
path = "%s"
}

resource "vault_auth_backend" "approle" {
namespace = vault_namespace.test.path
type = "approle"
path = "%s"
}

resource "vault_approle_auth_backend_role" "role" {
namespace = vault_namespace.test.path
backend = vault_auth_backend.approle.path
role_name = "%s"
token_policies = ["default", "dev", "prod"]
}

resource "vault_quota_lease_count" "foobar" {
name = "%s"
path = "${vault_namespace.test.path}/auth/${vault_auth_backend.approle.path}/"
role = vault_approle_auth_backend_role.role.role_name
max_leases = %s
}
`, ns, backend, role, name, maxLeases)
}

// Caution: Don't set test max_leases values too low or other tests running concurrently might fail
func testQuotaLeaseCountConfigInheritable(ns, name, path, maxLeases string, inheritable bool) string {
return fmt.Sprintf(`
resource "vault_namespace" "test" {
path = "%s"
}

resource "vault_quota_lease_count" "foobar" {
name = "%s"
path = "${vault_namespace.test.path}/%s"
max_leases = %s
inheritable = %t
}
`, ns, name, path, maxLeases, inheritable)
}

func testQuotaLeaseCountWithRoleConfigInheritable(ns, backend, role, name, maxLeases string, inheritable bool) string {
return fmt.Sprintf(`
resource "vault_namespace" "test" {
path = "%s"
}

resource "vault_auth_backend" "approle" {
namespace = vault_namespace.test.path
type = "approle"
path = "%s"
}

resource "vault_approle_auth_backend_role" "role" {
namespace = vault_namespace.test.path
backend = vault_auth_backend.approle.path
role_name = "%s"
token_policies = ["default", "dev", "prod"]
}

resource "vault_quota_lease_count" "foobar" {
name = "%s"
path = "auth/${vault_auth_backend.approle.path}/"
path = "${vault_namespace.test.path}/auth/${vault_auth_backend.approle.path}/"
role = vault_approle_auth_backend_role.role.role_name
max_leases = %s
inheritable = %t
}
`, backend, role, name, maxLeases)
`, ns, backend, role, name, maxLeases, inheritable)
}
23 changes: 23 additions & 0 deletions vault/resource_quota_rate_limit.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ func quotaRateLimitResource() *schema.Resource {
Optional: true,
Description: "If set on a quota where path is set to an auth mount with a concept of roles (such as /auth/approle/), this will make the quota restrict login requests to that mount that are made with the specified role.",
},
"inheritable": {
Type: schema.TypeBool,
Optional: true,
Description: "If set to true on a quota where path is set to a namespace, the same quota will be cumulatively applied to all child namespace. The inheritable parameter cannot be set to true if the path does not specify a namespace. Only the quotas associated with the root namespace are inheritable by default.",
},
},
}
}
Expand Down Expand Up @@ -94,6 +99,12 @@ func quotaRateLimitCreate(d *schema.ResourceData, meta interface{}) error {
data["block_interval"] = v
}

if provider.IsAPISupported(meta, provider.VaultVersion115) {
if v, ok := d.GetOkExists("inheritable"); ok {
data["inheritable"] = v.(bool)
}
}

if provider.IsAPISupported(meta, provider.VaultVersion112) {
if v, ok := d.GetOk(consts.FieldRole); ok {
data[consts.FieldRole] = v
Expand Down Expand Up @@ -136,6 +147,12 @@ func quotaRateLimitRead(d *schema.ResourceData, meta interface{}) error {
fields = append(fields, consts.FieldRole)
}

if provider.IsAPISupported(meta, provider.VaultVersion115) {
if _, ok := d.GetOkExists("inheritable"); ok {
fields = append(fields, "inheritable")
}
}

for _, k := range fields {
v, ok := resp.Data[k]
if ok {
Expand Down Expand Up @@ -171,6 +188,12 @@ func quotaRateLimitUpdate(d *schema.ResourceData, meta interface{}) error {
data["block_interval"] = v
}

if provider.IsAPISupported(meta, provider.VaultVersion115) {
if v, ok := d.GetOkExists("inheritable"); ok {
data["inheritable"] = v.(bool)
}
}

if provider.IsAPISupported(meta, provider.VaultVersion112) {
if v, ok := d.GetOk(consts.FieldRole); ok {
data[consts.FieldRole] = v
Expand Down
Loading
Loading