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"
|
||||
)
|
||||
|
||||
// Top-level consul_catalog_nodes attributes
|
||||
const (
|
||||
catalogNodes typeKey = iota
|
||||
catalogNodesAllowStale
|
||||
catalogNodesDatacenter
|
||||
catalogNodesNear
|
||||
catalogNodesRequireConsistent
|
||||
catalogNodesToken
|
||||
catalogNodesWaitIndex
|
||||
catalogNodesWaitTime
|
||||
allowStale = "allow_stale"
|
||||
nodeMeta = "node_meta"
|
||||
nodesAttr = "nodes"
|
||||
requireConsistent = "require_consistent"
|
||||
token = "token"
|
||||
waitIndex = "wait_index"
|
||||
waitTime = "wait_time"
|
||||
|
||||
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 {
|
||||
return &schema.Resource{
|
||||
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 {
|
||||
client := meta.(*consulapi.Client)
|
||||
|
||||
// Parse out data source filters to populate Consul's query options
|
||||
|
||||
dc, err := getDC(d, client)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -327,20 +125,34 @@ func dataSourceConsulCatalogNodesRead(d *schema.ResourceData, meta interface{})
|
|||
Datacenter: dc,
|
||||
}
|
||||
|
||||
cfgReader := newConfigReader(d)
|
||||
|
||||
// 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 := d.GetOk(allowStale); ok {
|
||||
queryOpts.AllowStale = v.(bool)
|
||||
}
|
||||
|
||||
if v, ok := e.ConfigRead(e, cfgReader); ok {
|
||||
if err := e.ConfigUse(e, v, queryOpts); err != nil {
|
||||
return errwrap.Wrapf(fmt.Sprintf("error writing %q's query option: {{err}}", e.SchemaName), err)
|
||||
if v, ok := d.GetOk(requireConsistent); ok {
|
||||
queryOpts.RequireConsistent = v.(bool)
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -348,41 +160,51 @@ func dataSourceConsulCatalogNodesRead(d *schema.ResourceData, meta interface{})
|
|||
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))
|
||||
|
||||
for _, node := range nodes {
|
||||
mWriter := newMapWriter(make(map[string]interface{}, len(catalogNodeAttrs)))
|
||||
|
||||
// /v1/catalog/nodes returns a list of node objects
|
||||
for _, e := range catalogNodesAttrs[catalogNodes].ListSchema {
|
||||
// Only evaluate attributes that impact the state
|
||||
if e.Source&modifyState == 0 {
|
||||
continue
|
||||
const defaultNodeAttrs = 4
|
||||
m := make(map[string]interface{}, defaultNodeAttrs)
|
||||
id := node.ID
|
||||
if id == "" {
|
||||
id = node.Node
|
||||
}
|
||||
|
||||
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 {
|
||||
return errwrap.Wrapf(fmt.Sprintf("error writing %q's data to state: {{err}}", e.SchemaName), err)
|
||||
{
|
||||
const initNumTaggedAddrs = 2
|
||||
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"
|
||||
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
|
||||
}
|
||||
|
|
|
@ -28,7 +28,6 @@ const testAccDataConsulCatalogNodesConfig = `
|
|||
data "consul_catalog_nodes" "read" {
|
||||
allow_stale = true
|
||||
require_consistent = false
|
||||
near = "_agent"
|
||||
token = ""
|
||||
wait_index = 0
|
||||
wait_time = "1m"
|
||||
|
|
|
@ -3,10 +3,8 @@ package consul
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/hashicorp/terraform/helper/hashcode"
|
||||
|
@ -28,19 +26,6 @@ type sourceFlags int
|
|||
// typeKey is the lookup mechanism for the generated schema.
|
||||
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 (
|
||||
// sourceUserRequired indicates the parameter must be provided by the user in
|
||||
// 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 {
|
||||
e.Validate()
|
||||
|
||||
|
@ -550,7 +504,7 @@ func (e *typeEntry) ToSchema() *schema.Schema {
|
|||
Optional: e.Source&optionalAttrMask != 0,
|
||||
Required: e.Source&requiredAttrMask != 0,
|
||||
Type: e.Type,
|
||||
ValidateFunc: e.MakeValidationFunc(),
|
||||
// ValidateFunc: e.MakeValidationFunc(),
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
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