provider/consul: consul_keys data source (#7678)

Previously the consul_keys resource did double-duty as both a reader and
writer of values from the Consul key/value store, but that made its
interface rather confusing and complex, as well as having all of the other
general problems associated with read-only resources.

Here we split the functionality such that reading is done with the
consul_keys data source while writing is done with the consul_keys
resource.

The old read behavior of the resource is still supported, but it's no
longer documented (except as a deprecation note) and will generate
deprecation warnings when used.

In future it should be possible to simplify the consul_keys resource by
removing all of the read support, but that is deferred for now to give
users a chance to gracefully migrate to the new data source.
This commit is contained in:
Martin Atkins 2016-07-26 10:23:38 -07:00 committed by Paul Stack
parent deba1b63e9
commit 19d0b42911
7 changed files with 246 additions and 40 deletions

View File

@ -0,0 +1,96 @@
package consul
import (
consulapi "github.com/hashicorp/consul/api"
"github.com/hashicorp/terraform/helper/schema"
)
func dataSourceConsulKeys() *schema.Resource {
return &schema.Resource{
Read: dataSourceConsulKeysRead,
Schema: map[string]*schema.Schema{
"datacenter": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},
"token": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"key": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"path": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"default": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
},
},
},
"var": &schema.Schema{
Type: schema.TypeMap,
Computed: true,
},
},
}
}
func dataSourceConsulKeysRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*consulapi.Client)
kv := client.KV()
token := d.Get("token").(string)
dc, err := getDC(d, client)
if err != nil {
return err
}
keyClient := newKeyClient(kv, dc, token)
vars := make(map[string]string)
keys := d.Get("key").(*schema.Set).List()
for _, raw := range keys {
key, path, sub, err := parseKey(raw)
if err != nil {
return err
}
value, err := keyClient.Get(path)
if err != nil {
return err
}
value = attributeValue(sub, value)
vars[key] = value
}
if err := d.Set("var", vars); err != nil {
return err
}
// Store the datacenter on this resource, which can be helpful for reference
// in case it was read from the provider
d.Set("datacenter", dc)
d.SetId("-")
return nil
}

View File

@ -0,0 +1,44 @@
package consul
import (
"testing"
"github.com/hashicorp/terraform/helper/resource"
)
func TestAccDataConsulKeys_basic(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccDataConsulKeysConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckConsulKeysValue("data.consul_keys.read", "read", "written"),
),
},
},
})
}
const testAccDataConsulKeysConfig = `
resource "consul_keys" "write" {
datacenter = "dc1"
key {
path = "test/data_source"
value = "written"
}
}
data "consul_keys" "read" {
# Create a dependency on the resource so we're sure to
# have the value in place before we try to read it.
datacenter = "${consul_keys.write.datacenter}"
key {
path = "test/data_source"
name = "read"
}
}
`

View File

@ -37,8 +37,9 @@ func resourceConsulKeys() *schema.Resource {
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
Type: schema.TypeString,
Optional: true,
Deprecated: "Using consul_keys resource to *read* is deprecated; please use consul_keys data source instead",
},
"path": &schema.Schema{
@ -220,7 +221,12 @@ func resourceConsulKeysRead(d *schema.ResourceData, meta interface{}) error {
}
value = attributeValue(sub, value)
vars[key] = value
if key != "" {
// If key is set then we'll update vars, for backward-compatibilty
// with the pre-0.7 capability to read from Consul with this
// resource.
vars[key] = value
}
// If there is already a "value" attribute present for this key
// then it was created as a "write" block. We need to update the
@ -290,10 +296,7 @@ func parseKey(raw interface{}) (string, string, map[string]interface{}, error) {
return "", "", nil, fmt.Errorf("Failed to unroll: %#v", raw)
}
key, ok := sub["name"].(string)
if !ok {
return "", "", nil, fmt.Errorf("Failed to expand key '%#v'", sub)
}
key := sub["name"].(string)
path, ok := sub["path"].(string)
if !ok {

View File

@ -28,6 +28,10 @@ func Provider() terraform.ResourceProvider {
},
},
DataSourcesMap: map[string]*schema.Resource{
"consul_keys": dataSourceConsulKeys(),
},
ResourcesMap: map[string]*schema.Resource{
"consul_keys": resourceConsulKeys(),
"consul_key_prefix": resourceConsulKeyPrefix(),

View File

@ -0,0 +1,67 @@
---
layout: "consul"
page_title: "Consul: consul_keys"
sidebar_current: "docs-consul-data-source-keys"
description: |-
Reads values from the Consul key/value store.
---
# consul\_keys
`consul_keys` reads values from the Consul key/value store.
This is a powerful way dynamically set values in templates.
## Example Usage
```
data "consul_keys" "app" {
datacenter = "nyc1"
token = "abcd"
# Read the launch AMI from Consul
key {
name = "ami"
path = "service/app/launch_ami"
default = "ami-1234"
}
}
# Start our instance with the dynamic ami value
resource "aws_instance" "app" {
ami = "${data.consul_keys.app.var.ami}"
...
}
```
## Argument Reference
The following arguments are supported:
* `datacenter` - (Optional) The datacenter to use. This overrides the
datacenter in the provider setup and the agent's default datacenter.
* `token` - (Optional) The ACL token to use. This overrides the
token that the agent provides by default.
* `key` - (Required) Specifies a key in Consul to be read or written.
Supported values documented below.
The `key` block supports the following:
* `name` - (Required) This is the name of the key. This value of the
key is exposed as `var.<name>`. This is not the path of the key
in Consul.
* `path` - (Required) This is the path in Consul that should be read
or written to.
* `default` - (Optional) This is the default value to set for `var.<name>`
if the key does not exist in Consul. Defaults to the empty string.
## Attributes Reference
The following attributes are exported:
* `datacenter` - The datacenter the keys are being read from to.
* `var.<name>` - For each name given, the corresponding attribute
has the value of the key.

View File

@ -3,15 +3,13 @@ layout: "consul"
page_title: "Consul: consul_keys"
sidebar_current: "docs-consul-resource-keys"
description: |-
Provides access to Key/Value data in Consul. This can be used to both read keys from Consul, but also to set the value of keys in Consul. This is a powerful way dynamically set values in templates, and to expose infrastructure details to clients.
Writes values into the Consul key/value store.
---
# consul\_keys
Provides access to Key/Value data in Consul. This can be used
to both read keys from Consul, but also to set the value of keys
in Consul. This is a powerful way dynamically set values in templates,
and to expose infrastructure details to clients.
`consul_keys` writes sets of individual values into Consul.
This is a powerful way to expose infrastructure details to clients.
This resource manages individual keys, and thus it can create, update and
delete the keys explicitly given. Howver, It is not able to detect and remove
@ -27,13 +25,6 @@ resource "consul_keys" "app" {
datacenter = "nyc1"
token = "abcd"
# Read the launch AMI from Consul
key {
name = "ami"
path = "service/app/launch_ami"
default = "ami-1234"
}
# Set the CNAME of our load balancer as a key
key {
name = "elb_cname"
@ -41,12 +32,6 @@ resource "consul_keys" "app" {
value = "${aws_elb.app.dns_name}"
}
}
# Start our instance with the dynamic ami value
resource "aws_instance" "app" {
ami = "${consul_keys.app.var.ami}"
...
}
```
## Argument Reference
@ -59,33 +44,31 @@ The following arguments are supported:
* `token` - (Optional) The ACL token to use. This overrides the
token that the agent provides by default.
* `key` - (Required) Specifies a key in Consul to be read or written.
* `key` - (Required) Specifies a key in Consul to be written.
Supported values documented below.
The `key` block supports the following:
* `name` - (Required) This is the name of the key. This value of the
key is exposed as `var.<name>`. This is not the path of the key
in Consul.
* `path` - (Required) This is the path in Consul that should be written to.
* `path` - (Required) This is the path in Consul that should be read
or written to.
* `default` - (Optional) This is the default value to set for `var.<name>`
if the key does not exist in Consul. Defaults to the empty string.
* `value` - (Optional) If set, the key will be set to this value.
This allows a key to be written to.
* `value` - (Required) The value to write to the given path.
* `delete` - (Optional) If true, then the key will be deleted when
either its configuration block is removed from the configuration or
the entire resource is destroyed. Otherwise, it will be left in Consul.
Defaults to false.
### Deprecated `key` arguments
Prior to Terraform 0.7 this resource was used both to read *and* write the
Consul key/value store. The read functionality has moved to the `consul_keys`
*data source*, whose documentation can be found via the navigation.
The pre-0.7 interface for reading is still supported for backward compatibilty,
but will be removed in a future version of Terraform.
## Attributes Reference
The following attributes are exported:
* `datacenter` - The datacenter the keys are being read/written to.
* `var.<name>` - For each name given, the corresponding attribute
has the value of the key.
* `datacenter` - The datacenter the keys are being written to.

View File

@ -10,6 +10,15 @@
<a href="/docs/providers/consul/index.html">Consul Provider</a>
</li>
<li<%= sidebar_current(/^docs-consul-data-source/) %>>
<a href="#">Data Sources</a>
<ul class="nav nav-visible">
<li<%= sidebar_current("docs-consul-data-source-keys") %>>
<a href="/docs/providers/consul/d/keys.html">consul_keys</a>
</li>
</ul>
</li>
<li<%= sidebar_current(/^docs-consul-resource/) %>>
<a href="#">Resources</a>
<ul class="nav nav-visible">