Skip to content

Commit

Permalink
add namespaces data source (#2212)
Browse files Browse the repository at this point in the history
* Add `vault_namespaces` data source

Lists paths of all direct child namespaces.

* fix build

* add test checks

* update docs

* changelog

* use string slice for paths return type

* fix changelog

---------

Co-authored-by: Teemu Matilainen <teemu.matilainen@iki.fi>
  • Loading branch information
fairclothjm and tmatilai committed Mar 27, 2024
1 parent c89a45a commit 7b81a2a
Show file tree
Hide file tree
Showing 6 changed files with 204 additions and 2 deletions.
5 changes: 3 additions & 2 deletions CHANGELOG.md
Expand Up @@ -2,10 +2,11 @@

FEATURES:
* Add `granularity` to Secrets Sync destination resources. Requires Vault 1.16+. ([#2202](https://github.com/hashicorp/terraform-provider-vault/pull/2202))

IMPROVEMENTS:
* Add support for `allowed_kubernetes_namespace_selector` in `vault_kubernetes_secret_backend_role` ([#2180](https://github.com/hashicorp/terraform-provider-vault/pull/2180)).
* Add new data source `vault_namespace`. Requires Vault Enterprise: ([#2208](https://github.com/hashicorp/terraform-provider-vault/pull/2208)).
* Add new data source `vault_namespaces`. Requires Vault Enterprise: ([#2212](https://github.com/hashicorp/terraform-provider-vault/pull/2212)).

IMPROVEMENTS:
* Enable Secrets Sync Association resource to track sync status across all subkeys of a secret ([#2202](https://github.com/hashicorp/terraform-provider-vault/pull/2202))

BUGS:
Expand Down
68 changes: 68 additions & 0 deletions vault/data_source_namespaces.go
@@ -0,0 +1,68 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package vault

import (
"context"
"log"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/vault/api"

"github.com/hashicorp/terraform-provider-vault/internal/consts"
"github.com/hashicorp/terraform-provider-vault/internal/provider"
"github.com/hashicorp/terraform-provider-vault/util/mountutil"
)

func namespacesDataSource() *schema.Resource {
return &schema.Resource{
ReadContext: provider.ReadContextWrapper(namespacesDataSourceRead),

Schema: map[string]*schema.Schema{
consts.FieldPaths: {
Type: schema.TypeSet,
Computed: true,
Description: "Namespace paths.",
Elem: &schema.Schema{Type: schema.TypeString},
},
},
}
}

func namespacesDataSourceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client, e := provider.GetClient(d, meta)
if e != nil {
return diag.FromErr(e)
}

log.Printf("[DEBUG] Reading namespaces from Vault")

resp, err := client.Logical().ListWithContext(ctx, consts.SysNamespaceRoot)
if err != nil {
return diag.Errorf("error reading namespaces from Vault: %s", err)
}
if err := d.Set(consts.FieldPaths, flattenPaths(resp)); err != nil {
return diag.Errorf("error setting %q to state: %s", consts.FieldPaths, err)
}

id := mountutil.NormalizeMountPath(client.Namespace())
d.SetId(id)

return nil
}

func flattenPaths(resp *api.Secret) []string {
if resp == nil {
return nil
}

var paths []string
if keys, ok := resp.Data["keys"]; ok {
for _, key := range keys.([]interface{}) {
paths = append(paths, mountutil.TrimSlashes(key.(string)))
}
}
return paths
}
73 changes: 73 additions & 0 deletions vault/data_source_namespaces_test.go
@@ -0,0 +1,73 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package vault

import (
"fmt"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"

"github.com/hashicorp/terraform-provider-vault/internal/consts"
"github.com/hashicorp/terraform-provider-vault/testutil"
)

func TestAccDataSourceNamespaces(t *testing.T) {
testutil.SkipTestAccEnt(t)

ns := acctest.RandomWithPrefix("tf-ns")
resourceName := "data.vault_namespaces"

resource.Test(t, resource.TestCase{
Providers: testProviders,
PreCheck: func() { testutil.TestAccPreCheck(t) },
Steps: []resource.TestStep{
{
Config: testAccDataSourceNamespacesConfig(ns, 3),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(resourceName+".test", consts.FieldPaths+".#", "3"),
resource.TestCheckTypeSetElemAttr(resourceName+".test", consts.FieldPaths+".*", "test-0"),
resource.TestCheckTypeSetElemAttr(resourceName+".test", consts.FieldPaths+".*", "test-1"),
resource.TestCheckTypeSetElemAttr(resourceName+".test", consts.FieldPaths+".*", "test-2"),
resource.TestCheckResourceAttr(resourceName+".test", consts.FieldNamespace, ns),
// test nested which will not have any namespaces under it
resource.TestCheckResourceAttr(resourceName+".nested", consts.FieldPaths+".#", "0"),
resource.TestCheckResourceAttr(resourceName+".nested", consts.FieldNamespace, ns+"/test-0/nested"),
),
},
},
})
}

func testAccDataSourceNamespacesConfig(ns string, count int) string {
config := fmt.Sprintf(`
resource "vault_namespace" "parent" {
path = %q
}
resource "vault_namespace" "test" {
count = %d
namespace = vault_namespace.parent.path
path = "test-${count.index}"
}
# this will create a namespace with the path "test/test-0/nested"
resource "vault_namespace" "nested" {
namespace = vault_namespace.test[0].path_fq
path = "nested"
}
data "vault_namespaces" "test" {
namespace = vault_namespace.parent.path
depends_on = [vault_namespace.test]
}
data "vault_namespaces" "nested" {
namespace = vault_namespace.nested.path_fq
}
`, ns, count)

return config
}
5 changes: 5 additions & 0 deletions vault/provider.go
Expand Up @@ -78,6 +78,11 @@ var (
Resource: UpdateSchemaResource(adAccessCredentialsDataSource()),
PathInventory: []string{"/ad/creds/{role}"},
},
"vault_namespaces": {
Resource: UpdateSchemaResource(namespacesDataSource()),
PathInventory: []string{"/sys/namespaces"},
EnterpriseOnly: true,
},
"vault_nomad_access_token": {
Resource: UpdateSchemaResource(nomadAccessCredentialsDataSource()),
PathInventory: []string{"/nomad/creds/{role}"},
Expand Down
51 changes: 51 additions & 0 deletions website/docs/d/namespaces.html.md
@@ -0,0 +1,51 @@
---
layout: "vault"
page_title: "Vault: vault_namespaces data source"
sidebar_current: "docs-vault-datasource-namespaces"
description: |-
Fetches a list of all direct child namespaces in Vault
---

# vault\_namespace

Lists all direct child [Namespaces](https://developer.hashicorp.com/vault/docs/enterprise/namespaces) in Vault.

**Note** this feature is available only with Vault Enterprise.

## Example Usage

### Child namespaces

```hcl
data "vault_namespaces" "children" {}
```

### Nested namespace

To fetch the details of nested namespaces:

```hcl
data "vault_namespaces" "children" {
namespace = "parent"
}
data "vault_namespace" "child" {
for_each = data.vault_namespaces.children.paths
namespace = data.vault_namespaces.children.namespace
path = each.key
}
```

## Argument Reference

The following arguments are supported:

* `namespace` - (Optional) The namespace to provision the resource in.
The value should not contain leading or trailing forward slashes.
The `namespace` is always relative to the provider's configured [namespace](/docs/providers/vault#namespace).

## Attributes Reference

In addition to the above arguments, the following attributes are exported:

* `paths` - Set of the paths of direct child namespaces.
4 changes: 4 additions & 0 deletions website/vault.erb
Expand Up @@ -149,6 +149,10 @@
<a href="/docs/providers/vault/d/namespace.html">vault_namespace</a>
</li>

<li<%= sidebar_current("docs-vault-datasource-namespaces") %>>
<a href="/docs/providers/vault/d/namespaces.html">vault_namespaces</a>
</li>

</ul>
</li>

Expand Down

0 comments on commit 7b81a2a

Please sign in to comment.