diff --git a/vault/provider.go b/vault/provider.go index bfb8b2967..c32c01006 100644 --- a/vault/provider.go +++ b/vault/provider.go @@ -495,6 +495,10 @@ var ( Resource: transitSecretBackendKeyResource(), PathInventory: []string{"/transit/keys/{name}"}, }, + "vault_transit_secret_cache_config": { + Resource: transitSecretBackendCacheConfig(), + PathInventory: []string{"/transit/cache-config"}, + }, } ) diff --git a/vault/resource_transit_cache_config.go b/vault/resource_transit_cache_config.go new file mode 100644 index 000000000..52de1b496 --- /dev/null +++ b/vault/resource_transit_cache_config.go @@ -0,0 +1,91 @@ +package vault + +import ( + "fmt" + "log" + "strings" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/vault/api" +) + +func transitSecretBackendCacheConfig() *schema.Resource { + return &schema.Resource{ + Create: transitSecretBackendCacheConfigUpdate, + Update: transitSecretBackendCacheConfigUpdate, + Read: transitSecretBackendCacheConfigRead, + Delete: transitSecretBackendCacheConfigDelete, + + Schema: map[string]*schema.Schema{ + "backend": { + Type: schema.TypeString, + Required: true, + Description: "The Transit secret backend the resource belongs to.", + ForceNew: true, + StateFunc: func(v interface{}) string { + return strings.Trim(v.(string), "/") + }, + }, + "size": { + Type: schema.TypeInt, + Description: "Number of cache entries. A size of 0 mean unlimited.", + Required: true, + }, + }, + } +} + +func transitSecretBackendCacheConfigUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*api.Client) + size := d.Get("size").(int) + + backend := d.Get("backend").(string) + "/cache-config" + + log.Printf("[DEBUG] Setting transit cache size to: %d", size) + + data := map[string]interface{}{ + "size": size, + } + _, err := client.Logical().Write(backend, data) + if err != nil { + return fmt.Errorf("error writing transit cache-config: %v", err) + } + log.Printf("[DEBUG] Set transit cache size") + d.SetId(backend) + + data = map[string]interface{}{ + "mounts": []string{d.Get("backend").(string) + "/"}, + } + _, err = client.Logical().Write("sys/plugins/reload/backend", data) + if err != nil { + return fmt.Errorf("error reloading transit plugin: %v", err) + } + + return transitSecretBackendCacheConfigRead(d, meta) +} + +func transitSecretBackendCacheConfigRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*api.Client) + + backend := d.Id() + + secret, err := client.Logical().Read(backend) + if err != nil { + return fmt.Errorf("error reading transit cache-config: %v", err) + } + + if secret == nil { + log.Printf("[WARN] transit cache-config not found, removing from state") + d.SetId("") + return nil + } + + d.Set("size", secret.Data["size"]) + + return nil +} + +func transitSecretBackendCacheConfigDelete(d *schema.ResourceData, meta interface{}) error { + // Deleting the cache configuration is not supported in the Vault API + return nil +} diff --git a/vault/resource_transit_cache_config_test.go b/vault/resource_transit_cache_config_test.go new file mode 100644 index 000000000..07f4bdaa7 --- /dev/null +++ b/vault/resource_transit_cache_config_test.go @@ -0,0 +1,123 @@ +package vault + +import ( + "encoding/json" + "errors" + "fmt" + "strconv" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/hashicorp/vault/api" +) + +func TestAccTransitCacheConfig(t *testing.T) { + name := acctest.RandomWithPrefix("test-cache-config") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testProviders, + CheckDestroy: testAccTransitCacheConfigCheckDestroyed, + Steps: []resource.TestStep{ + { + Config: testAccTransitCacheConfig(name, 600), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("vault_transit_secret_cache_config.cfg", "size", "600"), + testAccTransitCacheConfigCheckApi(600), + ), + }, + { + Config: testAccTransitCacheConfig(name, 700), + Check: resource.TestCheckResourceAttr("vault_transit_secret_cache_config.cfg", "size", "700"), + }, + { + Config: testAccTransitCacheConfig(name, 0), + Check: resource.TestCheckResourceAttr("vault_transit_secret_cache_config.cfg", "size", "0"), + }, + { + Config: testAccTransitCacheConfigRemoved(name), + Check: testAccTransitCacheConfigCheckRemoved, + }, + }, + }) +} + +func testAccTransitCacheConfigCheckDestroyed(s *terraform.State) error { + client := testProvider.Meta().(*api.Client) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "vault_transit_secret_cache_config" { + continue + } + secret, err := client.Logical().Read(rs.Primary.ID) + if err != nil { + return fmt.Errorf("Error checking for transit cache config %q: %s", rs.Primary.ID, err) + } + if secret != nil { + return fmt.Errorf("Transit cache config %q still exists", rs.Primary.ID) + } + } + return nil +} + +func testAccTransitCacheConfigCheckApi(size int) resource.TestCheckFunc { + return func(s *terraform.State) error { + resourceState := s.Modules[0].Resources["vault_transit_secret_cache_config.cfg"] + if resourceState == nil { + return fmt.Errorf("resource not found in state") + } + + instanceState := resourceState.Primary + if instanceState == nil { + return fmt.Errorf("instance not found in state") + } + + id := instanceState.ID + + client := testProvider.Meta().(*api.Client) + resp, err := client.Logical().Read(id) + if err != nil { + return err + } + + sizeStr := strconv.Itoa(size) + act := resp.Data["size"].(json.Number).String() + if act != sizeStr { + return fmt.Errorf("expected side %q, got %q", sizeStr, act) + } + + return nil + } +} + +func testAccTransitCacheConfigCheckRemoved(s *terraform.State) error { + resourceState := s.Modules[0].Resources["vault_transit_secret_cache_config.cfg"] + if resourceState != nil { + return errors.New("transit cache config still present in state") + } + + return nil +} + +func testAccTransitCacheConfig(entityName string, size int) string { + return fmt.Sprintf(` +resource "vault_mount" "transit" { + path = "%s" + type = "transit" +} + +resource "vault_transit_secret_cache_config" "cfg" { + backend = "${vault_mount.transit.path}" + size = %d +}`, entityName, size) +} + +func testAccTransitCacheConfigRemoved(entityName string) string { + return fmt.Sprintf(` +resource "vault_mount" "transit" { + path = "%s" + type = "transit" +}`, entityName) +} diff --git a/website/docs/r/transit_secret_backend_cache_config.html.md b/website/docs/r/transit_secret_backend_cache_config.html.md new file mode 100644 index 000000000..d11f113de --- /dev/null +++ b/website/docs/r/transit_secret_backend_cache_config.html.md @@ -0,0 +1,41 @@ +--- +layout: "vault" +page_title: "Vault: vault_transit_secret_backend_cache_config resource" +sidebar_current: "docs-vault-resource-transit-secret-backend-cache-config" +description: |- + Configure the cache for the Transit Secret Backend in Vault. +--- + +# vault\_transit\_secret\_backend\_cache\_config + +Configure the cache for the Transit Secret Backend in Vault. + +## Example Usage + +```hcl +resource "vault_mount" "transit" { + path = "transit" + type = "transit" + description = "Example description" + default_lease_ttl_seconds = 3600 + max_lease_ttl_seconds = 86400 +} + +resource "vault_transit_secret_backend_cache_config" "cfg" { + backend = "${vault_mount.transit.path}" + size = 500 +} + +``` +## Argument Reference + +The following arguments are supported: + +* `backend` - (Required) The path the transit secret backend is mounted at, with no leading or trailing `/`s. + +* `size` - (Required) The number of cache entries. 0 means unlimited. + + +## Attributes Reference + +No additional attributes are exported by this resource.