diff --git a/builtin/providers/consul/GNUmakefile b/builtin/providers/consul/GNUmakefile new file mode 100644 index 0000000000..224edfbc42 --- /dev/null +++ b/builtin/providers/consul/GNUmakefile @@ -0,0 +1,4 @@ +# env TESTARGS='-test.parallel=1 -run TestAccDataConsulAgentSelf_basic' TF_LOG=debug make test +test:: + 2>&1 env \ + make -C ../../.. testacc TEST=./builtin/providers/consul | tee test.log diff --git a/builtin/providers/consul/attr_writer.go b/builtin/providers/consul/attr_writer.go new file mode 100644 index 0000000000..c1e5d4fe40 --- /dev/null +++ b/builtin/providers/consul/attr_writer.go @@ -0,0 +1,14 @@ +package consul + +import "github.com/hashicorp/terraform/helper/schema" + +type _AttrWriter interface { + BackingType() string + + SetBool(_SchemaAttr, bool) error + SetFloat64(_SchemaAttr, float64) error + SetList(_SchemaAttr, []interface{}) error + SetMap(_SchemaAttr, map[string]interface{}) error + SetSet(_SchemaAttr, *schema.Set) error + SetString(_SchemaAttr, string) error +} diff --git a/builtin/providers/consul/attr_writer_map.go b/builtin/providers/consul/attr_writer_map.go new file mode 100644 index 0000000000..f603865013 --- /dev/null +++ b/builtin/providers/consul/attr_writer_map.go @@ -0,0 +1,77 @@ +package consul + +import ( + "fmt" + "strconv" + "strings" + + "github.com/hashicorp/terraform/helper/schema" +) + +type _AttrWriterMap struct { + m *map[string]interface{} +} + +func _NewMapWriter(m *map[string]interface{}) *_AttrWriterMap { + return &_AttrWriterMap{ + m: m, + } +} + +func (w *_AttrWriterMap) BackingType() string { + return "map" +} + +func (w *_AttrWriterMap) Set(name _SchemaAttr, v interface{}) error { + switch u := v.(type) { + case string: + return w.SetString(name, u) + case float64: + return w.SetFloat64(name, u) + case bool: + return w.SetBool(name, u) + case nil: + return w.SetString(name, "") + default: + panic(fmt.Sprintf("PROVIDER BUG: Set type %T not supported (%#v) for %s ", v, v, name)) + } +} + +func (w *_AttrWriterMap) SetBool(name _SchemaAttr, b bool) error { + (*w.m)[string(name)] = fmt.Sprintf("%t", b) + return nil +} + +func (w *_AttrWriterMap) SetFloat64(name _SchemaAttr, f float64) error { + (*w.m)[string(name)] = strconv.FormatFloat(f, 'g', -1, 64) + return nil +} + +func (w *_AttrWriterMap) SetList(name _SchemaAttr, l []interface{}) error { + panic(fmt.Sprintf("PROVIDER BUG: Cat set a list within a map for %s", name)) + out := make([]string, 0, len(l)) + for i, v := range l { + switch u := v.(type) { + case string: + out[i] = u + default: + panic(fmt.Sprintf("PROVIDER BUG: SetList type %T not supported (%#v)", v, v)) + } + } + + (*w.m)[string(name)] = strings.Join(out, ", ") + return nil +} + +func (w *_AttrWriterMap) SetMap(name _SchemaAttr, m map[string]interface{}) error { + panic(fmt.Sprintf("PROVIDER BUG: Cat set a map within a map for %s", name)) +} + +func (w *_AttrWriterMap) SetSet(name _SchemaAttr, s *schema.Set) error { + panic(fmt.Sprintf("PROVIDER BUG: Cat set a set within a map for %s", name)) +} + +func (w *_AttrWriterMap) SetString(name _SchemaAttr, s string) error { + (*w.m)[string(name)] = s + return nil +} diff --git a/builtin/providers/consul/attr_writer_state.go b/builtin/providers/consul/attr_writer_state.go new file mode 100644 index 0000000000..ab11ce0955 --- /dev/null +++ b/builtin/providers/consul/attr_writer_state.go @@ -0,0 +1,45 @@ +package consul + +import "github.com/hashicorp/terraform/helper/schema" + +type _AttrWriterState struct { + d *schema.ResourceData +} + +func _NewStateWriter(d *schema.ResourceData) *_AttrWriterState { + return &_AttrWriterState{ + d: d, + } +} + +func (w *_AttrWriterState) BackingType() string { + return "state" +} + +func (w *_AttrWriterState) SetBool(name _SchemaAttr, b bool) error { + return _StateSet(w.d, name, b) +} + +func (w *_AttrWriterState) SetID(id string) { + w.d.SetId(id) +} + +func (w *_AttrWriterState) SetFloat64(name _SchemaAttr, f float64) error { + return _StateSet(w.d, name, f) +} + +func (w *_AttrWriterState) SetList(name _SchemaAttr, l []interface{}) error { + return _StateSet(w.d, name, l) +} + +func (w *_AttrWriterState) SetMap(name _SchemaAttr, m map[string]interface{}) error { + return _StateSet(w.d, name, m) +} + +func (w *_AttrWriterState) SetSet(name _SchemaAttr, s *schema.Set) error { + return _StateSet(w.d, name, []interface{}{s}) +} + +func (w *_AttrWriterState) SetString(name _SchemaAttr, s string) error { + return _StateSet(w.d, name, s) +} diff --git a/builtin/providers/consul/data_source_consul_agent_self.go b/builtin/providers/consul/data_source_consul_agent_self.go new file mode 100644 index 0000000000..319b32f2b1 --- /dev/null +++ b/builtin/providers/consul/data_source_consul_agent_self.go @@ -0,0 +1,872 @@ +package consul + +import ( + "fmt" + + consulapi "github.com/hashicorp/consul/api" + "github.com/hashicorp/errwrap" + "github.com/hashicorp/terraform/helper/schema" +) + +const ( + _AgentSelfACLDatacenter _TypeKey = iota + _AgentSelfACLDefaultPolicy + _AgentSelfACLDisableTTL + _AgentSelfACLDownPolicy + _AgentSelfACLEnforceVersion8 + _AgentSelfACLTTL + _AgentSelfAddresses + _AgentSelfAdvertiseAddr + _AgentSelfAdvertiseAddrWAN + _AgentSelfAdvertiseAddrs + _AgentSelfAtlasJoin + _AgentSelfBindAddr + _AgentSelfBootstrap + _AgentSelfBootstrapExpect + _AgentSelfCAFile + _AgentSelfCertFile + _AgentSelfCheckDeregisterIntervalMin + _AgentSelfCheckDisableAnonymousSignature + _AgentSelfCheckDisableRemoteExec + _AgentSelfCheckReapInterval + _AgentSelfCheckUpdateInterval + _AgentSelfClientAddr + _AgentSelfDNSConfig + _AgentSelfDNSRecursors + _AgentSelfDataDir + _AgentSelfDatacenter + _AgentSelfDevMode + _AgentSelfDisableCoordinates + _AgentSelfDisableUpdateCheck + _AgentSelfDomain + _AgentSelfEnableDebug + _AgentSelfEnableSyslog + _AgentSelfEnableUI + _AgentSelfID + _AgentSelfKeyFile + _AgentSelfLeaveOnInt + _AgentSelfLeaveOnTerm + _AgentSelfLogLevel + _AgentSelfName + _AgentSelfPerformance + _AgentSelfPidFile + _AgentSelfPorts + _AgentSelfProtocol + _AgentSelfReconnectTimeoutLAN + _AgentSelfReconnectTimeoutWAN + _AgentSelfRejoinAfterLeave + _AgentSelfRetryJoin + _AgentSelfRetryJoinEC2 + _AgentSelfRetryJoinGCE + _AgentSelfRetryJoinWAN + _AgentSelfRetryMaxAttempts + _AgentSelfRetryMaxAttemptsWAN + _AgentSelfRevision + _AgentSelfSerfLANBindAddr + _AgentSelfSerfWANBindAddr + _AgentSelfServer + _AgentSelfServerName + _AgentSelfSessionTTLMin + _AgentSelfStartJoin + _AgentSelfStartJoinWAN + _AgentSelfSyslogFacility + _AgentSelfTLSMinVersion + _AgentSelfTaggedAddresses + _AgentSelfTelemetry + _AgentSelfTranslateWANAddrs + _AgentSelfUIDir + _AgentSelfUnixSockets + _AgentSelfVerifyIncoming + _AgentSelfVerifyOutgoing + _AgentSelfVerifyServerHostname + _AgentSelfVersion + _AgentSelfVersionPrerelease +) + +const ( + _AgentSelfDNSAllowStale _TypeKey = iota + _AgentSelfDNSMaxStale + _AgentSelfRecursorTimeout + _AgentSelfDNSDisableCompression + _AgentSelfDNSEnableTruncate + _AgentSelfDNSNodeTTL + _AgentSelfDNSOnlyPassing + _AgentSelfDNSUDPAnswerLimit + _AgentSelfServiceTTL +) + +const ( + _AgentSelfPerformanceRaftMultiplier _TypeKey = iota +) + +const ( + _AgentSelfPortsDNS _TypeKey = iota + _AgentSelfPortsHTTP + _AgentSelfPortsHTTPS + _AgentSelfPortsRPC + _AgentSelfPortsSerfLAN + _AgentSelfPortsSerfWAN + _AgentSelfPortsServer +) + +const ( + _AgentSelfTaggedAddressesLAN _TypeKey = iota + _AgentSelfTaggedAddressesWAN +) + +const ( + _AgentSelfTelemetryCirconusAPIApp _TypeKey = iota + _AgentSelfTelemetryCirconusAPIURL + _AgentSelfTelemetryCirconusBrokerID + _AgentSelfTelemetryCirconusBrokerSelectTag + _AgentSelfTelemetryCirconusCheckDisplayName + _AgentSelfTelemetryCirconusCheckForceMetricActiation + _AgentSelfTelemetryCirconusCheckID + _AgentSelfTelemetryCirconusCheckInstanceID + _AgentSelfTelemetryCirconusCheckSearchTag + _AgentSelfTelemetryCirconusCheckSubmissionURL + _AgentSelfTelemetryCirconusCheckTags + _AgentSelfTelemetryCirconusSubmissionInterval + _AgentSelfTelemetryDisableHostname + _AgentSelfTelemetryDogStatsdAddr + _AgentSelfTelemetryDogStatsdTags + _AgentSelfTelemetryStatsdAddr + _AgentSelfTelemetryStatsiteAddr + _AgentSelfTelemetryStatsitePrefix +) + +// Schema for consul's /v1/agent/self endpoint +var _AgentSelfMap = map[_TypeKey]*_TypeEntry{ + _AgentSelfACLDatacenter: { + APIName: "ACLDatacenter", + SchemaName: "acl_datacenter", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfACLDefaultPolicy: { + APIName: "ACLDefaultPolicy", + SchemaName: "acl_default_policy", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfACLDisableTTL: { + APIName: "ACLDisabledTTL", + SchemaName: "acl_disable_ttl", + Source: _SourceAPIResult, + Type: schema.TypeFloat, + }, + _AgentSelfACLDownPolicy: { + APIName: "ACLDownPolicy", + SchemaName: "acl_down_policy", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfACLEnforceVersion8: { + APIName: "ACLEnforceVersion8", + SchemaName: "acl_enforce_version_8", + Source: _SourceAPIResult, + Type: schema.TypeBool, + }, + _AgentSelfACLTTL: { + APIName: "ACLTTL", + SchemaName: "acl_ttl", + Source: _SourceAPIResult, + Type: schema.TypeFloat, + }, + _AgentSelfAddresses: { + APIName: "Addresses", + SchemaName: "addresses", + Source: _SourceAPIResult, + Type: schema.TypeMap, + }, + _AgentSelfAdvertiseAddr: { + APIName: "AdvertiseAddr", + SchemaName: "advertise_addr", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfAdvertiseAddrs: { + APIName: "AdvertiseAddrs", + SchemaName: "advertise_addrs", + Source: _SourceAPIResult, + Type: schema.TypeMap, + }, + _AgentSelfAdvertiseAddrWAN: { + APIName: "AdvertiseAddrWan", + SchemaName: "advertise_addr_wan", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + // Omitting the following since they've been depreciated: + // + // "AtlasInfrastructure": "", + // "AtlasEndpoint": "", + _AgentSelfAtlasJoin: { + APIName: "AtlasJoin", + SchemaName: "atlas_join", + Source: _SourceAPIResult, + Type: schema.TypeBool, + }, + _AgentSelfBindAddr: { + APIName: "BindAddr", + SchemaName: "bind_addr", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfBootstrap: { + APIName: "Bootstrap", + SchemaName: "bootstrap", + Source: _SourceAPIResult, + Type: schema.TypeBool, + }, + _AgentSelfBootstrapExpect: { + APIName: "BootstrapExpect", + SchemaName: "bootstrap_expect", + Source: _SourceAPIResult, + Type: schema.TypeFloat, + }, + _AgentSelfCAFile: { + APIName: "CAFile", + SchemaName: "ca_file", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfCertFile: { + APIName: "CertFile", + SchemaName: "cert_file", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfCheckDeregisterIntervalMin: { + APIName: "CheckDeregisterIntervalMin", + SchemaName: "check_deregister_interval_min", + Source: _SourceAPIResult, + Type: schema.TypeFloat, + }, + _AgentSelfCheckDisableAnonymousSignature: { + APIName: "DisableAnonymousSignature", + SchemaName: "disable_anonymous_signature", + Source: _SourceAPIResult, + Type: schema.TypeBool, + }, + _AgentSelfCheckDisableRemoteExec: { + APIName: "DisableRemoteExec", + SchemaName: "disable_remote_exec", + Source: _SourceAPIResult, + Type: schema.TypeBool, + }, + _AgentSelfCheckReapInterval: { + APIName: "CheckReapInterval", + SchemaName: "check_reap_interval", + Source: _SourceAPIResult, + Type: schema.TypeFloat, + }, + _AgentSelfCheckUpdateInterval: { + APIName: "CheckUpdateInterval", + SchemaName: "check_update_interval", + Source: _SourceAPIResult, + Type: schema.TypeFloat, + }, + _AgentSelfClientAddr: { + APIName: "ClientAddr", + SchemaName: "client_addr", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfDNSConfig: { + APIName: "DNSConfig", + SchemaName: "dns_config", + Source: _SourceAPIResult, + Type: schema.TypeMap, + SetMembers: map[_TypeKey]*_TypeEntry{ + _AgentSelfDNSAllowStale: { + APIName: "AllowStale", + SchemaName: "allow_stale", + Source: _SourceAPIResult, + Type: schema.TypeBool, + }, + _AgentSelfDNSDisableCompression: { + APIName: "DisableCompression", + SchemaName: "disable_compression", + Source: _SourceAPIResult, + Type: schema.TypeBool, + }, + _AgentSelfDNSEnableTruncate: { + APIName: "EnableTruncate", + SchemaName: "enable_truncate", + Source: _SourceAPIResult, + Type: schema.TypeBool, + }, + _AgentSelfDNSMaxStale: { + APIName: "MaxStale", + SchemaName: "max_stale", + Source: _SourceAPIResult, + Type: schema.TypeFloat, + }, + _AgentSelfDNSNodeTTL: { + APIName: "NodeTTL", + SchemaName: "node_ttl", + Source: _SourceAPIResult, + Type: schema.TypeFloat, + }, + _AgentSelfDNSOnlyPassing: { + APIName: "OnlyPassing", + SchemaName: "only_passing", + Source: _SourceAPIResult, + Type: schema.TypeBool, + }, + _AgentSelfRecursorTimeout: { + APIName: "RecursorTimeout", + SchemaName: "recursor_timeout", + Source: _SourceAPIResult, + Type: schema.TypeFloat, + }, + _AgentSelfServiceTTL: { + APIName: "ServiceTTL", + SchemaName: "service_ttl", + Source: _SourceAPIResult, + Type: schema.TypeFloat, + }, + _AgentSelfDNSUDPAnswerLimit: { + APIName: "UDPAnswerLimit", + SchemaName: "udp_answer_limit", + Source: _SourceAPIResult, + Type: schema.TypeFloat, + }, + }, + }, + _AgentSelfDataDir: { + APIName: "DataDir", + SchemaName: "data_dir", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfDatacenter: { + APIName: "Datacenter", + SchemaName: "datacenter", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfDevMode: { + APIName: "DevMode", + SchemaName: "dev_mode", + Source: _SourceAPIResult, + Type: schema.TypeBool, + }, + _AgentSelfDisableCoordinates: { + APIName: "DisableCoordinates", + SchemaName: "coordinates", + Source: _SourceAPIResult, + Type: schema.TypeBool, + }, + _AgentSelfDisableUpdateCheck: { + APIName: "DisableUpdateCheck", + SchemaName: "update_check", + Source: _SourceAPIResult, + Type: schema.TypeBool, + }, + _AgentSelfDNSRecursors: { + APIName: "DNSRecursors", + APIAliases: []_APIAttr{"DNSRecursor"}, + SchemaName: "dns_recursors", + Source: _SourceAPIResult, + Type: schema.TypeList, + }, + _AgentSelfDomain: { + APIName: "Domain", + SchemaName: "domain", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfEnableDebug: { + APIName: "EnableDebug", + SchemaName: "debug", + Source: _SourceAPIResult, + Type: schema.TypeBool, + }, + _AgentSelfEnableSyslog: { + APIName: "EnableSyslog", + SchemaName: "syslog", + Source: _SourceAPIResult, + Type: schema.TypeBool, + }, + _AgentSelfEnableUI: { + APIName: "EnableUi", + SchemaName: "ui", + Source: _SourceAPIResult, + Type: schema.TypeBool, + }, + // "HTTPAPIResponseHeaders": nil, + _AgentSelfID: { + APIName: "NodeID", + SchemaName: "id", + Source: _SourceAPIResult, + Type: schema.TypeString, + ValidateFuncs: []interface{}{ + _ValidateRegexp(`(?i)^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$`), + }, + APITest: _APITestID, + APIToState: _APIToStateID, + }, + _AgentSelfKeyFile: { + APIName: "KeyFile", + SchemaName: "key_file", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfLeaveOnInt: { + APIName: "SkipLeaveOnInt", + SchemaName: "leave_on_int", + Source: _SourceAPIResult, + Type: schema.TypeBool, + APITest: _APITestBool, + APIToState: _NegateBoolToState(_APIToStateBool), + }, + _AgentSelfLeaveOnTerm: { + APIName: "LeaveOnTerm", + SchemaName: "leave_on_term", + Source: _SourceAPIResult, + Type: schema.TypeBool, + }, + _AgentSelfLogLevel: { + APIName: "LogLevel", + SchemaName: "log_level", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfName: { + APIName: "NodeName", + SchemaName: "name", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfPerformance: { + APIName: "Performance", + SchemaName: "performance", + Source: _SourceAPIResult, + Type: schema.TypeMap, + SetMembers: map[_TypeKey]*_TypeEntry{ + _AgentSelfPerformanceRaftMultiplier: { + APIName: "RaftMultiplier", + SchemaName: "raft_multiplier", + Source: _SourceAPIResult, + Type: schema.TypeFloat, + }, + }, + }, + _AgentSelfPidFile: { + APIName: "PidFile", + SchemaName: "pid_file", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfPorts: { + APIName: "Ports", + SchemaName: "ports", + Source: _SourceAPIResult, + Type: schema.TypeMap, + SetMembers: map[_TypeKey]*_TypeEntry{ + _AgentSelfPortsDNS: { + APIName: "DNS", + SchemaName: "dns", + Source: _SourceAPIResult, + Type: schema.TypeFloat, + }, + _AgentSelfPortsHTTP: { + APIName: "HTTP", + SchemaName: "http", + Source: _SourceAPIResult, + Type: schema.TypeFloat, + }, + _AgentSelfPortsHTTPS: { + APIName: "HTTPS", + SchemaName: "https", + Source: _SourceAPIResult, + Type: schema.TypeFloat, + }, + _AgentSelfPortsRPC: { + APIName: "RPC", + SchemaName: "rpc", + Source: _SourceAPIResult, + Type: schema.TypeFloat, + }, + _AgentSelfPortsSerfLAN: { + APIName: "SerfLan", + SchemaName: "serf_lan", + Source: _SourceAPIResult, + Type: schema.TypeFloat, + }, + _AgentSelfPortsSerfWAN: { + APIName: "SerfWan", + SchemaName: "serf_wan", + Source: _SourceAPIResult, + Type: schema.TypeFloat, + }, + _AgentSelfPortsServer: { + APIName: "Server", + SchemaName: "server", + Source: _SourceAPIResult, + Type: schema.TypeFloat, + }, + }, + }, + _AgentSelfProtocol: { + APIName: "Protocol", + SchemaName: "protocol", + Source: _SourceAPIResult, + Type: schema.TypeFloat, + }, + _AgentSelfReconnectTimeoutLAN: { + APIName: "ReconnectTimeoutLan", + SchemaName: "reconnect_timeout_lan", + Source: _SourceAPIResult, + Type: schema.TypeFloat, + }, + _AgentSelfReconnectTimeoutWAN: { + APIName: "ReconnectTimeoutWan", + SchemaName: "reconnect_timeout_wan", + Source: _SourceAPIResult, + Type: schema.TypeFloat, + }, + _AgentSelfRejoinAfterLeave: { + APIName: "RejoinAfterLeave", + SchemaName: "rejoin_after_leave", + Source: _SourceAPIResult, + Type: schema.TypeBool, + }, + // "RetryIntervalWanRaw": "", + _AgentSelfRetryJoin: { + APIName: "RetryJoin", + SchemaName: "retry_join", + Source: _SourceAPIResult, + Type: schema.TypeList, + }, + _AgentSelfRetryJoinWAN: { + APIName: "RetryJoinWan", + SchemaName: "retry_join_wan", + Source: _SourceAPIResult, + Type: schema.TypeList, + }, + _AgentSelfRetryMaxAttempts: { + APIName: "RetryMaxAttempts", + SchemaName: "retry_max_attempts", + Source: _SourceAPIResult, + Type: schema.TypeFloat, + }, + _AgentSelfRetryMaxAttemptsWAN: { + APIName: "RetryMaxAttemptsWan", + SchemaName: "retry_max_attempts_wan", + Source: _SourceAPIResult, + Type: schema.TypeFloat, + }, + _AgentSelfRetryJoinEC2: { + APIName: "RetryJoinEC2", + SchemaName: "retry_join_ec2", + Source: _SourceAPIResult, + Type: schema.TypeMap, + }, + _AgentSelfRetryJoinGCE: { + APIName: "RetryJoinGCE", + SchemaName: "retry_join_GCE", + Source: _SourceAPIResult, + Type: schema.TypeMap, + }, + _AgentSelfRevision: { + APIName: "Revision", + SchemaName: "revision", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfSerfLANBindAddr: { + APIName: "SerfLanBindAddr", + SchemaName: "serf_lan_bind_addr", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfSerfWANBindAddr: { + APIName: "SerfWanBindAddr", + SchemaName: "serf_wan_bind_addr", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfServer: { + APIName: "Server", + SchemaName: "server", + Source: _SourceAPIResult, + Type: schema.TypeBool, + }, + _AgentSelfServerName: { + APIName: "ServerName", + SchemaName: "server_name", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfSessionTTLMin: { + APIName: "SessionTTLMin", + SchemaName: "session_ttl_min", + Source: _SourceAPIResult, + Type: schema.TypeFloat, + }, + _AgentSelfStartJoin: { + APIName: "StartJoin", + SchemaName: "start_join", + Source: _SourceAPIResult, + Type: schema.TypeList, + }, + _AgentSelfStartJoinWAN: { + APIName: "StartJoinWan", + SchemaName: "start_join_wan", + Source: _SourceAPIResult, + Type: schema.TypeList, + }, + _AgentSelfSyslogFacility: { + APIName: "SyslogFacility", + SchemaName: "syslog_facility", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfTaggedAddresses: { + APIName: "TaggedAddresses", + SchemaName: "tagged_addresses", + Source: _SourceAPIResult, + Type: schema.TypeMap, + SetMembers: map[_TypeKey]*_TypeEntry{ + _AgentSelfTaggedAddressesLAN: { + APIName: "LAN", + SchemaName: "lan", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfTaggedAddressesWAN: { + APIName: "WAN", + SchemaName: "wan", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + }, + }, + _AgentSelfTelemetry: { + APIName: "Telemetry", + SchemaName: "telemetry", + Source: _SourceAPIResult, + Type: schema.TypeMap, + SetMembers: map[_TypeKey]*_TypeEntry{ + _AgentSelfTelemetryCirconusAPIApp: { + APIName: "CirconusAPIApp", + SchemaName: "circonus_api_app", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfTelemetryCirconusAPIURL: { + APIName: "CirconusAPIURL", + SchemaName: "circonus_api_url", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfTelemetryCirconusBrokerID: { + APIName: "CirconusBrokerID", + SchemaName: "circonus_broker_id", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfTelemetryCirconusBrokerSelectTag: { + APIName: "CirconusBrokerSelectTag", + SchemaName: "circonus_broker_select_tag", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfTelemetryCirconusCheckDisplayName: { + APIName: "CirconusCheckDisplayName", + SchemaName: "circonus_check_display_name", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfTelemetryCirconusCheckForceMetricActiation: { + APIName: "CirconusCheckForceMetricActivation", + SchemaName: "circonus_check_force_metric_activation", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfTelemetryCirconusCheckID: { + APIName: "CirconusCheckID", + SchemaName: "circonus_check_id", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfTelemetryCirconusCheckInstanceID: { + APIName: "CirconusCheckInstanceID", + SchemaName: "circonus_check_instance_id", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfTelemetryCirconusCheckSearchTag: { + APIName: "CirconusCheckSearchTag", + SchemaName: "circonus_check_search_tag", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfTelemetryCirconusCheckSubmissionURL: { + APIName: "CirconusCheckSubmissionURL", + SchemaName: "circonus_check_submission_url", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfTelemetryCirconusCheckTags: { + APIName: "CirconusCheckTags", + SchemaName: "circonus_check_tags", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfTelemetryCirconusSubmissionInterval: { + APIName: "CirconusSubmissionInterval", + SchemaName: "circonus_submission_interval", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfTelemetryDisableHostname: { + APIName: "DisableHostname", + SchemaName: "disable_hostname", + Source: _SourceAPIResult, + Type: schema.TypeBool, + }, + _AgentSelfTelemetryDogStatsdAddr: { + APIName: "DogStatsdAddr", + SchemaName: "dog_statsd_addr", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfTelemetryDogStatsdTags: { + APIName: "DogStatsdTags", + SchemaName: "dog_statsd_tags", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfTelemetryStatsdAddr: { + APIName: "StatsdTags", + SchemaName: "statsd_tags", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfTelemetryStatsiteAddr: { + APIName: "StatsiteAddr", + SchemaName: "statsite_addr", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfTelemetryStatsitePrefix: { + APIName: "StatsitePrefix", + SchemaName: "statsite_prefix", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + }, + }, + _AgentSelfTLSMinVersion: { + APIName: "TLSMinVersion", + SchemaName: "tls_min_version", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfTranslateWANAddrs: { + APIName: "TranslateWanAddrs", + SchemaName: "translate_wan_addrs", + Source: _SourceAPIResult, + Type: schema.TypeBool, + }, + _AgentSelfUIDir: { + APIName: "UiDir", + SchemaName: "ui_dir", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfUnixSockets: { + APIName: "UnixSockets", + SchemaName: "unix_sockets", + Source: _SourceAPIResult, + Type: schema.TypeMap, + }, + _AgentSelfVerifyIncoming: { + APIName: "VerifyIncoming", + SchemaName: "verify_incoming", + Source: _SourceAPIResult, + Type: schema.TypeBool, + }, + _AgentSelfVerifyServerHostname: { + APIName: "VerifyServerHostname", + SchemaName: "verify_server_hostname", + Source: _SourceAPIResult, + Type: schema.TypeBool, + }, + _AgentSelfVerifyOutgoing: { + APIName: "VerifyOutgoing", + SchemaName: "verify_outgoing", + Source: _SourceAPIResult, + Type: schema.TypeBool, + }, + _AgentSelfVersion: { + APIName: "Version", + SchemaName: "version", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + _AgentSelfVersionPrerelease: { + APIName: "VersionPrerelease", + SchemaName: "version_prerelease", + Source: _SourceAPIResult, + Type: schema.TypeString, + }, + // "Watches": nil, +} + +func dataSourceConsulAgentSelf() *schema.Resource { + return &schema.Resource{ + Read: dataSourceConsulAgentSelfRead, + Schema: _TypeEntryMapToSchema(_AgentSelfMap), + } +} + +func dataSourceConsulAgentSelfRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*consulapi.Client) + info, err := client.Agent().Self() + if err != nil { + return err + } + + const _APIAgentConfig = "Config" + cfg, ok := info[_APIAgentConfig] + if !ok { + return fmt.Errorf("No %s info available within provider's agent/self endpoint", _APIAgentConfig) + } + + // 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 names + // would be sufficient in the most basic case. Food for thought. + dataSourceWriter := _NewStateWriter(d) + + for k, e := range _AgentSelfMap { + apiTest := e.APITest + if apiTest == nil { + apiTest = e.LookupDefaultTypeHandler().APITest + } + if apiTest == nil { + panic(fmt.Sprintf("PROVIDER BUG: %v missing APITest method", k)) + } + + apiToState := e.APIToState + if apiToState == nil { + apiToState = e.LookupDefaultTypeHandler().APIToState + } + if apiToState == nil { + panic(fmt.Sprintf("PROVIDER BUG: %v missing APIToState method", k)) + } + + if v, ok := apiTest(e, cfg); ok { + if err := apiToState(e, v, dataSourceWriter); err != nil { + return errwrap.Wrapf(fmt.Sprintf("error writing %q's data to state: {{err}}", e.SchemaName), err) + } + } + } + + return nil +} diff --git a/builtin/providers/consul/data_source_consul_agent_self_test.go b/builtin/providers/consul/data_source_consul_agent_self_test.go new file mode 100644 index 0000000000..b52b3f8706 --- /dev/null +++ b/builtin/providers/consul/data_source_consul_agent_self_test.go @@ -0,0 +1,53 @@ +package consul + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccDataConsulAgentSelf_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + 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", "id", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "name", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "server", "true"), + ), + }, + }, + }) +} + +func testAccCheckDataSourceValue(n, attr, val string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rn, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Resource not found") + } + out, ok := rn.Primary.Attributes[attr] + if !ok { + return fmt.Errorf("Attribute '%s' not found: %#v", attr, rn.Primary.Attributes) + } + if val != "" && out != val { + return fmt.Errorf("Attribute '%s' value '%s' != '%s'", attr, out, val) + } + if val == "" && out == "" { + return fmt.Errorf("Attribute '%s' value '%s'", attr, out) + } + return nil + } +} + +const testAccDataConsulAgentSelfConfig = ` +data "consul_agent_self" "read" { +} +` diff --git a/builtin/providers/consul/resource_provider.go b/builtin/providers/consul/resource_provider.go index 2b4d01079b..4688f543cd 100644 --- a/builtin/providers/consul/resource_provider.go +++ b/builtin/providers/consul/resource_provider.go @@ -64,7 +64,8 @@ func Provider() terraform.ResourceProvider { }, DataSourcesMap: map[string]*schema.Resource{ - "consul_keys": dataSourceConsulKeys(), + "consul_agent_self": dataSourceConsulAgentSelf(), + "consul_keys": dataSourceConsulKeys(), }, ResourcesMap: map[string]*schema.Resource{ diff --git a/builtin/providers/consul/utils.go b/builtin/providers/consul/utils.go new file mode 100644 index 0000000000..d3594a50ef --- /dev/null +++ b/builtin/providers/consul/utils.go @@ -0,0 +1,498 @@ +package consul + +import ( + "bytes" + "fmt" + "regexp" + "sort" + "strconv" + + "github.com/hashicorp/errwrap" + "github.com/hashicorp/terraform/helper/hashcode" + "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 + +// 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{} + +// _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. + _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 +) + +type _TypeEntry struct { + APIName _APIAttr + APIAliases []_APIAttr + Source _SourceFlags + Description string + SchemaName _SchemaAttr + Type schema.ValueType + ValidateFuncs []interface{} + SetMembers 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) + + // APIToState takes the value from APITest and writes it to the _AttrWriter + APIToState func(*_TypeEntry, interface{}, _AttrWriter) error +} + +type _TypeHandlers struct { + APITest func(*_TypeEntry, map[string]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 map[string]interface{}) (interface{}, bool) { + v, found := self[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 map[string]interface{}) (interface{}, bool) { + v, found := self[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 map[string]interface{}) (interface{}, bool) { + v, _ := _APITestString(e, self) + + // 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) { + 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)] + 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, self map[string]interface{}) (interface{}, bool) { + 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, self map[string]interface{}) (interface{}, bool) { + 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, self map[string]interface{}) (interface{}, bool) { + 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) + } + + m := make(map[string]interface{}, len(rawMap)) + mWriter := _NewMapWriter(&m) + + // 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, m) +} + +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 +} + +// _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 _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 + } + + return out +} + +func (e *_TypeEntry) Validate() { + if e.Source&_SourceAPIResult == _SourceAPIResult && e.Type == schema.TypeSet { + panic(fmt.Sprintf("PROVIDER BUG: %s can not be computed and of type Set", 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)) + } +} + +// 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), len(e.ValidateFuncs)) + for _, v := range e.ValidateFuncs { + switch u := v.(type) { + 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 + } +} + +// _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 + } + + 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))) + } + } + + 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 _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 + } +}