diff --git a/builtin/providers/consul/GNUmakefile b/builtin/providers/consul/GNUmakefile new file mode 100644 index 000000000000..224edfbc4262 --- /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/data_source_consul_agent_self.go b/builtin/providers/consul/data_source_consul_agent_self.go new file mode 100644 index 000000000000..a1996a510526 --- /dev/null +++ b/builtin/providers/consul/data_source_consul_agent_self.go @@ -0,0 +1,1443 @@ +package consul + +import ( + "fmt" + "strconv" + "time" + + consulapi "github.com/hashicorp/consul/api" + "github.com/hashicorp/errwrap" + "github.com/hashicorp/terraform/helper/schema" +) + +const ( + agentSelfACLDatacenter = "acl_datacenter" + agentSelfACLDefaultPolicy = "acl_default_policy" + agentSelfACLDisabledTTL = "acl_disabled_ttl" + agentSelfACLDownPolicy = "acl_down_policy" + agentSelfACLEnforceVersion8 = "acl_enforce_0_8_semantics" + agentSelfACLTTL = "acl_ttl" + agentSelfAddresses = "addresses" + agentSelfAdvertiseAddr = "advertise_addr" + agentSelfAdvertiseAddrWAN = "advertise_addr_wan" + agentSelfAdvertiseAddrs = "advertise_addrs" + agentSelfAtlasJoin = "atlas_join" + agentSelfBindAddr = "bind_addr" + agentSelfBootstrapExpect = "bootstrap_expect" + agentSelfBootstrapMode = "bootstrap_mode" + agentSelfCheckDeregisterIntervalMin = "check_deregister_interval_min" + agentSelfCheckReapInterval = "check_reap_interval" + agentSelfCheckUpdateInterval = "check_update_interval" + agentSelfClientAddr = "client_addr" + agentSelfDNSConfig = "dns" + agentSelfDNSRecursors = "dns_recursors" + agentSelfDataDir = "data_dir" + agentSelfDatacenter = "datacenter" + agentSelfDevMode = "dev_mode" + agentSelfDomain = "domain" + agentSelfEnableAnonymousSignature = "enable_anonymous_signature" + agentSelfEnableCoordinates = "enable_coordinates" + agentSelfEnableDebug = "enable_debug" + agentSelfEnableRemoteExec = "enable_remote_exec" + agentSelfEnableSyslog = "enable_syslog" + agentSelfEnableUI = "enable_ui" + agentSelfEnableUpdateCheck = "enable_update_check" + agentSelfID = "id" + agentSelfLeaveOnInt = "leave_on_int" + agentSelfLeaveOnTerm = "leave_on_term" + agentSelfLogLevel = "log_level" + agentSelfName = "name" + agentSelfPerformance = "performance" + agentSelfPidFile = "pid_file" + agentSelfPorts = "ports" + agentSelfProtocol = "protocol_version" + agentSelfReconnectTimeoutLAN = "reconnect_timeout_lan" + agentSelfReconnectTimeoutWAN = "reconnect_timeout_wan" + agentSelfRejoinAfterLeave = "rejoin_after_leave" + agentSelfRetryJoin = "retry_join" + agentSelfRetryJoinEC2 = "retry_join_ec2" + agentSelfRetryJoinGCE = "retry_join_gce" + agentSelfRetryJoinWAN = "retry_join_wan" + agentSelfRetryMaxAttempts = "retry_max_attempts" + agentSelfRetryMaxAttemptsWAN = "retry_max_attempts_wan" + agentSelfSerfLANBindAddr = "serf_lan_bind_addr" + agentSelfSerfWANBindAddr = "serf_wan_bind_addr" + agentSelfServerMode = "server_mode" + agentSelfServerName = "server_name" + agentSelfSessionTTLMin = "session_ttl_min" + agentSelfStartJoin = "start_join" + agentSelfStartJoinWAN = "start_join_wan" + agentSelfSyslogFacility = "syslog_facility" + agentSelfTLSCAFile = "tls_ca_file" + agentSelfTLSCertFile = "tls_cert_file" + agentSelfTLSKeyFile = "tls_key_file" + agentSelfTLSMinVersion = "tls_min_version" + agentSelfTLSVerifyIncoming = "tls_verify_incoming" + agentSelfTLSVerifyOutgoing = "tls_verify_outgoing" + agentSelfTLSVerifyServerHostname = "tls_verify_server_hostname" + agentSelfTaggedAddresses = "tagged_addresses" + agentSelfTelemetry = "telemetry" + agentSelfTranslateWANAddrs = "translate_wan_addrs" + agentSelfUIDir = "ui_dir" + agentSelfUnixSockets = "unix_sockets" + agentSelfVersion = "version" + agentSelfVersionPrerelease = "version_prerelease" + agentSelfVersionRevision = "version_revision" +) + +const ( + agentSelfRetryJoinAWSAccessKeyID = "access_key_id" + agentSelfRetryJoinAWSRegion = "region" + agentSelfRetryJoinAWSSecretAccessKey = "secret_access_key" + agentSelfRetryJoinAWSTagKey = "tag_key" + agentSelfRetryJoinAWSTagValue = "tag_value" +) + +const ( + agentSelfRetryJoinGCECredentialsFile = "credentials_file" + agentSelfRetryJoinGCEProjectName = "project_name" + agentSelfRetryJoinGCETagValue = "tag_value" + agentSelfRetryJoinGCEZonePattern = "zone_pattern" +) + +const ( + agentSelfDNSAllowStale = "allow_stale" + agentSelfDNSEnableCompression = "enable_compression" + agentSelfDNSEnableTruncate = "enable_truncate" + agentSelfDNSMaxStale = "max_stale" + agentSelfDNSNodeTTL = "node_ttl" + agentSelfDNSOnlyPassing = "only_passing" + agentSelfDNSRecursorTimeout = "recursor_timeout" + agentSelfDNSServiceTTL = "service_ttl" + agentSelfDNSUDPAnswerLimit = "udp_answer_limit" +) + +const ( + agentSelfPerformanceRaftMultiplier = "raft_multiplier" +) + +const ( + agentSelfAPIPortsDNS = "dns" + agentSelfAPIPortsHTTP = "http" + agentSelfAPIPortsHTTPS = "https" + agentSelfAPIPortsRPC = "rpc" + agentSelfAPIPortsSerfLAN = "serf_lan" + agentSelfAPIPortsSerfWAN = "serf_wan" + agentSelfAPIPortsServer = "server" + + agentSelfSchemaPortsDNS = "dns" + agentSelfSchemaPortsHTTP = "http" + agentSelfSchemaPortsHTTPS = "https" + agentSelfSchemaPortsRPC = "rpc" + agentSelfSchemaPortsSerfLAN = "serf_lan" + agentSelfSchemaPortsSerfWAN = "serf_wan" + agentSelfSchemaPortsServer = "server" +) + +const ( + agentSelfTaggedAddressesLAN = "lan" + agentSelfTaggedAddressesWAN = "wan" +) + +const ( + agentSelfTelemetryCirconusAPIApp = "circonus_api_app" + agentSelfTelemetryCirconusAPIToken = "circonus_api_token" + agentSelfTelemetryCirconusAPIURL = "circonus_api_url" + agentSelfTelemetryCirconusBrokerID = "circonus_broker_id" + agentSelfTelemetryCirconusBrokerSelectTag = "circonus_select_tag" + agentSelfTelemetryCirconusCheckDisplayName = "circonus_display_name" + agentSelfTelemetryCirconusCheckForceMetricActiation = "circonus_force_metric_activation" + agentSelfTelemetryCirconusCheckID = "circonus_check_id" + agentSelfTelemetryCirconusCheckInstanceID = "circonus_instance_id" + agentSelfTelemetryCirconusCheckSearchTag = "circonus_search_tag" + agentSelfTelemetryCirconusCheckSubmissionURL = "circonus_submission_url" + agentSelfTelemetryCirconusCheckTags = "circonus_check_tags" + agentSelfTelemetryCirconusSubmissionInterval = "circonus_submission_interval" + + agentSelfTelemetryDogStatsdAddr = "dogstatsd_addr" + agentSelfTelemetryDogStatsdTags = "dogstatsd_tags" + agentSelfTelemetryEnableHostname = "enable_hostname" + agentSelfTelemetryStatsdAddr = "statsd_addr" + agentSelfTelemetryStatsiteAddr = "statsite_addr" + agentSelfTelemetryStatsitePrefix = "statsite_prefix" +) + +const ( + agentSelfUnixSocketGroup = "group" + agentSelfUnixSocketMode = "mode" + agentSelfUnixSocketUser = "user" +) + +func dataSourceConsulAgentSelf() *schema.Resource { + return &schema.Resource{ + Read: dataSourceConsulAgentSelfRead, + Schema: map[string]*schema.Schema{ + agentSelfACLDatacenter: { + Computed: true, + Type: schema.TypeString, + }, + agentSelfACLDefaultPolicy: { + Computed: true, + Type: schema.TypeString, + }, + agentSelfACLDisabledTTL: { + Computed: true, + Type: schema.TypeString, + ValidateFunc: makeValidationFunc(agentSelfACLDisabledTTL, validatorInputs{ + validateDurationMin("0ns"), + }), + }, + agentSelfACLDownPolicy: { + Computed: true, + Type: schema.TypeString, + }, + agentSelfACLEnforceVersion8: { + Computed: true, + Type: schema.TypeBool, + }, + agentSelfACLTTL: { + Computed: true, + Type: schema.TypeString, + ValidateFunc: makeValidationFunc(agentSelfACLTTL, validatorInputs{ + validateDurationMin("0ns"), + }), + }, + agentSelfAddresses: { + Computed: true, + Type: schema.TypeMap, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + agentSelfSchemaPortsDNS: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + agentSelfSchemaPortsHTTP: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + agentSelfSchemaPortsHTTPS: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + agentSelfSchemaPortsRPC: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + agentSelfAdvertiseAddr: { + Computed: true, + Type: schema.TypeString, + }, + agentSelfAdvertiseAddrs: { + Computed: true, + Type: schema.TypeMap, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + agentSelfSchemaPortsSerfLAN: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + agentSelfSchemaPortsSerfWAN: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + agentSelfSchemaPortsRPC: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + agentSelfAdvertiseAddrWAN: { + Computed: true, + Type: schema.TypeString, + }, + // Omitting the following since they've been depreciated: + // + // "AtlasInfrastructure": "", + // "AtlasEndpoint": "", + agentSelfAtlasJoin: { + Computed: true, + Type: schema.TypeBool, + }, + agentSelfBindAddr: { + Computed: true, + Type: schema.TypeString, + }, + agentSelfBootstrapMode: { + Computed: true, + Type: schema.TypeBool, + }, + agentSelfBootstrapExpect: { + Computed: true, + Type: schema.TypeInt, + }, + agentSelfCheckDeregisterIntervalMin: { + Computed: true, + Type: schema.TypeString, + ValidateFunc: makeValidationFunc(agentSelfCheckDeregisterIntervalMin, validatorInputs{ + validateDurationMin("0ns"), + }), + }, + agentSelfCheckReapInterval: { + Computed: true, + Type: schema.TypeString, + ValidateFunc: makeValidationFunc(agentSelfCheckReapInterval, validatorInputs{ + validateDurationMin("0ns"), + }), + }, + agentSelfCheckUpdateInterval: { + Computed: true, + Type: schema.TypeString, + ValidateFunc: makeValidationFunc(agentSelfCheckUpdateInterval, validatorInputs{ + validateDurationMin("0ns"), + }), + }, + agentSelfClientAddr: { + Computed: true, + Type: schema.TypeString, + }, + agentSelfDNSConfig: { + Computed: true, + Type: schema.TypeMap, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + agentSelfDNSAllowStale: { + Computed: true, + Type: schema.TypeBool, + }, + agentSelfDNSEnableCompression: { + Computed: true, + Type: schema.TypeBool, + }, + agentSelfDNSEnableTruncate: { + Computed: true, + Type: schema.TypeBool, + }, + agentSelfDNSMaxStale: { + Computed: true, + Type: schema.TypeString, + ValidateFunc: makeValidationFunc(agentSelfDNSMaxStale, validatorInputs{ + validateDurationMin("0ns"), + }), + }, + agentSelfDNSNodeTTL: { + Computed: true, + Type: schema.TypeString, + ValidateFunc: makeValidationFunc(agentSelfDNSNodeTTL, validatorInputs{ + validateDurationMin("0ns"), + }), + }, + agentSelfDNSOnlyPassing: { + Computed: true, + Type: schema.TypeBool, + }, + agentSelfDNSRecursorTimeout: { + Computed: true, + Type: schema.TypeString, + ValidateFunc: makeValidationFunc(agentSelfDNSRecursorTimeout, validatorInputs{ + validateDurationMin("0ns"), + }), + }, + agentSelfDNSServiceTTL: { + Computed: true, + Type: schema.TypeString, + ValidateFunc: makeValidationFunc(agentSelfDNSServiceTTL, validatorInputs{ + validateDurationMin("0ns"), + }), + }, + agentSelfDNSUDPAnswerLimit: { + Computed: true, + Type: schema.TypeInt, + }, + }, + }, + }, + agentSelfDataDir: { + Computed: true, + Type: schema.TypeString, + }, + agentSelfDatacenter: { + Computed: true, + Type: schema.TypeString, + }, + agentSelfDevMode: { + Computed: true, + Type: schema.TypeBool, + }, + agentSelfEnableAnonymousSignature: { + Computed: true, + Type: schema.TypeBool, + }, + agentSelfEnableCoordinates: { + Computed: true, + Type: schema.TypeBool, + }, + agentSelfEnableRemoteExec: { + Computed: true, + Type: schema.TypeBool, + }, + agentSelfEnableUpdateCheck: { + Computed: true, + Type: schema.TypeBool, + }, + agentSelfDNSRecursors: { + Computed: true, + Type: schema.TypeList, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + agentSelfDomain: { + Computed: true, + Type: schema.TypeString, + }, + agentSelfEnableDebug: { + Computed: true, + Type: schema.TypeBool, + }, + agentSelfEnableSyslog: { + Computed: true, + Type: schema.TypeBool, + }, + agentSelfEnableUI: { + Computed: true, + Type: schema.TypeBool, + }, + // "HTTPAPIResponseHeaders": nil, // TODO(sean@) + agentSelfID: { + Computed: true, + Type: schema.TypeString, + ValidateFunc: makeValidationFunc(agentSelfID, validatorInputs{ + validateRegexp(`(?i)^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$`), + }), + }, + agentSelfLeaveOnInt: { + Computed: true, + Type: schema.TypeBool, + }, + agentSelfLeaveOnTerm: { + Computed: true, + Type: schema.TypeBool, + }, + agentSelfLogLevel: { + Computed: true, + Type: schema.TypeString, + }, + agentSelfName: { + Computed: true, + Type: schema.TypeString, + }, + agentSelfPerformance: { + Computed: true, + Type: schema.TypeMap, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + agentSelfPerformanceRaftMultiplier: { + Computed: true, + Type: schema.TypeString, // FIXME(sean@): should be schema.TypeInt + ValidateFunc: makeValidationFunc(agentSelfPerformanceRaftMultiplier, validatorInputs{ + validateIntMin(0), + }), + }, + }, + }, + }, + agentSelfPidFile: { + Computed: true, + Type: schema.TypeString, + }, + agentSelfPorts: { + Computed: true, + Type: schema.TypeMap, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + agentSelfSchemaPortsDNS: { + Computed: true, + Type: schema.TypeInt, + ValidateFunc: makeValidationFunc(agentSelfSchemaPortsDNS, validatorInputs{ + validateIntMin(1), + validateIntMax(65535), + }), + }, + agentSelfSchemaPortsHTTP: { + Computed: true, + Type: schema.TypeInt, + ValidateFunc: makeValidationFunc(agentSelfSchemaPortsHTTP, validatorInputs{ + validateIntMin(1), + validateIntMax(65535), + }), + }, + agentSelfSchemaPortsHTTPS: { + Computed: true, + Type: schema.TypeInt, + ValidateFunc: makeValidationFunc(agentSelfSchemaPortsHTTPS, validatorInputs{ + validateIntMin(1), + validateIntMax(65535), + }), + }, + agentSelfSchemaPortsRPC: { + Computed: true, + Type: schema.TypeInt, + ValidateFunc: makeValidationFunc(agentSelfSchemaPortsRPC, validatorInputs{ + validateIntMin(1), + validateIntMax(65535), + }), + }, + agentSelfSchemaPortsSerfLAN: { + Computed: true, + Type: schema.TypeInt, + ValidateFunc: makeValidationFunc(agentSelfSchemaPortsSerfLAN, validatorInputs{ + validateIntMin(1), + validateIntMax(65535), + }), + }, + agentSelfSchemaPortsSerfWAN: { + Computed: true, + Type: schema.TypeInt, + ValidateFunc: makeValidationFunc(agentSelfSchemaPortsSerfWAN, validatorInputs{ + validateIntMin(1), + validateIntMax(65535), + }), + }, + agentSelfSchemaPortsServer: { + Computed: true, + Type: schema.TypeInt, + ValidateFunc: makeValidationFunc(agentSelfSchemaPortsServer, validatorInputs{ + validateIntMin(1), + validateIntMax(65535), + }), + }, + }, + }, + }, + agentSelfProtocol: { + Computed: true, + Type: schema.TypeInt, + }, + agentSelfReconnectTimeoutLAN: { + Computed: true, + Type: schema.TypeString, + ValidateFunc: makeValidationFunc(agentSelfReconnectTimeoutLAN, validatorInputs{ + validateDurationMin("0ns"), + }), + }, + agentSelfReconnectTimeoutWAN: { + Computed: true, + Type: schema.TypeString, + ValidateFunc: makeValidationFunc(agentSelfReconnectTimeoutWAN, validatorInputs{ + validateDurationMin("0ns"), + }), + }, + agentSelfRejoinAfterLeave: { + Computed: true, + Type: schema.TypeBool, + }, + agentSelfRetryJoin: { + Computed: true, + Type: schema.TypeList, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + agentSelfRetryJoinWAN: { + Computed: true, + Type: schema.TypeList, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + agentSelfRetryMaxAttempts: { + Computed: true, + Type: schema.TypeInt, + }, + agentSelfRetryMaxAttemptsWAN: { + Computed: true, + Type: schema.TypeInt, + }, + agentSelfRetryJoinEC2: { + Computed: true, + Type: schema.TypeMap, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + agentSelfRetryJoinAWSRegion: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + agentSelfRetryJoinAWSTagKey: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + agentSelfRetryJoinAWSTagValue: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + agentSelfRetryJoinAWSAccessKeyID: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Sensitive: true, + }, + agentSelfRetryJoinAWSSecretAccessKey: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Sensitive: true, + }, + }, + }, + }, + agentSelfRetryJoinGCE: { + Computed: true, + Type: schema.TypeMap, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + agentSelfRetryJoinGCEProjectName: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + agentSelfRetryJoinGCEZonePattern: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + agentSelfRetryJoinGCETagValue: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + agentSelfRetryJoinGCECredentialsFile: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + agentSelfSerfLANBindAddr: { + Computed: true, + Type: schema.TypeString, + }, + agentSelfSerfWANBindAddr: { + Computed: true, + Type: schema.TypeString, + }, + agentSelfServerMode: { + Computed: true, + Type: schema.TypeBool, + }, + agentSelfServerName: { + Computed: true, + Type: schema.TypeString, + }, + agentSelfSessionTTLMin: { + Computed: true, + Type: schema.TypeString, + ValidateFunc: makeValidationFunc(agentSelfSessionTTLMin, validatorInputs{ + validateDurationMin("0ns"), + }), + }, + agentSelfStartJoin: { + Computed: true, + Type: schema.TypeList, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + agentSelfStartJoinWAN: { + Computed: true, + Type: schema.TypeList, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + agentSelfSyslogFacility: { + Computed: true, + Type: schema.TypeString, + }, + agentSelfTaggedAddresses: { + Computed: true, + Type: schema.TypeMap, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + agentSelfTaggedAddressesLAN: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + agentSelfTaggedAddressesWAN: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + agentSelfTelemetry: { + Computed: true, + Type: schema.TypeMap, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + agentSelfTelemetryCirconusAPIApp: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + agentSelfTelemetryCirconusAPIToken: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + agentSelfTelemetryCirconusAPIURL: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + agentSelfTelemetryCirconusBrokerID: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + agentSelfTelemetryCirconusBrokerSelectTag: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + agentSelfTelemetryCirconusCheckDisplayName: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + agentSelfTelemetryCirconusCheckID: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + agentSelfTelemetryCirconusCheckInstanceID: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + agentSelfTelemetryCirconusCheckSearchTag: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + agentSelfTelemetryCirconusCheckSubmissionURL: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + agentSelfTelemetryCirconusCheckTags: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + agentSelfTelemetryCirconusCheckForceMetricActiation: &schema.Schema{ + Type: schema.TypeBool, + Computed: true, + }, + agentSelfTelemetryCirconusSubmissionInterval: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + ValidateFunc: makeValidationFunc(agentSelfTelemetryCirconusSubmissionInterval, validatorInputs{ + validateDurationMin("0ns"), + }), + }, + agentSelfTelemetryEnableHostname: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + agentSelfTelemetryDogStatsdAddr: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + agentSelfTelemetryDogStatsdTags: &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + agentSelfTelemetryStatsdAddr: { + Type: schema.TypeString, + Computed: true, + }, + agentSelfTelemetryStatsiteAddr: { + Type: schema.TypeString, + Computed: true, + }, + agentSelfTelemetryStatsitePrefix: { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + agentSelfTLSCAFile: { + Computed: true, + Type: schema.TypeString, + }, + agentSelfTLSCertFile: { + Computed: true, + Type: schema.TypeString, + }, + agentSelfTLSKeyFile: { + Computed: true, + Type: schema.TypeString, + }, + agentSelfTLSMinVersion: { + Computed: true, + Type: schema.TypeString, + }, + agentSelfTLSVerifyIncoming: { + Computed: true, + Type: schema.TypeBool, + }, + agentSelfTLSVerifyServerHostname: { + Computed: true, + Type: schema.TypeBool, + }, + agentSelfTLSVerifyOutgoing: { + Computed: true, + Type: schema.TypeBool, + }, + agentSelfTranslateWANAddrs: { + Computed: true, + Type: schema.TypeBool, + }, + agentSelfUIDir: { + Computed: true, + Type: schema.TypeString, + }, + agentSelfUnixSockets: { + Computed: true, + Type: schema.TypeMap, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + agentSelfUnixSocketUser: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + agentSelfUnixSocketGroup: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + agentSelfUnixSocketMode: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + agentSelfVersion: { + Computed: true, + Type: schema.TypeString, + }, + agentSelfVersionPrerelease: { + Computed: true, + Type: schema.TypeString, + }, + agentSelfVersionRevision: { + Computed: true, + Type: schema.TypeString, + }, + // "Watches": nil, + }, + } +} + +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) + } + + // Pull the datacenter first because we use it when setting the ID + var dc string + if v, found := cfg["Datacenter"]; found { + dc = v.(string) + } + + const idKeyFmt = "agent-self-%s" + d.SetId(fmt.Sprintf(idKeyFmt, dc)) + + if v, found := cfg["ACLDatacenter"]; found { + d.Set(agentSelfACLDatacenter, v.(string)) + } + + if v, found := cfg["ACLDefaultPolicy"]; found { + d.Set(agentSelfACLDefaultPolicy, v.(string)) + } + + if v, found := cfg["ACLDisabledTTL"]; found { + dur := time.Duration(int64(v.(float64))) + d.Set(agentSelfACLDisabledTTL, dur.String()) + } + + if v, found := cfg["ACLDownPolicy"]; found { + d.Set(agentSelfACLDownPolicy, v.(string)) + } + + if v, found := cfg["ACLEnforceVersion8"]; found { + d.Set(agentSelfACLEnforceVersion8, v.(bool)) + } + + if v, found := cfg["ACLTTL"]; found { + dur := time.Duration(int64(v.(float64))) + d.Set(agentSelfACLTTL, dur.String()) + } + + if v, found := cfg["Addresses"]; found { + addrs := v.(map[string]interface{}) + + m := make(map[string]interface{}, len(addrs)) + + if v, found := addrs["DNS"]; found { + m[agentSelfSchemaPortsDNS] = v.(string) + } + + if v, found := addrs["HTTP"]; found { + m[agentSelfSchemaPortsHTTP] = v.(string) + } + + if v, found := addrs["HTTPS"]; found { + m[agentSelfSchemaPortsHTTPS] = v.(string) + } + + if v, found := addrs["RPC"]; found { + m[agentSelfSchemaPortsRPC] = v.(string) + } + + if err := d.Set(agentSelfAddresses, m); err != nil { + return errwrap.Wrapf(fmt.Sprintf("Unable to set %s: {{err}}", agentSelfAddresses), err) + } + } + + if v, found := cfg["AdvertiseAddr"]; found { + d.Set(agentSelfAdvertiseAddr, v.(string)) + } + + if v, found := cfg["AdvertiseAddrs"]; found { + addrs := v.(map[string]interface{}) + + m := make(map[string]interface{}, len(addrs)) + + if v, found := addrs["SerfLan"]; found && v != nil { + m[agentSelfSchemaPortsSerfLAN] = v.(string) + } + + if v, found := addrs["SerfWan"]; found && v != nil { + m[agentSelfSchemaPortsSerfWAN] = v.(string) + } + + if v, found := addrs["RPC"]; found && v != nil { + m[agentSelfSchemaPortsRPC] = v.(string) + } + + if err := d.Set(agentSelfAdvertiseAddrs, m); err != nil { + return errwrap.Wrapf(fmt.Sprintf("Unable to set %s: {{err}}", agentSelfAdvertiseAddrs), err) + } + } + + if v, found := cfg["AtlasJoin"]; found { + d.Set(agentSelfAtlasJoin, v.(bool)) + } + + if v, found := cfg["BindAddr"]; found { + d.Set(agentSelfBindAddr, v.(string)) + } + + if v, found := cfg["Bootstrap"]; found { + d.Set(agentSelfBootstrapMode, v.(bool)) + } + + if v, found := cfg["BootstrapExpect"]; found { + d.Set(agentSelfBootstrapExpect, int(v.(float64))) + } + + if v, found := cfg["CheckDeregisterIntervalMin"]; found { + dur := time.Duration(int64(v.(float64))) + d.Set(agentSelfCheckDeregisterIntervalMin, dur.String()) + } + + if v, found := cfg["CheckReapInterval"]; found { + dur := time.Duration(int64(v.(float64))) + d.Set(agentSelfCheckReapInterval, dur.String()) + } + + if v, found := cfg["CheckUpdateInterval"]; found { + dur := time.Duration(int64(v.(float64))) + d.Set(agentSelfCheckUpdateInterval, dur.String()) + } + + if v, found := cfg["ClientAddr"]; found { + d.Set(agentSelfClientAddr, v.(string)) + } + + if v, found := cfg["DNS"]; found { + dnsOpts := v.(map[string]interface{}) + + m := make(map[string]interface{}, len(dnsOpts)) + + if v, found := dnsOpts["AllowStale"]; found { + m[agentSelfDNSAllowStale] = v.(bool) + } + + if v, found := dnsOpts["DisableCompression"]; found { + m[agentSelfDNSEnableCompression] = !v.(bool) + } + + if v, found := dnsOpts["EnableTruncate"]; found { + m[agentSelfDNSEnableTruncate] = v.(bool) + } + + if v, found := dnsOpts["MaxStale"]; found { + dur := time.Duration(int64(v.(float64))) + m[agentSelfDNSMaxStale] = dur.String() + } + + if v, found := dnsOpts["NodeTTL"]; found { + dur := time.Duration(int64(v.(float64))) + m[agentSelfDNSNodeTTL] = dur.String() + } + + if v, found := dnsOpts["OnlyPassing"]; found { + m[agentSelfDNSOnlyPassing] = v.(bool) + } + + if v, found := dnsOpts["RecursorTimeout"]; found { + dur := time.Duration(int64(v.(float64))) + m[agentSelfDNSRecursorTimeout] = dur.String() + } + + if v, found := dnsOpts["ServiceTTL"]; found { + dur := time.Duration(int64(v.(float64))) + m[agentSelfDNSServiceTTL] = dur.String() + } + + if v, found := dnsOpts["UDPAnswerLimit"]; found { + m[agentSelfDNSServiceTTL] = v.(int) + } + + if err := d.Set(agentSelfDNSConfig, m); err != nil { + return errwrap.Wrapf(fmt.Sprintf("Unable to set %s: {{err}}", agentSelfDNSConfig), err) + } + } + + { + var l []interface{} + + if v, found := cfg["DNSRecursors"]; found { + l = make([]interface{}, 0, len(v.([]interface{}))+1) + l = append(l, v.([]interface{})...) + } + + if v, found := cfg["DNSRecursor"]; found { + l = append([]interface{}{v.(string)}, l...) + } + + if len(l) > 0 { + if err := d.Set(agentSelfDNSRecursors, l); err != nil { + return errwrap.Wrapf(fmt.Sprintf("Unable to set %s: {{err}}", agentSelfDNSRecursors), err) + } + } + } + + if v, found := cfg["DataDir"]; found { + d.Set(agentSelfDataDir, v.(string)) + } + + if len(dc) > 0 { + d.Set(agentSelfDatacenter, dc) + } + + if v, found := cfg["DevMode"]; found { + d.Set(agentSelfDevMode, v.(bool)) + } + + if v, found := cfg["DisableAnonymousSignature"]; found { + d.Set(agentSelfEnableAnonymousSignature, !v.(bool)) + } + + if v, found := cfg["DisableCoordinates"]; found { + d.Set(agentSelfEnableCoordinates, !v.(bool)) + } + + if v, found := cfg["DisableRemoteExec"]; found { + d.Set(agentSelfEnableRemoteExec, !v.(bool)) + } + + if v, found := cfg["DisableUpdateCheck"]; found { + d.Set(agentSelfEnableUpdateCheck, !v.(bool)) + } + + if v, found := cfg["Domain"]; found { + d.Set(agentSelfDomain, v.(string)) + } + + if v, found := cfg["EnableDebug"]; found { + d.Set(agentSelfEnableDebug, v.(bool)) + } + + if v, found := cfg["EnableSyslog"]; found { + d.Set(agentSelfEnableSyslog, v.(bool)) + } + + if v, found := cfg["EnableUi"]; found { + d.Set(agentSelfEnableUI, v.(bool)) + } + + if v, found := cfg["id"]; found { + d.Set(agentSelfID, v.(string)) + } + + if v, found := cfg["SkipLeaveOnInt"]; found { + d.Set(agentSelfLeaveOnInt, !v.(bool)) + } + + if v, found := cfg["LeaveOnTerm"]; found { + d.Set(agentSelfLeaveOnTerm, v.(bool)) + } + + if v, found := cfg["LogLevel"]; found { + d.Set(agentSelfLogLevel, v.(string)) + } + + if v, found := cfg["NodeName"]; found { + d.Set(agentSelfName, v.(string)) + } + + if v, found := cfg["Performance"]; found { + cfgs := v.(map[string]interface{}) + + m := make(map[string]interface{}, len(cfgs)) + + if v, found := cfgs["RaftMultiplier"]; found { + m[agentSelfPerformanceRaftMultiplier] = strconv.FormatFloat(v.(float64), 'g', -1, 64) + } + + if err := d.Set(agentSelfPerformance, m); err != nil { + return errwrap.Wrapf(fmt.Sprintf("Unable to set %s: {{err}}", agentSelfPerformance), err) + } + } + + if v, found := cfg["PidFile"]; found { + d.Set(agentSelfPidFile, v.(string)) + } + + if v, found := cfg["Ports"]; found { + cfgs := v.(map[string]interface{}) + + m := make(map[string]interface{}, len(cfgs)) + + if v, found := cfgs[agentSelfAPIPortsDNS]; found { + m[agentSelfSchemaPortsDNS] = int(v.(float64)) + } + + if v, found := cfgs[agentSelfAPIPortsHTTP]; found { + m[agentSelfSchemaPortsHTTP] = int(v.(float64)) + } + + if v, found := cfgs[agentSelfAPIPortsHTTPS]; found { + m[agentSelfSchemaPortsHTTPS] = int(v.(float64)) + } + + if v, found := cfgs[agentSelfAPIPortsRPC]; found { + m[agentSelfSchemaPortsRPC] = int(v.(float64)) + } + + if v, found := cfgs[agentSelfAPIPortsSerfLAN]; found { + m[agentSelfSchemaPortsSerfLAN] = int(v.(float64)) + } + + if v, found := cfgs[agentSelfAPIPortsSerfWAN]; found { + m[agentSelfSchemaPortsSerfWAN] = int(v.(float64)) + } + + if v, found := cfgs[agentSelfAPIPortsServer]; found { + m[agentSelfSchemaPortsServer] = int(v.(float64)) + } + + if err := d.Set(agentSelfPorts, m); err != nil { + return errwrap.Wrapf(fmt.Sprintf("Unable to set %s: {{err}}", agentSelfPorts), err) + } + } + + if v, found := cfg["Protocol"]; found { + d.Set(agentSelfProtocol, int(v.(float64))) + } + + if v, found := cfg["ReconnectTimeoutLan"]; found { + dur := time.Duration(int64(v.(float64))) + d.Set(agentSelfReconnectTimeoutLAN, dur.String()) + } + + if v, found := cfg["ReconnectTimeoutWan"]; found { + dur := time.Duration(int64(v.(float64))) + d.Set(agentSelfReconnectTimeoutWAN, dur.String()) + } + + if v, found := cfg["RejoinAfterLeave"]; found { + d.Set(agentSelfRejoinAfterLeave, v.(bool)) + } + + if v, found := cfg["RetryJoin"]; found { + l := make([]string, 0, len(v.([]interface{}))) + for _, e := range v.([]interface{}) { + l = append(l, e.(string)) + } + + if err := d.Set(agentSelfRetryJoin, l); err != nil { + return errwrap.Wrapf(fmt.Sprintf("Unable to set %s: {{err}}", agentSelfRetryJoin), err) + } + } + + if v, found := cfg["RetryJoinEC2"]; found { + ec2Config := v.(map[string]interface{}) + + m := make(map[string]interface{}, len(ec2Config)) + + if v, found := ec2Config["Region"]; found { + m[agentSelfRetryJoinAWSRegion] = v.(string) + } + + if v, found := ec2Config["TagKey"]; found { + m[agentSelfRetryJoinAWSTagKey] = v.(string) + } + + if v, found := ec2Config["TagValue"]; found { + m[agentSelfRetryJoinAWSTagValue] = v.(string) + } + + if v, found := ec2Config["AccessKeyID"]; found { + m[agentSelfRetryJoinAWSAccessKeyID] = v.(string) + } + + if v, found := ec2Config["SecretAccessKey"]; found { + m[agentSelfRetryJoinAWSSecretAccessKey] = v.(string) + } + + if err := d.Set(agentSelfRetryJoinEC2, m); err != nil { + return errwrap.Wrapf(fmt.Sprintf("Unable to set %s: {{err}}", agentSelfRetryJoinEC2), err) + } + } + + if v, found := cfg["RetryJoinWan"]; found { + l := make([]string, 0, len(v.([]interface{}))) + for _, e := range v.([]interface{}) { + l = append(l, e.(string)) + } + + if err := d.Set(agentSelfRetryJoinWAN, l); err != nil { + return errwrap.Wrapf(fmt.Sprintf("Unable to set %s: {{err}}", agentSelfRetryJoinWAN), err) + } + } + + if v, found := cfg["RetryMaxAttempts"]; found { + d.Set(agentSelfRetryMaxAttempts, int(v.(float64))) + } + + if v, found := cfg["RetryMaxAttemptsWan"]; found { + d.Set(agentSelfRetryMaxAttemptsWAN, int(v.(float64))) + } + + if v, found := cfg["SerfLanBindAddr"]; found { + d.Set(agentSelfSerfLANBindAddr, v.(string)) + } + + if v, found := cfg["SerfWanBindAddr"]; found { + d.Set(agentSelfSerfWANBindAddr, v.(string)) + } + + if v, found := cfg["Server"]; found { + d.Set(agentSelfServerMode, v.(bool)) + } + + if v, found := cfg["ServerName"]; found { + d.Set(agentSelfServerName, v.(string)) + } + + if v, found := cfg["SessionTTLMin"]; found { + dur := time.Duration(int64(v.(float64))) + d.Set(agentSelfSessionTTLMin, dur.String()) + } + + if v, found := cfg["StartJoin"]; found { + serverList := v.([]interface{}) + l := make([]interface{}, 0, len(serverList)) + l = append(l, serverList...) + if err := d.Set(agentSelfStartJoin, l); err != nil { + return errwrap.Wrapf(fmt.Sprintf("Unable to set %s: {{err}}", agentSelfStartJoin), err) + } + } + + if v, found := cfg["StartJoinWan"]; found { + serverList := v.([]interface{}) + l := make([]interface{}, 0, len(serverList)) + l = append(l, serverList...) + if err := d.Set(agentSelfStartJoinWAN, l); err != nil { + return errwrap.Wrapf(fmt.Sprintf("Unable to set %s: {{err}}", agentSelfStartJoinWAN), err) + } + } + + if v, found := cfg["SyslogFacility"]; found { + d.Set(agentSelfSyslogFacility, v.(string)) + } + + if v, found := cfg["CAFile"]; found { + d.Set(agentSelfTLSCAFile, v.(string)) + } + + if v, found := cfg["CertFile"]; found { + d.Set(agentSelfTLSCertFile, v.(string)) + } + + if v, found := cfg["KeyFile"]; found { + d.Set(agentSelfTLSKeyFile, v.(string)) + } + + if v, found := cfg["TLSMinVersion"]; found { + d.Set(agentSelfTLSMinVersion, v.(string)) + } + + if v, found := cfg["VerifyIncoming"]; found { + d.Set(agentSelfTLSVerifyIncoming, v.(bool)) + } + + if v, found := cfg["VerifyOutgoing"]; found { + d.Set(agentSelfTLSVerifyOutgoing, v.(bool)) + } + + if v, found := cfg["VerifyServerHostname"]; found { + d.Set(agentSelfTLSVerifyServerHostname, v.(bool)) + } + + if v, found := cfg["TaggedAddresses"]; found { + addrs := v.(map[string]interface{}) + + m := make(map[string]interface{}, len(addrs)) + + // NOTE(sean@): agentSelfTaggedAddressesLAN and agentSelfTaggedAddressesWAN + // are the only two known values that should be in this map at present, but + // in the future this value could/will expand and the schema should be + // releaxed to include both the known *{L,W}AN values as well as whatever + // else the user specifies. + for s, t := range addrs { + m[s] = t + } + + if err := d.Set(agentSelfTaggedAddresses, m); err != nil { + return errwrap.Wrapf(fmt.Sprintf("Unable to set %s: {{err}}", agentSelfTaggedAddresses), err) + } + } + + if v, found := cfg["Telemetry"]; found { + telemetryCfg := v.(map[string]interface{}) + + m := make(map[string]interface{}, len(telemetryCfg)) + + if v, found := telemetryCfg["CirconusAPIApp"]; found { + m[agentSelfTelemetryCirconusAPIApp] = v.(string) + } + + if v, found := telemetryCfg["CirconusAPIURL"]; found { + m[agentSelfTelemetryCirconusAPIURL] = v.(string) + } + + if v, found := telemetryCfg["CirconusBrokerID"]; found { + m[agentSelfTelemetryCirconusBrokerID] = v.(string) + } + + if v, found := telemetryCfg["CirconusBrokerSelectTag"]; found { + m[agentSelfTelemetryCirconusBrokerSelectTag] = v.(string) + } + + if v, found := telemetryCfg["CirconusCheckDisplayName"]; found { + m[agentSelfTelemetryCirconusCheckDisplayName] = v.(string) + } + + if v, found := telemetryCfg["CirconusCheckID"]; found { + m[agentSelfTelemetryCirconusCheckID] = v.(string) + } + + if v, found := telemetryCfg["CirconusCheckInstanceID"]; found { + m[agentSelfTelemetryCirconusCheckInstanceID] = v.(string) + } + + if v, found := telemetryCfg["CirconusCheckSearchTag"]; found { + m[agentSelfTelemetryCirconusCheckSearchTag] = v.(string) + } + + if v, found := telemetryCfg["CirconusCheckSubmissionURL"]; found { + m[agentSelfTelemetryCirconusCheckSubmissionURL] = v.(string) + } + + if v, found := telemetryCfg["CirconusCheckTags"]; found { + m[agentSelfTelemetryCirconusCheckTags] = v.(string) + } + + if v, found := telemetryCfg["CirconusCheckForceMetricActivation"]; found { + m[agentSelfTelemetryCirconusCheckForceMetricActiation] = v.(string) + } + + if v, found := telemetryCfg["CirconusSubmissionInterval"]; found { + m[agentSelfTelemetryCirconusSubmissionInterval] = v.(string) + } + + if v, found := telemetryCfg["DisableHostname"]; found { + m[agentSelfTelemetryEnableHostname] = fmt.Sprintf("%t", !v.(bool)) + } + + if v, found := telemetryCfg["DogStatsdAddr"]; found { + m[agentSelfTelemetryDogStatsdAddr] = v.(string) + } + + if v, found := telemetryCfg["DogStatsdTags"]; found && v != nil { + m[agentSelfTelemetryDogStatsdTags] = append([]interface{}(nil), v.([]interface{})...) + } + + if v, found := telemetryCfg["StatsdAddr"]; found { + m[agentSelfTelemetryStatsdAddr] = v.(string) + } + + if v, found := telemetryCfg["StatsiteAddr"]; found { + m[agentSelfTelemetryStatsiteAddr] = v.(string) + } + + if v, found := telemetryCfg["StatsitePrefix"]; found { + m[agentSelfTelemetryStatsitePrefix] = v.(string) + } + + if err := d.Set(agentSelfTelemetry, m); err != nil { + return errwrap.Wrapf(fmt.Sprintf("Unable to set %s: {{err}}", agentSelfTelemetry), err) + } + } + + if v, found := cfg["TranslateWanTelemetryCfg"]; found { + d.Set(agentSelfTranslateWANAddrs, v.(bool)) + } + + if v, found := cfg["UiDir"]; found { + d.Set(agentSelfUIDir, v.(string)) + } + + if v, found := cfg["UnixSockets"]; found { + socketConfig := v.(map[string]interface{}) + + m := make(map[string]interface{}, len(socketConfig)) + + if v, found := socketConfig["Grp"]; found { + m[agentSelfUnixSocketGroup] = v.(string) + } + + if v, found := socketConfig["Mode"]; found { + m[agentSelfUnixSocketMode] = v.(string) + } + + if v, found := socketConfig["Usr"]; found { + m[agentSelfUnixSocketUser] = v.(string) + } + + if err := d.Set(agentSelfUnixSockets, m); err != nil { + return errwrap.Wrapf(fmt.Sprintf("Unable to set %s: {{err}}", agentSelfUnixSockets), err) + } + } + + if v, found := cfg["Version"]; found { + d.Set(agentSelfVersion, v.(string)) + } + + if v, found := cfg["VersionPrerelease"]; found { + d.Set(agentSelfVersionPrerelease, v.(string)) + } + + if v, found := cfg["VersionPrerelease"]; found { + d.Set(agentSelfVersionPrerelease, v.(string)) + } + + if v, found := cfg["Revision"]; found { + d.Set(agentSelfVersionRevision, v.(string)) + } + + 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 000000000000..341a89c9c3b1 --- /dev/null +++ b/builtin/providers/consul/data_source_consul_agent_self_test.go @@ -0,0 +1,95 @@ +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", "acl_datacenter", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "acl_default_policy", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "acl_disabled_ttl", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "acl_down_policy", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "acl_enforce_0_8_semantics", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "acl_ttl", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "advertise_addr", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "bind_addr", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "bootstrap_expect", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "bootstrap_mode", "false"), + testAccCheckDataSourceValue("data.consul_agent_self.read", "client_addr", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "datacenter", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "dev_mode", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "domain", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "enable_anonymous_signature", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "enable_coordinates", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "enable_debug", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "enable_remote_exec", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "enable_syslog", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "enable_ui", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "enable_update_check", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "id", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "leave_on_int", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "leave_on_term", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "log_level", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "name", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "pid_file", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "rejoin_after_leave", ""), + // testAccCheckDataSourceValue("data.consul_agent_self.read", "retry_join", ""), + // testAccCheckDataSourceValue("data.consul_agent_self.read", "retry_join_wan", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "retry_max_attempts", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "retry_max_attempts_wan", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "serf_lan_bind_addr", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "serf_wan_bind_addr", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "server_mode", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "server_name", ""), + // testAccCheckDataSourceValue("data.consul_agent_self.read", "start_join", ""), + // testAccCheckDataSourceValue("data.consul_agent_self.read", "start_join_wan", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "syslog_facility", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "telemetry.enable_hostname", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "tls_ca_file", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "tls_cert_file", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "tls_key_file", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "tls_verify_incoming", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "tls_verify_outgoing", ""), + testAccCheckDataSourceValue("data.consul_agent_self.read", "tls_verify_server_hostname", ""), + ), + }, + }, + }) +} + +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, found := rn.Primary.Attributes[attr] + switch { + case !found: + return fmt.Errorf("Attribute '%s' not found: %#v", attr, rn.Primary.Attributes) + case val == "": + // Value found, don't care what the payload is (including the zero value) + case val != "" && out != val: + return fmt.Errorf("Attribute '%s' value '%s' != '%s'", attr, out, val) + case 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/data_source_consul_catalog_nodes.go b/builtin/providers/consul/data_source_consul_catalog_nodes.go new file mode 100644 index 000000000000..666d71653982 --- /dev/null +++ b/builtin/providers/consul/data_source_consul_catalog_nodes.go @@ -0,0 +1,155 @@ +package consul + +import ( + "fmt" + + consulapi "github.com/hashicorp/consul/api" + "github.com/hashicorp/errwrap" + "github.com/hashicorp/terraform/helper/schema" +) + +const ( + catalogNodesElem = "nodes" + catalogNodesDatacenter = "datacenter" + catalogNodesQueryOpts = "query_options" + + catalogNodesNodeID = "id" + catalogNodesNodeAddress = "address" + catalogNodesNodeMeta = "meta" + catalogNodesNodeName = "name" + catalogNodesNodeTaggedAddresses = "tagged_addresses" + + catalogNodesNodeIDs = "node_ids" + catalogNodesNodeNames = "node_names" + + catalogNodesAPITaggedLAN = "lan" + catalogNodesAPITaggedWAN = "wan" + catalogNodesSchemaTaggedLAN = "lan" + catalogNodesSchemaTaggedWAN = "wan" +) + +func dataSourceConsulCatalogNodes() *schema.Resource { + return &schema.Resource{ + Read: dataSourceConsulCatalogNodesRead, + Schema: map[string]*schema.Schema{ + // Filters + catalogNodesQueryOpts: schemaQueryOpts, + + // Out parameters + catalogNodesDatacenter: &schema.Schema{ + Computed: true, + Type: schema.TypeString, + }, + catalogNodesNodeIDs: &schema.Schema{ + Computed: true, + Type: schema.TypeList, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + catalogNodesNodeNames: &schema.Schema{ + Computed: true, + Type: schema.TypeList, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + catalogNodesElem: &schema.Schema{ + Computed: true, + Type: schema.TypeList, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + catalogNodesNodeID: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + ValidateFunc: makeValidationFunc(catalogNodesNodeID, []interface{}{validateRegexp(`^[\S]+$`)}), + }, + catalogNodesNodeName: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + ValidateFunc: makeValidationFunc(catalogNodesNodeName, []interface{}{validateRegexp(`^[\S]+$`)}), + }, + catalogNodesNodeAddress: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + catalogNodesNodeMeta: &schema.Schema{ + Type: schema.TypeMap, + Computed: true, + }, + catalogNodesNodeTaggedAddresses: &schema.Schema{ + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + catalogNodesSchemaTaggedLAN: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + catalogNodesSchemaTaggedWAN: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func dataSourceConsulCatalogNodesRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*consulapi.Client) + + // Parse out data source filters to populate Consul's query options + queryOpts, err := getQueryOpts(d, client) + if err != nil { + return errwrap.Wrapf("unable to get query options for fetching catalog nodes: {{err}}", err) + } + + nodes, meta, err := client.Catalog().Nodes(queryOpts) + if err != nil { + return err + } + + l := make([]interface{}, 0, len(nodes)) + + nodeNames := make([]interface{}, 0, len(nodes)) + nodeIDs := make([]interface{}, 0, len(nodes)) + + for _, node := range nodes { + const defaultNodeAttrs = 4 + m := make(map[string]interface{}, defaultNodeAttrs) + id := node.ID + if id == "" { + id = node.Node + } + + nodeIDs = append(nodeIDs, id) + nodeNames = append(nodeNames, node.Node) + + m[catalogNodesNodeAddress] = node.Address + m[catalogNodesNodeID] = id + m[catalogNodesNodeName] = node.Node + m[catalogNodesNodeMeta] = node.Meta + m[catalogNodesNodeTaggedAddresses] = node.TaggedAddresses + + l = append(l, m) + } + + const idKeyFmt = "catalog-nodes-%s" + d.SetId(fmt.Sprintf(idKeyFmt, queryOpts.Datacenter)) + + d.Set(catalogNodesDatacenter, queryOpts.Datacenter) + if err := d.Set(catalogNodesNodeIDs, nodeIDs); err != nil { + return errwrap.Wrapf("Unable to store node IDs: {{err}}", err) + } + + if err := d.Set(catalogNodesNodeNames, nodeNames); err != nil { + return errwrap.Wrapf("Unable to store node names: {{err}}", err) + } + + if err := d.Set(catalogNodesElem, l); err != nil { + return errwrap.Wrapf("Unable to store nodes: {{err}}", err) + } + + return nil +} diff --git a/builtin/providers/consul/data_source_consul_catalog_nodes_test.go b/builtin/providers/consul/data_source_consul_catalog_nodes_test.go new file mode 100644 index 000000000000..94cea160d4bc --- /dev/null +++ b/builtin/providers/consul/data_source_consul_catalog_nodes_test.go @@ -0,0 +1,37 @@ +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", ""), + testAccCheckDataSourceValue("data.consul_catalog_nodes.read", "nodes.0.name", ""), + testAccCheckDataSourceValue("data.consul_catalog_nodes.read", "nodes.0.address", ""), + ), + }, + }, + }) +} + +const testAccDataConsulCatalogNodesConfig = ` +data "consul_catalog_nodes" "read" { + query_options { + allow_stale = true + require_consistent = false + token = "" + wait_index = 0 + wait_time = "1m" + } +} +` diff --git a/builtin/providers/consul/data_source_consul_catalog_service.go b/builtin/providers/consul/data_source_consul_catalog_service.go new file mode 100644 index 000000000000..1affc781efb9 --- /dev/null +++ b/builtin/providers/consul/data_source_consul_catalog_service.go @@ -0,0 +1,202 @@ +package consul + +import ( + "fmt" + "sort" + + consulapi "github.com/hashicorp/consul/api" + "github.com/hashicorp/errwrap" + "github.com/hashicorp/terraform/helper/schema" +) + +const ( + catalogServiceElem = "service" + + catalogServiceCreateIndex = "create_index" + catalogServiceDatacenter = "datacenter" + catalogServiceModifyIndex = "modify_index" + catalogServiceNodeAddress = "node_address" + catalogServiceNodeID = "node_id" + catalogServiceNodeMeta = "node_meta" + catalogServiceNodeName = "node_name" + catalogServiceServiceAddress = "address" + catalogServiceServiceEnableTagOverride = "enable_tag_override" + catalogServiceServiceID = "id" + catalogServiceServiceName = "name" + catalogServiceServicePort = "port" + catalogServiceServiceTags = "tags" + catalogServiceTaggedAddresses = "tagged_addresses" + + // Filters + catalogServiceName = "name" + catalogServiceTag = "tag" +) + +func dataSourceConsulCatalogService() *schema.Resource { + return &schema.Resource{ + Read: dataSourceConsulCatalogServiceRead, + Schema: map[string]*schema.Schema{ + // Data Source Predicate(s) + catalogServiceDatacenter: &schema.Schema{ + // Used in the query, must be stored and force a refresh if the value + // changes. + Computed: true, + Type: schema.TypeString, + ForceNew: true, + }, + catalogServiceTag: &schema.Schema{ + // Used in the query, must be stored and force a refresh if the value + // changes. + Computed: true, + Type: schema.TypeString, + ForceNew: true, + }, + catalogServiceName: &schema.Schema{ + Required: true, + Type: schema.TypeString, + }, + catalogNodesQueryOpts: schemaQueryOpts, + + // Out parameters + catalogServiceElem: &schema.Schema{ + Computed: true, + Type: schema.TypeList, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + catalogServiceCreateIndex: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + catalogServiceNodeAddress: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + catalogServiceNodeID: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + catalogServiceModifyIndex: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + catalogServiceNodeName: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + catalogServiceNodeMeta: &schema.Schema{ + Type: schema.TypeMap, + Computed: true, + }, + catalogServiceServiceAddress: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + catalogServiceServiceEnableTagOverride: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + catalogServiceServiceID: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + catalogServiceServiceName: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + catalogServiceServicePort: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + catalogServiceServiceTags: &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + catalogServiceTaggedAddresses: &schema.Schema{ + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + catalogNodesSchemaTaggedLAN: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + catalogNodesSchemaTaggedWAN: &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func dataSourceConsulCatalogServiceRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*consulapi.Client) + + // Parse out data source filters to populate Consul's query options + queryOpts, err := getQueryOpts(d, client) + if err != nil { + return errwrap.Wrapf("unable to get query options for fetching catalog services: {{err}}", err) + } + + var serviceName string + if v, ok := d.GetOk(catalogServiceName); ok { + serviceName = v.(string) + } + + var serviceTag string + if v, ok := d.GetOk(catalogServiceTag); ok { + serviceTag = v.(string) + } + + // services, meta, err := client.Catalog().Services(queryOpts) + services, meta, err := client.Catalog().Service(serviceName, serviceTag, queryOpts) + if err != nil { + return err + } + + l := make([]interface{}, 0, len(services)) + for _, service := range services { + const defaultServiceAttrs = 13 + m := make(map[string]interface{}, defaultServiceAttrs) + + m[catalogServiceCreateIndex] = fmt.Sprintf("%d", service.CreateIndex) + m[catalogServiceModifyIndex] = fmt.Sprintf("%d", service.ModifyIndex) + m[catalogServiceNodeAddress] = service.Address + m[catalogServiceNodeID] = service.ID + m[catalogServiceNodeMeta] = service.NodeMeta + m[catalogServiceNodeName] = service.Node + switch service.ServiceAddress { + case "": + m[catalogServiceServiceAddress] = service.Address + default: + m[catalogServiceServiceAddress] = service.ServiceAddress + } + m[catalogServiceServiceEnableTagOverride] = fmt.Sprintf("%t", service.ServiceEnableTagOverride) + m[catalogServiceServiceID] = service.ServiceID + m[catalogServiceServiceName] = service.ServiceName + m[catalogServiceServicePort] = fmt.Sprintf("%d", service.ServicePort) + sort.Strings(service.ServiceTags) + m[catalogServiceServiceTags] = service.ServiceTags + m[catalogServiceTaggedAddresses] = service.TaggedAddresses + + l = append(l, m) + } + + const idKeyFmt = "catalog-service-%s-%q-%q" + d.SetId(fmt.Sprintf(idKeyFmt, queryOpts.Datacenter, serviceName, serviceTag)) + + d.Set(catalogServiceDatacenter, queryOpts.Datacenter) + d.Set(catalogServiceName, serviceName) + d.Set(catalogServiceTag, serviceTag) + if err := d.Set(catalogServiceElem, l); err != nil { + return errwrap.Wrapf("Unable to store service: {{err}}", err) + } + + return nil +} diff --git a/builtin/providers/consul/data_source_consul_catalog_service_test.go b/builtin/providers/consul/data_source_consul_catalog_service_test.go new file mode 100644 index 000000000000..0ef7d1ab0a57 --- /dev/null +++ b/builtin/providers/consul/data_source_consul_catalog_service_test.go @@ -0,0 +1,50 @@ +package consul + +import ( + "testing" + + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccDataConsulCatalogService_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDataConsulCatalogServiceConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckDataSourceValue("data.consul_catalog_service.read", "datacenter", "dc1"), + testAccCheckDataSourceValue("data.consul_catalog_service.read", "service.#", "1"), + testAccCheckDataSourceValue("data.consul_catalog_service.read", "service.0.address", ""), + testAccCheckDataSourceValue("data.consul_catalog_service.read", "service.0.create_index", ""), + testAccCheckDataSourceValue("data.consul_catalog_service.read", "service.0.enable_tag_override", ""), + testAccCheckDataSourceValue("data.consul_catalog_service.read", "service.0.id", ""), + testAccCheckDataSourceValue("data.consul_catalog_service.read", "service.0.modify_index", ""), + testAccCheckDataSourceValue("data.consul_catalog_service.read", "service.0.name", ""), + testAccCheckDataSourceValue("data.consul_catalog_service.read", "service.0.node_address", ""), + testAccCheckDataSourceValue("data.consul_catalog_service.read", "service.0.node_id", ""), + testAccCheckDataSourceValue("data.consul_catalog_service.read", "service.0.node_meta.%", "0"), + testAccCheckDataSourceValue("data.consul_catalog_service.read", "service.0.node_name", ""), + testAccCheckDataSourceValue("data.consul_catalog_service.read", "service.0.port", ""), + testAccCheckDataSourceValue("data.consul_catalog_service.read", "service.0.tagged_addresses.%", "2"), + testAccCheckDataSourceValue("data.consul_catalog_service.read", "service.0.tags.#", "0"), + ), + }, + }, + }) +} + +const testAccDataConsulCatalogServiceConfig = ` +data "consul_catalog_service" "read" { + query_options { + allow_stale = true + require_consistent = false + token = "" + wait_index = 0 + wait_time = "1m" + } + + name = "consul" +} +` diff --git a/builtin/providers/consul/data_source_consul_catalog_services.go b/builtin/providers/consul/data_source_consul_catalog_services.go new file mode 100644 index 000000000000..85770d611315 --- /dev/null +++ b/builtin/providers/consul/data_source_consul_catalog_services.go @@ -0,0 +1,104 @@ +package consul + +import ( + "fmt" + "sort" + "strings" + + consulapi "github.com/hashicorp/consul/api" + "github.com/hashicorp/errwrap" + "github.com/hashicorp/terraform/helper/schema" +) + +const ( + // Datasource predicates + catalogServicesServiceName = "name" + + // Out parameters + catalogServicesDatacenter = "datacenter" + catalogServicesNames = "names" + catalogServicesServices = "services" + catalogServicesServiceTags = "tags" +) + +func dataSourceConsulCatalogServices() *schema.Resource { + return &schema.Resource{ + Read: dataSourceConsulCatalogServicesRead, + Schema: map[string]*schema.Schema{ + // Data Source Predicate(s) + catalogServicesDatacenter: &schema.Schema{ + // Used in the query, must be stored and force a refresh if the value + // changes. + Computed: true, + Type: schema.TypeString, + ForceNew: true, + }, + catalogNodesQueryOpts: schemaQueryOpts, + + // Out parameters + catalogServicesNames: &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + catalogServicesServices: &schema.Schema{ + Computed: true, + Type: schema.TypeMap, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + catalogServiceServiceTags: &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + }, + }, + }, + } +} + +func dataSourceConsulCatalogServicesRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*consulapi.Client) + + // Parse out data source filters to populate Consul's query options + queryOpts, err := getQueryOpts(d, client) + if err != nil { + return errwrap.Wrapf("unable to get query options for fetching catalog services: {{err}}", err) + } + + services, meta, err := client.Catalog().Services(queryOpts) + if err != nil { + return err + } + + catalogServices := make(map[string]interface{}, len(services)) + for name, tags := range services { + tagList := make([]string, 0, len(tags)) + for _, tag := range tags { + tagList = append(tagList, tag) + } + + sort.Strings(tagList) + catalogServices[name] = strings.Join(tagList, " ") + } + + serviceNames := make([]interface{}, 0, len(services)) + for k := range catalogServices { + serviceNames = append(serviceNames, k) + } + + const idKeyFmt = "catalog-services-%s" + d.SetId(fmt.Sprintf(idKeyFmt, queryOpts.Datacenter)) + + d.Set(catalogServicesDatacenter, queryOpts.Datacenter) + if err := d.Set(catalogServicesServices, catalogServices); err != nil { + return errwrap.Wrapf("Unable to store services: {{err}}", err) + } + + if err := d.Set(catalogServicesNames, serviceNames); err != nil { + return errwrap.Wrapf("Unable to store service names: {{err}}", err) + } + + return nil +} diff --git a/builtin/providers/consul/data_source_consul_catalog_services_test.go b/builtin/providers/consul/data_source_consul_catalog_services_test.go new file mode 100644 index 000000000000..1087073f7d54 --- /dev/null +++ b/builtin/providers/consul/data_source_consul_catalog_services_test.go @@ -0,0 +1,36 @@ +package consul + +import ( + "testing" + + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccDataConsulCatalogServices_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDataConsulCatalogServicesConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckDataSourceValue("data.consul_catalog_services.read", "datacenter", "dc1"), + testAccCheckDataSourceValue("data.consul_catalog_services.read", "services.%", "1"), + testAccCheckDataSourceValue("data.consul_catalog_services.read", "services.consul", ""), + ), + }, + }, + }) +} + +const testAccDataConsulCatalogServicesConfig = ` +data "consul_catalog_services" "read" { + query_options { + allow_stale = true + require_consistent = false + token = "" + wait_index = 0 + wait_time = "1m" + } +} +` diff --git a/builtin/providers/consul/query_options.go b/builtin/providers/consul/query_options.go new file mode 100644 index 000000000000..1cce0fabe6eb --- /dev/null +++ b/builtin/providers/consul/query_options.go @@ -0,0 +1,122 @@ +package consul + +import ( + "time" + + consulapi "github.com/hashicorp/consul/api" + "github.com/hashicorp/terraform/helper/schema" +) + +const ( + queryOptAllowStale = "allow_stale" + queryOptDatacenter = "datacenter" + queryOptNear = "near" + queryOptNodeMeta = "node_meta" + queryOptRequireConsistent = "require_consistent" + queryOptToken = "token" + queryOptWaitIndex = "wait_index" + queryOptWaitTime = "wait_time" +) + +var schemaQueryOpts = &schema.Schema{ + Optional: true, + Type: schema.TypeSet, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + queryOptAllowStale: &schema.Schema{ + Optional: true, + Default: true, + Type: schema.TypeBool, + }, + queryOptDatacenter: &schema.Schema{ + // Optional because we'll pull the default from the local agent if it's + // not specified, but we can query remote data centers as a result. + Optional: true, + Type: schema.TypeString, + }, + queryOptNear: &schema.Schema{ + Optional: true, + Type: schema.TypeString, + }, + queryOptNodeMeta: &schema.Schema{ + Optional: true, + Type: schema.TypeMap, + }, + queryOptRequireConsistent: &schema.Schema{ + Optional: true, + Default: false, + Type: schema.TypeBool, + }, + queryOptToken: &schema.Schema{ + Optional: true, + Type: schema.TypeString, + }, + queryOptWaitIndex: &schema.Schema{ + Optional: true, + Type: schema.TypeInt, + ValidateFunc: makeValidationFunc(queryOptWaitIndex, []interface{}{ + validateIntMin(0), + }), + }, + queryOptWaitTime: &schema.Schema{ + Optional: true, + Type: schema.TypeString, + ValidateFunc: makeValidationFunc(queryOptWaitTime, []interface{}{ + validateDurationMin("0ns"), + }), + }, + }, + }, +} + +func getQueryOpts(d *schema.ResourceData, client *consulapi.Client) (*consulapi.QueryOptions, error) { + queryOpts := &consulapi.QueryOptions{} + + if v, ok := d.GetOk(queryOptAllowStale); ok { + queryOpts.AllowStale = v.(bool) + } + + if v, ok := d.GetOk(queryOptDatacenter); ok { + queryOpts.Datacenter = v.(string) + } + + if queryOpts.Datacenter == "" { + dc, err := getDC(d, client) + if err != nil { + return nil, err + } + queryOpts.Datacenter = dc + } + + if v, ok := d.GetOk(queryOptNear); ok { + queryOpts.Near = v.(string) + } + + if v, ok := d.GetOk(queryOptRequireConsistent); ok { + queryOpts.RequireConsistent = v.(bool) + } + + if v, ok := d.GetOk(queryOptNodeMeta); ok { + m := v.(map[string]interface{}) + nodeMetaMap := make(map[string]string, len(queryOptNodeMeta)) + for s, t := range m { + nodeMetaMap[s] = t.(string) + } + queryOpts.NodeMeta = nodeMetaMap + } + + if v, ok := d.GetOk(queryOptToken); ok { + queryOpts.Token = v.(string) + } + + if v, ok := d.GetOk(queryOptWaitIndex); ok { + queryOpts.WaitIndex = uint64(v.(int)) + } + + if v, ok := d.GetOk(queryOptWaitTime); ok { + d, _ := time.ParseDuration(v.(string)) + queryOpts.WaitTime = d + } + + return queryOpts, nil +} diff --git a/builtin/providers/consul/resource_consul_prepared_query.go b/builtin/providers/consul/resource_consul_prepared_query.go index 329bd2f7d896..28ff2bf8251c 100644 --- a/builtin/providers/consul/resource_consul_prepared_query.go +++ b/builtin/providers/consul/resource_consul_prepared_query.go @@ -208,12 +208,12 @@ func resourceConsulPreparedQueryRead(d *schema.ResourceData, meta interface{}) e func resourceConsulPreparedQueryDelete(d *schema.ResourceData, meta interface{}) error { client := meta.(*consulapi.Client) - qo := &consulapi.QueryOptions{ + writeOpts := &consulapi.WriteOptions{ Datacenter: d.Get("datacenter").(string), Token: d.Get("token").(string), } - if _, err := client.PreparedQuery().Delete(d.Id(), qo); err != nil { + if _, err := client.PreparedQuery().Delete(d.Id(), writeOpts); err != nil { return err } diff --git a/builtin/providers/consul/resource_provider.go b/builtin/providers/consul/resource_provider.go index 2b4d01079b17..fb316adccf91 100644 --- a/builtin/providers/consul/resource_provider.go +++ b/builtin/providers/consul/resource_provider.go @@ -64,7 +64,11 @@ func Provider() terraform.ResourceProvider { }, DataSourcesMap: map[string]*schema.Resource{ - "consul_keys": dataSourceConsulKeys(), + "consul_agent_self": dataSourceConsulAgentSelf(), + "consul_catalog_nodes": dataSourceConsulCatalogNodes(), + "consul_catalog_service": dataSourceConsulCatalogService(), + "consul_catalog_services": dataSourceConsulCatalogServices(), + "consul_keys": dataSourceConsulKeys(), }, ResourcesMap: map[string]*schema.Resource{ diff --git a/builtin/providers/consul/validators.go b/builtin/providers/consul/validators.go new file mode 100644 index 000000000000..af4f44080905 --- /dev/null +++ b/builtin/providers/consul/validators.go @@ -0,0 +1,146 @@ +package consul + +import ( + "fmt" + "regexp" + "strconv" + "time" + + "github.com/hashicorp/errwrap" +) + +// An array of inputs used as typed arguments and converted from their type into +// function objects that are dynamically constructed and executed. +type validatorInputs []interface{} + +// validateDurationMin is the minimum duration to accept as input +type validateDurationMin string + +// validateIntMax is the maximum integer value to accept as input +type validateIntMax int + +// validateIntMin is the minimum integer value to accept as input +type validateIntMin int + +// validateRegexp is a regexp pattern to use to validate schema input. +type validateRegexp string + +// makeValidateionFunc takes the name of the attribute and a list of typed +// validator inputs in order to create a validation closure that calls each +// validator in serial until either a warning or error is returned from the +// first validation function. +func makeValidationFunc(name string, validators []interface{}) func(v interface{}, key string) (warnings []string, errors []error) { + if len(validators) == 0 { + return nil + } + + fns := make([]func(v interface{}, key string) (warnings []string, errors []error), 0, len(validators)) + for _, v := range validators { + switch u := v.(type) { + case validateDurationMin: + fns = append(fns, validateDurationMinFactory(name, string(u))) + case validateIntMax: + fns = append(fns, validateIntMaxFactory(name, int(u))) + case validateIntMin: + fns = append(fns, validateIntMinFactory(name, int(u))) + case validateRegexp: + fns = append(fns, validateRegexpFactory(name, string(u))) + } + } + + return func(v interface{}, key string) (warnings []string, errors []error) { + for _, fn := range fns { + warnings, errors = fn(v, key) + if len(warnings) > 0 || len(errors) > 0 { + break + } + } + return warnings, errors + } +} + +func validateDurationMinFactory(name, minDuration string) func(v interface{}, key string) (warnings []string, errors []error) { + dMin, err := time.ParseDuration(minDuration) + if err != nil { + return func(interface{}, string) (warnings []string, errors []error) { + return nil, []error{ + errwrap.Wrapf(fmt.Sprintf("PROVIDER BUG: duration %q not valid: {{err}}", minDuration), err), + } + } + } + + return func(v interface{}, key string) (warnings []string, errors []error) { + d, err := time.ParseDuration(v.(string)) + if err != nil { + errors = append(errors, errwrap.Wrapf(fmt.Sprintf("Invalid %s specified (%q): {{err}}", name, v.(string)), err)) + } + + if d < dMin { + errors = append(errors, fmt.Errorf("Invalid %s specified: duration %q less than the required minimum %s", name, v.(string), dMin)) + } + + return warnings, errors + } +} + +func validateIntMaxFactory(name string, max int) func(v interface{}, key string) (warnings []string, errors []error) { + return func(v interface{}, key string) (warnings []string, errors []error) { + switch u := v.(type) { + case string: + i, err := strconv.ParseInt(u, 10, 64) + if err != nil { + errors = append(errors, errwrap.Wrapf(fmt.Sprintf("unable to convert %q to an integer: {{err}}", u), err)) + break + } + + if i > int64(max) { + errors = append(errors, fmt.Errorf("Invalid %s specified: %d more than the required maximum %d", name, v.(int), max)) + } + case int: + if u > max { + errors = append(errors, fmt.Errorf("Invalid %s specified: %d more than the required maximum %d", name, v.(int), max)) + } + default: + errors = append(errors, fmt.Errorf("Unsupported type in int max validation: %T", v)) + } + + return warnings, errors + } +} + +func validateIntMinFactory(name string, min int) func(v interface{}, key string) (warnings []string, errors []error) { + return func(v interface{}, key string) (warnings []string, errors []error) { + switch u := v.(type) { + case string: + i, err := strconv.ParseInt(u, 10, 64) + if err != nil { + errors = append(errors, errwrap.Wrapf(fmt.Sprintf("unable to convert %q to an integer: {{err}}", u), err)) + break + } + + if i < int64(min) { + errors = append(errors, fmt.Errorf("Invalid %s specified: %d less than the required minimum %d", name, v.(int), min)) + } + case int: + if u < min { + errors = append(errors, fmt.Errorf("Invalid %s specified: %d less than the required minimum %d", name, v.(int), min)) + } + default: + errors = append(errors, fmt.Errorf("Unsupported type in int min validation: %T", v)) + } + + return warnings, errors + } +} + +func validateRegexpFactory(name string, reString string) func(v interface{}, key string) (warnings []string, errors []error) { + re := regexp.MustCompile(reString) + + return func(v interface{}, key string) (warnings []string, errors []error) { + if !re.MatchString(v.(string)) { + errors = append(errors, fmt.Errorf("Invalid %s specified (%q): regexp failed to match string", name, v.(string))) + } + + return warnings, errors + } +} diff --git a/vendor/github.com/hashicorp/consul/api/agent.go b/vendor/github.com/hashicorp/consul/api/agent.go index 3df013cc5b9e..1893d1cf359d 100644 --- a/vendor/github.com/hashicorp/consul/api/agent.go +++ b/vendor/github.com/hashicorp/consul/api/agent.go @@ -1,6 +1,7 @@ package api import ( + "bufio" "fmt" ) @@ -62,8 +63,7 @@ type AgentCheckRegistration struct { AgentServiceCheck } -// AgentServiceCheck is used to create an associated -// check for a service +// AgentServiceCheck is used to define a node or service level check type AgentServiceCheck struct { Script string `json:",omitempty"` DockerContainerID string `json:",omitempty"` @@ -74,6 +74,16 @@ type AgentServiceCheck struct { HTTP string `json:",omitempty"` TCP string `json:",omitempty"` Status string `json:",omitempty"` + Notes string `json:",omitempty"` + TLSSkipVerify bool `json:",omitempty"` + + // In Consul 0.7 and later, checks that are associated with a service + // may also contain this optional DeregisterCriticalServiceAfter field, + // which is a timeout in the same Go time format as Interval and TTL. If + // a check is in the critical state for more than this configured value, + // then its associated service (and all of its associated checks) will + // automatically be deregistered. + DeregisterCriticalServiceAfter string `json:",omitempty"` } type AgentServiceChecks []*AgentServiceCheck @@ -107,6 +117,17 @@ func (a *Agent) Self() (map[string]map[string]interface{}, error) { return out, nil } +// Reload triggers a configuration reload for the agent we are connected to. +func (a *Agent) Reload() error { + r := a.c.newRequest("PUT", "/v1/agent/reload") + _, resp, err := requireOK(a.c.doRequest(r)) + if err != nil { + return err + } + resp.Body.Close() + return nil +} + // NodeName is used to get the node name of the agent func (a *Agent) NodeName() (string, error) { if a.nodeName != "" { @@ -338,6 +359,17 @@ func (a *Agent) Join(addr string, wan bool) error { return nil } +// Leave is used to have the agent gracefully leave the cluster and shutdown +func (a *Agent) Leave() error { + r := a.c.newRequest("PUT", "/v1/agent/leave") + _, resp, err := requireOK(a.c.doRequest(r)) + if err != nil { + return err + } + resp.Body.Close() + return nil +} + // ForceLeave is used to have the agent eject a failed node func (a *Agent) ForceLeave(node string) error { r := a.c.newRequest("PUT", "/v1/agent/force-leave/"+node) @@ -402,3 +434,38 @@ func (a *Agent) DisableNodeMaintenance() error { resp.Body.Close() return nil } + +// Monitor returns a channel which will receive streaming logs from the agent +// Providing a non-nil stopCh can be used to close the connection and stop the +// log stream +func (a *Agent) Monitor(loglevel string, stopCh chan struct{}, q *QueryOptions) (chan string, error) { + r := a.c.newRequest("GET", "/v1/agent/monitor") + r.setQueryOptions(q) + if loglevel != "" { + r.params.Add("loglevel", loglevel) + } + _, resp, err := requireOK(a.c.doRequest(r)) + if err != nil { + return nil, err + } + + logCh := make(chan string, 64) + go func() { + defer resp.Body.Close() + + scanner := bufio.NewScanner(resp.Body) + for { + select { + case <-stopCh: + close(logCh) + return + default: + } + if scanner.Scan() { + logCh <- scanner.Text() + } + } + }() + + return logCh, nil +} diff --git a/vendor/github.com/hashicorp/consul/api/api.go b/vendor/github.com/hashicorp/consul/api/api.go index 590b858e1fca..9a59b724cbae 100644 --- a/vendor/github.com/hashicorp/consul/api/api.go +++ b/vendor/github.com/hashicorp/consul/api/api.go @@ -20,6 +20,28 @@ import ( "github.com/hashicorp/go-cleanhttp" ) +const ( + // HTTPAddrEnvName defines an environment variable name which sets + // the HTTP address if there is no -http-addr specified. + HTTPAddrEnvName = "CONSUL_HTTP_ADDR" + + // HTTPTokenEnvName defines an environment variable name which sets + // the HTTP token. + HTTPTokenEnvName = "CONSUL_HTTP_TOKEN" + + // HTTPAuthEnvName defines an environment variable name which sets + // the HTTP authentication header. + HTTPAuthEnvName = "CONSUL_HTTP_AUTH" + + // HTTPSSLEnvName defines an environment variable name which sets + // whether or not to use HTTPS. + HTTPSSLEnvName = "CONSUL_HTTP_SSL" + + // HTTPSSLVerifyEnvName defines an environment variable name which sets + // whether or not to disable certificate checking. + HTTPSSLVerifyEnvName = "CONSUL_HTTP_SSL_VERIFY" +) + // QueryOptions are used to parameterize a query type QueryOptions struct { // Providing a datacenter overwrites the DC provided @@ -52,6 +74,11 @@ type QueryOptions struct { // that node. Setting this to "_agent" will use the agent's node // for the sort. Near string + + // NodeMeta is used to filter results by nodes with the given + // metadata key/value pairs. Currently, only one key/value pair can + // be provided for filtering. + NodeMeta map[string]string } // WriteOptions are used to parameterize a write @@ -80,6 +107,9 @@ type QueryMeta struct { // How long did the request take RequestTime time.Duration + + // Is address translation enabled for HTTP responses on this agent + AddressTranslationEnabled bool } // WriteMeta is used to return meta data about a write @@ -178,15 +208,15 @@ func defaultConfig(transportFn func() *http.Transport) *Config { }, } - if addr := os.Getenv("CONSUL_HTTP_ADDR"); addr != "" { + if addr := os.Getenv(HTTPAddrEnvName); addr != "" { config.Address = addr } - if token := os.Getenv("CONSUL_HTTP_TOKEN"); token != "" { + if token := os.Getenv(HTTPTokenEnvName); token != "" { config.Token = token } - if auth := os.Getenv("CONSUL_HTTP_AUTH"); auth != "" { + if auth := os.Getenv(HTTPAuthEnvName); auth != "" { var username, password string if strings.Contains(auth, ":") { split := strings.SplitN(auth, ":", 2) @@ -202,10 +232,10 @@ func defaultConfig(transportFn func() *http.Transport) *Config { } } - if ssl := os.Getenv("CONSUL_HTTP_SSL"); ssl != "" { + if ssl := os.Getenv(HTTPSSLEnvName); ssl != "" { enabled, err := strconv.ParseBool(ssl) if err != nil { - log.Printf("[WARN] client: could not parse CONSUL_HTTP_SSL: %s", err) + log.Printf("[WARN] client: could not parse %s: %s", HTTPSSLEnvName, err) } if enabled { @@ -213,10 +243,10 @@ func defaultConfig(transportFn func() *http.Transport) *Config { } } - if verify := os.Getenv("CONSUL_HTTP_SSL_VERIFY"); verify != "" { + if verify := os.Getenv(HTTPSSLVerifyEnvName); verify != "" { doVerify, err := strconv.ParseBool(verify) if err != nil { - log.Printf("[WARN] client: could not parse CONSUL_HTTP_SSL_VERIFY: %s", err) + log.Printf("[WARN] client: could not parse %s: %s", HTTPSSLVerifyEnvName, err) } if !doVerify { @@ -330,6 +360,7 @@ type request struct { url *url.URL params url.Values body io.Reader + header http.Header obj interface{} } @@ -355,11 +386,16 @@ func (r *request) setQueryOptions(q *QueryOptions) { r.params.Set("wait", durToMsec(q.WaitTime)) } if q.Token != "" { - r.params.Set("token", q.Token) + r.header.Set("X-Consul-Token", q.Token) } if q.Near != "" { r.params.Set("near", q.Near) } + if len(q.NodeMeta) > 0 { + for key, value := range q.NodeMeta { + r.params.Add("node-meta", key+":"+value) + } + } } // durToMsec converts a duration to a millisecond specified string. If the @@ -399,7 +435,7 @@ func (r *request) setWriteOptions(q *WriteOptions) { r.params.Set("dc", q.Datacenter) } if q.Token != "" { - r.params.Set("token", q.Token) + r.header.Set("X-Consul-Token", q.Token) } } @@ -426,6 +462,7 @@ func (r *request) toHTTP() (*http.Request, error) { req.URL.Host = r.url.Host req.URL.Scheme = r.url.Scheme req.Host = r.url.Host + req.Header = r.header // Setup auth if r.config.HttpAuth != nil { @@ -446,6 +483,7 @@ func (c *Client) newRequest(method, path string) *request { Path: path, }, params: make(map[string][]string), + header: make(http.Header), } if c.config.Datacenter != "" { r.params.Set("dc", c.config.Datacenter) @@ -454,7 +492,7 @@ func (c *Client) newRequest(method, path string) *request { r.params.Set("wait", durToMsec(r.config.WaitTime)) } if c.config.Token != "" { - r.params.Set("token", r.config.Token) + r.header.Set("X-Consul-Token", r.config.Token) } return r } @@ -539,6 +577,15 @@ func parseQueryMeta(resp *http.Response, q *QueryMeta) error { default: q.KnownLeader = false } + + // Parse X-Consul-Translate-Addresses + switch header.Get("X-Consul-Translate-Addresses") { + case "true": + q.AddressTranslationEnabled = true + default: + q.AddressTranslationEnabled = false + } + return nil } diff --git a/vendor/github.com/hashicorp/consul/api/catalog.go b/vendor/github.com/hashicorp/consul/api/catalog.go index 52a00b3043f8..96226f11f897 100644 --- a/vendor/github.com/hashicorp/consul/api/catalog.go +++ b/vendor/github.com/hashicorp/consul/api/catalog.go @@ -1,19 +1,27 @@ package api type Node struct { - Node string - Address string + ID string + Node string + Address string + TaggedAddresses map[string]string + Meta map[string]string } type CatalogService struct { + ID string Node string Address string + TaggedAddresses map[string]string + NodeMeta map[string]string ServiceID string ServiceName string ServiceAddress string ServiceTags []string ServicePort int ServiceEnableTagOverride bool + CreateIndex uint64 + ModifyIndex uint64 } type CatalogNode struct { @@ -22,16 +30,19 @@ type CatalogNode struct { } type CatalogRegistration struct { - Node string - Address string - Datacenter string - Service *AgentService - Check *AgentCheck + ID string + Node string + Address string + TaggedAddresses map[string]string + NodeMeta map[string]string + Datacenter string + Service *AgentService + Check *AgentCheck } type CatalogDeregistration struct { Node string - Address string + Address string // Obsolete. Datacenter string ServiceID string CheckID string diff --git a/vendor/github.com/hashicorp/consul/api/health.go b/vendor/github.com/hashicorp/consul/api/health.go index 5bb403f554f0..8abe2393ad50 100644 --- a/vendor/github.com/hashicorp/consul/api/health.go +++ b/vendor/github.com/hashicorp/consul/api/health.go @@ -2,16 +2,25 @@ package api import ( "fmt" + "strings" ) const ( // HealthAny is special, and is used as a wild card, // not as a specific state. HealthAny = "any" - HealthUnknown = "unknown" HealthPassing = "passing" HealthWarning = "warning" HealthCritical = "critical" + HealthMaint = "maintenance" +) + +const ( + // NodeMaint is the special key set by a node in maintenance mode. + NodeMaint = "_node_maintenance" + + // ServiceMaintPrefix is the prefix for a service in maintenance mode. + ServiceMaintPrefix = "_service_maintenance:" ) // HealthCheck is used to represent a single check @@ -26,11 +35,56 @@ type HealthCheck struct { ServiceName string } +// HealthChecks is a collection of HealthCheck structs. +type HealthChecks []*HealthCheck + +// AggregatedStatus returns the "best" status for the list of health checks. +// Because a given entry may have many service and node-level health checks +// attached, this function determines the best representative of the status as +// as single string using the following heuristic: +// +// maintenance > critical > warning > passing +// +func (c HealthChecks) AggregatedStatus() string { + var passing, warning, critical, maintenance bool + for _, check := range c { + id := string(check.CheckID) + if id == NodeMaint || strings.HasPrefix(id, ServiceMaintPrefix) { + maintenance = true + continue + } + + switch check.Status { + case HealthPassing: + passing = true + case HealthWarning: + warning = true + case HealthCritical: + critical = true + default: + return "" + } + } + + switch { + case maintenance: + return HealthMaint + case critical: + return HealthCritical + case warning: + return HealthWarning + case passing: + return HealthPassing + default: + return HealthPassing + } +} + // ServiceEntry is used for the health service endpoint type ServiceEntry struct { Node *Node Service *AgentService - Checks []*HealthCheck + Checks HealthChecks } // Health can be used to query the Health endpoints @@ -44,7 +98,7 @@ func (c *Client) Health() *Health { } // Node is used to query for checks belonging to a given node -func (h *Health) Node(node string, q *QueryOptions) ([]*HealthCheck, *QueryMeta, error) { +func (h *Health) Node(node string, q *QueryOptions) (HealthChecks, *QueryMeta, error) { r := h.c.newRequest("GET", "/v1/health/node/"+node) r.setQueryOptions(q) rtt, resp, err := requireOK(h.c.doRequest(r)) @@ -57,7 +111,7 @@ func (h *Health) Node(node string, q *QueryOptions) ([]*HealthCheck, *QueryMeta, parseQueryMeta(resp, qm) qm.RequestTime = rtt - var out []*HealthCheck + var out HealthChecks if err := decodeBody(resp, &out); err != nil { return nil, nil, err } @@ -65,7 +119,7 @@ func (h *Health) Node(node string, q *QueryOptions) ([]*HealthCheck, *QueryMeta, } // Checks is used to return the checks associated with a service -func (h *Health) Checks(service string, q *QueryOptions) ([]*HealthCheck, *QueryMeta, error) { +func (h *Health) Checks(service string, q *QueryOptions) (HealthChecks, *QueryMeta, error) { r := h.c.newRequest("GET", "/v1/health/checks/"+service) r.setQueryOptions(q) rtt, resp, err := requireOK(h.c.doRequest(r)) @@ -78,7 +132,7 @@ func (h *Health) Checks(service string, q *QueryOptions) ([]*HealthCheck, *Query parseQueryMeta(resp, qm) qm.RequestTime = rtt - var out []*HealthCheck + var out HealthChecks if err := decodeBody(resp, &out); err != nil { return nil, nil, err } @@ -116,13 +170,12 @@ func (h *Health) Service(service, tag string, passingOnly bool, q *QueryOptions) // State is used to retrieve all the checks in a given state. // The wildcard "any" state can also be used for all checks. -func (h *Health) State(state string, q *QueryOptions) ([]*HealthCheck, *QueryMeta, error) { +func (h *Health) State(state string, q *QueryOptions) (HealthChecks, *QueryMeta, error) { switch state { case HealthAny: case HealthWarning: case HealthCritical: case HealthPassing: - case HealthUnknown: default: return nil, nil, fmt.Errorf("Unsupported state: %v", state) } @@ -138,7 +191,7 @@ func (h *Health) State(state string, q *QueryOptions) ([]*HealthCheck, *QueryMet parseQueryMeta(resp, qm) qm.RequestTime = rtt - var out []*HealthCheck + var out HealthChecks if err := decodeBody(resp, &out); err != nil { return nil, nil, err } diff --git a/vendor/github.com/hashicorp/consul/api/kv.go b/vendor/github.com/hashicorp/consul/api/kv.go index 3dac2583c125..44e06bbb470d 100644 --- a/vendor/github.com/hashicorp/consul/api/kv.go +++ b/vendor/github.com/hashicorp/consul/api/kv.go @@ -11,13 +11,35 @@ import ( // KVPair is used to represent a single K/V entry type KVPair struct { - Key string + // Key is the name of the key. It is also part of the URL path when accessed + // via the API. + Key string + + // CreateIndex holds the index corresponding the creation of this KVPair. This + // is a read-only field. CreateIndex uint64 + + // ModifyIndex is used for the Check-And-Set operations and can also be fed + // back into the WaitIndex of the QueryOptions in order to perform blocking + // queries. ModifyIndex uint64 - LockIndex uint64 - Flags uint64 - Value []byte - Session string + + // LockIndex holds the index corresponding to a lock on this key, if any. This + // is a read-only field. + LockIndex uint64 + + // Flags are any user-defined flags on the key. It is up to the implementer + // to check these values, since Consul does not treat them specially. + Flags uint64 + + // Value is the value for the key. This can be any value, but it will be + // base64 encoded upon transport. + Value []byte + + // Session is a string representing the ID of the session. Any other + // interactions with this key over the same session must specify the same + // session ID. + Session string } // KVPairs is a list of KVPair objects @@ -28,21 +50,21 @@ type KVOp string const ( KVSet KVOp = "set" - KVDelete = "delete" - KVDeleteCAS = "delete-cas" - KVDeleteTree = "delete-tree" - KVCAS = "cas" - KVLock = "lock" - KVUnlock = "unlock" - KVGet = "get" - KVGetTree = "get-tree" - KVCheckSession = "check-session" - KVCheckIndex = "check-index" + KVDelete KVOp = "delete" + KVDeleteCAS KVOp = "delete-cas" + KVDeleteTree KVOp = "delete-tree" + KVCAS KVOp = "cas" + KVLock KVOp = "lock" + KVUnlock KVOp = "unlock" + KVGet KVOp = "get" + KVGetTree KVOp = "get-tree" + KVCheckSession KVOp = "check-session" + KVCheckIndex KVOp = "check-index" ) // KVTxnOp defines a single operation inside a transaction. type KVTxnOp struct { - Verb string + Verb KVOp Key string Value []byte Flags uint64 @@ -70,7 +92,8 @@ func (c *Client) KV() *KV { return &KV{c} } -// Get is used to lookup a single key +// Get is used to lookup a single key. The returned pointer +// to the KVPair will be nil if the key does not exist. func (k *KV) Get(key string, q *QueryOptions) (*KVPair, *QueryMeta, error) { resp, qm, err := k.getInternal(key, nil, q) if err != nil { @@ -133,7 +156,7 @@ func (k *KV) Keys(prefix, separator string, q *QueryOptions) ([]string, *QueryMe } func (k *KV) getInternal(key string, params map[string]string, q *QueryOptions) (*http.Response, *QueryMeta, error) { - r := k.c.newRequest("GET", "/v1/kv/"+key) + r := k.c.newRequest("GET", "/v1/kv/"+strings.TrimPrefix(key, "/")) r.setQueryOptions(q) for param, val := range params { r.params.Set(param, val) @@ -254,7 +277,7 @@ func (k *KV) DeleteTree(prefix string, w *WriteOptions) (*WriteMeta, error) { } func (k *KV) deleteInternal(key string, params map[string]string, q *WriteOptions) (bool, *WriteMeta, error) { - r := k.c.newRequest("DELETE", "/v1/kv/"+key) + r := k.c.newRequest("DELETE", "/v1/kv/"+strings.TrimPrefix(key, "/")) r.setWriteOptions(q) for param, val := range params { r.params.Set(param, val) diff --git a/vendor/github.com/hashicorp/consul/api/lock.go b/vendor/github.com/hashicorp/consul/api/lock.go index 08e8e793108d..9f9845a4325b 100644 --- a/vendor/github.com/hashicorp/consul/api/lock.go +++ b/vendor/github.com/hashicorp/consul/api/lock.go @@ -72,8 +72,9 @@ type LockOptions struct { Key string // Must be set and have write permissions Value []byte // Optional, value to associate with the lock Session string // Optional, created if not specified - SessionName string // Optional, defaults to DefaultLockSessionName - SessionTTL string // Optional, defaults to DefaultLockSessionTTL + SessionOpts *SessionEntry // Optional, options to use when creating a session + SessionName string // Optional, defaults to DefaultLockSessionName (ignored if SessionOpts is given) + SessionTTL string // Optional, defaults to DefaultLockSessionTTL (ignored if SessionOpts is given) MonitorRetries int // Optional, defaults to 0 which means no retries MonitorRetryTime time.Duration // Optional, defaults to DefaultMonitorRetryTime LockWaitTime time.Duration // Optional, defaults to DefaultLockWaitTime @@ -329,9 +330,12 @@ func (l *Lock) Destroy() error { // createSession is used to create a new managed session func (l *Lock) createSession() (string, error) { session := l.c.Session() - se := &SessionEntry{ - Name: l.opts.SessionName, - TTL: l.opts.SessionTTL, + se := l.opts.SessionOpts + if se == nil { + se = &SessionEntry{ + Name: l.opts.SessionName, + TTL: l.opts.SessionTTL, + } } id, _, err := session.Create(se, nil) if err != nil { diff --git a/vendor/github.com/hashicorp/consul/api/operator.go b/vendor/github.com/hashicorp/consul/api/operator.go new file mode 100644 index 000000000000..a8d04a38eb88 --- /dev/null +++ b/vendor/github.com/hashicorp/consul/api/operator.go @@ -0,0 +1,163 @@ +package api + +// Operator can be used to perform low-level operator tasks for Consul. +type Operator struct { + c *Client +} + +// Operator returns a handle to the operator endpoints. +func (c *Client) Operator() *Operator { + return &Operator{c} +} + +// RaftServer has information about a server in the Raft configuration. +type RaftServer struct { + // ID is the unique ID for the server. These are currently the same + // as the address, but they will be changed to a real GUID in a future + // release of Consul. + ID string + + // Node is the node name of the server, as known by Consul, or this + // will be set to "(unknown)" otherwise. + Node string + + // Address is the IP:port of the server, used for Raft communications. + Address string + + // Leader is true if this server is the current cluster leader. + Leader bool + + // Voter is true if this server has a vote in the cluster. This might + // be false if the server is staging and still coming online, or if + // it's a non-voting server, which will be added in a future release of + // Consul. + Voter bool +} + +// RaftConfigration is returned when querying for the current Raft configuration. +type RaftConfiguration struct { + // Servers has the list of servers in the Raft configuration. + Servers []*RaftServer + + // Index has the Raft index of this configuration. + Index uint64 +} + +// keyringRequest is used for performing Keyring operations +type keyringRequest struct { + Key string +} + +// KeyringResponse is returned when listing the gossip encryption keys +type KeyringResponse struct { + // Whether this response is for a WAN ring + WAN bool + + // The datacenter name this request corresponds to + Datacenter string + + // A map of the encryption keys to the number of nodes they're installed on + Keys map[string]int + + // The total number of nodes in this ring + NumNodes int +} + +// RaftGetConfiguration is used to query the current Raft peer set. +func (op *Operator) RaftGetConfiguration(q *QueryOptions) (*RaftConfiguration, error) { + r := op.c.newRequest("GET", "/v1/operator/raft/configuration") + r.setQueryOptions(q) + _, resp, err := requireOK(op.c.doRequest(r)) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + var out RaftConfiguration + if err := decodeBody(resp, &out); err != nil { + return nil, err + } + return &out, nil +} + +// RaftRemovePeerByAddress is used to kick a stale peer (one that it in the Raft +// quorum but no longer known to Serf or the catalog) by address in the form of +// "IP:port". +func (op *Operator) RaftRemovePeerByAddress(address string, q *WriteOptions) error { + r := op.c.newRequest("DELETE", "/v1/operator/raft/peer") + r.setWriteOptions(q) + + // TODO (slackpad) Currently we made address a query parameter. Once + // IDs are in place this will be DELETE /v1/operator/raft/peer/. + r.params.Set("address", string(address)) + + _, resp, err := requireOK(op.c.doRequest(r)) + if err != nil { + return err + } + + resp.Body.Close() + return nil +} + +// KeyringInstall is used to install a new gossip encryption key into the cluster +func (op *Operator) KeyringInstall(key string, q *WriteOptions) error { + r := op.c.newRequest("POST", "/v1/operator/keyring") + r.setWriteOptions(q) + r.obj = keyringRequest{ + Key: key, + } + _, resp, err := requireOK(op.c.doRequest(r)) + if err != nil { + return err + } + resp.Body.Close() + return nil +} + +// KeyringList is used to list the gossip keys installed in the cluster +func (op *Operator) KeyringList(q *QueryOptions) ([]*KeyringResponse, error) { + r := op.c.newRequest("GET", "/v1/operator/keyring") + r.setQueryOptions(q) + _, resp, err := requireOK(op.c.doRequest(r)) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + var out []*KeyringResponse + if err := decodeBody(resp, &out); err != nil { + return nil, err + } + return out, nil +} + +// KeyringRemove is used to remove a gossip encryption key from the cluster +func (op *Operator) KeyringRemove(key string, q *WriteOptions) error { + r := op.c.newRequest("DELETE", "/v1/operator/keyring") + r.setWriteOptions(q) + r.obj = keyringRequest{ + Key: key, + } + _, resp, err := requireOK(op.c.doRequest(r)) + if err != nil { + return err + } + resp.Body.Close() + return nil +} + +// KeyringUse is used to change the active gossip encryption key +func (op *Operator) KeyringUse(key string, q *WriteOptions) error { + r := op.c.newRequest("PUT", "/v1/operator/keyring") + r.setWriteOptions(q) + r.obj = keyringRequest{ + Key: key, + } + _, resp, err := requireOK(op.c.doRequest(r)) + if err != nil { + return err + } + resp.Body.Close() + return nil +} diff --git a/vendor/github.com/hashicorp/consul/api/prepared_query.go b/vendor/github.com/hashicorp/consul/api/prepared_query.go index 63e741e050d5..ff210de3f009 100644 --- a/vendor/github.com/hashicorp/consul/api/prepared_query.go +++ b/vendor/github.com/hashicorp/consul/api/prepared_query.go @@ -43,6 +43,11 @@ type ServiceQuery struct { // this list it must be present. If the tag is preceded with "!" then // it is disallowed. Tags []string + + // NodeMeta is a map of required node metadata fields. If a key/value + // pair is in this map it must be present on the node in order for the + // service entry to be returned. + NodeMeta map[string]string } // QueryTemplate carries the arguments for creating a templated query. @@ -167,19 +172,18 @@ func (c *PreparedQuery) Get(queryID string, q *QueryOptions) ([]*PreparedQueryDe } // Delete is used to delete a specific prepared query. -func (c *PreparedQuery) Delete(queryID string, q *QueryOptions) (*QueryMeta, error) { +func (c *PreparedQuery) Delete(queryID string, q *WriteOptions) (*WriteMeta, error) { r := c.c.newRequest("DELETE", "/v1/query/"+queryID) - r.setQueryOptions(q) + r.setWriteOptions(q) rtt, resp, err := requireOK(c.c.doRequest(r)) if err != nil { return nil, err } defer resp.Body.Close() - qm := &QueryMeta{} - parseQueryMeta(resp, qm) - qm.RequestTime = rtt - return qm, nil + wm := &WriteMeta{} + wm.RequestTime = rtt + return wm, nil } // Execute is used to execute a specific prepared query. You can execute using diff --git a/vendor/github.com/hashicorp/consul/api/snapshot.go b/vendor/github.com/hashicorp/consul/api/snapshot.go new file mode 100644 index 000000000000..e902377dd5ca --- /dev/null +++ b/vendor/github.com/hashicorp/consul/api/snapshot.go @@ -0,0 +1,47 @@ +package api + +import ( + "io" +) + +// Snapshot can be used to query the /v1/snapshot endpoint to take snapshots of +// Consul's internal state and restore snapshots for disaster recovery. +type Snapshot struct { + c *Client +} + +// Snapshot returns a handle that exposes the snapshot endpoints. +func (c *Client) Snapshot() *Snapshot { + return &Snapshot{c} +} + +// Save requests a new snapshot and provides an io.ReadCloser with the snapshot +// data to save. If this doesn't return an error, then it's the responsibility +// of the caller to close it. Only a subset of the QueryOptions are supported: +// Datacenter, AllowStale, and Token. +func (s *Snapshot) Save(q *QueryOptions) (io.ReadCloser, *QueryMeta, error) { + r := s.c.newRequest("GET", "/v1/snapshot") + r.setQueryOptions(q) + + rtt, resp, err := requireOK(s.c.doRequest(r)) + if err != nil { + return nil, nil, err + } + + qm := &QueryMeta{} + parseQueryMeta(resp, qm) + qm.RequestTime = rtt + return resp.Body, qm, nil +} + +// Restore streams in an existing snapshot and attempts to restore it. +func (s *Snapshot) Restore(q *WriteOptions, in io.Reader) error { + r := s.c.newRequest("PUT", "/v1/snapshot") + r.body = in + r.setWriteOptions(q) + _, _, err := requireOK(s.c.doRequest(r)) + if err != nil { + return err + } + return nil +} diff --git a/vendor/vendor.json b/vendor/vendor.json index 6fbd5ef67b10..b3587ece60af 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -1634,11 +1634,11 @@ "revisionTime": "2016-11-07T20:49:10Z" }, { - "checksumSHA1": "ZY6NCrR80zUmtOtPtKffbmFxRWw=", + "checksumSHA1": "ygEjA1d52B1RDmZu8+1WTwkrYDQ=", "comment": "v0.6.3-28-g3215b87", "path": "github.com/hashicorp/consul/api", - "revision": "6e061b2d580d80347b7c5c4dfc8730de7403a145", - "revisionTime": "2016-07-03T02:45:54Z" + "revision": "48d7b069ad443a48ffa93364048ff8909b5d1fa2", + "revisionTime": "2017-02-07T15:38:46Z" }, { "checksumSHA1": "cdOCt0Yb+hdErz8NAQqayxPmRsY=", diff --git a/website/source/docs/providers/consul/d/agent_self.html.markdown b/website/source/docs/providers/consul/d/agent_self.html.markdown new file mode 100644 index 000000000000..c6323cc7076f --- /dev/null +++ b/website/source/docs/providers/consul/d/agent_self.html.markdown @@ -0,0 +1,163 @@ +--- +layout: "consul" +page_title: "Consul: consul_agent_self" +sidebar_current: "docs-consul-data-source-agent-self" +description: |- + Provides the configuration information of the local Consul agent. +--- + +# consul\_agent_\_self + +The `consul_agent_self` data source returns +[configuration and status data](https://www.consul.io/docs/agent/http/agent.html#agent_self) +from the agent specified in the `provider`. + +## Example Usage + +``` +data "consul_agent_self" "read-dc1-agent" { + # query_options { + # # Optional parameter: implicitly uses the current datacenter of the agent + # datacenter = "dc1" + # } +} + +# Set the description to a whitespace delimited list of the services +resource "example_resource" "app" { + description = "Consul datacenter ${data.consul_agent_self.read-dc1-agent.datacenter}" + ... +} +``` + +## Attributes Reference + +The following attributes are exported: + +* [`acl_datacenter`](https://www.consul.io/docs/agent/options.html#acl_datacenter) +* [`acl_default_policy`](https://www.consul.io/docs/agent/options.html#acl_default_policy) +* `acl_disabled_ttl` +* [`acl_down_policy`](https://www.consul.io/docs/agent/options.html#acl_down_policy) +* [`acl_enforce_0_8_semantics`](https://www.consul.io/docs/agent/options.html#acl_enforce_version_8) +* [`acl_ttl`](https://www.consul.io/docs/agent/options.html#acl_ttl) +* [`addresses`](https://www.consul.io/docs/agent/options.html#addresses) +* [`advertise_addr`](https://www.consul.io/docs/agent/options.html#_advertise) +* [`advertise_addr_wan`](https://www.consul.io/docs/agent/options.html#_advertise-wan) +* [`advertise_addrs`](https://www.consul.io/docs/agent/options.html#advertise_addrs) +* [`atlas_join`](https://www.consul.io/docs/agent/options.html#_atlas_join) +* [`bind_addr`](https://www.consul.io/docs/agent/options.html#_bind) +* [`bootstrap_expect`](https://www.consul.io/docs/agent/options.html#_bootstrap_expect) +* [`bootstrap_mode`](https://www.consul.io/docs/agent/options.html#_bootstrap) +* `check_deregister_interval_min` +* `check_reap_interval` +* [`check_update_interval`](https://www.consul.io/docs/agent/options.html#check_update_interval) +* [`client_addr`](https://www.consul.io/docs/agent/options.html#_client) +* `dns` - A map of DNS configuration attributes. See below for details on the + contents of the `dns` attribute. +* [`dns_recursors`](https://www.consul.io/docs/agent/options.html#recursors) - A + list of all DNS recursors. +* [`data_dir`](https://www.consul.io/docs/agent/options.html#_data_dir) +* [`datacenter`](https://www.consul.io/docs/agent/options.html#_datacenter) +* [`dev_mode`](https://www.consul.io/docs/agent/options.html#_dev) +* [`domain`](https://www.consul.io/docs/agent/options.html#_domain) +* [`enable_anonymous_signature`](https://www.consul.io/docs/agent/options.html#disable_anonymous_signature) +* `enable_coordinates` +* [`enable_debug`](https://www.consul.io/docs/agent/options.html#enable_debug) +* [`enable_remote_exec`](https://www.consul.io/docs/agent/options.html#disable_remote_exec) +* [`enable_syslog`](https://www.consul.io/docs/agent/options.html#_syslog) +* [`enable_ui`](https://www.consul.io/docs/agent/options.html#_ui) +* [`enable_update_check`](https://www.consul.io/docs/agent/options.html#disable_update_check) +* [`id`](https://www.consul.io/docs/agent/options.html#_node_id) +* [`leave_on_int`](https://www.consul.io/docs/agent/options.html#skip_leave_on_interrupt) +* [`leave_on_term`](https://www.consul.io/docs/agent/options.html#leave_on_terminate) +* [`log_level`](https://www.consul.io/docs/agent/options.html#_log_level) +* [`name`](https://www.consul.io/docs/agent/options.html#_node) +* [`performance`](https://www.consul.io/docs/agent/options.html#performance) +* [`pid_file`](https://www.consul.io/docs/agent/options.html#_pid_file) +* [`ports`](https://www.consul.io/docs/agent/options.html#ports) +* [`protocol_version`](https://www.consul.io/docs/agent/options.html#_protocol) +* [`reconnect_timeout_lan`](https://www.consul.io/docs/agent/options.html#reconnect_timeout) +* [`reconnect_timeout_wan`](https://www.consul.io/docs/agent/options.html#reconnect_timeout_wan) +* [`rejoin_after_leave`](https://www.consul.io/docs/agent/options.html#_rejoin) +* [`retry_join`](https://www.consul.io/docs/agent/options.html#retry_join) +* [`retry_join_ec2`](https://www.consul.io/docs/agent/options.html#retry_join_ec2) - + A map of EC2 retry attributes. See below for details on the available + information. +* [`retry_join_gce`](https://www.consul.io/docs/agent/options.html#retry_join_gce) - + A map of GCE retry attributes. See below for details on the available + information. +* [`retry_join_wan`](https://www.consul.io/docs/agent/options.html#_retry_join_wan) +* [`retry_max_attempts`](https://www.consul.io/docs/agent/options.html#_retry_max) +* [`retry_max_attempts_wan`](https://www.consul.io/docs/agent/options.html#_retry_max_wan) +* [`serf_lan_bind_addr`](https://www.consul.io/docs/agent/options.html#_serf_lan_bind) +* [`serf_wan_bind_addr`](https://www.consul.io/docs/agent/options.html#_serf_wan_bind) +* [`server_mode`](https://www.consul.io/docs/agent/options.html#_server) +* [`server_name`](https://www.consul.io/docs/agent/options.html#server_name) +* [`session_ttl_min`](https://www.consul.io/docs/agent/options.html#session_ttl_min) +* [`start_join`](https://www.consul.io/docs/agent/options.html#start_join) +* [`start_join_wan`](https://www.consul.io/docs/agent/options.html#start_join_wan) +* [`syslog_facility`](https://www.consul.io/docs/agent/options.html#syslog_facility) +* [`tls_ca_file`](https://www.consul.io/docs/agent/options.html#ca_file) +* [`tls_cert_file`](https://www.consul.io/docs/agent/options.html#cert_file) +* [`tls_key_file`](https://www.consul.io/docs/agent/options.html#key_file) +* [`tls_min_version`](https://www.consul.io/docs/agent/options.html#tls_min_version) +* [`tls_verify_incoming`](https://www.consul.io/docs/agent/options.html#verify_incoming) +* [`tls_verify_outgoing`](https://www.consul.io/docs/agent/options.html#verify_outgoing) +* [`tls_verify_server_hostname`](https://www.consul.io/docs/agent/options.html#verify_server_hostname) +* [`tagged_addresses`](https://www.consul.io/docs/agent/options.html#translate_wan_addrs) +* [`telemetry`](https://www.consul.io/docs/agent/options.html#telemetry) - A map + of telemetry configuration. +* [`translate_wan_addrs`](https://www.consul.io/docs/agent/options.html#translate_wan_addrs) +* [`ui_dir`](https://www.consul.io/docs/agent/options.html#ui_dir) +* [`unix_sockets`](https://www.consul.io/docs/agent/options.html#unix_sockets) +* `version` - The version of the Consul agent. +* `version_prerelease` +* `version_revision` + +### DNS Attributes + +* [`allow_stale`](https://www.consul.io/docs/agent/options.html#allow_stale) +* [`enable_compression`](https://www.consul.io/docs/agent/options.html#disable_compression) +* [`enable_truncate`](https://www.consul.io/docs/agent/options.html#enable_truncate) +* [`max_stale`](https://www.consul.io/docs/agent/options.html#max_stale) +* [`node_ttl`](https://www.consul.io/docs/agent/options.html#node_ttl) +* [`only_passing`](https://www.consul.io/docs/agent/options.html#only_passing) +* [`recursor_timeout`](https://www.consul.io/docs/agent/options.html#recursor_timeout) +* [`service_ttl`](https://www.consul.io/docs/agent/options.html#service_ttl) +* [`udp_answer_limit`](https://www.consul.io/docs/agent/options.html#udp_answer_limit) + +### Retry Join EC2 Attributes + +* [`access_key_id`](https://www.consul.io/docs/agent/options.html#access_key_id) +* [`region`](https://www.consul.io/docs/agent/options.html#region) +* [`secret_access_key`](https://www.consul.io/docs/agent/options.html#secret_access_key) +* [`tag_key`](https://www.consul.io/docs/agent/options.html#tag_key) +* [`tag_value`](https://www.consul.io/docs/agent/options.html#tag_value) + +### Retry Join GCE Attributes + +* [`credentials_file`](https://www.consul.io/docs/agent/options.html#credentials_file) +* [`project_name`](https://www.consul.io/docs/agent/options.html#project_name) +* [`tag_value`](https://www.consul.io/docs/agent/options.html#tag_value) +* [`zone_pattern`](https://www.consul.io/docs/agent/options.html#zone_pattern) + +### Telemetry Attributes + +* [`circonus_api_app`](https://www.consul.io/docs/agent/options.html#telemetry-circonus_api_app) +* [`circonus_api_token`](https://www.consul.io/docs/agent/options.html#telemetry-circonus_api_token) +* [`circonus_api_url`](https://www.consul.io/docs/agent/options.html#telemetry-circonus_api_url) +* [`circonus_broker_id`](https://www.consul.io/docs/agent/options.html#telemetry-circonus_broker_id) +* [`circonus_check_id`](https://www.consul.io/docs/agent/options.html#telemetry-circonus_check_id) +* [`circonus_check_tags`](https://www.consul.io/docs/agent/options.html#telemetry-circonus_check_tags) +* [`circonus_display_name`](https://www.consul.io/docs/agent/options.html#telemetry-circonus_check_display_name) +* [`circonus_force_metric_activation`](https://www.consul.io/docs/agent/options.html#telemetry-circonus_check_force_metric_activation) +* [`circonus_instance_id`](https://www.consul.io/docs/agent/options.html#telemetry-circonus_check_instance_id) +* [`circonus_search_tag`](https://www.consul.io/docs/agent/options.html#telemetry-circonus_check_search_tag) +* [`circonus_select_tag`](https://www.consul.io/docs/agent/options.html#telemetry-circonus_broker_select_tag) +* [`circonus_submission_interval`](https://www.consul.io/docs/agent/options.html#telemetry-circonus_submission_interval) +* [`circonus_submission_url`](https://www.consul.io/docs/agent/options.html#telemetry-circonus_submission_url) +* [`dogstatsd_addr`](https://www.consul.io/docs/agent/options.html#telemetry-dogstatsd_addr) +* [`dogstatsd_tags`](https://www.consul.io/docs/agent/options.html#telemetry-dogstatsd_tags) +* [`enable_hostname`](https://www.consul.io/docs/agent/options.html#telemetry-disable_hostname) +* [`statsd_addr`](https://www.consul.io/docs/agent/options.html#telemetry-statsd_address) +* [`statsite_addr`](https://www.consul.io/docs/agent/options.html#telemetry-statsite_address) +* [`statsite_prefix`](https://www.consul.io/docs/agent/options.html#telemetry-statsite_prefix) diff --git a/website/source/docs/providers/consul/d/nodes.html.markdown b/website/source/docs/providers/consul/d/nodes.html.markdown new file mode 100644 index 000000000000..8c1c24005796 --- /dev/null +++ b/website/source/docs/providers/consul/d/nodes.html.markdown @@ -0,0 +1,83 @@ +--- +layout: "consul" +page_title: "Consul: consul_catalog_nodes" +sidebar_current: "docs-consul-data-source-catalog-nodes" +description: |- + Provides a list of nodes in a given Consul datacenter. +--- + +# consul\_catalog\_nodes + +The `consul_catalog_nodes` data source returns a list of Consul nodes that have +been registered with the Consul cluster in a given datacenter. By specifying a +different datacenter in the `query_options` it is possible to retrieve a list of +nodes from a different WAN-attached Consul datacenter. + +## Example Usage + +``` +data "consul_catalog_nodes" "read-dc1-nodes" { + # query_options { + # # Optional parameter: implicitly uses the current datacenter of the agent + # datacenter = "dc1" + # } +} + +# Set the description to a whitespace delimited list of the node names +resource "example_resource" "app" { + description = "${join(" ", formatlist("%s", data.consul_catalog_nodes.node_names))}" + ... +} +``` + +## Argument Reference + +The following arguments are supported: + +* `datacenter` - (Optional) The Consul datacenter to query. Defaults to the + same value found in `query_options` parameter specified below, or if that is + empty, the `datacenter` value found in the Consul agent that this provider is + configured to talk to. + +* `query_options` - (Optional) See below. + +The `query_options` block supports the following: + +* `allow_stale` - (Optional) When `true`, the default, allow responses from + Consul servers that are followers. + +* `require_consistent` - (Optional) When `true` force the client to perform a + read on at least quorum servers and verify the result is the same. Defaults + to `false`. + +* `token` - (Optional) Specify the Consul ACL token to use when performing the + request. This defaults to the same API token configured by the `consul` + provider but may be overriden if necessary. + +* `wait_index` - (Optional) Index number used to enable blocking quereis. + +* `wait_time` - (Optional) Max time the client should wait for a blocking query + to return. + +## Attributes Reference + +The following attributes are exported: + +* `datacenter` - The datacenter the keys are being read from to. +* `node_ids` - A list of the Consul node IDs. +* `node_names` - A list of the Consul node names. +* `nodes` - A list of nodes and details about each Consul agent. The list of + per-node attributes is detailed below. + +The following is a list of the per-node attributes contained within the `nodes` +map: + +* `id` - The Node ID of the Consul agent. +* [`meta`](https://www.consul.io/docs/agent/http/catalog.html#Meta) - Node meta + data tag information, if any. +* [`name`](https://www.consul.io/docs/agent/http/catalog.html#Node) - The name + of the Consul node. +* [`address`](https://www.consul.io/docs/agent/http/catalog.html#Address) - The + IP address the node is advertising to the Consul cluster. +* [`tagged_addresses`](https://www.consul.io/docs/agent/http/catalog.html#TaggedAddresses) - + List of explicit LAN and WAN IP addresses for the agent. diff --git a/website/source/docs/providers/consul/d/service.html.markdown b/website/source/docs/providers/consul/d/service.html.markdown new file mode 100644 index 000000000000..d0cf8a47630c --- /dev/null +++ b/website/source/docs/providers/consul/d/service.html.markdown @@ -0,0 +1,112 @@ +--- +layout: "consul" +page_title: "Consul: consul_catalog_service" +sidebar_current: "docs-consul-data-source-catalog-service" +description: |- + Provides details about a specific Consul service +--- + +# consul\_catalog\_service + +`consul_catalog_service` provides details about a specific Consul service in a +given datacenter. The results include a list of nodes advertising the specified +service, the node's IP address, port number, node ID, etc. By specifying a +different datacenter in the `query_options` it is possible to retrieve a list of +services from a different WAN-attached Consul datacenter. + +This data source is different from the `consul_catalog_services` (plural) data +source, which provides a summary of the current Consul services. + +## Example Usage + +``` +data "consul_catalog_service" "read-consul-dc1" { + # query_options { + # # Optional parameter: implicitly uses the current datacenter of the agent + # datacenter = "dc1" + # } + + name = "consul" +} + +# Set the description to a whitespace delimited list of the node names +resource "example_resource" "app" { + description = "${join(" ", data.consul_catalog_service.nodes)}" + ... +} +``` + +## Argument Reference + +The following arguments are supported: + +* `datacenter` - (Optional) The Consul datacenter to query. Defaults to the + same value found in `query_options` parameter specified below, or if that is + empty, the `datacenter` value found in the Consul agent that this provider is + configured to talk to. + +* `name` - (Required) The service name to select. + +* `query_options` - (Optional) See below. + +* `tag` - (Optional) A single tag that can be used to filter the list of nodes + to return based on a single matching tag.. + +The `query_options` block supports the following: + +* `allow_stale` - (Optional) When `true`, the default, allow responses from + Consul servers that are followers. + +* `require_consistent` - (Optional) When `true` force the client to perform a + read on at least quorum servers and verify the result is the same. Defaults + to `false`. + +* `token` - (Optional) Specify the Consul ACL token to use when performing the + request. This defaults to the same API token configured by the `consul` + provider but may be overriden if necessary. + +* `wait_index` - (Optional) Index number used to enable blocking quereis. + +* `wait_time` - (Optional) Max time the client should wait for a blocking query + to return. + +## Attributes Reference + +The following attributes are exported: + +* `datacenter` - The datacenter the keys are being read from to. +* `name` - The name of the service +* `tag` - The name of the tag used to filter the list of nodes in `service`. +* `service` - A list of nodes and details about each endpoint advertising a + service. Each element in the list is a map of attributes that correspond to + each individual node. The list of per-node attributes is detailed below. + +The following is a list of the per-node `service` attributes: + +* [`create_index`](https://www.consul.io/docs/agent/http/catalog.html#CreateIndex) - + The index entry at which point this entry was added to the catalog. +* [`modify_index`](https://www.consul.io/docs/agent/http/catalog.html#ModifyIndex) - + The index entry at which point this entry was modified in the catalog. +* [`node_address`](https://www.consul.io/docs/agent/http/catalog.html#Address) - + The address of the Consul node advertising the service. +* `node_id` - The Node ID of the Consul agent advertising the service. +* [`node_meta`](https://www.consul.io/docs/agent/http/catalog.html#Meta) - Node + meta data tag information, if any. +* [`node_name`](https://www.consul.io/docs/agent/http/catalog.html#Node) - The + name of the Consul node. +* [`address`](https://www.consul.io/docs/agent/http/catalog.html#ServiceAddress) - + The IP address of the service. If the `ServiceAddress` in the Consul catalog + is empty, this value is automatically populated with the `node_address` (the + `Address` in the Consul Catalog). +* [`enable_tag_override`](https://www.consul.io/docs/agent/http/catalog.html#ServiceEnableTagOverride) - + Whether service tags can be overridden on this service. +* [`id`](https://www.consul.io/docs/agent/http/catalog.html#ServiceID) - A + unique service instance identifier. +* [`name`](https://www.consul.io/docs/agent/http/catalog.html#ServiceName) - The + name of the service. +* [`port`](https://www.consul.io/docs/agent/http/catalog.html#ServicePort) - + Port number of the service. +* [`tagged_addresses`](https://www.consul.io/docs/agent/http/catalog.html#TaggedAddresses) - + List of explicit LAN and WAN IP addresses for the agent. +* [`tags`](https://www.consul.io/docs/agent/http/catalog.html#ServiceTags) - + List of tags for the service. diff --git a/website/source/docs/providers/consul/d/services.html.markdown b/website/source/docs/providers/consul/d/services.html.markdown new file mode 100644 index 000000000000..4bda1d3190ef --- /dev/null +++ b/website/source/docs/providers/consul/d/services.html.markdown @@ -0,0 +1,78 @@ +--- +layout: "consul" +page_title: "Consul: consul_catalog_services" +sidebar_current: "docs-consul-data-source-catalog-services" +description: |- + Provides a list of services in a given Consul datacenter. +--- + +# consul\_catalog\_services + +The `consul_catalog_services` data source returns a list of Consul services that +have been registered with the Consul cluster in a given datacenter. By +specifying a different datacenter in the `query_options` it is possible to +retrieve a list of services from a different WAN-attached Consul datacenter. + +This data source is different from the `consul_catalog_service` (singular) data +source, which provides a detailed response about a specific Consul service. + +## Example Usage + +``` +data "consul_catalog_services" "read-dc1" { + # query_options { + # # Optional parameter: implicitly uses the current datacenter of the agent + # datacenter = "dc1" + # } +} + +# Set the description to a whitespace delimited list of the services +resource "example_resource" "app" { + description = "${join(" ", data.consul_catalog_services.names)}" + ... +} +``` + +## Argument Reference + +The following arguments are supported: + +* `datacenter` - (Optional) The Consul datacenter to query. Defaults to the + same value found in `query_options` parameter specified below, or if that is + empty, the `datacenter` value found in the Consul agent that this provider is + configured to talk to. + +* `query_options` - (Optional) See below. + +The `query_options` block supports the following: + +* `allow_stale` - (Optional) When `true`, the default, allow responses from + Consul servers that are followers. + +* `require_consistent` - (Optional) When `true` force the client to perform a + read on at least quorum servers and verify the result is the same. Defaults + to `false`. + +* `token` - (Optional) Specify the Consul ACL token to use when performing the + request. This defaults to the same API token configured by the `consul` + provider but may be overriden if necessary. + +* `wait_index` - (Optional) Index number used to enable blocking quereis. + +* `wait_time` - (Optional) Max time the client should wait for a blocking query + to return. + +## Attributes Reference + +The following attributes are exported: + +* `datacenter` - The datacenter the keys are being read from to. +* `names` - A list of the Consul services found. This will always contain the + list of services found. +* `services.` - For each name given, the corresponding attribute is a + Terraform map of services and their tags. The value is an alphanumerically + sorted, whitespace delimited set of tags associated with the service. +* `tags` - A map of the tags found for each service. If more than one service + shares the same tag, unique service names will be joined by whitespace (this + is the inverse of `services` and can be used to lookup the services that match + a single tag). diff --git a/website/source/layouts/consul.erb b/website/source/layouts/consul.erb index df831b0587b9..195b0fe421de 100644 --- a/website/source/layouts/consul.erb +++ b/website/source/layouts/consul.erb @@ -13,10 +13,22 @@ > Data Sources + > + consul_agent_self + + > + consul_catalog_nodes + + > + consul_catalog_service + + > + consul_catalog_services + + > + consul_keys + + >