Add a datasource for catalog nodes.
This commit is contained in:
parent
db2f217f25
commit
ac1294633a
|
@ -0,0 +1,388 @@
|
|||
package consul
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
consulapi "github.com/hashicorp/consul/api"
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
// Top-level consul_catalog_nodes attributes
|
||||
const (
|
||||
_CatalogNodes _TypeKey = iota
|
||||
_CatalogNodesAllowStale
|
||||
_CatalogNodesDatacenter
|
||||
_CatalogNodesNear
|
||||
_CatalogNodesRequireConsistent
|
||||
_CatalogNodesToken
|
||||
_CatalogNodesWaitIndex
|
||||
_CatalogNodesWaitTime
|
||||
)
|
||||
|
||||
// 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),
|
||||
}
|
||||
}
|
||||
|
||||
func dataSourceConsulCatalogNodesRead(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*consulapi.Client)
|
||||
|
||||
dc, err := getDC(d, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
queryOpts := &consulapi.QueryOptions{
|
||||
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 := 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nodes, meta, err := client.Catalog().Nodes(queryOpts)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
h := e.MustLookupTypeHandler()
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
l = append(l, mWriter.ToMap())
|
||||
}
|
||||
|
||||
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))
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package consul
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
func TestAccDataConsulCatalogNodes_basic(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccDataConsulCatalogNodesConfig,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDataSourceValue("data.consul_catalog_nodes.read", "nodes.#", "1"),
|
||||
testAccCheckDataSourceValue("data.consul_catalog_nodes.read", "nodes.0.id", "<any>"),
|
||||
testAccCheckDataSourceValue("data.consul_catalog_nodes.read", "nodes.0.name", "<any>"),
|
||||
testAccCheckDataSourceValue("data.consul_catalog_nodes.read", "nodes.0.address", "<any>"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const testAccDataConsulCatalogNodesConfig = `
|
||||
data "consul_catalog_nodes" "read" {
|
||||
allow_stale = true
|
||||
require_consistent = false
|
||||
near = "_agent"
|
||||
token = ""
|
||||
wait_index = 0
|
||||
wait_time = "1m"
|
||||
}
|
||||
`
|
|
@ -64,8 +64,9 @@ func Provider() terraform.ResourceProvider {
|
|||
},
|
||||
|
||||
DataSourcesMap: map[string]*schema.Resource{
|
||||
"consul_agent_self": dataSourceConsulAgentSelf(),
|
||||
"consul_keys": dataSourceConsulKeys(),
|
||||
"consul_agent_self": dataSourceConsulAgentSelf(),
|
||||
"consul_catalog_nodes": dataSourceConsulCatalogNodes(),
|
||||
"consul_keys": dataSourceConsulKeys(),
|
||||
},
|
||||
|
||||
ResourcesMap: map[string]*schema.Resource{
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/hashicorp/terraform/helper/hashcode"
|
||||
|
@ -31,6 +32,12 @@ type _TypeKey int
|
|||
// 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
|
||||
|
||||
|
@ -46,29 +53,61 @@ const (
|
|||
// _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, map[string]interface{}) (interface{}, bool)
|
||||
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, map[string]interface{}) (interface{}, bool)
|
||||
APITest func(*_TypeEntry, interface{}) (interface{}, bool)
|
||||
APIToState func(*_TypeEntry, interface{}, _AttrWriter) error
|
||||
}
|
||||
|
||||
|
@ -99,8 +138,10 @@ var _TypeHandlerLookupMap = map[schema.ValueType]*_TypeHandlers{
|
|||
},
|
||||
}
|
||||
|
||||
func _APITestBool(e *_TypeEntry, self map[string]interface{}) (interface{}, bool) {
|
||||
v, found := self[string(e.APIName)]
|
||||
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
|
||||
|
@ -112,8 +153,10 @@ func _APITestBool(e *_TypeEntry, self map[string]interface{}) (interface{}, bool
|
|||
return false, false
|
||||
}
|
||||
|
||||
func _APITestFloat64(e *_TypeEntry, self map[string]interface{}) (interface{}, bool) {
|
||||
v, found := self[string(e.APIName)]
|
||||
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
|
||||
|
@ -124,22 +167,26 @@ func _APITestFloat64(e *_TypeEntry, self map[string]interface{}) (interface{}, b
|
|||
return 0.0, false
|
||||
}
|
||||
|
||||
func _APITestID(e *_TypeEntry, self map[string]interface{}) (interface{}, bool) {
|
||||
v, _ := _APITestString(e, self)
|
||||
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 map[string]interface{}) (interface{}, bool) {
|
||||
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 := self[string(name)]
|
||||
v, found := m[string(name)]
|
||||
if found {
|
||||
foundName = true
|
||||
// TODO(sean@): should make a list writer that normalizes v.(type) to a
|
||||
|
@ -162,7 +209,9 @@ func _APITestList(e *_TypeEntry, self map[string]interface{}) (interface{}, bool
|
|||
return []interface{}{}, false
|
||||
}
|
||||
|
||||
func _APITestMap(e *_TypeEntry, self map[string]interface{}) (interface{}, 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 {
|
||||
|
@ -174,7 +223,9 @@ func _APITestMap(e *_TypeEntry, self map[string]interface{}) (interface{}, bool)
|
|||
return "", false
|
||||
}
|
||||
|
||||
func _APITestSet(e *_TypeEntry, self map[string]interface{}) (interface{}, 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 {
|
||||
|
@ -186,7 +237,9 @@ func _APITestSet(e *_TypeEntry, self map[string]interface{}) (interface{}, bool)
|
|||
return "", false
|
||||
}
|
||||
|
||||
func _APITestString(e *_TypeEntry, self map[string]interface{}) (interface{}, bool) {
|
||||
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 {
|
||||
|
@ -361,6 +414,33 @@ func (e *_TypeEntry) LookupDefaultTypeHandler() *_TypeHandlers {
|
|||
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 {
|
||||
|
@ -384,43 +464,42 @@ func _StateSet(d *schema.ResourceData, attrName _SchemaAttr, v interface{}) erro
|
|||
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 {
|
||||
e.Validate()
|
||||
|
||||
attr := &schema.Schema{
|
||||
Type: e.Type,
|
||||
Description: e.Description,
|
||||
Optional: e.Source&_SourceAPIResult == _SourceAPIResult,
|
||||
Required: e.Source&_SourceUserRequired == _SourceUserRequired,
|
||||
Computed: e.Source&_SourceAPIResult == _SourceAPIResult,
|
||||
ValidateFunc: e.MakeValidationFunc(),
|
||||
}
|
||||
|
||||
// Fixup the type: use the real type vs a surrogate type
|
||||
switch e.Type {
|
||||
case schema.TypeList:
|
||||
attr.Elem = &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
}
|
||||
case schema.TypeSet:
|
||||
attr.Elem = &schema.Resource{
|
||||
Schema: _TypeEntryMapToSchema(e.SetMembers),
|
||||
}
|
||||
}
|
||||
|
||||
out[string(e.SchemaName)] = attr
|
||||
out[string(e.SchemaName)] = e.ToSchema()
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (e *_TypeEntry) Validate() {
|
||||
if e.Source&_SourceAPIResult == _SourceAPIResult && e.Type == schema.TypeSet {
|
||||
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))
|
||||
}
|
||||
|
@ -438,9 +517,13 @@ func (e *_TypeEntry) MakeValidationFunc() func(v interface{}, key string) (warni
|
|||
return nil
|
||||
}
|
||||
|
||||
fns := make([]func(v interface{}, key string) (warnings []string, errors []error), len(e.ValidateFuncs))
|
||||
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)))
|
||||
}
|
||||
|
@ -457,29 +540,73 @@ func (e *_TypeEntry) MakeValidationFunc() func(v interface{}, key string) (warni
|
|||
}
|
||||
}
|
||||
|
||||
// _ValidateFuncs takes a list of typed validator inputs, creates validation
|
||||
// functions for each and then runs them in serial until either a warning or
|
||||
// error is returned from the first validation function.
|
||||
func _ValidateFuncs(e *_TypeEntry, in ...interface{}) func(v interface{}, key string) (warnings []string, errors []error) {
|
||||
if len(in) == 0 {
|
||||
return nil
|
||||
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(),
|
||||
}
|
||||
|
||||
fns := make([]func(v interface{}, key string) (warnings []string, errors []error), len(in))
|
||||
for _, v := range in {
|
||||
switch v.(type) {
|
||||
case _ValidateRegexp:
|
||||
fns = append(fns, _ValidateRegexpFactory(e, v.(string)))
|
||||
// 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
|
||||
}
|
||||
|
||||
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) {
|
||||
for _, fn := range fns {
|
||||
warnings, errors = fn(v, key)
|
||||
if len(warnings) > 0 || len(errors) > 0 {
|
||||
break
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue