Update the style of the `consul_catalog_nodes` data source.
This commit is contained in:
parent
1476445593
commit
efb76b3374
File diff suppressed because it is too large
Load Diff
|
@ -16,11 +16,52 @@ func TestAccDataConsulAgentSelf_basic(t *testing.T) {
|
|||
resource.TestStep{
|
||||
Config: testAccDataConsulAgentSelfConfig,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDataSourceValue("data.consul_agent_self.read", "bootstrap", "false"),
|
||||
testAccCheckDataSourceValue("data.consul_agent_self.read", "datacenter", "dc1"),
|
||||
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", "server", "true"),
|
||||
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>"),
|
||||
),
|
||||
},
|
||||
},
|
||||
|
@ -33,14 +74,15 @@ func testAccCheckDataSourceValue(n, attr, val string) resource.TestCheckFunc {
|
|||
if !ok {
|
||||
return fmt.Errorf("Resource not found")
|
||||
}
|
||||
out, ok := rn.Primary.Attributes[attr]
|
||||
if !ok {
|
||||
out, found := rn.Primary.Attributes[attr]
|
||||
switch {
|
||||
case !found:
|
||||
return fmt.Errorf("Attribute '%s' not found: %#v", attr, rn.Primary.Attributes)
|
||||
}
|
||||
if val != "<any>" && out != val {
|
||||
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)
|
||||
}
|
||||
if val == "<any>" && out == "" {
|
||||
case val == "<any>" && out == "":
|
||||
return fmt.Errorf("Attribute '%s' value '%s'", attr, out)
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -2,7 +2,6 @@ package consul
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
consulapi "github.com/hashicorp/consul/api"
|
||||
"github.com/hashicorp/errwrap"
|
||||
|
@ -115,44 +114,9 @@ func dataSourceConsulCatalogNodesRead(d *schema.ResourceData, meta interface{})
|
|||
client := meta.(*consulapi.Client)
|
||||
|
||||
// Parse out data source filters to populate Consul's query options
|
||||
|
||||
dc, err := getDC(d, client)
|
||||
queryOpts, err := getQueryOpts(d, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
queryOpts := &consulapi.QueryOptions{
|
||||
Datacenter: dc,
|
||||
}
|
||||
|
||||
if v, ok := d.GetOk(allowStale); ok {
|
||||
queryOpts.AllowStale = v.(bool)
|
||||
}
|
||||
|
||||
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
|
||||
return errwrap.Wrapf("unable to get query options for fetching catalog nodes: {{err}}", err)
|
||||
}
|
||||
|
||||
nodes, meta, err := client.Catalog().Nodes(queryOpts)
|
||||
|
@ -199,9 +163,9 @@ func dataSourceConsulCatalogNodesRead(d *schema.ResourceData, meta interface{})
|
|||
}
|
||||
|
||||
const idKeyFmt = "catalog-nodes-%s"
|
||||
d.SetId(fmt.Sprintf(idKeyFmt, dc))
|
||||
d.SetId(fmt.Sprintf(idKeyFmt, queryOpts.Datacenter))
|
||||
|
||||
d.Set("datacenter", dc)
|
||||
d.Set("datacenter", queryOpts.Datacenter)
|
||||
if err := d.Set(nodesAttr, l); err != nil {
|
||||
return errwrap.Wrapf("Unable to store nodes: {{err}}", err)
|
||||
}
|
||||
|
|
|
@ -1,536 +1,51 @@
|
|||
package consul
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/hashicorp/terraform/helper/hashcode"
|
||||
consulapi "github.com/hashicorp/consul/api"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
// apiAttr is the type used for constants representing well known keys within
|
||||
// the API that are transmitted back to a resource over an API.
|
||||
type apiAttr string
|
||||
|
||||
// schemaAttr is the type used for constants representing well known keys
|
||||
// within the schema for a resource.
|
||||
type schemaAttr string
|
||||
|
||||
// sourceFlags represent the ways in which an attribute can be written. Some
|
||||
// sources are mutually exclusive, yet other flag combinations are composable.
|
||||
type sourceFlags int
|
||||
|
||||
// typeKey is the lookup mechanism for the generated schema.
|
||||
type typeKey int
|
||||
|
||||
const (
|
||||
// sourceUserRequired indicates the parameter must be provided by the user in
|
||||
// their configuration.
|
||||
sourceUserRequired sourceFlags = 1 << iota
|
||||
|
||||
// sourceUserOptional indicates the parameter may optionally be specified by
|
||||
// the user in their configuration.
|
||||
sourceUserOptional
|
||||
|
||||
// sourceAPIResult indicates the parameter may only be set by the return of
|
||||
// an API call.
|
||||
sourceAPIResult
|
||||
|
||||
// sourceLocalFilter indicates the parameter is only used as input to the
|
||||
// resource or data source and not to be entered into the state file.
|
||||
sourceLocalFilter
|
||||
)
|
||||
|
||||
const (
|
||||
// modifyState is a mask that selects all attribute sources that can modify
|
||||
// the state (i.e. everything but filters used in data sources).
|
||||
modifyState = sourceUserRequired | sourceUserOptional | sourceAPIResult
|
||||
|
||||
// computedAttrMask is a mask that selects source*'s that are Computed in the
|
||||
// schema.
|
||||
computedAttrMask = sourceAPIResult
|
||||
|
||||
// optionalAttrMask is a mask that selects source*'s that are Optional in the
|
||||
// schema.
|
||||
optionalAttrMask = sourceAPIResult | sourceLocalFilter
|
||||
|
||||
// requiredAttrMask is a mask that selects source*'s that are Required in the
|
||||
// schema.
|
||||
requiredAttrMask = sourceUserRequired
|
||||
)
|
||||
|
||||
type typeEntry struct {
|
||||
APIName apiAttr
|
||||
APIAliases []apiAttr
|
||||
Source sourceFlags
|
||||
Default interface{}
|
||||
Description string
|
||||
SchemaName schemaAttr
|
||||
Type schema.ValueType
|
||||
ValidateFuncs []interface{}
|
||||
SetMembers map[typeKey]*typeEntry
|
||||
ListSchema map[typeKey]*typeEntry
|
||||
|
||||
// APITest, if returns true, will call APIToState. The if the value was
|
||||
// found, the second return parameter will include the value that should be
|
||||
// set in the state store.
|
||||
APITest func(*typeEntry, interface{}) (interface{}, bool)
|
||||
|
||||
// APIToState takes the value from APITest and writes it to the attrWriter
|
||||
APIToState func(*typeEntry, interface{}, attrWriter) error
|
||||
|
||||
// ConfigRead, if it returns true, returned a value that will be passed to its
|
||||
// ConfigUse handler.
|
||||
ConfigRead func(*typeEntry, attrReader) (interface{}, bool)
|
||||
|
||||
// ConfigUse takes the value returned from ConfigRead as the second argument
|
||||
// and a 3rd optional opaque context argument.
|
||||
ConfigUse func(e *typeEntry, v interface{}, target interface{}) error
|
||||
}
|
||||
|
||||
type typeHandlers struct {
|
||||
APITest func(*typeEntry, interface{}) (interface{}, bool)
|
||||
APIToState func(*typeEntry, interface{}, attrWriter) error
|
||||
}
|
||||
|
||||
var typeHandlerLookupMap = map[schema.ValueType]*typeHandlers{
|
||||
schema.TypeBool: &typeHandlers{
|
||||
APITest: apiTestBool,
|
||||
APIToState: apiToStateBool,
|
||||
},
|
||||
schema.TypeFloat: &typeHandlers{
|
||||
APITest: apiTestFloat64,
|
||||
APIToState: apiToStateFloat64,
|
||||
},
|
||||
schema.TypeList: &typeHandlers{
|
||||
APITest: apiTestList,
|
||||
APIToState: apiToStateList,
|
||||
},
|
||||
schema.TypeMap: &typeHandlers{
|
||||
APITest: apiTestMap,
|
||||
APIToState: apiToStateMap,
|
||||
},
|
||||
schema.TypeSet: &typeHandlers{
|
||||
APITest: apiTestSet,
|
||||
APIToState: apiToStateSet,
|
||||
},
|
||||
schema.TypeString: &typeHandlers{
|
||||
APITest: apiTestString,
|
||||
APIToState: apiToStateString,
|
||||
},
|
||||
}
|
||||
|
||||
func apiTestBool(e *typeEntry, self interface{}) (interface{}, bool) {
|
||||
m := self.(map[string]interface{})
|
||||
|
||||
v, found := m[string(e.APIName)]
|
||||
if found {
|
||||
if b, ok := v.(bool); ok {
|
||||
return b, true
|
||||
} else {
|
||||
panic(fmt.Sprintf("PROVIDER BUG: %q fails bool type assertion", e.SchemaName))
|
||||
}
|
||||
}
|
||||
|
||||
return false, false
|
||||
}
|
||||
|
||||
func apiTestFloat64(e *typeEntry, self interface{}) (interface{}, bool) {
|
||||
m := self.(map[string]interface{})
|
||||
|
||||
v, found := m[string(e.APIName)]
|
||||
if found {
|
||||
if f, ok := v.(float64); ok {
|
||||
return f, true
|
||||
} else {
|
||||
panic(fmt.Sprintf("PROVIDER BUG: %q fails float64 type assertion", e.SchemaName))
|
||||
}
|
||||
}
|
||||
return 0.0, false
|
||||
}
|
||||
|
||||
func apiTestID(e *typeEntry, self interface{}) (interface{}, bool) {
|
||||
m := self.(map[string]interface{})
|
||||
|
||||
v, _ := apiTestString(e, m)
|
||||
|
||||
// Unconditionally return true so that the call to the APIToState handler can
|
||||
// return an error.
|
||||
return v, true
|
||||
}
|
||||
|
||||
func apiTestList(e *typeEntry, self interface{}) (interface{}, bool) {
|
||||
m := self.(map[string]interface{})
|
||||
|
||||
names := append([]apiAttr{e.APIName}, e.APIAliases...)
|
||||
const defaultListLen = 8
|
||||
l := make([]interface{}, 0, defaultListLen)
|
||||
|
||||
var foundName bool
|
||||
for _, name := range names {
|
||||
v, found := m[string(name)]
|
||||
if found {
|
||||
foundName = true
|
||||
// TODO(sean@): should make a list writer that normalizes v.(type) to a
|
||||
// string. For now we only accept strings and lists.
|
||||
switch u := v.(type) {
|
||||
case []interface{}:
|
||||
l = append(l, u...)
|
||||
case string:
|
||||
l = append(l, u)
|
||||
default:
|
||||
panic(fmt.Sprintf("PROVIDER BUG: %q fails list type assertion", e.SchemaName))
|
||||
}
|
||||
}
|
||||
func getQueryOpts(d *schema.ResourceData, client *consulapi.Client) (*consulapi.QueryOptions, error) {
|
||||
dc, err := getDC(d, client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if foundName {
|
||||
return l, true
|
||||
queryOpts := &consulapi.QueryOptions{
|
||||
Datacenter: dc,
|
||||
}
|
||||
|
||||
return []interface{}{}, false
|
||||
if v, ok := d.GetOk(allowStale); ok {
|
||||
queryOpts.AllowStale = v.(bool)
|
||||
}
|
||||
|
||||
func apiTestMap(e *typeEntry, selfRaw interface{}) (interface{}, bool) {
|
||||
self := selfRaw.(map[string]interface{})
|
||||
|
||||
v, found := self[string(e.APIName)]
|
||||
if found {
|
||||
if m, ok := v.(map[string]interface{}); ok {
|
||||
return m, true
|
||||
} else {
|
||||
panic(fmt.Sprintf("PROVIDER BUG: %q fails map type assertion", e.SchemaName))
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
if v, ok := d.GetOk(requireConsistent); ok {
|
||||
queryOpts.RequireConsistent = v.(bool)
|
||||
}
|
||||
|
||||
func apiTestSet(e *typeEntry, selfRaw interface{}) (interface{}, bool) {
|
||||
self := selfRaw.(map[string]interface{})
|
||||
|
||||
v, found := self[string(e.APIName)]
|
||||
if found {
|
||||
if m, ok := v.(map[string]interface{}); ok {
|
||||
return m, true
|
||||
} else {
|
||||
panic(fmt.Sprintf("PROVIDER BUG: %q fails map type assertion", e.SchemaName))
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
func apiTestString(e *typeEntry, selfRaw interface{}) (interface{}, bool) {
|
||||
self := selfRaw.(map[string]interface{})
|
||||
|
||||
v, found := self[string(e.APIName)]
|
||||
if found {
|
||||
if s, ok := v.(string); ok {
|
||||
return s, true
|
||||
} else {
|
||||
panic(fmt.Sprintf("PROVIDER BUG: %q fails string type assertion", e.SchemaName))
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
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)
|
||||
}
|
||||
|
||||
func apiToStateBool(e *typeEntry, v interface{}, w attrWriter) error {
|
||||
return w.SetBool(e.SchemaName, v.(bool))
|
||||
}
|
||||
|
||||
func apiToStateID(e *typeEntry, v interface{}, w attrWriter) error {
|
||||
s, ok := v.(string)
|
||||
if !ok || len(s) == 0 {
|
||||
return fmt.Errorf("Unable to set %q's ID to an empty or non-string value: %#v", e.SchemaName, v)
|
||||
}
|
||||
|
||||
stateWriter, ok := w.(*attrWriterState)
|
||||
if !ok {
|
||||
return fmt.Errorf("PROVIDER BUG: unable to SetID with a non-attrWriterState")
|
||||
}
|
||||
|
||||
stateWriter.SetID(s)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func apiToStateFloat64(e *typeEntry, v interface{}, w attrWriter) error {
|
||||
f, ok := v.(float64)
|
||||
if !ok {
|
||||
return fmt.Errorf("PROVIDER BUG: unable to cast %s to a float64", e.SchemaName)
|
||||
queryOpts.NodeMeta = nodeMetaMap
|
||||
}
|
||||
|
||||
return w.SetFloat64(e.SchemaName, f)
|
||||
if v, ok := d.GetOk(token); ok {
|
||||
queryOpts.Token = v.(string)
|
||||
}
|
||||
|
||||
func apiToStateList(e *typeEntry, v interface{}, w attrWriter) error {
|
||||
l, ok := v.([]interface{})
|
||||
if !ok {
|
||||
return fmt.Errorf("PROVIDER BUG: unable to cast %s to a list", e.SchemaName)
|
||||
if v, ok := d.GetOk(waitIndex); ok {
|
||||
queryOpts.WaitIndex = uint64(v.(int))
|
||||
}
|
||||
|
||||
return w.SetList(e.SchemaName, l)
|
||||
}
|
||||
|
||||
func apiToStateMap(e *typeEntry, v interface{}, w attrWriter) error {
|
||||
rawMap, ok := v.(map[string]interface{})
|
||||
if !ok {
|
||||
return fmt.Errorf("PROVIDER BUG: unable to cast %s to a map", e.SchemaName)
|
||||
}
|
||||
|
||||
mWriter := newMapWriter(make(map[string]interface{}, len(rawMap)))
|
||||
|
||||
// Make a lookup map by API Schema Name
|
||||
var setMembersLen int
|
||||
if e.SetMembers != nil {
|
||||
setMembersLen = len(e.SetMembers)
|
||||
}
|
||||
apiLookup := make(map[string]*typeEntry, setMembersLen)
|
||||
for _, typeEntry := range e.SetMembers {
|
||||
apiLookup[string(e.SchemaName)] = typeEntry
|
||||
}
|
||||
|
||||
for k, v := range rawMap {
|
||||
var usedSchemaHandler bool
|
||||
if attrEntry, found := apiLookup[k]; found {
|
||||
usedSchemaHandler = true
|
||||
if err := attrEntry.APIToState(e, v, mWriter); err != nil {
|
||||
return errwrap.Wrapf(fmt.Sprintf("Error calling API to state handler on %s: {{err}}", k), err)
|
||||
}
|
||||
}
|
||||
|
||||
if !usedSchemaHandler {
|
||||
if err := mWriter.Set(schemaAttr(k), v); err != nil {
|
||||
return errwrap.Wrapf("Unable to store map in state: {{err}}", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return w.SetMap(e.SchemaName, mWriter.ToMap())
|
||||
}
|
||||
|
||||
func apiToStateSet(e *typeEntry, v interface{}, w attrWriter) error {
|
||||
s, ok := v.([]map[string]interface{})
|
||||
if !ok {
|
||||
return fmt.Errorf("PROVIDER BUG: unable to cast %s to a set", e.SchemaName)
|
||||
}
|
||||
|
||||
set := schema.NewSet(schema.HashResource(nil), nil)
|
||||
set.Add(s)
|
||||
|
||||
return w.SetSet(e.SchemaName, set)
|
||||
if v, ok := d.GetOk(waitTime); ok {
|
||||
d, _ := time.ParseDuration(v.(string))
|
||||
queryOpts.WaitTime = d
|
||||
}
|
||||
|
||||
func apiToStateString(e *typeEntry, v interface{}, w attrWriter) error {
|
||||
s, ok := v.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("PROVIDER BUG: unable to cast %s to a float64", e.SchemaName)
|
||||
}
|
||||
|
||||
return w.SetString(e.SchemaName, s)
|
||||
}
|
||||
|
||||
func hashMap(in interface{}) int {
|
||||
return 0
|
||||
m, ok := in.(map[string]interface{})
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("PROVIDER BUG: Unable to cast %#v to a map", in))
|
||||
}
|
||||
|
||||
keys := make([]string, 0, len(m))
|
||||
for k, _ := range m {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
|
||||
sort.Strings(keys)
|
||||
|
||||
b := &bytes.Buffer{}
|
||||
const defaultHashBufSize = 4096
|
||||
b.Grow(defaultHashBufSize)
|
||||
|
||||
for _, k := range keys {
|
||||
v, found := m[k]
|
||||
if !found {
|
||||
panic("PROVIDER BUG: race condition: key should not be missing")
|
||||
}
|
||||
|
||||
fmt.Fprintf(b, k)
|
||||
switch u := v.(type) {
|
||||
case string:
|
||||
fmt.Fprint(b, u)
|
||||
case bool:
|
||||
fmt.Fprintf(b, "%t", u)
|
||||
case float64:
|
||||
fmt.Fprint(b, strconv.FormatFloat(u, 'g', -1, 64))
|
||||
case int, uint:
|
||||
fmt.Fprintf(b, "%d", u)
|
||||
case nil:
|
||||
default:
|
||||
panic(fmt.Sprintf("Unsupported type %T in map hasher", v))
|
||||
}
|
||||
}
|
||||
|
||||
return hashcode.String(b.String())
|
||||
}
|
||||
|
||||
func indirect(v interface{}) interface{} {
|
||||
switch v.(type) {
|
||||
case string:
|
||||
return v
|
||||
case *string:
|
||||
p := v.(*string)
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
return *p
|
||||
default:
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
func (e *typeEntry) LookupDefaultTypeHandler() *typeHandlers {
|
||||
h, found := typeHandlerLookupMap[e.Type]
|
||||
if !found {
|
||||
panic(fmt.Sprintf("PROVIDER BUG: unable to lookup %q's type (%#v)", e.SchemaName, e.Type))
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func (e *typeEntry) MustLookupTypeHandler() *typeHandlers {
|
||||
h := &typeHandlers{
|
||||
APITest: e.APITest,
|
||||
APIToState: e.APIToState,
|
||||
}
|
||||
|
||||
defaultHandler := e.LookupDefaultTypeHandler()
|
||||
|
||||
if h.APITest == nil {
|
||||
h.APITest = defaultHandler.APITest
|
||||
|
||||
if h.APITest == nil {
|
||||
panic(fmt.Sprint("PROVIDER BUG: %v missing APITest method", e.SchemaName))
|
||||
}
|
||||
}
|
||||
|
||||
if h.APIToState == nil {
|
||||
h.APIToState = defaultHandler.APIToState
|
||||
|
||||
if h.APIToState == nil {
|
||||
panic(fmt.Sprint("PROVIDER BUG: %v missing APIToState method", e.SchemaName))
|
||||
}
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
// negateBoolToState is a factory function that creates a new function that
|
||||
// negates whatever the bool is that's passed in as an argument.
|
||||
func negateBoolToState(fn func(*typeEntry, interface{}, attrWriter) error) func(*typeEntry, interface{}, attrWriter) error {
|
||||
return func(e *typeEntry, v interface{}, w attrWriter) error {
|
||||
b, ok := v.(bool)
|
||||
if !ok {
|
||||
return fmt.Errorf("Unable to type assert non-bool value: %#v", v)
|
||||
}
|
||||
|
||||
return fn(e, !b, w)
|
||||
}
|
||||
}
|
||||
|
||||
// stateSet sets an attribute based on an attrName. Return an error if the
|
||||
// Set() to schema.ResourceData fails.
|
||||
func stateSet(d *schema.ResourceData, attrName schemaAttr, v interface{}) error {
|
||||
if err := d.Set(string(attrName), indirect(v)); err != nil {
|
||||
return fmt.Errorf("PROVIDER BUG: failed set schema attribute %s to value %#v: %v", attrName, v, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func typeEntryListToSchema(e *typeEntry) map[string]*schema.Schema {
|
||||
return map[string]*schema.Schema{
|
||||
string(e.SchemaName): e.ToSchema(),
|
||||
}
|
||||
}
|
||||
|
||||
func typeEntryMapToResource(in map[typeKey]*typeEntry) *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Schema: typeEntryMapToSchema(in),
|
||||
}
|
||||
}
|
||||
|
||||
func typeEntryMapToSchema(in map[typeKey]*typeEntry) map[string]*schema.Schema {
|
||||
out := make(map[string]*schema.Schema, len(in))
|
||||
for _, e := range in {
|
||||
out[string(e.SchemaName)] = e.ToSchema()
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (e *typeEntry) Validate() {
|
||||
if e.Source&sourceAPIResult != 0 && e.Type == schema.TypeSet {
|
||||
panic(fmt.Sprintf("PROVIDER BUG: %s can not be computed and of type Set", e.SchemaName))
|
||||
}
|
||||
|
||||
if e.Source&sourceLocalFilter != 0 {
|
||||
if e.ConfigRead == nil {
|
||||
panic(fmt.Sprintf("PROVIDER BUG: %s can not be configured as a local filter and be missing a config read handler", e.SchemaName))
|
||||
}
|
||||
|
||||
if e.ConfigUse == nil {
|
||||
panic(fmt.Sprintf("PROVIDER BUG: %s can not be configured as a local filter and be missing a config use handler", e.SchemaName))
|
||||
}
|
||||
}
|
||||
|
||||
if len(e.SetMembers) != 0 && !(e.Type == schema.TypeSet || e.Type == schema.TypeMap) {
|
||||
panic(fmt.Sprintf("PROVIDER BUG: %s is not of type Set but has SetMembers set", e.SchemaName))
|
||||
}
|
||||
|
||||
if e.Source&(sourceUserRequired|sourceAPIResult) == (sourceUserRequired | sourceAPIResult) {
|
||||
panic(fmt.Sprintf("PROVIDER BUG: %#v and %#v are mutually exclusive Source flags", sourceUserRequired, sourceAPIResult))
|
||||
}
|
||||
}
|
||||
|
||||
func (e *typeEntry) ToSchema() *schema.Schema {
|
||||
e.Validate()
|
||||
|
||||
attr := &schema.Schema{
|
||||
Computed: e.Source&computedAttrMask != 0,
|
||||
Default: e.Default,
|
||||
Description: e.Description,
|
||||
Optional: e.Source&optionalAttrMask != 0,
|
||||
Required: e.Source&requiredAttrMask != 0,
|
||||
Type: e.Type,
|
||||
// ValidateFunc: e.MakeValidationFunc(),
|
||||
}
|
||||
|
||||
// Fixup the type: use the real type vs a surrogate type
|
||||
switch e.Type {
|
||||
case schema.TypeList:
|
||||
if e.ListSchema == nil {
|
||||
attr.Elem = &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
}
|
||||
} else {
|
||||
attr.Elem = typeEntryMapToResource(e.ListSchema)
|
||||
}
|
||||
|
||||
case schema.TypeSet:
|
||||
attr.Elem = &schema.Resource{
|
||||
Schema: typeEntryMapToSchema(e.SetMembers),
|
||||
}
|
||||
}
|
||||
|
||||
return attr
|
||||
}
|
||||
|
||||
func mapStringToMapInterface(in map[string]string) map[string]interface{} {
|
||||
out := make(map[string]interface{}, len(in))
|
||||
for k, v := range in {
|
||||
out[k] = v
|
||||
}
|
||||
return out
|
||||
return queryOpts, nil
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package consul
|
|||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
|
@ -15,6 +16,9 @@ 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
|
||||
|
||||
|
@ -35,6 +39,8 @@ func makeValidationFunc(name string, validators []interface{}) func(v interface{
|
|||
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:
|
||||
|
@ -77,11 +83,51 @@ func validateDurationMinFactory(name, minDuration string) func(v interface{}, ke
|
|||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
if v.(int) < min {
|
||||
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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue