Update `consul_catalog_nodes` to conform to normal resource guidelines.
This commit is contained in:
parent
5567732814
commit
1476445593
|
@ -9,315 +9,113 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Top-level consul_catalog_nodes attributes
|
|
||||||
const (
|
const (
|
||||||
catalogNodes typeKey = iota
|
allowStale = "allow_stale"
|
||||||
catalogNodesAllowStale
|
nodeMeta = "node_meta"
|
||||||
catalogNodesDatacenter
|
nodesAttr = "nodes"
|
||||||
catalogNodesNear
|
requireConsistent = "require_consistent"
|
||||||
catalogNodesRequireConsistent
|
token = "token"
|
||||||
catalogNodesToken
|
waitIndex = "wait_index"
|
||||||
catalogNodesWaitIndex
|
waitTime = "wait_time"
|
||||||
catalogNodesWaitTime
|
|
||||||
|
nodeID = "id"
|
||||||
|
nodeAddress = "address"
|
||||||
|
nodeMetaAttr = "meta"
|
||||||
|
nodeName = "name"
|
||||||
|
nodeTaggedAddresses = "tagged_addresses"
|
||||||
|
|
||||||
|
apiTaggedLAN = "lan"
|
||||||
|
apiTaggedWAN = "wan"
|
||||||
|
schemaTaggedLAN = "lan"
|
||||||
|
schemaTaggedWAN = "wan"
|
||||||
)
|
)
|
||||||
|
|
||||||
// node.* attributes
|
|
||||||
const (
|
|
||||||
catalogNodeID typeKey = iota
|
|
||||||
catalogNodeName
|
|
||||||
catalogNodeAddress
|
|
||||||
catalogNodeTaggedAddresses
|
|
||||||
catalogNodeMeta
|
|
||||||
)
|
|
||||||
|
|
||||||
// node.tagged_addresses.* attributes
|
|
||||||
const (
|
|
||||||
catalogNodeTaggedAddressesLAN typeKey = iota
|
|
||||||
catalogNodeTaggedAddressesWAN
|
|
||||||
)
|
|
||||||
|
|
||||||
var catalogNodeAttrs = map[typeKey]*typeEntry{
|
|
||||||
catalogNodeID: {
|
|
||||||
APIName: "ID",
|
|
||||||
SchemaName: "id",
|
|
||||||
Source: sourceAPIResult,
|
|
||||||
Type: schema.TypeString,
|
|
||||||
ValidateFuncs: []interface{}{
|
|
||||||
validateRegexp(`^[\S]+$`),
|
|
||||||
},
|
|
||||||
APITest: func(e *typeEntry, v interface{}) (interface{}, bool) {
|
|
||||||
node := v.(*consulapi.Node)
|
|
||||||
|
|
||||||
if id := node.ID; id != "" {
|
|
||||||
return id, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use the node name - confusingly stored in the Node attribute - if no ID
|
|
||||||
// is available.
|
|
||||||
if name := node.Node; name != "" {
|
|
||||||
return name, true
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", false
|
|
||||||
},
|
|
||||||
},
|
|
||||||
catalogNodeName: {
|
|
||||||
APIName: "Name",
|
|
||||||
SchemaName: "name",
|
|
||||||
Source: sourceAPIResult,
|
|
||||||
Type: schema.TypeString,
|
|
||||||
ValidateFuncs: []interface{}{
|
|
||||||
validateRegexp(`^[\S]+$`),
|
|
||||||
},
|
|
||||||
APITest: func(e *typeEntry, v interface{}) (interface{}, bool) {
|
|
||||||
node := v.(*consulapi.Node)
|
|
||||||
|
|
||||||
if name := node.Node; name != "" {
|
|
||||||
return name, true
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", false
|
|
||||||
},
|
|
||||||
},
|
|
||||||
catalogNodeAddress: {
|
|
||||||
APIName: "Address",
|
|
||||||
SchemaName: "address",
|
|
||||||
Source: sourceAPIResult,
|
|
||||||
Type: schema.TypeString,
|
|
||||||
APITest: func(e *typeEntry, v interface{}) (interface{}, bool) {
|
|
||||||
node := v.(*consulapi.Node)
|
|
||||||
|
|
||||||
if addr := node.Address; addr != "" {
|
|
||||||
return addr, true
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", false
|
|
||||||
},
|
|
||||||
},
|
|
||||||
catalogNodeTaggedAddresses: {
|
|
||||||
APIName: "TaggedAddresses",
|
|
||||||
SchemaName: "tagged_addresses",
|
|
||||||
Source: sourceAPIResult,
|
|
||||||
Type: schema.TypeMap,
|
|
||||||
SetMembers: map[typeKey]*typeEntry{
|
|
||||||
catalogNodeTaggedAddressesLAN: {
|
|
||||||
APIName: "LAN",
|
|
||||||
SchemaName: "lan",
|
|
||||||
Source: sourceAPIResult,
|
|
||||||
Type: schema.TypeString,
|
|
||||||
APITest: func(e *typeEntry, v interface{}) (interface{}, bool) {
|
|
||||||
m := v.(map[string]string)
|
|
||||||
|
|
||||||
if addr, found := m[string(e.SchemaName)]; found {
|
|
||||||
return addr, true
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, false
|
|
||||||
},
|
|
||||||
},
|
|
||||||
catalogNodeTaggedAddressesWAN: {
|
|
||||||
APIName: "WAN",
|
|
||||||
SchemaName: "wan",
|
|
||||||
Source: sourceAPIResult,
|
|
||||||
Type: schema.TypeString,
|
|
||||||
APITest: func(e *typeEntry, v interface{}) (interface{}, bool) {
|
|
||||||
m := v.(map[string]string)
|
|
||||||
|
|
||||||
if addr, found := m[string(e.SchemaName)]; found {
|
|
||||||
return addr, true
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, false
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
APITest: func(e *typeEntry, v interface{}) (interface{}, bool) {
|
|
||||||
node := v.(*consulapi.Node)
|
|
||||||
|
|
||||||
if addrs := node.TaggedAddresses; len(addrs) > 0 {
|
|
||||||
return mapStringToMapInterface(addrs), true
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, false
|
|
||||||
},
|
|
||||||
},
|
|
||||||
catalogNodeMeta: {
|
|
||||||
APIName: "Meta",
|
|
||||||
SchemaName: "meta",
|
|
||||||
Source: sourceAPIResult,
|
|
||||||
Type: schema.TypeMap,
|
|
||||||
APITest: func(e *typeEntry, v interface{}) (interface{}, bool) {
|
|
||||||
node := v.(*consulapi.Node)
|
|
||||||
|
|
||||||
if meta := node.Meta; len(meta) > 0 {
|
|
||||||
return mapStringToMapInterface(meta), true
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, false
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var catalogNodesAttrs = map[typeKey]*typeEntry{
|
|
||||||
catalogNodesAllowStale: {
|
|
||||||
SchemaName: "allow_stale",
|
|
||||||
Source: sourceLocalFilter,
|
|
||||||
Type: schema.TypeBool,
|
|
||||||
Default: true,
|
|
||||||
ConfigRead: func(e *typeEntry, r attrReader) (interface{}, bool) {
|
|
||||||
b, ok := r.GetBoolOK(e.SchemaName)
|
|
||||||
if !ok {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
return b, true
|
|
||||||
},
|
|
||||||
ConfigUse: func(e *typeEntry, v interface{}, target interface{}) error {
|
|
||||||
b := v.(bool)
|
|
||||||
queryOpts := target.(*consulapi.QueryOptions)
|
|
||||||
queryOpts.AllowStale = b
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
catalogNodesDatacenter: {
|
|
||||||
SchemaName: "datacenter",
|
|
||||||
Source: sourceLocalFilter,
|
|
||||||
Type: schema.TypeString,
|
|
||||||
ConfigRead: func(e *typeEntry, r attrReader) (interface{}, bool) {
|
|
||||||
s, ok := r.GetStringOK(e.SchemaName)
|
|
||||||
if !ok {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
return s, true
|
|
||||||
},
|
|
||||||
ConfigUse: func(e *typeEntry, v interface{}, target interface{}) error {
|
|
||||||
s := v.(string)
|
|
||||||
queryOpts := target.(*consulapi.QueryOptions)
|
|
||||||
queryOpts.Datacenter = s
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
catalogNodesNear: {
|
|
||||||
SchemaName: "near",
|
|
||||||
Source: sourceLocalFilter,
|
|
||||||
Type: schema.TypeString,
|
|
||||||
ConfigRead: func(e *typeEntry, r attrReader) (interface{}, bool) {
|
|
||||||
s, ok := r.GetStringOK(e.SchemaName)
|
|
||||||
if !ok {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
return s, true
|
|
||||||
},
|
|
||||||
ConfigUse: func(e *typeEntry, v interface{}, target interface{}) error {
|
|
||||||
s := v.(string)
|
|
||||||
queryOpts := target.(*consulapi.QueryOptions)
|
|
||||||
queryOpts.Near = s
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
catalogNodes: {
|
|
||||||
SchemaName: "nodes",
|
|
||||||
Source: sourceAPIResult,
|
|
||||||
Type: schema.TypeList,
|
|
||||||
ListSchema: catalogNodeAttrs,
|
|
||||||
},
|
|
||||||
catalogNodesRequireConsistent: {
|
|
||||||
SchemaName: "require_consistent",
|
|
||||||
Source: sourceLocalFilter,
|
|
||||||
Type: schema.TypeBool,
|
|
||||||
Default: false,
|
|
||||||
ConfigRead: func(e *typeEntry, r attrReader) (interface{}, bool) {
|
|
||||||
b, ok := r.GetBoolOK(e.SchemaName)
|
|
||||||
if !ok {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
return b, true
|
|
||||||
},
|
|
||||||
ConfigUse: func(e *typeEntry, v interface{}, target interface{}) error {
|
|
||||||
b := v.(bool)
|
|
||||||
queryOpts := target.(*consulapi.QueryOptions)
|
|
||||||
queryOpts.RequireConsistent = b
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
catalogNodesToken: {
|
|
||||||
SchemaName: "token",
|
|
||||||
Source: sourceLocalFilter,
|
|
||||||
Type: schema.TypeString,
|
|
||||||
ConfigRead: func(e *typeEntry, r attrReader) (interface{}, bool) {
|
|
||||||
s, ok := r.GetStringOK(e.SchemaName)
|
|
||||||
if !ok {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
return s, true
|
|
||||||
},
|
|
||||||
ConfigUse: func(e *typeEntry, v interface{}, target interface{}) error {
|
|
||||||
s := v.(string)
|
|
||||||
queryOpts := target.(*consulapi.QueryOptions)
|
|
||||||
queryOpts.Token = s
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
catalogNodesWaitIndex: {
|
|
||||||
SchemaName: "wait_index",
|
|
||||||
Source: sourceLocalFilter,
|
|
||||||
Type: schema.TypeInt,
|
|
||||||
ValidateFuncs: []interface{}{
|
|
||||||
validateIntMin(0),
|
|
||||||
},
|
|
||||||
ConfigRead: func(e *typeEntry, r attrReader) (interface{}, bool) {
|
|
||||||
i, ok := r.GetIntOK(e.SchemaName)
|
|
||||||
if !ok {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
return uint64(i), true
|
|
||||||
},
|
|
||||||
ConfigUse: func(e *typeEntry, v interface{}, target interface{}) error {
|
|
||||||
i := v.(uint64)
|
|
||||||
queryOpts := target.(*consulapi.QueryOptions)
|
|
||||||
queryOpts.WaitIndex = i
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
catalogNodesWaitTime: {
|
|
||||||
SchemaName: "wait_time",
|
|
||||||
Source: sourceLocalFilter,
|
|
||||||
Type: schema.TypeString,
|
|
||||||
ValidateFuncs: []interface{}{
|
|
||||||
validateDurationMin("0ns"),
|
|
||||||
},
|
|
||||||
ConfigRead: func(e *typeEntry, r attrReader) (interface{}, bool) {
|
|
||||||
d, ok := r.GetDurationOK(e.SchemaName)
|
|
||||||
if !ok {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
return d, true
|
|
||||||
},
|
|
||||||
ConfigUse: func(e *typeEntry, v interface{}, target interface{}) error {
|
|
||||||
d := v.(time.Duration)
|
|
||||||
queryOpts := target.(*consulapi.QueryOptions)
|
|
||||||
queryOpts.WaitTime = d
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func dataSourceConsulCatalogNodes() *schema.Resource {
|
func dataSourceConsulCatalogNodes() *schema.Resource {
|
||||||
return &schema.Resource{
|
return &schema.Resource{
|
||||||
Read: dataSourceConsulCatalogNodesRead,
|
Read: dataSourceConsulCatalogNodesRead,
|
||||||
Schema: typeEntryMapToSchema(catalogNodesAttrs),
|
Schema: map[string]*schema.Schema{
|
||||||
|
allowStale: &schema.Schema{
|
||||||
|
Optional: true,
|
||||||
|
Default: true,
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
},
|
||||||
|
nodesAttr: &schema.Schema{
|
||||||
|
Computed: true,
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
nodeID: &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
ValidateFunc: makeValidationFunc(nodeID, []interface{}{validateRegexp(`^[\S]+$`)}),
|
||||||
|
},
|
||||||
|
nodeName: &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
ValidateFunc: makeValidationFunc(nodeName, []interface{}{validateRegexp(`^[\S]+$`)}),
|
||||||
|
},
|
||||||
|
nodeAddress: &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
nodeMetaAttr: &schema.Schema{
|
||||||
|
Type: schema.TypeMap,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
nodeTaggedAddresses: &schema.Schema{
|
||||||
|
Type: schema.TypeMap,
|
||||||
|
Computed: true,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
schemaTaggedLAN: &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
schemaTaggedWAN: &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
requireConsistent: &schema.Schema{
|
||||||
|
Optional: true,
|
||||||
|
Default: false,
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
},
|
||||||
|
token: &schema.Schema{
|
||||||
|
Optional: true,
|
||||||
|
Default: true,
|
||||||
|
Type: schema.TypeString,
|
||||||
|
},
|
||||||
|
waitIndex: &schema.Schema{
|
||||||
|
Optional: true,
|
||||||
|
Default: true,
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
ValidateFunc: makeValidationFunc(waitIndex, []interface{}{
|
||||||
|
validateIntMin(0),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
waitTime: &schema.Schema{
|
||||||
|
Optional: true,
|
||||||
|
Default: true,
|
||||||
|
Type: schema.TypeString,
|
||||||
|
ValidateFunc: makeValidationFunc(waitTime, []interface{}{
|
||||||
|
validateDurationMin("0ns"),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func dataSourceConsulCatalogNodesRead(d *schema.ResourceData, meta interface{}) error {
|
func dataSourceConsulCatalogNodesRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
client := meta.(*consulapi.Client)
|
client := meta.(*consulapi.Client)
|
||||||
|
|
||||||
|
// Parse out data source filters to populate Consul's query options
|
||||||
|
|
||||||
dc, err := getDC(d, client)
|
dc, err := getDC(d, client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -327,20 +125,34 @@ func dataSourceConsulCatalogNodesRead(d *schema.ResourceData, meta interface{})
|
||||||
Datacenter: dc,
|
Datacenter: dc,
|
||||||
}
|
}
|
||||||
|
|
||||||
cfgReader := newConfigReader(d)
|
if v, ok := d.GetOk(allowStale); ok {
|
||||||
|
queryOpts.AllowStale = v.(bool)
|
||||||
// Construct the query options
|
|
||||||
for _, e := range catalogNodesAttrs[catalogNodes].ListSchema {
|
|
||||||
// Only evaluate attributes that impact the state
|
|
||||||
if e.Source&sourceLocalFilter == 0 {
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, ok := e.ConfigRead(e, cfgReader); ok {
|
if v, ok := d.GetOk(requireConsistent); ok {
|
||||||
if err := e.ConfigUse(e, v, queryOpts); err != nil {
|
queryOpts.RequireConsistent = v.(bool)
|
||||||
return errwrap.Wrapf(fmt.Sprintf("error writing %q's query option: {{err}}", e.SchemaName), err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk(nodeMeta); ok {
|
||||||
|
m := v.(map[string]interface{})
|
||||||
|
nodeMetaMap := make(map[string]string, len(nodeMeta))
|
||||||
|
for s, t := range m {
|
||||||
|
nodeMetaMap[s] = t.(string)
|
||||||
}
|
}
|
||||||
|
queryOpts.NodeMeta = nodeMetaMap
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk(token); ok {
|
||||||
|
queryOpts.Token = v.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk(waitIndex); ok {
|
||||||
|
queryOpts.WaitIndex = uint64(v.(int))
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk(waitTime); ok {
|
||||||
|
d, _ := time.ParseDuration(v.(string))
|
||||||
|
queryOpts.WaitTime = d
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes, meta, err := client.Catalog().Nodes(queryOpts)
|
nodes, meta, err := client.Catalog().Nodes(queryOpts)
|
||||||
|
@ -348,41 +160,51 @@ func dataSourceConsulCatalogNodesRead(d *schema.ResourceData, meta interface{})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(sean@): It'd be nice if this data source had a way of filtering out
|
|
||||||
// irrelevant data so only the important bits are persisted in the state file.
|
|
||||||
// Something like an attribute mask or even a regexp of matching schema
|
|
||||||
// attributesknames would be sufficient in the most basic case. Food for
|
|
||||||
// thought.
|
|
||||||
|
|
||||||
l := make([]interface{}, 0, len(nodes))
|
l := make([]interface{}, 0, len(nodes))
|
||||||
|
|
||||||
for _, node := range nodes {
|
for _, node := range nodes {
|
||||||
mWriter := newMapWriter(make(map[string]interface{}, len(catalogNodeAttrs)))
|
const defaultNodeAttrs = 4
|
||||||
|
m := make(map[string]interface{}, defaultNodeAttrs)
|
||||||
// /v1/catalog/nodes returns a list of node objects
|
id := node.ID
|
||||||
for _, e := range catalogNodesAttrs[catalogNodes].ListSchema {
|
if id == "" {
|
||||||
// Only evaluate attributes that impact the state
|
id = node.Node
|
||||||
if e.Source&modifyState == 0 {
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h := e.MustLookupTypeHandler()
|
m[nodeID] = id
|
||||||
|
m[nodeName] = node.Node
|
||||||
|
m[nodeAddress] = node.Address
|
||||||
|
|
||||||
if v, ok := h.APITest(e, node); ok {
|
{
|
||||||
if err := h.APIToState(e, v, mWriter); err != nil {
|
const initNumTaggedAddrs = 2
|
||||||
return errwrap.Wrapf(fmt.Sprintf("error writing %q's data to state: {{err}}", e.SchemaName), err)
|
taggedAddrs := make(map[string]interface{}, initNumTaggedAddrs)
|
||||||
|
if addr, found := node.TaggedAddresses[apiTaggedLAN]; found {
|
||||||
|
taggedAddrs[schemaTaggedLAN] = addr
|
||||||
}
|
}
|
||||||
|
if addr, found := node.TaggedAddresses[apiTaggedWAN]; found {
|
||||||
|
taggedAddrs[schemaTaggedWAN] = addr
|
||||||
}
|
}
|
||||||
|
m[nodeTaggedAddresses] = taggedAddrs
|
||||||
}
|
}
|
||||||
|
|
||||||
l = append(l, mWriter.ToMap())
|
{
|
||||||
|
const initNumMetaAddrs = 4
|
||||||
|
metaVals := make(map[string]interface{}, initNumMetaAddrs)
|
||||||
|
for s, t := range node.Meta {
|
||||||
|
metaVals[s] = t
|
||||||
|
}
|
||||||
|
m[nodeMetaAttr] = metaVals
|
||||||
|
}
|
||||||
|
|
||||||
|
l = append(l, m)
|
||||||
}
|
}
|
||||||
|
|
||||||
dataSourceWriter := newStateWriter(d)
|
|
||||||
dataSourceWriter.SetList(catalogNodesAttrs[catalogNodes].SchemaName, l)
|
|
||||||
dataSourceWriter.SetString(catalogNodesAttrs[catalogNodesDatacenter].SchemaName, dc)
|
|
||||||
const idKeyFmt = "catalog-nodes-%s"
|
const idKeyFmt = "catalog-nodes-%s"
|
||||||
dataSourceWriter.SetID(fmt.Sprintf(idKeyFmt, dc))
|
d.SetId(fmt.Sprintf(idKeyFmt, dc))
|
||||||
|
|
||||||
|
d.Set("datacenter", dc)
|
||||||
|
if err := d.Set(nodesAttr, l); err != nil {
|
||||||
|
return errwrap.Wrapf("Unable to store nodes: {{err}}", err)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,6 @@ const testAccDataConsulCatalogNodesConfig = `
|
||||||
data "consul_catalog_nodes" "read" {
|
data "consul_catalog_nodes" "read" {
|
||||||
allow_stale = true
|
allow_stale = true
|
||||||
require_consistent = false
|
require_consistent = false
|
||||||
near = "_agent"
|
|
||||||
token = ""
|
token = ""
|
||||||
wait_index = 0
|
wait_index = 0
|
||||||
wait_time = "1m"
|
wait_time = "1m"
|
||||||
|
|
|
@ -3,10 +3,8 @@ package consul
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/hashicorp/errwrap"
|
"github.com/hashicorp/errwrap"
|
||||||
"github.com/hashicorp/terraform/helper/hashcode"
|
"github.com/hashicorp/terraform/helper/hashcode"
|
||||||
|
@ -28,19 +26,6 @@ type sourceFlags int
|
||||||
// typeKey is the lookup mechanism for the generated schema.
|
// typeKey is the lookup mechanism for the generated schema.
|
||||||
type typeKey int
|
type typeKey int
|
||||||
|
|
||||||
// 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
|
|
||||||
|
|
||||||
// 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
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// sourceUserRequired indicates the parameter must be provided by the user in
|
// sourceUserRequired indicates the parameter must be provided by the user in
|
||||||
// their configuration.
|
// their configuration.
|
||||||
|
@ -509,37 +494,6 @@ func (e *typeEntry) Validate() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeValidateionFunc takes a list of typed validator inputs from the receiver
|
|
||||||
// and creates a validation closure that calls each validator in serial until
|
|
||||||
// either a warning or error is returned from the first validation function.
|
|
||||||
func (e *typeEntry) MakeValidationFunc() func(v interface{}, key string) (warnings []string, errors []error) {
|
|
||||||
if len(e.ValidateFuncs) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
fns := make([]func(v interface{}, key string) (warnings []string, errors []error), 0, len(e.ValidateFuncs))
|
|
||||||
for _, v := range e.ValidateFuncs {
|
|
||||||
switch u := v.(type) {
|
|
||||||
case validateDurationMin:
|
|
||||||
fns = append(fns, validateDurationMinFactory(e, string(u)))
|
|
||||||
case validateIntMin:
|
|
||||||
fns = append(fns, validateIntMinFactory(e, int(u)))
|
|
||||||
case validateRegexp:
|
|
||||||
fns = append(fns, validateRegexpFactory(e, 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 (e *typeEntry) ToSchema() *schema.Schema {
|
func (e *typeEntry) ToSchema() *schema.Schema {
|
||||||
e.Validate()
|
e.Validate()
|
||||||
|
|
||||||
|
@ -550,7 +504,7 @@ func (e *typeEntry) ToSchema() *schema.Schema {
|
||||||
Optional: e.Source&optionalAttrMask != 0,
|
Optional: e.Source&optionalAttrMask != 0,
|
||||||
Required: e.Source&requiredAttrMask != 0,
|
Required: e.Source&requiredAttrMask != 0,
|
||||||
Type: e.Type,
|
Type: e.Type,
|
||||||
ValidateFunc: e.MakeValidationFunc(),
|
// ValidateFunc: e.MakeValidationFunc(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fixup the type: use the real type vs a surrogate type
|
// Fixup the type: use the real type vs a surrogate type
|
||||||
|
@ -580,45 +534,3 @@ func mapStringToMapInterface(in map[string]string) map[string]interface{} {
|
||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateDurationMinFactory(e *typeEntry, minDuration string) func(v interface{}, key string) (warnings []string, errors []error) {
|
|
||||||
dMin, err := time.ParseDuration(minDuration)
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("PROVIDER BUG: duration %q not valid: %#v", 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}}", e.SchemaName), err))
|
|
||||||
}
|
|
||||||
|
|
||||||
if d < dMin {
|
|
||||||
errors = append(errors, fmt.Errorf("Invalid %s specified: duration %q less than the required minimum %s", e.SchemaName, v.(string), dMin))
|
|
||||||
}
|
|
||||||
|
|
||||||
return warnings, errors
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateIntMinFactory(e *typeEntry, min int) func(v interface{}, key string) (warnings []string, errors []error) {
|
|
||||||
return func(v interface{}, key string) (warnings []string, errors []error) {
|
|
||||||
if v.(int) < min {
|
|
||||||
errors = append(errors, fmt.Errorf("Invalid %s specified: %d less than the required minimum %d", e.SchemaName, v.(int), min))
|
|
||||||
}
|
|
||||||
|
|
||||||
return warnings, errors
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateRegexpFactory(e *typeEntry, 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", e.SchemaName, v.(string)))
|
|
||||||
}
|
|
||||||
|
|
||||||
return warnings, errors
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
package consul
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"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
|
||||||
|
|
||||||
|
// 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 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), 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 validateIntMinFactory(name string, min int) func(v interface{}, key string) (warnings []string, errors []error) {
|
||||||
|
return func(v interface{}, key string) (warnings []string, errors []error) {
|
||||||
|
if v.(int) < min {
|
||||||
|
errors = append(errors, fmt.Errorf("Invalid %s specified: %d less than the required minimum %d", name, v.(int), min))
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue