Merge pull request #11729 from hashicorp/f-consul-data-source
Consul Data Sources for nodes and services
This commit is contained in:
commit
df19f674e6
|
@ -0,0 +1,4 @@
|
||||||
|
# env TESTARGS='-test.parallel=1 -run TestAccDataConsulAgentSelf_basic' TF_LOG=debug make test
|
||||||
|
test::
|
||||||
|
2>&1 env \
|
||||||
|
make -C ../../.. testacc TEST=./builtin/providers/consul | tee test.log
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,95 @@
|
||||||
|
package consul
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAccDataConsulAgentSelf_basic(t *testing.T) {
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccDataConsulAgentSelfConfig,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "acl_datacenter", "<all>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "acl_default_policy", "<all>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "acl_disabled_ttl", "<all>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "acl_down_policy", "<all>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "acl_enforce_0_8_semantics", "<all>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "acl_ttl", "<all>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "advertise_addr", "<any>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "bind_addr", "<any>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "bootstrap_expect", "<all>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "bootstrap_mode", "false"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "client_addr", "<any>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "datacenter", "<any>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "dev_mode", "<any>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "domain", "<any>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "enable_anonymous_signature", "<any>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "enable_coordinates", "<any>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "enable_debug", "<any>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "enable_remote_exec", "<any>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "enable_syslog", "<any>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "enable_ui", "<any>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "enable_update_check", "<any>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "id", "<any>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "leave_on_int", "<any>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "leave_on_term", "<any>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "log_level", "<any>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "name", "<any>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "pid_file", "<all>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "rejoin_after_leave", "<any>"),
|
||||||
|
// testAccCheckDataSourceValue("data.consul_agent_self.read", "retry_join", "<all>"),
|
||||||
|
// testAccCheckDataSourceValue("data.consul_agent_self.read", "retry_join_wan", "<any>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "retry_max_attempts", "<any>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "retry_max_attempts_wan", "<any>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "serf_lan_bind_addr", "<all>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "serf_wan_bind_addr", "<all>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "server_mode", "<any>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "server_name", "<all>"),
|
||||||
|
// testAccCheckDataSourceValue("data.consul_agent_self.read", "start_join", "<all>"),
|
||||||
|
// testAccCheckDataSourceValue("data.consul_agent_self.read", "start_join_wan", "<all>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "syslog_facility", "<any>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "telemetry.enable_hostname", "<all>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "tls_ca_file", "<all>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "tls_cert_file", "<all>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "tls_key_file", "<all>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "tls_verify_incoming", "<any>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "tls_verify_outgoing", "<any>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "tls_verify_server_hostname", "<any>"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckDataSourceValue(n, attr, val string) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
rn, ok := s.RootModule().Resources[n]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Resource not found")
|
||||||
|
}
|
||||||
|
out, found := rn.Primary.Attributes[attr]
|
||||||
|
switch {
|
||||||
|
case !found:
|
||||||
|
return fmt.Errorf("Attribute '%s' not found: %#v", attr, rn.Primary.Attributes)
|
||||||
|
case val == "<all>":
|
||||||
|
// Value found, don't care what the payload is (including the zero value)
|
||||||
|
case val != "<any>" && out != val:
|
||||||
|
return fmt.Errorf("Attribute '%s' value '%s' != '%s'", attr, out, val)
|
||||||
|
case val == "<any>" && out == "":
|
||||||
|
return fmt.Errorf("Attribute '%s' value '%s'", attr, out)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const testAccDataConsulAgentSelfConfig = `
|
||||||
|
data "consul_agent_self" "read" {
|
||||||
|
}
|
||||||
|
`
|
|
@ -0,0 +1,155 @@
|
||||||
|
package consul
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
consulapi "github.com/hashicorp/consul/api"
|
||||||
|
"github.com/hashicorp/errwrap"
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
catalogNodesElem = "nodes"
|
||||||
|
catalogNodesDatacenter = "datacenter"
|
||||||
|
catalogNodesQueryOpts = "query_options"
|
||||||
|
|
||||||
|
catalogNodesNodeID = "id"
|
||||||
|
catalogNodesNodeAddress = "address"
|
||||||
|
catalogNodesNodeMeta = "meta"
|
||||||
|
catalogNodesNodeName = "name"
|
||||||
|
catalogNodesNodeTaggedAddresses = "tagged_addresses"
|
||||||
|
|
||||||
|
catalogNodesNodeIDs = "node_ids"
|
||||||
|
catalogNodesNodeNames = "node_names"
|
||||||
|
|
||||||
|
catalogNodesAPITaggedLAN = "lan"
|
||||||
|
catalogNodesAPITaggedWAN = "wan"
|
||||||
|
catalogNodesSchemaTaggedLAN = "lan"
|
||||||
|
catalogNodesSchemaTaggedWAN = "wan"
|
||||||
|
)
|
||||||
|
|
||||||
|
func dataSourceConsulCatalogNodes() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Read: dataSourceConsulCatalogNodesRead,
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
// Filters
|
||||||
|
catalogNodesQueryOpts: schemaQueryOpts,
|
||||||
|
|
||||||
|
// Out parameters
|
||||||
|
catalogNodesDatacenter: &schema.Schema{
|
||||||
|
Computed: true,
|
||||||
|
Type: schema.TypeString,
|
||||||
|
},
|
||||||
|
catalogNodesNodeIDs: &schema.Schema{
|
||||||
|
Computed: true,
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
},
|
||||||
|
catalogNodesNodeNames: &schema.Schema{
|
||||||
|
Computed: true,
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
},
|
||||||
|
catalogNodesElem: &schema.Schema{
|
||||||
|
Computed: true,
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
catalogNodesNodeID: &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
ValidateFunc: makeValidationFunc(catalogNodesNodeID, []interface{}{validateRegexp(`^[\S]+$`)}),
|
||||||
|
},
|
||||||
|
catalogNodesNodeName: &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
ValidateFunc: makeValidationFunc(catalogNodesNodeName, []interface{}{validateRegexp(`^[\S]+$`)}),
|
||||||
|
},
|
||||||
|
catalogNodesNodeAddress: &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
catalogNodesNodeMeta: &schema.Schema{
|
||||||
|
Type: schema.TypeMap,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
catalogNodesNodeTaggedAddresses: &schema.Schema{
|
||||||
|
Type: schema.TypeMap,
|
||||||
|
Computed: true,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
catalogNodesSchemaTaggedLAN: &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
catalogNodesSchemaTaggedWAN: &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func dataSourceConsulCatalogNodesRead(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 nodes: {{err}}", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes, meta, err := client.Catalog().Nodes(queryOpts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
l := make([]interface{}, 0, len(nodes))
|
||||||
|
|
||||||
|
nodeNames := make([]interface{}, 0, len(nodes))
|
||||||
|
nodeIDs := make([]interface{}, 0, len(nodes))
|
||||||
|
|
||||||
|
for _, node := range nodes {
|
||||||
|
const defaultNodeAttrs = 4
|
||||||
|
m := make(map[string]interface{}, defaultNodeAttrs)
|
||||||
|
id := node.ID
|
||||||
|
if id == "" {
|
||||||
|
id = node.Node
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeIDs = append(nodeIDs, id)
|
||||||
|
nodeNames = append(nodeNames, node.Node)
|
||||||
|
|
||||||
|
m[catalogNodesNodeAddress] = node.Address
|
||||||
|
m[catalogNodesNodeID] = id
|
||||||
|
m[catalogNodesNodeName] = node.Node
|
||||||
|
m[catalogNodesNodeMeta] = node.Meta
|
||||||
|
m[catalogNodesNodeTaggedAddresses] = node.TaggedAddresses
|
||||||
|
|
||||||
|
l = append(l, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
const idKeyFmt = "catalog-nodes-%s"
|
||||||
|
d.SetId(fmt.Sprintf(idKeyFmt, queryOpts.Datacenter))
|
||||||
|
|
||||||
|
d.Set(catalogNodesDatacenter, queryOpts.Datacenter)
|
||||||
|
if err := d.Set(catalogNodesNodeIDs, nodeIDs); err != nil {
|
||||||
|
return errwrap.Wrapf("Unable to store node IDs: {{err}}", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.Set(catalogNodesNodeNames, nodeNames); err != nil {
|
||||||
|
return errwrap.Wrapf("Unable to store node names: {{err}}", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.Set(catalogNodesElem, l); err != nil {
|
||||||
|
return errwrap.Wrapf("Unable to store nodes: {{err}}", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package consul
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAccDataConsulCatalogNodes_basic(t *testing.T) {
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccDataConsulCatalogNodesConfig,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckDataSourceValue("data.consul_catalog_nodes.read", "nodes.#", "1"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_catalog_nodes.read", "nodes.0.id", "<any>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_catalog_nodes.read", "nodes.0.name", "<any>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_catalog_nodes.read", "nodes.0.address", "<any>"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const testAccDataConsulCatalogNodesConfig = `
|
||||||
|
data "consul_catalog_nodes" "read" {
|
||||||
|
query_options {
|
||||||
|
allow_stale = true
|
||||||
|
require_consistent = false
|
||||||
|
token = ""
|
||||||
|
wait_index = 0
|
||||||
|
wait_time = "1m"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
|
@ -0,0 +1,202 @@
|
||||||
|
package consul
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
consulapi "github.com/hashicorp/consul/api"
|
||||||
|
"github.com/hashicorp/errwrap"
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
catalogServiceElem = "service"
|
||||||
|
|
||||||
|
catalogServiceCreateIndex = "create_index"
|
||||||
|
catalogServiceDatacenter = "datacenter"
|
||||||
|
catalogServiceModifyIndex = "modify_index"
|
||||||
|
catalogServiceNodeAddress = "node_address"
|
||||||
|
catalogServiceNodeID = "node_id"
|
||||||
|
catalogServiceNodeMeta = "node_meta"
|
||||||
|
catalogServiceNodeName = "node_name"
|
||||||
|
catalogServiceServiceAddress = "address"
|
||||||
|
catalogServiceServiceEnableTagOverride = "enable_tag_override"
|
||||||
|
catalogServiceServiceID = "id"
|
||||||
|
catalogServiceServiceName = "name"
|
||||||
|
catalogServiceServicePort = "port"
|
||||||
|
catalogServiceServiceTags = "tags"
|
||||||
|
catalogServiceTaggedAddresses = "tagged_addresses"
|
||||||
|
|
||||||
|
// Filters
|
||||||
|
catalogServiceName = "name"
|
||||||
|
catalogServiceTag = "tag"
|
||||||
|
)
|
||||||
|
|
||||||
|
func dataSourceConsulCatalogService() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Read: dataSourceConsulCatalogServiceRead,
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
// Data Source Predicate(s)
|
||||||
|
catalogServiceDatacenter: &schema.Schema{
|
||||||
|
// Used in the query, must be stored and force a refresh if the value
|
||||||
|
// changes.
|
||||||
|
Computed: true,
|
||||||
|
Type: schema.TypeString,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
catalogServiceTag: &schema.Schema{
|
||||||
|
// Used in the query, must be stored and force a refresh if the value
|
||||||
|
// changes.
|
||||||
|
Computed: true,
|
||||||
|
Type: schema.TypeString,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
catalogServiceName: &schema.Schema{
|
||||||
|
Required: true,
|
||||||
|
Type: schema.TypeString,
|
||||||
|
},
|
||||||
|
catalogNodesQueryOpts: schemaQueryOpts,
|
||||||
|
|
||||||
|
// Out parameters
|
||||||
|
catalogServiceElem: &schema.Schema{
|
||||||
|
Computed: true,
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
catalogServiceCreateIndex: &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
catalogServiceNodeAddress: &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
catalogServiceNodeID: &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
catalogServiceModifyIndex: &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
catalogServiceNodeName: &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
catalogServiceNodeMeta: &schema.Schema{
|
||||||
|
Type: schema.TypeMap,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
catalogServiceServiceAddress: &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
catalogServiceServiceEnableTagOverride: &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
catalogServiceServiceID: &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
catalogServiceServiceName: &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
catalogServiceServicePort: &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
catalogServiceServiceTags: &schema.Schema{
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Computed: true,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
},
|
||||||
|
catalogServiceTaggedAddresses: &schema.Schema{
|
||||||
|
Type: schema.TypeMap,
|
||||||
|
Computed: true,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
catalogNodesSchemaTaggedLAN: &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
catalogNodesSchemaTaggedWAN: &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func dataSourceConsulCatalogServiceRead(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)
|
||||||
|
}
|
||||||
|
|
||||||
|
var serviceName string
|
||||||
|
if v, ok := d.GetOk(catalogServiceName); ok {
|
||||||
|
serviceName = v.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
var serviceTag string
|
||||||
|
if v, ok := d.GetOk(catalogServiceTag); ok {
|
||||||
|
serviceTag = v.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// services, meta, err := client.Catalog().Services(queryOpts)
|
||||||
|
services, meta, err := client.Catalog().Service(serviceName, serviceTag, queryOpts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
l := make([]interface{}, 0, len(services))
|
||||||
|
for _, service := range services {
|
||||||
|
const defaultServiceAttrs = 13
|
||||||
|
m := make(map[string]interface{}, defaultServiceAttrs)
|
||||||
|
|
||||||
|
m[catalogServiceCreateIndex] = fmt.Sprintf("%d", service.CreateIndex)
|
||||||
|
m[catalogServiceModifyIndex] = fmt.Sprintf("%d", service.ModifyIndex)
|
||||||
|
m[catalogServiceNodeAddress] = service.Address
|
||||||
|
m[catalogServiceNodeID] = service.ID
|
||||||
|
m[catalogServiceNodeMeta] = service.NodeMeta
|
||||||
|
m[catalogServiceNodeName] = service.Node
|
||||||
|
switch service.ServiceAddress {
|
||||||
|
case "":
|
||||||
|
m[catalogServiceServiceAddress] = service.Address
|
||||||
|
default:
|
||||||
|
m[catalogServiceServiceAddress] = service.ServiceAddress
|
||||||
|
}
|
||||||
|
m[catalogServiceServiceEnableTagOverride] = fmt.Sprintf("%t", service.ServiceEnableTagOverride)
|
||||||
|
m[catalogServiceServiceID] = service.ServiceID
|
||||||
|
m[catalogServiceServiceName] = service.ServiceName
|
||||||
|
m[catalogServiceServicePort] = fmt.Sprintf("%d", service.ServicePort)
|
||||||
|
sort.Strings(service.ServiceTags)
|
||||||
|
m[catalogServiceServiceTags] = service.ServiceTags
|
||||||
|
m[catalogServiceTaggedAddresses] = service.TaggedAddresses
|
||||||
|
|
||||||
|
l = append(l, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
const idKeyFmt = "catalog-service-%s-%q-%q"
|
||||||
|
d.SetId(fmt.Sprintf(idKeyFmt, queryOpts.Datacenter, serviceName, serviceTag))
|
||||||
|
|
||||||
|
d.Set(catalogServiceDatacenter, queryOpts.Datacenter)
|
||||||
|
d.Set(catalogServiceName, serviceName)
|
||||||
|
d.Set(catalogServiceTag, serviceTag)
|
||||||
|
if err := d.Set(catalogServiceElem, l); err != nil {
|
||||||
|
return errwrap.Wrapf("Unable to store service: {{err}}", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package consul
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAccDataConsulCatalogService_basic(t *testing.T) {
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccDataConsulCatalogServiceConfig,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckDataSourceValue("data.consul_catalog_service.read", "datacenter", "dc1"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_catalog_service.read", "service.#", "1"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_catalog_service.read", "service.0.address", "<all>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_catalog_service.read", "service.0.create_index", "<any>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_catalog_service.read", "service.0.enable_tag_override", "<any>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_catalog_service.read", "service.0.id", "<any>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_catalog_service.read", "service.0.modify_index", "<any>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_catalog_service.read", "service.0.name", "<any>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_catalog_service.read", "service.0.node_address", "<any>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_catalog_service.read", "service.0.node_id", "<any>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_catalog_service.read", "service.0.node_meta.%", "0"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_catalog_service.read", "service.0.node_name", "<any>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_catalog_service.read", "service.0.port", "<any>"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_catalog_service.read", "service.0.tagged_addresses.%", "2"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_catalog_service.read", "service.0.tags.#", "0"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const testAccDataConsulCatalogServiceConfig = `
|
||||||
|
data "consul_catalog_service" "read" {
|
||||||
|
query_options {
|
||||||
|
allow_stale = true
|
||||||
|
require_consistent = false
|
||||||
|
token = ""
|
||||||
|
wait_index = 0
|
||||||
|
wait_time = "1m"
|
||||||
|
}
|
||||||
|
|
||||||
|
name = "consul"
|
||||||
|
}
|
||||||
|
`
|
|
@ -0,0 +1,104 @@
|
||||||
|
package consul
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
consulapi "github.com/hashicorp/consul/api"
|
||||||
|
"github.com/hashicorp/errwrap"
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Datasource predicates
|
||||||
|
catalogServicesServiceName = "name"
|
||||||
|
|
||||||
|
// Out parameters
|
||||||
|
catalogServicesDatacenter = "datacenter"
|
||||||
|
catalogServicesNames = "names"
|
||||||
|
catalogServicesServices = "services"
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
catalogNodesQueryOpts: schemaQueryOpts,
|
||||||
|
|
||||||
|
// Out parameters
|
||||||
|
catalogServicesNames: &schema.Schema{
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Computed: true,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
},
|
||||||
|
catalogServicesServices: &schema.Schema{
|
||||||
|
Computed: true,
|
||||||
|
Type: schema.TypeMap,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
catalogServiceServiceTags: &schema.Schema{
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Computed: true,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
catalogServices := 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)
|
||||||
|
catalogServices[name] = strings.Join(tagList, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceNames := make([]interface{}, 0, len(services))
|
||||||
|
for k := range catalogServices {
|
||||||
|
serviceNames = append(serviceNames, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
const idKeyFmt = "catalog-services-%s"
|
||||||
|
d.SetId(fmt.Sprintf(idKeyFmt, queryOpts.Datacenter))
|
||||||
|
|
||||||
|
d.Set(catalogServicesDatacenter, queryOpts.Datacenter)
|
||||||
|
if err := d.Set(catalogServicesServices, catalogServices); err != nil {
|
||||||
|
return errwrap.Wrapf("Unable to store services: {{err}}", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.Set(catalogServicesNames, serviceNames); err != nil {
|
||||||
|
return errwrap.Wrapf("Unable to store service names: {{err}}", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -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", "services.%", "1"),
|
||||||
|
testAccCheckDataSourceValue("data.consul_catalog_services.read", "services.consul", ""),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const testAccDataConsulCatalogServicesConfig = `
|
||||||
|
data "consul_catalog_services" "read" {
|
||||||
|
query_options {
|
||||||
|
allow_stale = true
|
||||||
|
require_consistent = false
|
||||||
|
token = ""
|
||||||
|
wait_index = 0
|
||||||
|
wait_time = "1m"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
|
@ -0,0 +1,122 @@
|
||||||
|
package consul
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
consulapi "github.com/hashicorp/consul/api"
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
queryOptAllowStale = "allow_stale"
|
||||||
|
queryOptDatacenter = "datacenter"
|
||||||
|
queryOptNear = "near"
|
||||||
|
queryOptNodeMeta = "node_meta"
|
||||||
|
queryOptRequireConsistent = "require_consistent"
|
||||||
|
queryOptToken = "token"
|
||||||
|
queryOptWaitIndex = "wait_index"
|
||||||
|
queryOptWaitTime = "wait_time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var schemaQueryOpts = &schema.Schema{
|
||||||
|
Optional: true,
|
||||||
|
Type: schema.TypeSet,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
queryOptAllowStale: &schema.Schema{
|
||||||
|
Optional: true,
|
||||||
|
Default: true,
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
},
|
||||||
|
queryOptDatacenter: &schema.Schema{
|
||||||
|
// Optional because we'll pull the default from the local agent if it's
|
||||||
|
// not specified, but we can query remote data centers as a result.
|
||||||
|
Optional: true,
|
||||||
|
Type: schema.TypeString,
|
||||||
|
},
|
||||||
|
queryOptNear: &schema.Schema{
|
||||||
|
Optional: true,
|
||||||
|
Type: schema.TypeString,
|
||||||
|
},
|
||||||
|
queryOptNodeMeta: &schema.Schema{
|
||||||
|
Optional: true,
|
||||||
|
Type: schema.TypeMap,
|
||||||
|
},
|
||||||
|
queryOptRequireConsistent: &schema.Schema{
|
||||||
|
Optional: true,
|
||||||
|
Default: false,
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
},
|
||||||
|
queryOptToken: &schema.Schema{
|
||||||
|
Optional: true,
|
||||||
|
Type: schema.TypeString,
|
||||||
|
},
|
||||||
|
queryOptWaitIndex: &schema.Schema{
|
||||||
|
Optional: true,
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
ValidateFunc: makeValidationFunc(queryOptWaitIndex, []interface{}{
|
||||||
|
validateIntMin(0),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
queryOptWaitTime: &schema.Schema{
|
||||||
|
Optional: true,
|
||||||
|
Type: schema.TypeString,
|
||||||
|
ValidateFunc: makeValidationFunc(queryOptWaitTime, []interface{}{
|
||||||
|
validateDurationMin("0ns"),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func getQueryOpts(d *schema.ResourceData, client *consulapi.Client) (*consulapi.QueryOptions, error) {
|
||||||
|
queryOpts := &consulapi.QueryOptions{}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk(queryOptAllowStale); ok {
|
||||||
|
queryOpts.AllowStale = v.(bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk(queryOptDatacenter); ok {
|
||||||
|
queryOpts.Datacenter = v.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
if queryOpts.Datacenter == "" {
|
||||||
|
dc, err := getDC(d, client)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
queryOpts.Datacenter = dc
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk(queryOptNear); ok {
|
||||||
|
queryOpts.Near = v.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk(queryOptRequireConsistent); ok {
|
||||||
|
queryOpts.RequireConsistent = v.(bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk(queryOptNodeMeta); ok {
|
||||||
|
m := v.(map[string]interface{})
|
||||||
|
nodeMetaMap := make(map[string]string, len(queryOptNodeMeta))
|
||||||
|
for s, t := range m {
|
||||||
|
nodeMetaMap[s] = t.(string)
|
||||||
|
}
|
||||||
|
queryOpts.NodeMeta = nodeMetaMap
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk(queryOptToken); ok {
|
||||||
|
queryOpts.Token = v.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk(queryOptWaitIndex); ok {
|
||||||
|
queryOpts.WaitIndex = uint64(v.(int))
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk(queryOptWaitTime); ok {
|
||||||
|
d, _ := time.ParseDuration(v.(string))
|
||||||
|
queryOpts.WaitTime = d
|
||||||
|
}
|
||||||
|
|
||||||
|
return queryOpts, nil
|
||||||
|
}
|
|
@ -208,12 +208,12 @@ func resourceConsulPreparedQueryRead(d *schema.ResourceData, meta interface{}) e
|
||||||
|
|
||||||
func resourceConsulPreparedQueryDelete(d *schema.ResourceData, meta interface{}) error {
|
func resourceConsulPreparedQueryDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
client := meta.(*consulapi.Client)
|
client := meta.(*consulapi.Client)
|
||||||
qo := &consulapi.QueryOptions{
|
writeOpts := &consulapi.WriteOptions{
|
||||||
Datacenter: d.Get("datacenter").(string),
|
Datacenter: d.Get("datacenter").(string),
|
||||||
Token: d.Get("token").(string),
|
Token: d.Get("token").(string),
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := client.PreparedQuery().Delete(d.Id(), qo); err != nil {
|
if _, err := client.PreparedQuery().Delete(d.Id(), writeOpts); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,6 +64,10 @@ func Provider() terraform.ResourceProvider {
|
||||||
},
|
},
|
||||||
|
|
||||||
DataSourcesMap: map[string]*schema.Resource{
|
DataSourcesMap: map[string]*schema.Resource{
|
||||||
|
"consul_agent_self": dataSourceConsulAgentSelf(),
|
||||||
|
"consul_catalog_nodes": dataSourceConsulCatalogNodes(),
|
||||||
|
"consul_catalog_service": dataSourceConsulCatalogService(),
|
||||||
|
"consul_catalog_services": dataSourceConsulCatalogServices(),
|
||||||
"consul_keys": dataSourceConsulKeys(),
|
"consul_keys": dataSourceConsulKeys(),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,146 @@
|
||||||
|
package consul
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/errwrap"
|
||||||
|
)
|
||||||
|
|
||||||
|
// An array of inputs used as typed arguments and converted from their type into
|
||||||
|
// function objects that are dynamically constructed and executed.
|
||||||
|
type validatorInputs []interface{}
|
||||||
|
|
||||||
|
// validateDurationMin is the minimum duration to accept as input
|
||||||
|
type validateDurationMin string
|
||||||
|
|
||||||
|
// validateIntMax is the maximum integer value to accept as input
|
||||||
|
type validateIntMax int
|
||||||
|
|
||||||
|
// validateIntMin is the minimum integer value to accept as input
|
||||||
|
type validateIntMin int
|
||||||
|
|
||||||
|
// validateRegexp is a regexp pattern to use to validate schema input.
|
||||||
|
type validateRegexp string
|
||||||
|
|
||||||
|
// makeValidateionFunc takes the name of the attribute and a list of typed
|
||||||
|
// validator inputs in order to create a validation closure that calls each
|
||||||
|
// validator in serial until either a warning or error is returned from the
|
||||||
|
// first validation function.
|
||||||
|
func makeValidationFunc(name string, validators []interface{}) func(v interface{}, key string) (warnings []string, errors []error) {
|
||||||
|
if len(validators) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fns := make([]func(v interface{}, key string) (warnings []string, errors []error), 0, len(validators))
|
||||||
|
for _, v := range validators {
|
||||||
|
switch u := v.(type) {
|
||||||
|
case validateDurationMin:
|
||||||
|
fns = append(fns, validateDurationMinFactory(name, string(u)))
|
||||||
|
case validateIntMax:
|
||||||
|
fns = append(fns, validateIntMaxFactory(name, int(u)))
|
||||||
|
case validateIntMin:
|
||||||
|
fns = append(fns, validateIntMinFactory(name, int(u)))
|
||||||
|
case validateRegexp:
|
||||||
|
fns = append(fns, validateRegexpFactory(name, string(u)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return func(v interface{}, key string) (warnings []string, errors []error) {
|
||||||
|
for _, fn := range fns {
|
||||||
|
warnings, errors = fn(v, key)
|
||||||
|
if len(warnings) > 0 || len(errors) > 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return warnings, errors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateDurationMinFactory(name, minDuration string) func(v interface{}, key string) (warnings []string, errors []error) {
|
||||||
|
dMin, err := time.ParseDuration(minDuration)
|
||||||
|
if err != nil {
|
||||||
|
return func(interface{}, string) (warnings []string, errors []error) {
|
||||||
|
return nil, []error{
|
||||||
|
errwrap.Wrapf(fmt.Sprintf("PROVIDER BUG: duration %q not valid: {{err}}", minDuration), err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return func(v interface{}, key string) (warnings []string, errors []error) {
|
||||||
|
d, err := time.ParseDuration(v.(string))
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, errwrap.Wrapf(fmt.Sprintf("Invalid %s specified (%q): {{err}}", name, v.(string)), err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if d < dMin {
|
||||||
|
errors = append(errors, fmt.Errorf("Invalid %s specified: duration %q less than the required minimum %s", name, v.(string), dMin))
|
||||||
|
}
|
||||||
|
|
||||||
|
return warnings, errors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateIntMaxFactory(name string, max int) func(v interface{}, key string) (warnings []string, errors []error) {
|
||||||
|
return func(v interface{}, key string) (warnings []string, errors []error) {
|
||||||
|
switch u := v.(type) {
|
||||||
|
case string:
|
||||||
|
i, err := strconv.ParseInt(u, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, errwrap.Wrapf(fmt.Sprintf("unable to convert %q to an integer: {{err}}", u), err))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if i > int64(max) {
|
||||||
|
errors = append(errors, fmt.Errorf("Invalid %s specified: %d more than the required maximum %d", name, v.(int), max))
|
||||||
|
}
|
||||||
|
case int:
|
||||||
|
if u > max {
|
||||||
|
errors = append(errors, fmt.Errorf("Invalid %s specified: %d more than the required maximum %d", name, v.(int), max))
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
errors = append(errors, fmt.Errorf("Unsupported type in int max validation: %T", v))
|
||||||
|
}
|
||||||
|
|
||||||
|
return warnings, errors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateIntMinFactory(name string, min int) func(v interface{}, key string) (warnings []string, errors []error) {
|
||||||
|
return func(v interface{}, key string) (warnings []string, errors []error) {
|
||||||
|
switch u := v.(type) {
|
||||||
|
case string:
|
||||||
|
i, err := strconv.ParseInt(u, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, errwrap.Wrapf(fmt.Sprintf("unable to convert %q to an integer: {{err}}", u), err))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if i < int64(min) {
|
||||||
|
errors = append(errors, fmt.Errorf("Invalid %s specified: %d less than the required minimum %d", name, v.(int), min))
|
||||||
|
}
|
||||||
|
case int:
|
||||||
|
if u < min {
|
||||||
|
errors = append(errors, fmt.Errorf("Invalid %s specified: %d less than the required minimum %d", name, v.(int), min))
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
errors = append(errors, fmt.Errorf("Unsupported type in int min validation: %T", v))
|
||||||
|
}
|
||||||
|
|
||||||
|
return warnings, errors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateRegexpFactory(name string, reString string) func(v interface{}, key string) (warnings []string, errors []error) {
|
||||||
|
re := regexp.MustCompile(reString)
|
||||||
|
|
||||||
|
return func(v interface{}, key string) (warnings []string, errors []error) {
|
||||||
|
if !re.MatchString(v.(string)) {
|
||||||
|
errors = append(errors, fmt.Errorf("Invalid %s specified (%q): regexp failed to match string", name, v.(string)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return warnings, errors
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -62,8 +63,7 @@ type AgentCheckRegistration struct {
|
||||||
AgentServiceCheck
|
AgentServiceCheck
|
||||||
}
|
}
|
||||||
|
|
||||||
// AgentServiceCheck is used to create an associated
|
// AgentServiceCheck is used to define a node or service level check
|
||||||
// check for a service
|
|
||||||
type AgentServiceCheck struct {
|
type AgentServiceCheck struct {
|
||||||
Script string `json:",omitempty"`
|
Script string `json:",omitempty"`
|
||||||
DockerContainerID string `json:",omitempty"`
|
DockerContainerID string `json:",omitempty"`
|
||||||
|
@ -74,6 +74,16 @@ type AgentServiceCheck struct {
|
||||||
HTTP string `json:",omitempty"`
|
HTTP string `json:",omitempty"`
|
||||||
TCP string `json:",omitempty"`
|
TCP string `json:",omitempty"`
|
||||||
Status string `json:",omitempty"`
|
Status string `json:",omitempty"`
|
||||||
|
Notes string `json:",omitempty"`
|
||||||
|
TLSSkipVerify bool `json:",omitempty"`
|
||||||
|
|
||||||
|
// In Consul 0.7 and later, checks that are associated with a service
|
||||||
|
// may also contain this optional DeregisterCriticalServiceAfter field,
|
||||||
|
// which is a timeout in the same Go time format as Interval and TTL. If
|
||||||
|
// a check is in the critical state for more than this configured value,
|
||||||
|
// then its associated service (and all of its associated checks) will
|
||||||
|
// automatically be deregistered.
|
||||||
|
DeregisterCriticalServiceAfter string `json:",omitempty"`
|
||||||
}
|
}
|
||||||
type AgentServiceChecks []*AgentServiceCheck
|
type AgentServiceChecks []*AgentServiceCheck
|
||||||
|
|
||||||
|
@ -107,6 +117,17 @@ func (a *Agent) Self() (map[string]map[string]interface{}, error) {
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reload triggers a configuration reload for the agent we are connected to.
|
||||||
|
func (a *Agent) Reload() error {
|
||||||
|
r := a.c.newRequest("PUT", "/v1/agent/reload")
|
||||||
|
_, resp, err := requireOK(a.c.doRequest(r))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
resp.Body.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// NodeName is used to get the node name of the agent
|
// NodeName is used to get the node name of the agent
|
||||||
func (a *Agent) NodeName() (string, error) {
|
func (a *Agent) NodeName() (string, error) {
|
||||||
if a.nodeName != "" {
|
if a.nodeName != "" {
|
||||||
|
@ -338,6 +359,17 @@ func (a *Agent) Join(addr string, wan bool) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Leave is used to have the agent gracefully leave the cluster and shutdown
|
||||||
|
func (a *Agent) Leave() error {
|
||||||
|
r := a.c.newRequest("PUT", "/v1/agent/leave")
|
||||||
|
_, resp, err := requireOK(a.c.doRequest(r))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
resp.Body.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// ForceLeave is used to have the agent eject a failed node
|
// ForceLeave is used to have the agent eject a failed node
|
||||||
func (a *Agent) ForceLeave(node string) error {
|
func (a *Agent) ForceLeave(node string) error {
|
||||||
r := a.c.newRequest("PUT", "/v1/agent/force-leave/"+node)
|
r := a.c.newRequest("PUT", "/v1/agent/force-leave/"+node)
|
||||||
|
@ -402,3 +434,38 @@ func (a *Agent) DisableNodeMaintenance() error {
|
||||||
resp.Body.Close()
|
resp.Body.Close()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Monitor returns a channel which will receive streaming logs from the agent
|
||||||
|
// Providing a non-nil stopCh can be used to close the connection and stop the
|
||||||
|
// log stream
|
||||||
|
func (a *Agent) Monitor(loglevel string, stopCh chan struct{}, q *QueryOptions) (chan string, error) {
|
||||||
|
r := a.c.newRequest("GET", "/v1/agent/monitor")
|
||||||
|
r.setQueryOptions(q)
|
||||||
|
if loglevel != "" {
|
||||||
|
r.params.Add("loglevel", loglevel)
|
||||||
|
}
|
||||||
|
_, resp, err := requireOK(a.c.doRequest(r))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
logCh := make(chan string, 64)
|
||||||
|
go func() {
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(resp.Body)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-stopCh:
|
||||||
|
close(logCh)
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
if scanner.Scan() {
|
||||||
|
logCh <- scanner.Text()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return logCh, nil
|
||||||
|
}
|
||||||
|
|
|
@ -20,6 +20,28 @@ import (
|
||||||
"github.com/hashicorp/go-cleanhttp"
|
"github.com/hashicorp/go-cleanhttp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// HTTPAddrEnvName defines an environment variable name which sets
|
||||||
|
// the HTTP address if there is no -http-addr specified.
|
||||||
|
HTTPAddrEnvName = "CONSUL_HTTP_ADDR"
|
||||||
|
|
||||||
|
// HTTPTokenEnvName defines an environment variable name which sets
|
||||||
|
// the HTTP token.
|
||||||
|
HTTPTokenEnvName = "CONSUL_HTTP_TOKEN"
|
||||||
|
|
||||||
|
// HTTPAuthEnvName defines an environment variable name which sets
|
||||||
|
// the HTTP authentication header.
|
||||||
|
HTTPAuthEnvName = "CONSUL_HTTP_AUTH"
|
||||||
|
|
||||||
|
// HTTPSSLEnvName defines an environment variable name which sets
|
||||||
|
// whether or not to use HTTPS.
|
||||||
|
HTTPSSLEnvName = "CONSUL_HTTP_SSL"
|
||||||
|
|
||||||
|
// HTTPSSLVerifyEnvName defines an environment variable name which sets
|
||||||
|
// whether or not to disable certificate checking.
|
||||||
|
HTTPSSLVerifyEnvName = "CONSUL_HTTP_SSL_VERIFY"
|
||||||
|
)
|
||||||
|
|
||||||
// QueryOptions are used to parameterize a query
|
// QueryOptions are used to parameterize a query
|
||||||
type QueryOptions struct {
|
type QueryOptions struct {
|
||||||
// Providing a datacenter overwrites the DC provided
|
// Providing a datacenter overwrites the DC provided
|
||||||
|
@ -52,6 +74,11 @@ type QueryOptions struct {
|
||||||
// that node. Setting this to "_agent" will use the agent's node
|
// that node. Setting this to "_agent" will use the agent's node
|
||||||
// for the sort.
|
// for the sort.
|
||||||
Near string
|
Near string
|
||||||
|
|
||||||
|
// NodeMeta is used to filter results by nodes with the given
|
||||||
|
// metadata key/value pairs. Currently, only one key/value pair can
|
||||||
|
// be provided for filtering.
|
||||||
|
NodeMeta map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteOptions are used to parameterize a write
|
// WriteOptions are used to parameterize a write
|
||||||
|
@ -80,6 +107,9 @@ type QueryMeta struct {
|
||||||
|
|
||||||
// How long did the request take
|
// How long did the request take
|
||||||
RequestTime time.Duration
|
RequestTime time.Duration
|
||||||
|
|
||||||
|
// Is address translation enabled for HTTP responses on this agent
|
||||||
|
AddressTranslationEnabled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteMeta is used to return meta data about a write
|
// WriteMeta is used to return meta data about a write
|
||||||
|
@ -178,15 +208,15 @@ func defaultConfig(transportFn func() *http.Transport) *Config {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if addr := os.Getenv("CONSUL_HTTP_ADDR"); addr != "" {
|
if addr := os.Getenv(HTTPAddrEnvName); addr != "" {
|
||||||
config.Address = addr
|
config.Address = addr
|
||||||
}
|
}
|
||||||
|
|
||||||
if token := os.Getenv("CONSUL_HTTP_TOKEN"); token != "" {
|
if token := os.Getenv(HTTPTokenEnvName); token != "" {
|
||||||
config.Token = token
|
config.Token = token
|
||||||
}
|
}
|
||||||
|
|
||||||
if auth := os.Getenv("CONSUL_HTTP_AUTH"); auth != "" {
|
if auth := os.Getenv(HTTPAuthEnvName); auth != "" {
|
||||||
var username, password string
|
var username, password string
|
||||||
if strings.Contains(auth, ":") {
|
if strings.Contains(auth, ":") {
|
||||||
split := strings.SplitN(auth, ":", 2)
|
split := strings.SplitN(auth, ":", 2)
|
||||||
|
@ -202,10 +232,10 @@ func defaultConfig(transportFn func() *http.Transport) *Config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ssl := os.Getenv("CONSUL_HTTP_SSL"); ssl != "" {
|
if ssl := os.Getenv(HTTPSSLEnvName); ssl != "" {
|
||||||
enabled, err := strconv.ParseBool(ssl)
|
enabled, err := strconv.ParseBool(ssl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("[WARN] client: could not parse CONSUL_HTTP_SSL: %s", err)
|
log.Printf("[WARN] client: could not parse %s: %s", HTTPSSLEnvName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if enabled {
|
if enabled {
|
||||||
|
@ -213,10 +243,10 @@ func defaultConfig(transportFn func() *http.Transport) *Config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if verify := os.Getenv("CONSUL_HTTP_SSL_VERIFY"); verify != "" {
|
if verify := os.Getenv(HTTPSSLVerifyEnvName); verify != "" {
|
||||||
doVerify, err := strconv.ParseBool(verify)
|
doVerify, err := strconv.ParseBool(verify)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("[WARN] client: could not parse CONSUL_HTTP_SSL_VERIFY: %s", err)
|
log.Printf("[WARN] client: could not parse %s: %s", HTTPSSLVerifyEnvName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !doVerify {
|
if !doVerify {
|
||||||
|
@ -330,6 +360,7 @@ type request struct {
|
||||||
url *url.URL
|
url *url.URL
|
||||||
params url.Values
|
params url.Values
|
||||||
body io.Reader
|
body io.Reader
|
||||||
|
header http.Header
|
||||||
obj interface{}
|
obj interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -355,11 +386,16 @@ func (r *request) setQueryOptions(q *QueryOptions) {
|
||||||
r.params.Set("wait", durToMsec(q.WaitTime))
|
r.params.Set("wait", durToMsec(q.WaitTime))
|
||||||
}
|
}
|
||||||
if q.Token != "" {
|
if q.Token != "" {
|
||||||
r.params.Set("token", q.Token)
|
r.header.Set("X-Consul-Token", q.Token)
|
||||||
}
|
}
|
||||||
if q.Near != "" {
|
if q.Near != "" {
|
||||||
r.params.Set("near", q.Near)
|
r.params.Set("near", q.Near)
|
||||||
}
|
}
|
||||||
|
if len(q.NodeMeta) > 0 {
|
||||||
|
for key, value := range q.NodeMeta {
|
||||||
|
r.params.Add("node-meta", key+":"+value)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// durToMsec converts a duration to a millisecond specified string. If the
|
// durToMsec converts a duration to a millisecond specified string. If the
|
||||||
|
@ -399,7 +435,7 @@ func (r *request) setWriteOptions(q *WriteOptions) {
|
||||||
r.params.Set("dc", q.Datacenter)
|
r.params.Set("dc", q.Datacenter)
|
||||||
}
|
}
|
||||||
if q.Token != "" {
|
if q.Token != "" {
|
||||||
r.params.Set("token", q.Token)
|
r.header.Set("X-Consul-Token", q.Token)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -426,6 +462,7 @@ func (r *request) toHTTP() (*http.Request, error) {
|
||||||
req.URL.Host = r.url.Host
|
req.URL.Host = r.url.Host
|
||||||
req.URL.Scheme = r.url.Scheme
|
req.URL.Scheme = r.url.Scheme
|
||||||
req.Host = r.url.Host
|
req.Host = r.url.Host
|
||||||
|
req.Header = r.header
|
||||||
|
|
||||||
// Setup auth
|
// Setup auth
|
||||||
if r.config.HttpAuth != nil {
|
if r.config.HttpAuth != nil {
|
||||||
|
@ -446,6 +483,7 @@ func (c *Client) newRequest(method, path string) *request {
|
||||||
Path: path,
|
Path: path,
|
||||||
},
|
},
|
||||||
params: make(map[string][]string),
|
params: make(map[string][]string),
|
||||||
|
header: make(http.Header),
|
||||||
}
|
}
|
||||||
if c.config.Datacenter != "" {
|
if c.config.Datacenter != "" {
|
||||||
r.params.Set("dc", c.config.Datacenter)
|
r.params.Set("dc", c.config.Datacenter)
|
||||||
|
@ -454,7 +492,7 @@ func (c *Client) newRequest(method, path string) *request {
|
||||||
r.params.Set("wait", durToMsec(r.config.WaitTime))
|
r.params.Set("wait", durToMsec(r.config.WaitTime))
|
||||||
}
|
}
|
||||||
if c.config.Token != "" {
|
if c.config.Token != "" {
|
||||||
r.params.Set("token", r.config.Token)
|
r.header.Set("X-Consul-Token", r.config.Token)
|
||||||
}
|
}
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
@ -539,6 +577,15 @@ func parseQueryMeta(resp *http.Response, q *QueryMeta) error {
|
||||||
default:
|
default:
|
||||||
q.KnownLeader = false
|
q.KnownLeader = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse X-Consul-Translate-Addresses
|
||||||
|
switch header.Get("X-Consul-Translate-Addresses") {
|
||||||
|
case "true":
|
||||||
|
q.AddressTranslationEnabled = true
|
||||||
|
default:
|
||||||
|
q.AddressTranslationEnabled = false
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,27 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
type Node struct {
|
type Node struct {
|
||||||
|
ID string
|
||||||
Node string
|
Node string
|
||||||
Address string
|
Address string
|
||||||
|
TaggedAddresses map[string]string
|
||||||
|
Meta map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
type CatalogService struct {
|
type CatalogService struct {
|
||||||
|
ID string
|
||||||
Node string
|
Node string
|
||||||
Address string
|
Address string
|
||||||
|
TaggedAddresses map[string]string
|
||||||
|
NodeMeta map[string]string
|
||||||
ServiceID string
|
ServiceID string
|
||||||
ServiceName string
|
ServiceName string
|
||||||
ServiceAddress string
|
ServiceAddress string
|
||||||
ServiceTags []string
|
ServiceTags []string
|
||||||
ServicePort int
|
ServicePort int
|
||||||
ServiceEnableTagOverride bool
|
ServiceEnableTagOverride bool
|
||||||
|
CreateIndex uint64
|
||||||
|
ModifyIndex uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
type CatalogNode struct {
|
type CatalogNode struct {
|
||||||
|
@ -22,8 +30,11 @@ type CatalogNode struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type CatalogRegistration struct {
|
type CatalogRegistration struct {
|
||||||
|
ID string
|
||||||
Node string
|
Node string
|
||||||
Address string
|
Address string
|
||||||
|
TaggedAddresses map[string]string
|
||||||
|
NodeMeta map[string]string
|
||||||
Datacenter string
|
Datacenter string
|
||||||
Service *AgentService
|
Service *AgentService
|
||||||
Check *AgentCheck
|
Check *AgentCheck
|
||||||
|
@ -31,7 +42,7 @@ type CatalogRegistration struct {
|
||||||
|
|
||||||
type CatalogDeregistration struct {
|
type CatalogDeregistration struct {
|
||||||
Node string
|
Node string
|
||||||
Address string
|
Address string // Obsolete.
|
||||||
Datacenter string
|
Datacenter string
|
||||||
ServiceID string
|
ServiceID string
|
||||||
CheckID string
|
CheckID string
|
||||||
|
|
|
@ -2,16 +2,25 @@ package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// HealthAny is special, and is used as a wild card,
|
// HealthAny is special, and is used as a wild card,
|
||||||
// not as a specific state.
|
// not as a specific state.
|
||||||
HealthAny = "any"
|
HealthAny = "any"
|
||||||
HealthUnknown = "unknown"
|
|
||||||
HealthPassing = "passing"
|
HealthPassing = "passing"
|
||||||
HealthWarning = "warning"
|
HealthWarning = "warning"
|
||||||
HealthCritical = "critical"
|
HealthCritical = "critical"
|
||||||
|
HealthMaint = "maintenance"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// NodeMaint is the special key set by a node in maintenance mode.
|
||||||
|
NodeMaint = "_node_maintenance"
|
||||||
|
|
||||||
|
// ServiceMaintPrefix is the prefix for a service in maintenance mode.
|
||||||
|
ServiceMaintPrefix = "_service_maintenance:"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HealthCheck is used to represent a single check
|
// HealthCheck is used to represent a single check
|
||||||
|
@ -26,11 +35,56 @@ type HealthCheck struct {
|
||||||
ServiceName string
|
ServiceName string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HealthChecks is a collection of HealthCheck structs.
|
||||||
|
type HealthChecks []*HealthCheck
|
||||||
|
|
||||||
|
// AggregatedStatus returns the "best" status for the list of health checks.
|
||||||
|
// Because a given entry may have many service and node-level health checks
|
||||||
|
// attached, this function determines the best representative of the status as
|
||||||
|
// as single string using the following heuristic:
|
||||||
|
//
|
||||||
|
// maintenance > critical > warning > passing
|
||||||
|
//
|
||||||
|
func (c HealthChecks) AggregatedStatus() string {
|
||||||
|
var passing, warning, critical, maintenance bool
|
||||||
|
for _, check := range c {
|
||||||
|
id := string(check.CheckID)
|
||||||
|
if id == NodeMaint || strings.HasPrefix(id, ServiceMaintPrefix) {
|
||||||
|
maintenance = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch check.Status {
|
||||||
|
case HealthPassing:
|
||||||
|
passing = true
|
||||||
|
case HealthWarning:
|
||||||
|
warning = true
|
||||||
|
case HealthCritical:
|
||||||
|
critical = true
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case maintenance:
|
||||||
|
return HealthMaint
|
||||||
|
case critical:
|
||||||
|
return HealthCritical
|
||||||
|
case warning:
|
||||||
|
return HealthWarning
|
||||||
|
case passing:
|
||||||
|
return HealthPassing
|
||||||
|
default:
|
||||||
|
return HealthPassing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ServiceEntry is used for the health service endpoint
|
// ServiceEntry is used for the health service endpoint
|
||||||
type ServiceEntry struct {
|
type ServiceEntry struct {
|
||||||
Node *Node
|
Node *Node
|
||||||
Service *AgentService
|
Service *AgentService
|
||||||
Checks []*HealthCheck
|
Checks HealthChecks
|
||||||
}
|
}
|
||||||
|
|
||||||
// Health can be used to query the Health endpoints
|
// Health can be used to query the Health endpoints
|
||||||
|
@ -44,7 +98,7 @@ func (c *Client) Health() *Health {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Node is used to query for checks belonging to a given node
|
// Node is used to query for checks belonging to a given node
|
||||||
func (h *Health) Node(node string, q *QueryOptions) ([]*HealthCheck, *QueryMeta, error) {
|
func (h *Health) Node(node string, q *QueryOptions) (HealthChecks, *QueryMeta, error) {
|
||||||
r := h.c.newRequest("GET", "/v1/health/node/"+node)
|
r := h.c.newRequest("GET", "/v1/health/node/"+node)
|
||||||
r.setQueryOptions(q)
|
r.setQueryOptions(q)
|
||||||
rtt, resp, err := requireOK(h.c.doRequest(r))
|
rtt, resp, err := requireOK(h.c.doRequest(r))
|
||||||
|
@ -57,7 +111,7 @@ func (h *Health) Node(node string, q *QueryOptions) ([]*HealthCheck, *QueryMeta,
|
||||||
parseQueryMeta(resp, qm)
|
parseQueryMeta(resp, qm)
|
||||||
qm.RequestTime = rtt
|
qm.RequestTime = rtt
|
||||||
|
|
||||||
var out []*HealthCheck
|
var out HealthChecks
|
||||||
if err := decodeBody(resp, &out); err != nil {
|
if err := decodeBody(resp, &out); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -65,7 +119,7 @@ func (h *Health) Node(node string, q *QueryOptions) ([]*HealthCheck, *QueryMeta,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks is used to return the checks associated with a service
|
// Checks is used to return the checks associated with a service
|
||||||
func (h *Health) Checks(service string, q *QueryOptions) ([]*HealthCheck, *QueryMeta, error) {
|
func (h *Health) Checks(service string, q *QueryOptions) (HealthChecks, *QueryMeta, error) {
|
||||||
r := h.c.newRequest("GET", "/v1/health/checks/"+service)
|
r := h.c.newRequest("GET", "/v1/health/checks/"+service)
|
||||||
r.setQueryOptions(q)
|
r.setQueryOptions(q)
|
||||||
rtt, resp, err := requireOK(h.c.doRequest(r))
|
rtt, resp, err := requireOK(h.c.doRequest(r))
|
||||||
|
@ -78,7 +132,7 @@ func (h *Health) Checks(service string, q *QueryOptions) ([]*HealthCheck, *Query
|
||||||
parseQueryMeta(resp, qm)
|
parseQueryMeta(resp, qm)
|
||||||
qm.RequestTime = rtt
|
qm.RequestTime = rtt
|
||||||
|
|
||||||
var out []*HealthCheck
|
var out HealthChecks
|
||||||
if err := decodeBody(resp, &out); err != nil {
|
if err := decodeBody(resp, &out); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -116,13 +170,12 @@ func (h *Health) Service(service, tag string, passingOnly bool, q *QueryOptions)
|
||||||
|
|
||||||
// State is used to retrieve all the checks in a given state.
|
// State is used to retrieve all the checks in a given state.
|
||||||
// The wildcard "any" state can also be used for all checks.
|
// The wildcard "any" state can also be used for all checks.
|
||||||
func (h *Health) State(state string, q *QueryOptions) ([]*HealthCheck, *QueryMeta, error) {
|
func (h *Health) State(state string, q *QueryOptions) (HealthChecks, *QueryMeta, error) {
|
||||||
switch state {
|
switch state {
|
||||||
case HealthAny:
|
case HealthAny:
|
||||||
case HealthWarning:
|
case HealthWarning:
|
||||||
case HealthCritical:
|
case HealthCritical:
|
||||||
case HealthPassing:
|
case HealthPassing:
|
||||||
case HealthUnknown:
|
|
||||||
default:
|
default:
|
||||||
return nil, nil, fmt.Errorf("Unsupported state: %v", state)
|
return nil, nil, fmt.Errorf("Unsupported state: %v", state)
|
||||||
}
|
}
|
||||||
|
@ -138,7 +191,7 @@ func (h *Health) State(state string, q *QueryOptions) ([]*HealthCheck, *QueryMet
|
||||||
parseQueryMeta(resp, qm)
|
parseQueryMeta(resp, qm)
|
||||||
qm.RequestTime = rtt
|
qm.RequestTime = rtt
|
||||||
|
|
||||||
var out []*HealthCheck
|
var out HealthChecks
|
||||||
if err := decodeBody(resp, &out); err != nil {
|
if err := decodeBody(resp, &out); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,12 +11,34 @@ import (
|
||||||
|
|
||||||
// KVPair is used to represent a single K/V entry
|
// KVPair is used to represent a single K/V entry
|
||||||
type KVPair struct {
|
type KVPair struct {
|
||||||
|
// Key is the name of the key. It is also part of the URL path when accessed
|
||||||
|
// via the API.
|
||||||
Key string
|
Key string
|
||||||
|
|
||||||
|
// CreateIndex holds the index corresponding the creation of this KVPair. This
|
||||||
|
// is a read-only field.
|
||||||
CreateIndex uint64
|
CreateIndex uint64
|
||||||
|
|
||||||
|
// ModifyIndex is used for the Check-And-Set operations and can also be fed
|
||||||
|
// back into the WaitIndex of the QueryOptions in order to perform blocking
|
||||||
|
// queries.
|
||||||
ModifyIndex uint64
|
ModifyIndex uint64
|
||||||
|
|
||||||
|
// LockIndex holds the index corresponding to a lock on this key, if any. This
|
||||||
|
// is a read-only field.
|
||||||
LockIndex uint64
|
LockIndex uint64
|
||||||
|
|
||||||
|
// Flags are any user-defined flags on the key. It is up to the implementer
|
||||||
|
// to check these values, since Consul does not treat them specially.
|
||||||
Flags uint64
|
Flags uint64
|
||||||
|
|
||||||
|
// Value is the value for the key. This can be any value, but it will be
|
||||||
|
// base64 encoded upon transport.
|
||||||
Value []byte
|
Value []byte
|
||||||
|
|
||||||
|
// Session is a string representing the ID of the session. Any other
|
||||||
|
// interactions with this key over the same session must specify the same
|
||||||
|
// session ID.
|
||||||
Session string
|
Session string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,21 +50,21 @@ type KVOp string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
KVSet KVOp = "set"
|
KVSet KVOp = "set"
|
||||||
KVDelete = "delete"
|
KVDelete KVOp = "delete"
|
||||||
KVDeleteCAS = "delete-cas"
|
KVDeleteCAS KVOp = "delete-cas"
|
||||||
KVDeleteTree = "delete-tree"
|
KVDeleteTree KVOp = "delete-tree"
|
||||||
KVCAS = "cas"
|
KVCAS KVOp = "cas"
|
||||||
KVLock = "lock"
|
KVLock KVOp = "lock"
|
||||||
KVUnlock = "unlock"
|
KVUnlock KVOp = "unlock"
|
||||||
KVGet = "get"
|
KVGet KVOp = "get"
|
||||||
KVGetTree = "get-tree"
|
KVGetTree KVOp = "get-tree"
|
||||||
KVCheckSession = "check-session"
|
KVCheckSession KVOp = "check-session"
|
||||||
KVCheckIndex = "check-index"
|
KVCheckIndex KVOp = "check-index"
|
||||||
)
|
)
|
||||||
|
|
||||||
// KVTxnOp defines a single operation inside a transaction.
|
// KVTxnOp defines a single operation inside a transaction.
|
||||||
type KVTxnOp struct {
|
type KVTxnOp struct {
|
||||||
Verb string
|
Verb KVOp
|
||||||
Key string
|
Key string
|
||||||
Value []byte
|
Value []byte
|
||||||
Flags uint64
|
Flags uint64
|
||||||
|
@ -70,7 +92,8 @@ func (c *Client) KV() *KV {
|
||||||
return &KV{c}
|
return &KV{c}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get is used to lookup a single key
|
// Get is used to lookup a single key. The returned pointer
|
||||||
|
// to the KVPair will be nil if the key does not exist.
|
||||||
func (k *KV) Get(key string, q *QueryOptions) (*KVPair, *QueryMeta, error) {
|
func (k *KV) Get(key string, q *QueryOptions) (*KVPair, *QueryMeta, error) {
|
||||||
resp, qm, err := k.getInternal(key, nil, q)
|
resp, qm, err := k.getInternal(key, nil, q)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -133,7 +156,7 @@ func (k *KV) Keys(prefix, separator string, q *QueryOptions) ([]string, *QueryMe
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *KV) getInternal(key string, params map[string]string, q *QueryOptions) (*http.Response, *QueryMeta, error) {
|
func (k *KV) getInternal(key string, params map[string]string, q *QueryOptions) (*http.Response, *QueryMeta, error) {
|
||||||
r := k.c.newRequest("GET", "/v1/kv/"+key)
|
r := k.c.newRequest("GET", "/v1/kv/"+strings.TrimPrefix(key, "/"))
|
||||||
r.setQueryOptions(q)
|
r.setQueryOptions(q)
|
||||||
for param, val := range params {
|
for param, val := range params {
|
||||||
r.params.Set(param, val)
|
r.params.Set(param, val)
|
||||||
|
@ -254,7 +277,7 @@ func (k *KV) DeleteTree(prefix string, w *WriteOptions) (*WriteMeta, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *KV) deleteInternal(key string, params map[string]string, q *WriteOptions) (bool, *WriteMeta, error) {
|
func (k *KV) deleteInternal(key string, params map[string]string, q *WriteOptions) (bool, *WriteMeta, error) {
|
||||||
r := k.c.newRequest("DELETE", "/v1/kv/"+key)
|
r := k.c.newRequest("DELETE", "/v1/kv/"+strings.TrimPrefix(key, "/"))
|
||||||
r.setWriteOptions(q)
|
r.setWriteOptions(q)
|
||||||
for param, val := range params {
|
for param, val := range params {
|
||||||
r.params.Set(param, val)
|
r.params.Set(param, val)
|
||||||
|
|
|
@ -72,8 +72,9 @@ type LockOptions struct {
|
||||||
Key string // Must be set and have write permissions
|
Key string // Must be set and have write permissions
|
||||||
Value []byte // Optional, value to associate with the lock
|
Value []byte // Optional, value to associate with the lock
|
||||||
Session string // Optional, created if not specified
|
Session string // Optional, created if not specified
|
||||||
SessionName string // Optional, defaults to DefaultLockSessionName
|
SessionOpts *SessionEntry // Optional, options to use when creating a session
|
||||||
SessionTTL string // Optional, defaults to DefaultLockSessionTTL
|
SessionName string // Optional, defaults to DefaultLockSessionName (ignored if SessionOpts is given)
|
||||||
|
SessionTTL string // Optional, defaults to DefaultLockSessionTTL (ignored if SessionOpts is given)
|
||||||
MonitorRetries int // Optional, defaults to 0 which means no retries
|
MonitorRetries int // Optional, defaults to 0 which means no retries
|
||||||
MonitorRetryTime time.Duration // Optional, defaults to DefaultMonitorRetryTime
|
MonitorRetryTime time.Duration // Optional, defaults to DefaultMonitorRetryTime
|
||||||
LockWaitTime time.Duration // Optional, defaults to DefaultLockWaitTime
|
LockWaitTime time.Duration // Optional, defaults to DefaultLockWaitTime
|
||||||
|
@ -329,10 +330,13 @@ func (l *Lock) Destroy() error {
|
||||||
// createSession is used to create a new managed session
|
// createSession is used to create a new managed session
|
||||||
func (l *Lock) createSession() (string, error) {
|
func (l *Lock) createSession() (string, error) {
|
||||||
session := l.c.Session()
|
session := l.c.Session()
|
||||||
se := &SessionEntry{
|
se := l.opts.SessionOpts
|
||||||
|
if se == nil {
|
||||||
|
se = &SessionEntry{
|
||||||
Name: l.opts.SessionName,
|
Name: l.opts.SessionName,
|
||||||
TTL: l.opts.SessionTTL,
|
TTL: l.opts.SessionTTL,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
id, _, err := session.Create(se, nil)
|
id, _, err := session.Create(se, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
|
|
@ -0,0 +1,163 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
// Operator can be used to perform low-level operator tasks for Consul.
|
||||||
|
type Operator struct {
|
||||||
|
c *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// Operator returns a handle to the operator endpoints.
|
||||||
|
func (c *Client) Operator() *Operator {
|
||||||
|
return &Operator{c}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RaftServer has information about a server in the Raft configuration.
|
||||||
|
type RaftServer struct {
|
||||||
|
// ID is the unique ID for the server. These are currently the same
|
||||||
|
// as the address, but they will be changed to a real GUID in a future
|
||||||
|
// release of Consul.
|
||||||
|
ID string
|
||||||
|
|
||||||
|
// Node is the node name of the server, as known by Consul, or this
|
||||||
|
// will be set to "(unknown)" otherwise.
|
||||||
|
Node string
|
||||||
|
|
||||||
|
// Address is the IP:port of the server, used for Raft communications.
|
||||||
|
Address string
|
||||||
|
|
||||||
|
// Leader is true if this server is the current cluster leader.
|
||||||
|
Leader bool
|
||||||
|
|
||||||
|
// Voter is true if this server has a vote in the cluster. This might
|
||||||
|
// be false if the server is staging and still coming online, or if
|
||||||
|
// it's a non-voting server, which will be added in a future release of
|
||||||
|
// Consul.
|
||||||
|
Voter bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// RaftConfigration is returned when querying for the current Raft configuration.
|
||||||
|
type RaftConfiguration struct {
|
||||||
|
// Servers has the list of servers in the Raft configuration.
|
||||||
|
Servers []*RaftServer
|
||||||
|
|
||||||
|
// Index has the Raft index of this configuration.
|
||||||
|
Index uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// keyringRequest is used for performing Keyring operations
|
||||||
|
type keyringRequest struct {
|
||||||
|
Key string
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyringResponse is returned when listing the gossip encryption keys
|
||||||
|
type KeyringResponse struct {
|
||||||
|
// Whether this response is for a WAN ring
|
||||||
|
WAN bool
|
||||||
|
|
||||||
|
// The datacenter name this request corresponds to
|
||||||
|
Datacenter string
|
||||||
|
|
||||||
|
// A map of the encryption keys to the number of nodes they're installed on
|
||||||
|
Keys map[string]int
|
||||||
|
|
||||||
|
// The total number of nodes in this ring
|
||||||
|
NumNodes int
|
||||||
|
}
|
||||||
|
|
||||||
|
// RaftGetConfiguration is used to query the current Raft peer set.
|
||||||
|
func (op *Operator) RaftGetConfiguration(q *QueryOptions) (*RaftConfiguration, error) {
|
||||||
|
r := op.c.newRequest("GET", "/v1/operator/raft/configuration")
|
||||||
|
r.setQueryOptions(q)
|
||||||
|
_, resp, err := requireOK(op.c.doRequest(r))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
var out RaftConfiguration
|
||||||
|
if err := decodeBody(resp, &out); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RaftRemovePeerByAddress is used to kick a stale peer (one that it in the Raft
|
||||||
|
// quorum but no longer known to Serf or the catalog) by address in the form of
|
||||||
|
// "IP:port".
|
||||||
|
func (op *Operator) RaftRemovePeerByAddress(address string, q *WriteOptions) error {
|
||||||
|
r := op.c.newRequest("DELETE", "/v1/operator/raft/peer")
|
||||||
|
r.setWriteOptions(q)
|
||||||
|
|
||||||
|
// TODO (slackpad) Currently we made address a query parameter. Once
|
||||||
|
// IDs are in place this will be DELETE /v1/operator/raft/peer/<id>.
|
||||||
|
r.params.Set("address", string(address))
|
||||||
|
|
||||||
|
_, resp, err := requireOK(op.c.doRequest(r))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.Body.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyringInstall is used to install a new gossip encryption key into the cluster
|
||||||
|
func (op *Operator) KeyringInstall(key string, q *WriteOptions) error {
|
||||||
|
r := op.c.newRequest("POST", "/v1/operator/keyring")
|
||||||
|
r.setWriteOptions(q)
|
||||||
|
r.obj = keyringRequest{
|
||||||
|
Key: key,
|
||||||
|
}
|
||||||
|
_, resp, err := requireOK(op.c.doRequest(r))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
resp.Body.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyringList is used to list the gossip keys installed in the cluster
|
||||||
|
func (op *Operator) KeyringList(q *QueryOptions) ([]*KeyringResponse, error) {
|
||||||
|
r := op.c.newRequest("GET", "/v1/operator/keyring")
|
||||||
|
r.setQueryOptions(q)
|
||||||
|
_, resp, err := requireOK(op.c.doRequest(r))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
var out []*KeyringResponse
|
||||||
|
if err := decodeBody(resp, &out); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyringRemove is used to remove a gossip encryption key from the cluster
|
||||||
|
func (op *Operator) KeyringRemove(key string, q *WriteOptions) error {
|
||||||
|
r := op.c.newRequest("DELETE", "/v1/operator/keyring")
|
||||||
|
r.setWriteOptions(q)
|
||||||
|
r.obj = keyringRequest{
|
||||||
|
Key: key,
|
||||||
|
}
|
||||||
|
_, resp, err := requireOK(op.c.doRequest(r))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
resp.Body.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyringUse is used to change the active gossip encryption key
|
||||||
|
func (op *Operator) KeyringUse(key string, q *WriteOptions) error {
|
||||||
|
r := op.c.newRequest("PUT", "/v1/operator/keyring")
|
||||||
|
r.setWriteOptions(q)
|
||||||
|
r.obj = keyringRequest{
|
||||||
|
Key: key,
|
||||||
|
}
|
||||||
|
_, resp, err := requireOK(op.c.doRequest(r))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
resp.Body.Close()
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -43,6 +43,11 @@ type ServiceQuery struct {
|
||||||
// this list it must be present. If the tag is preceded with "!" then
|
// this list it must be present. If the tag is preceded with "!" then
|
||||||
// it is disallowed.
|
// it is disallowed.
|
||||||
Tags []string
|
Tags []string
|
||||||
|
|
||||||
|
// NodeMeta is a map of required node metadata fields. If a key/value
|
||||||
|
// pair is in this map it must be present on the node in order for the
|
||||||
|
// service entry to be returned.
|
||||||
|
NodeMeta map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
// QueryTemplate carries the arguments for creating a templated query.
|
// QueryTemplate carries the arguments for creating a templated query.
|
||||||
|
@ -167,19 +172,18 @@ func (c *PreparedQuery) Get(queryID string, q *QueryOptions) ([]*PreparedQueryDe
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete is used to delete a specific prepared query.
|
// Delete is used to delete a specific prepared query.
|
||||||
func (c *PreparedQuery) Delete(queryID string, q *QueryOptions) (*QueryMeta, error) {
|
func (c *PreparedQuery) Delete(queryID string, q *WriteOptions) (*WriteMeta, error) {
|
||||||
r := c.c.newRequest("DELETE", "/v1/query/"+queryID)
|
r := c.c.newRequest("DELETE", "/v1/query/"+queryID)
|
||||||
r.setQueryOptions(q)
|
r.setWriteOptions(q)
|
||||||
rtt, resp, err := requireOK(c.c.doRequest(r))
|
rtt, resp, err := requireOK(c.c.doRequest(r))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
qm := &QueryMeta{}
|
wm := &WriteMeta{}
|
||||||
parseQueryMeta(resp, qm)
|
wm.RequestTime = rtt
|
||||||
qm.RequestTime = rtt
|
return wm, nil
|
||||||
return qm, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute is used to execute a specific prepared query. You can execute using
|
// Execute is used to execute a specific prepared query. You can execute using
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Snapshot can be used to query the /v1/snapshot endpoint to take snapshots of
|
||||||
|
// Consul's internal state and restore snapshots for disaster recovery.
|
||||||
|
type Snapshot struct {
|
||||||
|
c *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// Snapshot returns a handle that exposes the snapshot endpoints.
|
||||||
|
func (c *Client) Snapshot() *Snapshot {
|
||||||
|
return &Snapshot{c}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save requests a new snapshot and provides an io.ReadCloser with the snapshot
|
||||||
|
// data to save. If this doesn't return an error, then it's the responsibility
|
||||||
|
// of the caller to close it. Only a subset of the QueryOptions are supported:
|
||||||
|
// Datacenter, AllowStale, and Token.
|
||||||
|
func (s *Snapshot) Save(q *QueryOptions) (io.ReadCloser, *QueryMeta, error) {
|
||||||
|
r := s.c.newRequest("GET", "/v1/snapshot")
|
||||||
|
r.setQueryOptions(q)
|
||||||
|
|
||||||
|
rtt, resp, err := requireOK(s.c.doRequest(r))
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
qm := &QueryMeta{}
|
||||||
|
parseQueryMeta(resp, qm)
|
||||||
|
qm.RequestTime = rtt
|
||||||
|
return resp.Body, qm, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore streams in an existing snapshot and attempts to restore it.
|
||||||
|
func (s *Snapshot) Restore(q *WriteOptions, in io.Reader) error {
|
||||||
|
r := s.c.newRequest("PUT", "/v1/snapshot")
|
||||||
|
r.body = in
|
||||||
|
r.setWriteOptions(q)
|
||||||
|
_, _, err := requireOK(s.c.doRequest(r))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -1634,11 +1634,11 @@
|
||||||
"revisionTime": "2016-11-07T20:49:10Z"
|
"revisionTime": "2016-11-07T20:49:10Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "ZY6NCrR80zUmtOtPtKffbmFxRWw=",
|
"checksumSHA1": "ygEjA1d52B1RDmZu8+1WTwkrYDQ=",
|
||||||
"comment": "v0.6.3-28-g3215b87",
|
"comment": "v0.6.3-28-g3215b87",
|
||||||
"path": "github.com/hashicorp/consul/api",
|
"path": "github.com/hashicorp/consul/api",
|
||||||
"revision": "6e061b2d580d80347b7c5c4dfc8730de7403a145",
|
"revision": "48d7b069ad443a48ffa93364048ff8909b5d1fa2",
|
||||||
"revisionTime": "2016-07-03T02:45:54Z"
|
"revisionTime": "2017-02-07T15:38:46Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "cdOCt0Yb+hdErz8NAQqayxPmRsY=",
|
"checksumSHA1": "cdOCt0Yb+hdErz8NAQqayxPmRsY=",
|
||||||
|
|
|
@ -0,0 +1,163 @@
|
||||||
|
---
|
||||||
|
layout: "consul"
|
||||||
|
page_title: "Consul: consul_agent_self"
|
||||||
|
sidebar_current: "docs-consul-data-source-agent-self"
|
||||||
|
description: |-
|
||||||
|
Provides the configuration information of the local Consul agent.
|
||||||
|
---
|
||||||
|
|
||||||
|
# consul\_agent_\_self
|
||||||
|
|
||||||
|
The `consul_agent_self` data source returns
|
||||||
|
[configuration and status data](https://www.consul.io/docs/agent/http/agent.html#agent_self)
|
||||||
|
from the agent specified in the `provider`.
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
data "consul_agent_self" "read-dc1-agent" {
|
||||||
|
# query_options {
|
||||||
|
# # Optional parameter: implicitly uses the current datacenter of the agent
|
||||||
|
# datacenter = "dc1"
|
||||||
|
# }
|
||||||
|
}
|
||||||
|
|
||||||
|
# Set the description to a whitespace delimited list of the services
|
||||||
|
resource "example_resource" "app" {
|
||||||
|
description = "Consul datacenter ${data.consul_agent_self.read-dc1-agent.datacenter}"
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Attributes Reference
|
||||||
|
|
||||||
|
The following attributes are exported:
|
||||||
|
|
||||||
|
* [`acl_datacenter`](https://www.consul.io/docs/agent/options.html#acl_datacenter)
|
||||||
|
* [`acl_default_policy`](https://www.consul.io/docs/agent/options.html#acl_default_policy)
|
||||||
|
* `acl_disabled_ttl`
|
||||||
|
* [`acl_down_policy`](https://www.consul.io/docs/agent/options.html#acl_down_policy)
|
||||||
|
* [`acl_enforce_0_8_semantics`](https://www.consul.io/docs/agent/options.html#acl_enforce_version_8)
|
||||||
|
* [`acl_ttl`](https://www.consul.io/docs/agent/options.html#acl_ttl)
|
||||||
|
* [`addresses`](https://www.consul.io/docs/agent/options.html#addresses)
|
||||||
|
* [`advertise_addr`](https://www.consul.io/docs/agent/options.html#_advertise)
|
||||||
|
* [`advertise_addr_wan`](https://www.consul.io/docs/agent/options.html#_advertise-wan)
|
||||||
|
* [`advertise_addrs`](https://www.consul.io/docs/agent/options.html#advertise_addrs)
|
||||||
|
* [`atlas_join`](https://www.consul.io/docs/agent/options.html#_atlas_join)
|
||||||
|
* [`bind_addr`](https://www.consul.io/docs/agent/options.html#_bind)
|
||||||
|
* [`bootstrap_expect`](https://www.consul.io/docs/agent/options.html#_bootstrap_expect)
|
||||||
|
* [`bootstrap_mode`](https://www.consul.io/docs/agent/options.html#_bootstrap)
|
||||||
|
* `check_deregister_interval_min`
|
||||||
|
* `check_reap_interval`
|
||||||
|
* [`check_update_interval`](https://www.consul.io/docs/agent/options.html#check_update_interval)
|
||||||
|
* [`client_addr`](https://www.consul.io/docs/agent/options.html#_client)
|
||||||
|
* `dns` - A map of DNS configuration attributes. See below for details on the
|
||||||
|
contents of the `dns` attribute.
|
||||||
|
* [`dns_recursors`](https://www.consul.io/docs/agent/options.html#recursors) - A
|
||||||
|
list of all DNS recursors.
|
||||||
|
* [`data_dir`](https://www.consul.io/docs/agent/options.html#_data_dir)
|
||||||
|
* [`datacenter`](https://www.consul.io/docs/agent/options.html#_datacenter)
|
||||||
|
* [`dev_mode`](https://www.consul.io/docs/agent/options.html#_dev)
|
||||||
|
* [`domain`](https://www.consul.io/docs/agent/options.html#_domain)
|
||||||
|
* [`enable_anonymous_signature`](https://www.consul.io/docs/agent/options.html#disable_anonymous_signature)
|
||||||
|
* `enable_coordinates`
|
||||||
|
* [`enable_debug`](https://www.consul.io/docs/agent/options.html#enable_debug)
|
||||||
|
* [`enable_remote_exec`](https://www.consul.io/docs/agent/options.html#disable_remote_exec)
|
||||||
|
* [`enable_syslog`](https://www.consul.io/docs/agent/options.html#_syslog)
|
||||||
|
* [`enable_ui`](https://www.consul.io/docs/agent/options.html#_ui)
|
||||||
|
* [`enable_update_check`](https://www.consul.io/docs/agent/options.html#disable_update_check)
|
||||||
|
* [`id`](https://www.consul.io/docs/agent/options.html#_node_id)
|
||||||
|
* [`leave_on_int`](https://www.consul.io/docs/agent/options.html#skip_leave_on_interrupt)
|
||||||
|
* [`leave_on_term`](https://www.consul.io/docs/agent/options.html#leave_on_terminate)
|
||||||
|
* [`log_level`](https://www.consul.io/docs/agent/options.html#_log_level)
|
||||||
|
* [`name`](https://www.consul.io/docs/agent/options.html#_node)
|
||||||
|
* [`performance`](https://www.consul.io/docs/agent/options.html#performance)
|
||||||
|
* [`pid_file`](https://www.consul.io/docs/agent/options.html#_pid_file)
|
||||||
|
* [`ports`](https://www.consul.io/docs/agent/options.html#ports)
|
||||||
|
* [`protocol_version`](https://www.consul.io/docs/agent/options.html#_protocol)
|
||||||
|
* [`reconnect_timeout_lan`](https://www.consul.io/docs/agent/options.html#reconnect_timeout)
|
||||||
|
* [`reconnect_timeout_wan`](https://www.consul.io/docs/agent/options.html#reconnect_timeout_wan)
|
||||||
|
* [`rejoin_after_leave`](https://www.consul.io/docs/agent/options.html#_rejoin)
|
||||||
|
* [`retry_join`](https://www.consul.io/docs/agent/options.html#retry_join)
|
||||||
|
* [`retry_join_ec2`](https://www.consul.io/docs/agent/options.html#retry_join_ec2) -
|
||||||
|
A map of EC2 retry attributes. See below for details on the available
|
||||||
|
information.
|
||||||
|
* [`retry_join_gce`](https://www.consul.io/docs/agent/options.html#retry_join_gce) -
|
||||||
|
A map of GCE retry attributes. See below for details on the available
|
||||||
|
information.
|
||||||
|
* [`retry_join_wan`](https://www.consul.io/docs/agent/options.html#_retry_join_wan)
|
||||||
|
* [`retry_max_attempts`](https://www.consul.io/docs/agent/options.html#_retry_max)
|
||||||
|
* [`retry_max_attempts_wan`](https://www.consul.io/docs/agent/options.html#_retry_max_wan)
|
||||||
|
* [`serf_lan_bind_addr`](https://www.consul.io/docs/agent/options.html#_serf_lan_bind)
|
||||||
|
* [`serf_wan_bind_addr`](https://www.consul.io/docs/agent/options.html#_serf_wan_bind)
|
||||||
|
* [`server_mode`](https://www.consul.io/docs/agent/options.html#_server)
|
||||||
|
* [`server_name`](https://www.consul.io/docs/agent/options.html#server_name)
|
||||||
|
* [`session_ttl_min`](https://www.consul.io/docs/agent/options.html#session_ttl_min)
|
||||||
|
* [`start_join`](https://www.consul.io/docs/agent/options.html#start_join)
|
||||||
|
* [`start_join_wan`](https://www.consul.io/docs/agent/options.html#start_join_wan)
|
||||||
|
* [`syslog_facility`](https://www.consul.io/docs/agent/options.html#syslog_facility)
|
||||||
|
* [`tls_ca_file`](https://www.consul.io/docs/agent/options.html#ca_file)
|
||||||
|
* [`tls_cert_file`](https://www.consul.io/docs/agent/options.html#cert_file)
|
||||||
|
* [`tls_key_file`](https://www.consul.io/docs/agent/options.html#key_file)
|
||||||
|
* [`tls_min_version`](https://www.consul.io/docs/agent/options.html#tls_min_version)
|
||||||
|
* [`tls_verify_incoming`](https://www.consul.io/docs/agent/options.html#verify_incoming)
|
||||||
|
* [`tls_verify_outgoing`](https://www.consul.io/docs/agent/options.html#verify_outgoing)
|
||||||
|
* [`tls_verify_server_hostname`](https://www.consul.io/docs/agent/options.html#verify_server_hostname)
|
||||||
|
* [`tagged_addresses`](https://www.consul.io/docs/agent/options.html#translate_wan_addrs)
|
||||||
|
* [`telemetry`](https://www.consul.io/docs/agent/options.html#telemetry) - A map
|
||||||
|
of telemetry configuration.
|
||||||
|
* [`translate_wan_addrs`](https://www.consul.io/docs/agent/options.html#translate_wan_addrs)
|
||||||
|
* [`ui_dir`](https://www.consul.io/docs/agent/options.html#ui_dir)
|
||||||
|
* [`unix_sockets`](https://www.consul.io/docs/agent/options.html#unix_sockets)
|
||||||
|
* `version` - The version of the Consul agent.
|
||||||
|
* `version_prerelease`
|
||||||
|
* `version_revision`
|
||||||
|
|
||||||
|
### DNS Attributes
|
||||||
|
|
||||||
|
* [`allow_stale`](https://www.consul.io/docs/agent/options.html#allow_stale)
|
||||||
|
* [`enable_compression`](https://www.consul.io/docs/agent/options.html#disable_compression)
|
||||||
|
* [`enable_truncate`](https://www.consul.io/docs/agent/options.html#enable_truncate)
|
||||||
|
* [`max_stale`](https://www.consul.io/docs/agent/options.html#max_stale)
|
||||||
|
* [`node_ttl`](https://www.consul.io/docs/agent/options.html#node_ttl)
|
||||||
|
* [`only_passing`](https://www.consul.io/docs/agent/options.html#only_passing)
|
||||||
|
* [`recursor_timeout`](https://www.consul.io/docs/agent/options.html#recursor_timeout)
|
||||||
|
* [`service_ttl`](https://www.consul.io/docs/agent/options.html#service_ttl)
|
||||||
|
* [`udp_answer_limit`](https://www.consul.io/docs/agent/options.html#udp_answer_limit)
|
||||||
|
|
||||||
|
### Retry Join EC2 Attributes
|
||||||
|
|
||||||
|
* [`access_key_id`](https://www.consul.io/docs/agent/options.html#access_key_id)
|
||||||
|
* [`region`](https://www.consul.io/docs/agent/options.html#region)
|
||||||
|
* [`secret_access_key`](https://www.consul.io/docs/agent/options.html#secret_access_key)
|
||||||
|
* [`tag_key`](https://www.consul.io/docs/agent/options.html#tag_key)
|
||||||
|
* [`tag_value`](https://www.consul.io/docs/agent/options.html#tag_value)
|
||||||
|
|
||||||
|
### Retry Join GCE Attributes
|
||||||
|
|
||||||
|
* [`credentials_file`](https://www.consul.io/docs/agent/options.html#credentials_file)
|
||||||
|
* [`project_name`](https://www.consul.io/docs/agent/options.html#project_name)
|
||||||
|
* [`tag_value`](https://www.consul.io/docs/agent/options.html#tag_value)
|
||||||
|
* [`zone_pattern`](https://www.consul.io/docs/agent/options.html#zone_pattern)
|
||||||
|
|
||||||
|
### Telemetry Attributes
|
||||||
|
|
||||||
|
* [`circonus_api_app`](https://www.consul.io/docs/agent/options.html#telemetry-circonus_api_app)
|
||||||
|
* [`circonus_api_token`](https://www.consul.io/docs/agent/options.html#telemetry-circonus_api_token)
|
||||||
|
* [`circonus_api_url`](https://www.consul.io/docs/agent/options.html#telemetry-circonus_api_url)
|
||||||
|
* [`circonus_broker_id`](https://www.consul.io/docs/agent/options.html#telemetry-circonus_broker_id)
|
||||||
|
* [`circonus_check_id`](https://www.consul.io/docs/agent/options.html#telemetry-circonus_check_id)
|
||||||
|
* [`circonus_check_tags`](https://www.consul.io/docs/agent/options.html#telemetry-circonus_check_tags)
|
||||||
|
* [`circonus_display_name`](https://www.consul.io/docs/agent/options.html#telemetry-circonus_check_display_name)
|
||||||
|
* [`circonus_force_metric_activation`](https://www.consul.io/docs/agent/options.html#telemetry-circonus_check_force_metric_activation)
|
||||||
|
* [`circonus_instance_id`](https://www.consul.io/docs/agent/options.html#telemetry-circonus_check_instance_id)
|
||||||
|
* [`circonus_search_tag`](https://www.consul.io/docs/agent/options.html#telemetry-circonus_check_search_tag)
|
||||||
|
* [`circonus_select_tag`](https://www.consul.io/docs/agent/options.html#telemetry-circonus_broker_select_tag)
|
||||||
|
* [`circonus_submission_interval`](https://www.consul.io/docs/agent/options.html#telemetry-circonus_submission_interval)
|
||||||
|
* [`circonus_submission_url`](https://www.consul.io/docs/agent/options.html#telemetry-circonus_submission_url)
|
||||||
|
* [`dogstatsd_addr`](https://www.consul.io/docs/agent/options.html#telemetry-dogstatsd_addr)
|
||||||
|
* [`dogstatsd_tags`](https://www.consul.io/docs/agent/options.html#telemetry-dogstatsd_tags)
|
||||||
|
* [`enable_hostname`](https://www.consul.io/docs/agent/options.html#telemetry-disable_hostname)
|
||||||
|
* [`statsd_addr`](https://www.consul.io/docs/agent/options.html#telemetry-statsd_address)
|
||||||
|
* [`statsite_addr`](https://www.consul.io/docs/agent/options.html#telemetry-statsite_address)
|
||||||
|
* [`statsite_prefix`](https://www.consul.io/docs/agent/options.html#telemetry-statsite_prefix)
|
|
@ -0,0 +1,83 @@
|
||||||
|
---
|
||||||
|
layout: "consul"
|
||||||
|
page_title: "Consul: consul_catalog_nodes"
|
||||||
|
sidebar_current: "docs-consul-data-source-catalog-nodes"
|
||||||
|
description: |-
|
||||||
|
Provides a list of nodes in a given Consul datacenter.
|
||||||
|
---
|
||||||
|
|
||||||
|
# consul\_catalog\_nodes
|
||||||
|
|
||||||
|
The `consul_catalog_nodes` data source returns a list of Consul nodes that have
|
||||||
|
been registered with the Consul cluster in a given datacenter. By specifying a
|
||||||
|
different datacenter in the `query_options` it is possible to retrieve a list of
|
||||||
|
nodes from a different WAN-attached Consul datacenter.
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
data "consul_catalog_nodes" "read-dc1-nodes" {
|
||||||
|
# query_options {
|
||||||
|
# # Optional parameter: implicitly uses the current datacenter of the agent
|
||||||
|
# datacenter = "dc1"
|
||||||
|
# }
|
||||||
|
}
|
||||||
|
|
||||||
|
# Set the description to a whitespace delimited list of the node names
|
||||||
|
resource "example_resource" "app" {
|
||||||
|
description = "${join(" ", formatlist("%s", data.consul_catalog_nodes.node_names))}"
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Argument Reference
|
||||||
|
|
||||||
|
The following arguments are supported:
|
||||||
|
|
||||||
|
* `datacenter` - (Optional) The Consul datacenter to query. Defaults to the
|
||||||
|
same value found in `query_options` parameter specified below, or if that is
|
||||||
|
empty, the `datacenter` value found in the Consul agent that this provider is
|
||||||
|
configured to talk to.
|
||||||
|
|
||||||
|
* `query_options` - (Optional) See below.
|
||||||
|
|
||||||
|
The `query_options` block supports the following:
|
||||||
|
|
||||||
|
* `allow_stale` - (Optional) When `true`, the default, allow responses from
|
||||||
|
Consul servers that are followers.
|
||||||
|
|
||||||
|
* `require_consistent` - (Optional) When `true` force the client to perform a
|
||||||
|
read on at least quorum servers and verify the result is the same. Defaults
|
||||||
|
to `false`.
|
||||||
|
|
||||||
|
* `token` - (Optional) Specify the Consul ACL token to use when performing the
|
||||||
|
request. This defaults to the same API token configured by the `consul`
|
||||||
|
provider but may be overriden if necessary.
|
||||||
|
|
||||||
|
* `wait_index` - (Optional) Index number used to enable blocking quereis.
|
||||||
|
|
||||||
|
* `wait_time` - (Optional) Max time the client should wait for a blocking query
|
||||||
|
to return.
|
||||||
|
|
||||||
|
## Attributes Reference
|
||||||
|
|
||||||
|
The following attributes are exported:
|
||||||
|
|
||||||
|
* `datacenter` - The datacenter the keys are being read from to.
|
||||||
|
* `node_ids` - A list of the Consul node IDs.
|
||||||
|
* `node_names` - A list of the Consul node names.
|
||||||
|
* `nodes` - A list of nodes and details about each Consul agent. The list of
|
||||||
|
per-node attributes is detailed below.
|
||||||
|
|
||||||
|
The following is a list of the per-node attributes contained within the `nodes`
|
||||||
|
map:
|
||||||
|
|
||||||
|
* `id` - The Node ID of the Consul agent.
|
||||||
|
* [`meta`](https://www.consul.io/docs/agent/http/catalog.html#Meta) - Node meta
|
||||||
|
data tag information, if any.
|
||||||
|
* [`name`](https://www.consul.io/docs/agent/http/catalog.html#Node) - The name
|
||||||
|
of the Consul node.
|
||||||
|
* [`address`](https://www.consul.io/docs/agent/http/catalog.html#Address) - The
|
||||||
|
IP address the node is advertising to the Consul cluster.
|
||||||
|
* [`tagged_addresses`](https://www.consul.io/docs/agent/http/catalog.html#TaggedAddresses) -
|
||||||
|
List of explicit LAN and WAN IP addresses for the agent.
|
|
@ -0,0 +1,112 @@
|
||||||
|
---
|
||||||
|
layout: "consul"
|
||||||
|
page_title: "Consul: consul_catalog_service"
|
||||||
|
sidebar_current: "docs-consul-data-source-catalog-service"
|
||||||
|
description: |-
|
||||||
|
Provides details about a specific Consul service
|
||||||
|
---
|
||||||
|
|
||||||
|
# consul\_catalog\_service
|
||||||
|
|
||||||
|
`consul_catalog_service` provides details about a specific Consul service in a
|
||||||
|
given datacenter. The results include a list of nodes advertising the specified
|
||||||
|
service, the node's IP address, port number, node ID, etc. By specifying a
|
||||||
|
different datacenter in the `query_options` it is possible to retrieve a list of
|
||||||
|
services from a different WAN-attached Consul datacenter.
|
||||||
|
|
||||||
|
This data source is different from the `consul_catalog_services` (plural) data
|
||||||
|
source, which provides a summary of the current Consul services.
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
data "consul_catalog_service" "read-consul-dc1" {
|
||||||
|
# query_options {
|
||||||
|
# # Optional parameter: implicitly uses the current datacenter of the agent
|
||||||
|
# datacenter = "dc1"
|
||||||
|
# }
|
||||||
|
|
||||||
|
name = "consul"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Set the description to a whitespace delimited list of the node names
|
||||||
|
resource "example_resource" "app" {
|
||||||
|
description = "${join(" ", data.consul_catalog_service.nodes)}"
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Argument Reference
|
||||||
|
|
||||||
|
The following arguments are supported:
|
||||||
|
|
||||||
|
* `datacenter` - (Optional) The Consul datacenter to query. Defaults to the
|
||||||
|
same value found in `query_options` parameter specified below, or if that is
|
||||||
|
empty, the `datacenter` value found in the Consul agent that this provider is
|
||||||
|
configured to talk to.
|
||||||
|
|
||||||
|
* `name` - (Required) The service name to select.
|
||||||
|
|
||||||
|
* `query_options` - (Optional) See below.
|
||||||
|
|
||||||
|
* `tag` - (Optional) A single tag that can be used to filter the list of nodes
|
||||||
|
to return based on a single matching tag..
|
||||||
|
|
||||||
|
The `query_options` block supports the following:
|
||||||
|
|
||||||
|
* `allow_stale` - (Optional) When `true`, the default, allow responses from
|
||||||
|
Consul servers that are followers.
|
||||||
|
|
||||||
|
* `require_consistent` - (Optional) When `true` force the client to perform a
|
||||||
|
read on at least quorum servers and verify the result is the same. Defaults
|
||||||
|
to `false`.
|
||||||
|
|
||||||
|
* `token` - (Optional) Specify the Consul ACL token to use when performing the
|
||||||
|
request. This defaults to the same API token configured by the `consul`
|
||||||
|
provider but may be overriden if necessary.
|
||||||
|
|
||||||
|
* `wait_index` - (Optional) Index number used to enable blocking quereis.
|
||||||
|
|
||||||
|
* `wait_time` - (Optional) Max time the client should wait for a blocking query
|
||||||
|
to return.
|
||||||
|
|
||||||
|
## Attributes Reference
|
||||||
|
|
||||||
|
The following attributes are exported:
|
||||||
|
|
||||||
|
* `datacenter` - The datacenter the keys are being read from to.
|
||||||
|
* `name` - The name of the service
|
||||||
|
* `tag` - The name of the tag used to filter the list of nodes in `service`.
|
||||||
|
* `service` - A list of nodes and details about each endpoint advertising a
|
||||||
|
service. Each element in the list is a map of attributes that correspond to
|
||||||
|
each individual node. The list of per-node attributes is detailed below.
|
||||||
|
|
||||||
|
The following is a list of the per-node `service` attributes:
|
||||||
|
|
||||||
|
* [`create_index`](https://www.consul.io/docs/agent/http/catalog.html#CreateIndex) -
|
||||||
|
The index entry at which point this entry was added to the catalog.
|
||||||
|
* [`modify_index`](https://www.consul.io/docs/agent/http/catalog.html#ModifyIndex) -
|
||||||
|
The index entry at which point this entry was modified in the catalog.
|
||||||
|
* [`node_address`](https://www.consul.io/docs/agent/http/catalog.html#Address) -
|
||||||
|
The address of the Consul node advertising the service.
|
||||||
|
* `node_id` - The Node ID of the Consul agent advertising the service.
|
||||||
|
* [`node_meta`](https://www.consul.io/docs/agent/http/catalog.html#Meta) - Node
|
||||||
|
meta data tag information, if any.
|
||||||
|
* [`node_name`](https://www.consul.io/docs/agent/http/catalog.html#Node) - The
|
||||||
|
name of the Consul node.
|
||||||
|
* [`address`](https://www.consul.io/docs/agent/http/catalog.html#ServiceAddress) -
|
||||||
|
The IP address of the service. If the `ServiceAddress` in the Consul catalog
|
||||||
|
is empty, this value is automatically populated with the `node_address` (the
|
||||||
|
`Address` in the Consul Catalog).
|
||||||
|
* [`enable_tag_override`](https://www.consul.io/docs/agent/http/catalog.html#ServiceEnableTagOverride) -
|
||||||
|
Whether service tags can be overridden on this service.
|
||||||
|
* [`id`](https://www.consul.io/docs/agent/http/catalog.html#ServiceID) - A
|
||||||
|
unique service instance identifier.
|
||||||
|
* [`name`](https://www.consul.io/docs/agent/http/catalog.html#ServiceName) - The
|
||||||
|
name of the service.
|
||||||
|
* [`port`](https://www.consul.io/docs/agent/http/catalog.html#ServicePort) -
|
||||||
|
Port number of the service.
|
||||||
|
* [`tagged_addresses`](https://www.consul.io/docs/agent/http/catalog.html#TaggedAddresses) -
|
||||||
|
List of explicit LAN and WAN IP addresses for the agent.
|
||||||
|
* [`tags`](https://www.consul.io/docs/agent/http/catalog.html#ServiceTags) -
|
||||||
|
List of tags for the service.
|
|
@ -0,0 +1,78 @@
|
||||||
|
---
|
||||||
|
layout: "consul"
|
||||||
|
page_title: "Consul: consul_catalog_services"
|
||||||
|
sidebar_current: "docs-consul-data-source-catalog-services"
|
||||||
|
description: |-
|
||||||
|
Provides a list of services in a given Consul datacenter.
|
||||||
|
---
|
||||||
|
|
||||||
|
# consul\_catalog\_services
|
||||||
|
|
||||||
|
The `consul_catalog_services` data source returns a list of Consul services that
|
||||||
|
have been registered with the Consul cluster in a given datacenter. By
|
||||||
|
specifying a different datacenter in the `query_options` it is possible to
|
||||||
|
retrieve a list of services from a different WAN-attached Consul datacenter.
|
||||||
|
|
||||||
|
This data source is different from the `consul_catalog_service` (singular) data
|
||||||
|
source, which provides a detailed response about a specific Consul service.
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
data "consul_catalog_services" "read-dc1" {
|
||||||
|
# query_options {
|
||||||
|
# # Optional parameter: implicitly uses the current datacenter of the agent
|
||||||
|
# datacenter = "dc1"
|
||||||
|
# }
|
||||||
|
}
|
||||||
|
|
||||||
|
# Set the description to a whitespace delimited list of the services
|
||||||
|
resource "example_resource" "app" {
|
||||||
|
description = "${join(" ", data.consul_catalog_services.names)}"
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Argument Reference
|
||||||
|
|
||||||
|
The following arguments are supported:
|
||||||
|
|
||||||
|
* `datacenter` - (Optional) The Consul datacenter to query. Defaults to the
|
||||||
|
same value found in `query_options` parameter specified below, or if that is
|
||||||
|
empty, the `datacenter` value found in the Consul agent that this provider is
|
||||||
|
configured to talk to.
|
||||||
|
|
||||||
|
* `query_options` - (Optional) See below.
|
||||||
|
|
||||||
|
The `query_options` block supports the following:
|
||||||
|
|
||||||
|
* `allow_stale` - (Optional) When `true`, the default, allow responses from
|
||||||
|
Consul servers that are followers.
|
||||||
|
|
||||||
|
* `require_consistent` - (Optional) When `true` force the client to perform a
|
||||||
|
read on at least quorum servers and verify the result is the same. Defaults
|
||||||
|
to `false`.
|
||||||
|
|
||||||
|
* `token` - (Optional) Specify the Consul ACL token to use when performing the
|
||||||
|
request. This defaults to the same API token configured by the `consul`
|
||||||
|
provider but may be overriden if necessary.
|
||||||
|
|
||||||
|
* `wait_index` - (Optional) Index number used to enable blocking quereis.
|
||||||
|
|
||||||
|
* `wait_time` - (Optional) Max time the client should wait for a blocking query
|
||||||
|
to return.
|
||||||
|
|
||||||
|
## Attributes Reference
|
||||||
|
|
||||||
|
The following attributes are exported:
|
||||||
|
|
||||||
|
* `datacenter` - The datacenter the keys are being read from to.
|
||||||
|
* `names` - A list of the Consul services found. This will always contain the
|
||||||
|
list of services found.
|
||||||
|
* `services.<service>` - For each name given, the corresponding attribute is a
|
||||||
|
Terraform map of services and their tags. The value is an alphanumerically
|
||||||
|
sorted, whitespace delimited set of tags associated with the service.
|
||||||
|
* `tags` - A map of the tags found for each service. If more than one service
|
||||||
|
shares the same tag, unique service names will be joined by whitespace (this
|
||||||
|
is the inverse of `services` and can be used to lookup the services that match
|
||||||
|
a single tag).
|
|
@ -13,6 +13,18 @@
|
||||||
<li<%= sidebar_current(/^docs-consul-data-source/) %>>
|
<li<%= sidebar_current(/^docs-consul-data-source/) %>>
|
||||||
<a href="#">Data Sources</a>
|
<a href="#">Data Sources</a>
|
||||||
<ul class="nav nav-visible">
|
<ul class="nav nav-visible">
|
||||||
|
<li<%= sidebar_current("docs-consul-data-source-agent-self") %>>
|
||||||
|
<a href="/docs/providers/consul/d/agent_self.html">consul_agent_self</a>
|
||||||
|
</li>
|
||||||
|
<li<%= sidebar_current("docs-consul-data-source-catalog-nodes") %>>
|
||||||
|
<a href="/docs/providers/consul/d/nodes.html">consul_catalog_nodes</a>
|
||||||
|
</li>
|
||||||
|
<li<%= sidebar_current("docs-consul-data-source-catalog-service") %>>
|
||||||
|
<a href="/docs/providers/consul/d/service.html">consul_catalog_service</a>
|
||||||
|
</li>
|
||||||
|
<li<%= sidebar_current("docs-consul-data-source-catalog-services") %>>
|
||||||
|
<a href="/docs/providers/consul/d/services.html">consul_catalog_services</a>
|
||||||
|
</li>
|
||||||
<li<%= sidebar_current("docs-consul-data-source-keys") %>>
|
<li<%= sidebar_current("docs-consul-data-source-keys") %>>
|
||||||
<a href="/docs/providers/consul/d/keys.html">consul_keys</a>
|
<a href="/docs/providers/consul/d/keys.html">consul_keys</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
Loading…
Reference in New Issue