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{
|
resource.TestStep{
|
||||||
Config: testAccDataConsulAgentSelfConfig,
|
Config: testAccDataConsulAgentSelfConfig,
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckDataSourceValue("data.consul_agent_self.read", "bootstrap", "false"),
|
testAccCheckDataSourceValue("data.consul_agent_self.read", "acl_datacenter", "<all>"),
|
||||||
testAccCheckDataSourceValue("data.consul_agent_self.read", "datacenter", "dc1"),
|
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", "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", "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 {
|
if !ok {
|
||||||
return fmt.Errorf("Resource not found")
|
return fmt.Errorf("Resource not found")
|
||||||
}
|
}
|
||||||
out, ok := rn.Primary.Attributes[attr]
|
out, found := rn.Primary.Attributes[attr]
|
||||||
if !ok {
|
switch {
|
||||||
|
case !found:
|
||||||
return fmt.Errorf("Attribute '%s' not found: %#v", attr, rn.Primary.Attributes)
|
return fmt.Errorf("Attribute '%s' not found: %#v", attr, rn.Primary.Attributes)
|
||||||
}
|
case val == "<all>":
|
||||||
if val != "<any>" && out != val {
|
// 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)
|
return fmt.Errorf("Attribute '%s' value '%s' != '%s'", attr, out, val)
|
||||||
}
|
case val == "<any>" && out == "":
|
||||||
if val == "<any>" && out == "" {
|
|
||||||
return fmt.Errorf("Attribute '%s' value '%s'", attr, out)
|
return fmt.Errorf("Attribute '%s' value '%s'", attr, out)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -2,7 +2,6 @@ package consul
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
consulapi "github.com/hashicorp/consul/api"
|
consulapi "github.com/hashicorp/consul/api"
|
||||||
"github.com/hashicorp/errwrap"
|
"github.com/hashicorp/errwrap"
|
||||||
|
@ -115,44 +114,9 @@ func dataSourceConsulCatalogNodesRead(d *schema.ResourceData, meta interface{})
|
||||||
client := meta.(*consulapi.Client)
|
client := meta.(*consulapi.Client)
|
||||||
|
|
||||||
// Parse out data source filters to populate Consul's query options
|
// Parse out data source filters to populate Consul's query options
|
||||||
|
queryOpts, err := getQueryOpts(d, client)
|
||||||
dc, err := getDC(d, client)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errwrap.Wrapf("unable to get query options for fetching catalog nodes: {{err}}", 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes, meta, err := client.Catalog().Nodes(queryOpts)
|
nodes, meta, err := client.Catalog().Nodes(queryOpts)
|
||||||
|
@ -199,9 +163,9 @@ func dataSourceConsulCatalogNodesRead(d *schema.ResourceData, meta interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
const idKeyFmt = "catalog-nodes-%s"
|
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 {
|
if err := d.Set(nodesAttr, l); err != nil {
|
||||||
return errwrap.Wrapf("Unable to store nodes: {{err}}", err)
|
return errwrap.Wrapf("Unable to store nodes: {{err}}", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,536 +1,51 @@
|
||||||
package consul
|
package consul
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"time"
|
||||||
"fmt"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/hashicorp/errwrap"
|
consulapi "github.com/hashicorp/consul/api"
|
||||||
"github.com/hashicorp/terraform/helper/hashcode"
|
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
)
|
)
|
||||||
|
|
||||||
// apiAttr is the type used for constants representing well known keys within
|
func getQueryOpts(d *schema.ResourceData, client *consulapi.Client) (*consulapi.QueryOptions, error) {
|
||||||
// the API that are transmitted back to a resource over an API.
|
dc, err := getDC(d, client)
|
||||||
type apiAttr string
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// schemaAttr is the type used for constants representing well known keys
|
queryOpts := &consulapi.QueryOptions{
|
||||||
// within the schema for a resource.
|
Datacenter: dc,
|
||||||
type schemaAttr string
|
}
|
||||||
|
|
||||||
// sourceFlags represent the ways in which an attribute can be written. Some
|
if v, ok := d.GetOk(allowStale); ok {
|
||||||
// sources are mutually exclusive, yet other flag combinations are composable.
|
queryOpts.AllowStale = v.(bool)
|
||||||
type sourceFlags int
|
}
|
||||||
|
|
||||||
// typeKey is the lookup mechanism for the generated schema.
|
if v, ok := d.GetOk(requireConsistent); ok {
|
||||||
type typeKey int
|
queryOpts.RequireConsistent = v.(bool)
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
if v, ok := d.GetOk(nodeMeta); ok {
|
||||||
// sourceUserRequired indicates the parameter must be provided by the user in
|
m := v.(map[string]interface{})
|
||||||
// their configuration.
|
nodeMetaMap := make(map[string]string, len(nodeMeta))
|
||||||
sourceUserRequired sourceFlags = 1 << iota
|
for s, t := range m {
|
||||||
|
nodeMetaMap[s] = t.(string)
|
||||||
// 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))
|
|
||||||
}
|
}
|
||||||
|
queryOpts.NodeMeta = nodeMetaMap
|
||||||
}
|
}
|
||||||
|
|
||||||
return false, false
|
if v, ok := d.GetOk(token); ok {
|
||||||
}
|
queryOpts.Token = v.(string)
|
||||||
|
}
|
||||||
func apiTestFloat64(e *typeEntry, self interface{}) (interface{}, bool) {
|
|
||||||
m := self.(map[string]interface{})
|
if v, ok := d.GetOk(waitIndex); ok {
|
||||||
|
queryOpts.WaitIndex = uint64(v.(int))
|
||||||
v, found := m[string(e.APIName)]
|
}
|
||||||
if found {
|
|
||||||
if f, ok := v.(float64); ok {
|
if v, ok := d.GetOk(waitTime); ok {
|
||||||
return f, true
|
d, _ := time.ParseDuration(v.(string))
|
||||||
} else {
|
queryOpts.WaitTime = d
|
||||||
panic(fmt.Sprintf("PROVIDER BUG: %q fails float64 type assertion", e.SchemaName))
|
}
|
||||||
}
|
|
||||||
}
|
return queryOpts, nil
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if foundName {
|
|
||||||
return l, true
|
|
||||||
}
|
|
||||||
|
|
||||||
return []interface{}{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
return w.SetFloat64(e.SchemaName, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package consul
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/errwrap"
|
"github.com/hashicorp/errwrap"
|
||||||
|
@ -15,6 +16,9 @@ type validatorInputs []interface{}
|
||||||
// validateDurationMin is the minimum duration to accept as input
|
// validateDurationMin is the minimum duration to accept as input
|
||||||
type validateDurationMin string
|
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
|
// validateIntMin is the minimum integer value to accept as input
|
||||||
type validateIntMin int
|
type validateIntMin int
|
||||||
|
|
||||||
|
@ -35,6 +39,8 @@ func makeValidationFunc(name string, validators []interface{}) func(v interface{
|
||||||
switch u := v.(type) {
|
switch u := v.(type) {
|
||||||
case validateDurationMin:
|
case validateDurationMin:
|
||||||
fns = append(fns, validateDurationMinFactory(name, string(u)))
|
fns = append(fns, validateDurationMinFactory(name, string(u)))
|
||||||
|
case validateIntMax:
|
||||||
|
fns = append(fns, validateIntMaxFactory(name, int(u)))
|
||||||
case validateIntMin:
|
case validateIntMin:
|
||||||
fns = append(fns, validateIntMinFactory(name, int(u)))
|
fns = append(fns, validateIntMinFactory(name, int(u)))
|
||||||
case validateRegexp:
|
case validateRegexp:
|
||||||
|
@ -77,10 +83,50 @@ 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) {
|
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) {
|
return func(v interface{}, key string) (warnings []string, errors []error) {
|
||||||
if v.(int) < min {
|
switch u := v.(type) {
|
||||||
errors = append(errors, fmt.Errorf("Invalid %s specified: %d less than the required minimum %d", name, v.(int), min))
|
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
|
return warnings, errors
|
||||||
|
|
Loading…
Reference in New Issue