Add a `consul_catalog_services` resource that enumerates the list of services in a given Consul datacenter.

This commit is contained in:
Sean Chittenden 2017-02-14 23:14:58 -08:00
parent 4003c47c71
commit b7c9dedf4e
No known key found for this signature in database
GPG Key ID: 4EBC9DC16C2E5E16
4 changed files with 138 additions and 10 deletions

View File

@ -9,7 +9,7 @@ import (
) )
const ( const (
queryOptServicesAttr = "services" catalogServiceElem = "services"
catalogServiceCreateIndex = "create_index" catalogServiceCreateIndex = "create_index"
catalogServiceDatacenter = "datacenter" catalogServiceDatacenter = "datacenter"
@ -37,13 +37,15 @@ func dataSourceConsulCatalogService() *schema.Resource {
Schema: map[string]*schema.Schema{ Schema: map[string]*schema.Schema{
// Data Source Predicate(s) // Data Source Predicate(s)
catalogServiceDatacenter: &schema.Schema{ catalogServiceDatacenter: &schema.Schema{
// Used in the query, must be stored and force a refresh if the value changes. // Used in the query, must be stored and force a refresh if the value
// changes.
Computed: true, Computed: true,
Type: schema.TypeString, Type: schema.TypeString,
ForceNew: true, ForceNew: true,
}, },
catalogServiceTag: &schema.Schema{ catalogServiceTag: &schema.Schema{
// Used in the query, must be stored and force a refresh if the value changes. // Used in the query, must be stored and force a refresh if the value
// changes.
Computed: true, Computed: true,
Type: schema.TypeString, Type: schema.TypeString,
ForceNew: true, ForceNew: true,
@ -55,7 +57,7 @@ func dataSourceConsulCatalogService() *schema.Resource {
queryOpts: schemaQueryOpts, queryOpts: schemaQueryOpts,
// Out parameters // Out parameters
queryOptServicesAttr: &schema.Schema{ catalogServiceElem: &schema.Schema{
Computed: true, Computed: true,
Type: schema.TypeList, Type: schema.TypeList,
Elem: &schema.Resource{ Elem: &schema.Resource{
@ -158,7 +160,6 @@ func dataSourceConsulCatalogServiceRead(d *schema.ResourceData, meta interface{}
} }
l := make([]interface{}, 0, len(services)) l := make([]interface{}, 0, len(services))
for _, service := range services { for _, service := range services {
const defaultServiceAttrs = 13 const defaultServiceAttrs = 13
m := make(map[string]interface{}, defaultServiceAttrs) m := make(map[string]interface{}, defaultServiceAttrs)
@ -197,7 +198,7 @@ func dataSourceConsulCatalogServiceRead(d *schema.ResourceData, meta interface{}
d.Set(catalogServiceDatacenter, queryOpts.Datacenter) d.Set(catalogServiceDatacenter, queryOpts.Datacenter)
d.Set(catalogServiceTag, serviceTag) d.Set(catalogServiceTag, serviceTag)
if err := d.Set(queryOptServicesAttr, l); err != nil { if err := d.Set(catalogServiceElem, l); err != nil {
return errwrap.Wrapf("Unable to store services: {{err}}", err) return errwrap.Wrapf("Unable to store services: {{err}}", err)
} }

View File

@ -0,0 +1,90 @@
package consul
import (
"fmt"
"sort"
"strings"
consulapi "github.com/hashicorp/consul/api"
"github.com/hashicorp/errwrap"
"github.com/hashicorp/terraform/helper/schema"
)
const (
catalogServicesDatacenter = "datacenter"
catalogServicesNames = "names"
catalogServicesServiceName = "name"
catalogServicesServiceTags = "tags"
)
func dataSourceConsulCatalogServices() *schema.Resource {
return &schema.Resource{
Read: dataSourceConsulCatalogServicesRead,
Schema: map[string]*schema.Schema{
// Data Source Predicate(s)
catalogServicesDatacenter: &schema.Schema{
// Used in the query, must be stored and force a refresh if the value
// changes.
Computed: true,
Type: schema.TypeString,
ForceNew: true,
},
queryOpts: schemaQueryOpts,
// Out parameters
catalogServicesNames: &schema.Schema{
Computed: true,
Type: schema.TypeMap,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
catalogServiceServiceTags: &schema.Schema{
// FIXME(sean@): Tags is currently a space separated list of tags.
// The ideal structure should be map[string][]string instead.
// When this is supported in the future this should be changed to
// be a TypeList instead.
Type: schema.TypeString,
Computed: true,
},
},
},
},
},
}
}
func dataSourceConsulCatalogServicesRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*consulapi.Client)
// Parse out data source filters to populate Consul's query options
queryOpts, err := getQueryOpts(d, client)
if err != nil {
return errwrap.Wrapf("unable to get query options for fetching catalog services: {{err}}", err)
}
services, meta, err := client.Catalog().Services(queryOpts)
if err != nil {
return err
}
m := make(map[string]interface{}, len(services))
for name, tags := range services {
tagList := make([]string, 0, len(tags))
for _, tag := range tags {
tagList = append(tagList, tag)
}
sort.Strings(tagList)
m[name] = strings.Join(tagList, " ")
}
const idKeyFmt = "catalog-services-%s"
d.SetId(fmt.Sprintf(idKeyFmt, queryOpts.Datacenter))
d.Set(catalogServicesDatacenter, queryOpts.Datacenter)
if err := d.Set(catalogServicesNames, m); err != nil {
return errwrap.Wrapf("Unable to store services: {{err}}", err)
}
return nil
}

View File

@ -0,0 +1,36 @@
package consul
import (
"testing"
"github.com/hashicorp/terraform/helper/resource"
)
func TestAccDataConsulCatalogServices_basic(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccDataConsulCatalogServicesConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckDataSourceValue("data.consul_catalog_services.read", "datacenter", "dc1"),
testAccCheckDataSourceValue("data.consul_catalog_services.read", "names.%", "1"),
testAccCheckDataSourceValue("data.consul_catalog_services.read", "names.consul", ""),
),
},
},
})
}
const testAccDataConsulCatalogServicesConfig = `
data "consul_catalog_services" "read" {
query_options {
allow_stale = true
require_consistent = false
token = ""
wait_index = 0
wait_time = "1m"
}
}
`

View File

@ -64,10 +64,11 @@ func Provider() terraform.ResourceProvider {
}, },
DataSourcesMap: map[string]*schema.Resource{ DataSourcesMap: map[string]*schema.Resource{
"consul_agent_self": dataSourceConsulAgentSelf(), "consul_agent_self": dataSourceConsulAgentSelf(),
"consul_catalog_nodes": dataSourceConsulCatalogNodes(), "consul_catalog_nodes": dataSourceConsulCatalogNodes(),
"consul_catalog_service": dataSourceConsulCatalogService(), "consul_catalog_service": dataSourceConsulCatalogService(),
"consul_keys": dataSourceConsulKeys(), "consul_catalog_services": dataSourceConsulCatalogServices(),
"consul_keys": dataSourceConsulKeys(),
}, },
ResourcesMap: map[string]*schema.Resource{ ResourcesMap: map[string]*schema.Resource{