mirror of
https://github.com/prometheus/prometheus.git
synced 2025-03-05 20:59:13 -08:00
Discovery consul service meta (#4280)
* Upgrade Consul client * Add ServiceMeta to the labels in ConsulSD Signed-off-by: Romain Baugue <romain.baugue@elwinar.com>
This commit is contained in:
parent
d0f11a3cc6
commit
b41be4ef52
|
@ -43,6 +43,8 @@ const (
|
||||||
nodeLabel = model.MetaLabelPrefix + "consul_node"
|
nodeLabel = model.MetaLabelPrefix + "consul_node"
|
||||||
// metaDataLabel is the prefix for the labels mapping to a target's metadata.
|
// metaDataLabel is the prefix for the labels mapping to a target's metadata.
|
||||||
metaDataLabel = model.MetaLabelPrefix + "consul_metadata_"
|
metaDataLabel = model.MetaLabelPrefix + "consul_metadata_"
|
||||||
|
// serviceMetaDataLabel is the prefix for the labels mapping to a target's service metadata.
|
||||||
|
serviceMetaDataLabel = model.MetaLabelPrefix + "consul_service_metadata_"
|
||||||
// tagsLabel is the name of the label containing the tags assigned to the target.
|
// tagsLabel is the name of the label containing the tags assigned to the target.
|
||||||
tagsLabel = model.MetaLabelPrefix + "consul_tags"
|
tagsLabel = model.MetaLabelPrefix + "consul_tags"
|
||||||
// serviceLabel is the name of the label containing the service name.
|
// serviceLabel is the name of the label containing the service name.
|
||||||
|
@ -505,6 +507,12 @@ func (srv *consulService) watch(ctx context.Context, ch chan<- []*targetgroup.Gr
|
||||||
labels[metaDataLabel+model.LabelName(name)] = model.LabelValue(v)
|
labels[metaDataLabel+model.LabelName(name)] = model.LabelValue(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add all key/value pairs from the service's metadata as their own labels
|
||||||
|
for k, v := range node.ServiceMeta {
|
||||||
|
name := strutil.SanitizeLabelName(k)
|
||||||
|
labels[serviceMetaDataLabel+model.LabelName(name)] = model.LabelValue(v)
|
||||||
|
}
|
||||||
|
|
||||||
tgroup.Targets = append(tgroup.Targets, labels)
|
tgroup.Targets = append(tgroup.Targets, labels)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -90,6 +90,7 @@ const (
|
||||||
"NodeMeta": {"rack_name": "2304"},
|
"NodeMeta": {"rack_name": "2304"},
|
||||||
"ServiceID": "test",
|
"ServiceID": "test",
|
||||||
"ServiceName": "test",
|
"ServiceName": "test",
|
||||||
|
"ServiceMeta": {"version":"1.0.0","environment":"stagging"},
|
||||||
"ServiceTags": ["tag1"],
|
"ServiceTags": ["tag1"],
|
||||||
"ServicePort": 3341,
|
"ServicePort": 3341,
|
||||||
"CreateIndex": 1,
|
"CreateIndex": 1,
|
||||||
|
|
|
@ -301,6 +301,7 @@ The following meta labels are available on targets during [relabeling](#relabel_
|
||||||
* `__meta_consul_node`: the node name defined for the target
|
* `__meta_consul_node`: the node name defined for the target
|
||||||
* `__meta_consul_service_address`: the service address of the target
|
* `__meta_consul_service_address`: the service address of the target
|
||||||
* `__meta_consul_service_id`: the service ID of the target
|
* `__meta_consul_service_id`: the service ID of the target
|
||||||
|
* `__meta_consul_service_metadata_<key>`: each service metadata key value of the target
|
||||||
* `__meta_consul_service_port`: the service port of the target
|
* `__meta_consul_service_port`: the service port of the target
|
||||||
* `__meta_consul_service`: the name of the service the target belongs to
|
* `__meta_consul_service`: the name of the service the target belongs to
|
||||||
* `__meta_consul_tags`: the list of tags of the target joined by the tag separator
|
* `__meta_consul_tags`: the list of tags of the target joined by the tag separator
|
||||||
|
|
55
vendor/github.com/hashicorp/consul/api/acl.go
generated
vendored
55
vendor/github.com/hashicorp/consul/api/acl.go
generated
vendored
|
@ -1,7 +1,11 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// ACLCLientType is the client type token
|
// ACLClientType is the client type token
|
||||||
ACLClientType = "client"
|
ACLClientType = "client"
|
||||||
|
|
||||||
// ACLManagementType is the management type token
|
// ACLManagementType is the management type token
|
||||||
|
@ -18,6 +22,16 @@ type ACLEntry struct {
|
||||||
Rules string
|
Rules string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ACLReplicationStatus is used to represent the status of ACL replication.
|
||||||
|
type ACLReplicationStatus struct {
|
||||||
|
Enabled bool
|
||||||
|
Running bool
|
||||||
|
SourceDatacenter string
|
||||||
|
ReplicatedIndex uint64
|
||||||
|
LastSuccess time.Time
|
||||||
|
LastError time.Time
|
||||||
|
}
|
||||||
|
|
||||||
// ACL can be used to query the ACL endpoints
|
// ACL can be used to query the ACL endpoints
|
||||||
type ACL struct {
|
type ACL struct {
|
||||||
c *Client
|
c *Client
|
||||||
|
@ -28,6 +42,24 @@ func (c *Client) ACL() *ACL {
|
||||||
return &ACL{c}
|
return &ACL{c}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bootstrap is used to perform a one-time ACL bootstrap operation on a cluster
|
||||||
|
// to get the first management token.
|
||||||
|
func (a *ACL) Bootstrap() (string, *WriteMeta, error) {
|
||||||
|
r := a.c.newRequest("PUT", "/v1/acl/bootstrap")
|
||||||
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
wm := &WriteMeta{RequestTime: rtt}
|
||||||
|
var out struct{ ID string }
|
||||||
|
if err := decodeBody(resp, &out); err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
return out.ID, wm, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Create is used to generate a new token with the given parameters
|
// Create is used to generate a new token with the given parameters
|
||||||
func (a *ACL) Create(acl *ACLEntry, q *WriteOptions) (string, *WriteMeta, error) {
|
func (a *ACL) Create(acl *ACLEntry, q *WriteOptions) (string, *WriteMeta, error) {
|
||||||
r := a.c.newRequest("PUT", "/v1/acl/create")
|
r := a.c.newRequest("PUT", "/v1/acl/create")
|
||||||
|
@ -138,3 +170,24 @@ func (a *ACL) List(q *QueryOptions) ([]*ACLEntry, *QueryMeta, error) {
|
||||||
}
|
}
|
||||||
return entries, qm, nil
|
return entries, qm, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Replication returns the status of the ACL replication process in the datacenter
|
||||||
|
func (a *ACL) Replication(q *QueryOptions) (*ACLReplicationStatus, *QueryMeta, error) {
|
||||||
|
r := a.c.newRequest("GET", "/v1/acl/replication")
|
||||||
|
r.setQueryOptions(q)
|
||||||
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
qm := &QueryMeta{}
|
||||||
|
parseQueryMeta(resp, qm)
|
||||||
|
qm.RequestTime = rtt
|
||||||
|
|
||||||
|
var entries *ACLReplicationStatus
|
||||||
|
if err := decodeBody(resp, &entries); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return entries, qm, nil
|
||||||
|
}
|
||||||
|
|
164
vendor/github.com/hashicorp/consul/api/agent.go
generated
vendored
164
vendor/github.com/hashicorp/consul/api/agent.go
generated
vendored
|
@ -15,6 +15,7 @@ type AgentCheck struct {
|
||||||
Output string
|
Output string
|
||||||
ServiceID string
|
ServiceID string
|
||||||
ServiceName string
|
ServiceName string
|
||||||
|
Definition HealthCheckDefinition
|
||||||
}
|
}
|
||||||
|
|
||||||
// AgentService represents a service known to the agent
|
// AgentService represents a service known to the agent
|
||||||
|
@ -22,9 +23,12 @@ type AgentService struct {
|
||||||
ID string
|
ID string
|
||||||
Service string
|
Service string
|
||||||
Tags []string
|
Tags []string
|
||||||
|
Meta map[string]string
|
||||||
Port int
|
Port int
|
||||||
Address string
|
Address string
|
||||||
EnableTagOverride bool
|
EnableTagOverride bool
|
||||||
|
CreateIndex uint64
|
||||||
|
ModifyIndex uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
// AgentMember represents a cluster member known to the agent
|
// AgentMember represents a cluster member known to the agent
|
||||||
|
@ -42,6 +46,19 @@ type AgentMember struct {
|
||||||
DelegateCur uint8
|
DelegateCur uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AllSegments is used to select for all segments in MembersOpts.
|
||||||
|
const AllSegments = "_all"
|
||||||
|
|
||||||
|
// MembersOpts is used for querying member information.
|
||||||
|
type MembersOpts struct {
|
||||||
|
// WAN is whether to show members from the WAN.
|
||||||
|
WAN bool
|
||||||
|
|
||||||
|
// Segment is the LAN segment to show members for. Setting this to the
|
||||||
|
// AllSegments value above will show members in all segments.
|
||||||
|
Segment string
|
||||||
|
}
|
||||||
|
|
||||||
// AgentServiceRegistration is used to register a new service
|
// AgentServiceRegistration is used to register a new service
|
||||||
type AgentServiceRegistration struct {
|
type AgentServiceRegistration struct {
|
||||||
ID string `json:",omitempty"`
|
ID string `json:",omitempty"`
|
||||||
|
@ -50,6 +67,7 @@ type AgentServiceRegistration struct {
|
||||||
Port int `json:",omitempty"`
|
Port int `json:",omitempty"`
|
||||||
Address string `json:",omitempty"`
|
Address string `json:",omitempty"`
|
||||||
EnableTagOverride bool `json:",omitempty"`
|
EnableTagOverride bool `json:",omitempty"`
|
||||||
|
Meta map[string]string `json:",omitempty"`
|
||||||
Check *AgentServiceCheck
|
Check *AgentServiceCheck
|
||||||
Checks AgentServiceChecks
|
Checks AgentServiceChecks
|
||||||
}
|
}
|
||||||
|
@ -65,17 +83,23 @@ type AgentCheckRegistration struct {
|
||||||
|
|
||||||
// AgentServiceCheck is used to define a node or service level check
|
// AgentServiceCheck is used to define a node or service level check
|
||||||
type AgentServiceCheck struct {
|
type AgentServiceCheck struct {
|
||||||
Script string `json:",omitempty"`
|
CheckID string `json:",omitempty"`
|
||||||
|
Name string `json:",omitempty"`
|
||||||
|
Args []string `json:"ScriptArgs,omitempty"`
|
||||||
DockerContainerID string `json:",omitempty"`
|
DockerContainerID string `json:",omitempty"`
|
||||||
Shell string `json:",omitempty"` // Only supported for Docker.
|
Shell string `json:",omitempty"` // Only supported for Docker.
|
||||||
Interval string `json:",omitempty"`
|
Interval string `json:",omitempty"`
|
||||||
Timeout string `json:",omitempty"`
|
Timeout string `json:",omitempty"`
|
||||||
TTL string `json:",omitempty"`
|
TTL string `json:",omitempty"`
|
||||||
HTTP string `json:",omitempty"`
|
HTTP string `json:",omitempty"`
|
||||||
|
Header map[string][]string `json:",omitempty"`
|
||||||
|
Method string `json:",omitempty"`
|
||||||
TCP string `json:",omitempty"`
|
TCP string `json:",omitempty"`
|
||||||
Status string `json:",omitempty"`
|
Status string `json:",omitempty"`
|
||||||
Notes string `json:",omitempty"`
|
Notes string `json:",omitempty"`
|
||||||
TLSSkipVerify bool `json:",omitempty"`
|
TLSSkipVerify bool `json:",omitempty"`
|
||||||
|
GRPC string `json:",omitempty"`
|
||||||
|
GRPCUseTLS bool `json:",omitempty"`
|
||||||
|
|
||||||
// In Consul 0.7 and later, checks that are associated with a service
|
// In Consul 0.7 and later, checks that are associated with a service
|
||||||
// may also contain this optional DeregisterCriticalServiceAfter field,
|
// may also contain this optional DeregisterCriticalServiceAfter field,
|
||||||
|
@ -87,6 +111,47 @@ type AgentServiceCheck struct {
|
||||||
}
|
}
|
||||||
type AgentServiceChecks []*AgentServiceCheck
|
type AgentServiceChecks []*AgentServiceCheck
|
||||||
|
|
||||||
|
// AgentToken is used when updating ACL tokens for an agent.
|
||||||
|
type AgentToken struct {
|
||||||
|
Token string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metrics info is used to store different types of metric values from the agent.
|
||||||
|
type MetricsInfo struct {
|
||||||
|
Timestamp string
|
||||||
|
Gauges []GaugeValue
|
||||||
|
Points []PointValue
|
||||||
|
Counters []SampledValue
|
||||||
|
Samples []SampledValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// GaugeValue stores one value that is updated as time goes on, such as
|
||||||
|
// the amount of memory allocated.
|
||||||
|
type GaugeValue struct {
|
||||||
|
Name string
|
||||||
|
Value float32
|
||||||
|
Labels map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// PointValue holds a series of points for a metric.
|
||||||
|
type PointValue struct {
|
||||||
|
Name string
|
||||||
|
Points []float32
|
||||||
|
}
|
||||||
|
|
||||||
|
// SampledValue stores info about a metric that is incremented over time,
|
||||||
|
// such as the number of requests to an HTTP endpoint.
|
||||||
|
type SampledValue struct {
|
||||||
|
Name string
|
||||||
|
Count int
|
||||||
|
Sum float64
|
||||||
|
Min float64
|
||||||
|
Max float64
|
||||||
|
Mean float64
|
||||||
|
Stddev float64
|
||||||
|
Labels map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
// Agent can be used to query the Agent endpoints
|
// Agent can be used to query the Agent endpoints
|
||||||
type Agent struct {
|
type Agent struct {
|
||||||
c *Client
|
c *Client
|
||||||
|
@ -117,6 +182,23 @@ func (a *Agent) Self() (map[string]map[string]interface{}, error) {
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Metrics is used to query the agent we are speaking to for
|
||||||
|
// its current internal metric data
|
||||||
|
func (a *Agent) Metrics() (*MetricsInfo, error) {
|
||||||
|
r := a.c.newRequest("GET", "/v1/agent/metrics")
|
||||||
|
_, resp, err := requireOK(a.c.doRequest(r))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
var out *MetricsInfo
|
||||||
|
if err := decodeBody(resp, &out); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Reload triggers a configuration reload for the agent we are connected to.
|
// Reload triggers a configuration reload for the agent we are connected to.
|
||||||
func (a *Agent) Reload() error {
|
func (a *Agent) Reload() error {
|
||||||
r := a.c.newRequest("PUT", "/v1/agent/reload")
|
r := a.c.newRequest("PUT", "/v1/agent/reload")
|
||||||
|
@ -194,6 +276,28 @@ func (a *Agent) Members(wan bool) ([]*AgentMember, error) {
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MembersOpts returns the known gossip members and can be passed
|
||||||
|
// additional options for WAN/segment filtering.
|
||||||
|
func (a *Agent) MembersOpts(opts MembersOpts) ([]*AgentMember, error) {
|
||||||
|
r := a.c.newRequest("GET", "/v1/agent/members")
|
||||||
|
r.params.Set("segment", opts.Segment)
|
||||||
|
if opts.WAN {
|
||||||
|
r.params.Set("wan", "1")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, resp, err := requireOK(a.c.doRequest(r))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
var out []*AgentMember
|
||||||
|
if err := decodeBody(resp, &out); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ServiceRegister is used to register a new service with
|
// ServiceRegister is used to register a new service with
|
||||||
// the local agent
|
// the local agent
|
||||||
func (a *Agent) ServiceRegister(service *AgentServiceRegistration) error {
|
func (a *Agent) ServiceRegister(service *AgentServiceRegistration) error {
|
||||||
|
@ -437,8 +541,9 @@ func (a *Agent) DisableNodeMaintenance() error {
|
||||||
|
|
||||||
// Monitor returns a channel which will receive streaming logs from the agent
|
// 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
|
// Providing a non-nil stopCh can be used to close the connection and stop the
|
||||||
// log stream
|
// log stream. An empty string will be sent down the given channel when there's
|
||||||
func (a *Agent) Monitor(loglevel string, stopCh chan struct{}, q *QueryOptions) (chan string, error) {
|
// nothing left to stream, after which the caller should close the stopCh.
|
||||||
|
func (a *Agent) Monitor(loglevel string, stopCh <-chan struct{}, q *QueryOptions) (chan string, error) {
|
||||||
r := a.c.newRequest("GET", "/v1/agent/monitor")
|
r := a.c.newRequest("GET", "/v1/agent/monitor")
|
||||||
r.setQueryOptions(q)
|
r.setQueryOptions(q)
|
||||||
if loglevel != "" {
|
if loglevel != "" {
|
||||||
|
@ -462,10 +567,61 @@ func (a *Agent) Monitor(loglevel string, stopCh chan struct{}, q *QueryOptions)
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
if scanner.Scan() {
|
if scanner.Scan() {
|
||||||
logCh <- scanner.Text()
|
// An empty string signals to the caller that
|
||||||
|
// the scan is done, so make sure we only emit
|
||||||
|
// that when the scanner says it's done, not if
|
||||||
|
// we happen to ingest an empty line.
|
||||||
|
if text := scanner.Text(); text != "" {
|
||||||
|
logCh <- text
|
||||||
|
} else {
|
||||||
|
logCh <- " "
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logCh <- ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return logCh, nil
|
return logCh, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateACLToken updates the agent's "acl_token". See updateToken for more
|
||||||
|
// details.
|
||||||
|
func (a *Agent) UpdateACLToken(token string, q *WriteOptions) (*WriteMeta, error) {
|
||||||
|
return a.updateToken("acl_token", token, q)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateACLAgentToken updates the agent's "acl_agent_token". See updateToken
|
||||||
|
// for more details.
|
||||||
|
func (a *Agent) UpdateACLAgentToken(token string, q *WriteOptions) (*WriteMeta, error) {
|
||||||
|
return a.updateToken("acl_agent_token", token, q)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateACLAgentMasterToken updates the agent's "acl_agent_master_token". See
|
||||||
|
// updateToken for more details.
|
||||||
|
func (a *Agent) UpdateACLAgentMasterToken(token string, q *WriteOptions) (*WriteMeta, error) {
|
||||||
|
return a.updateToken("acl_agent_master_token", token, q)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateACLReplicationToken updates the agent's "acl_replication_token". See
|
||||||
|
// updateToken for more details.
|
||||||
|
func (a *Agent) UpdateACLReplicationToken(token string, q *WriteOptions) (*WriteMeta, error) {
|
||||||
|
return a.updateToken("acl_replication_token", token, q)
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateToken can be used to update an agent's ACL token after the agent has
|
||||||
|
// started. The tokens are not persisted, so will need to be updated again if
|
||||||
|
// the agent is restarted.
|
||||||
|
func (a *Agent) updateToken(target, token string, q *WriteOptions) (*WriteMeta, error) {
|
||||||
|
r := a.c.newRequest("PUT", fmt.Sprintf("/v1/agent/token/%s", target))
|
||||||
|
r.setWriteOptions(q)
|
||||||
|
r.obj = &AgentToken{Token: token}
|
||||||
|
rtt, resp, err := requireOK(a.c.doRequest(r))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resp.Body.Close()
|
||||||
|
|
||||||
|
wm := &WriteMeta{RequestTime: rtt}
|
||||||
|
return wm, nil
|
||||||
|
}
|
||||||
|
|
252
vendor/github.com/hashicorp/consul/api/api.go
generated
vendored
252
vendor/github.com/hashicorp/consul/api/api.go
generated
vendored
|
@ -2,8 +2,8 @@ package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@ -18,6 +18,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/go-cleanhttp"
|
"github.com/hashicorp/go-cleanhttp"
|
||||||
|
"github.com/hashicorp/go-rootcerts"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -37,6 +38,26 @@ const (
|
||||||
// whether or not to use HTTPS.
|
// whether or not to use HTTPS.
|
||||||
HTTPSSLEnvName = "CONSUL_HTTP_SSL"
|
HTTPSSLEnvName = "CONSUL_HTTP_SSL"
|
||||||
|
|
||||||
|
// HTTPCAFile defines an environment variable name which sets the
|
||||||
|
// CA file to use for talking to Consul over TLS.
|
||||||
|
HTTPCAFile = "CONSUL_CACERT"
|
||||||
|
|
||||||
|
// HTTPCAPath defines an environment variable name which sets the
|
||||||
|
// path to a directory of CA certs to use for talking to Consul over TLS.
|
||||||
|
HTTPCAPath = "CONSUL_CAPATH"
|
||||||
|
|
||||||
|
// HTTPClientCert defines an environment variable name which sets the
|
||||||
|
// client cert file to use for talking to Consul over TLS.
|
||||||
|
HTTPClientCert = "CONSUL_CLIENT_CERT"
|
||||||
|
|
||||||
|
// HTTPClientKey defines an environment variable name which sets the
|
||||||
|
// client key file to use for talking to Consul over TLS.
|
||||||
|
HTTPClientKey = "CONSUL_CLIENT_KEY"
|
||||||
|
|
||||||
|
// HTTPTLSServerName defines an environment variable name which sets the
|
||||||
|
// server name to use as the SNI host when connecting via TLS
|
||||||
|
HTTPTLSServerName = "CONSUL_TLS_SERVER_NAME"
|
||||||
|
|
||||||
// HTTPSSLVerifyEnvName defines an environment variable name which sets
|
// HTTPSSLVerifyEnvName defines an environment variable name which sets
|
||||||
// whether or not to disable certificate checking.
|
// whether or not to disable certificate checking.
|
||||||
HTTPSSLVerifyEnvName = "CONSUL_HTTP_SSL_VERIFY"
|
HTTPSSLVerifyEnvName = "CONSUL_HTTP_SSL_VERIFY"
|
||||||
|
@ -79,6 +100,31 @@ type QueryOptions struct {
|
||||||
// metadata key/value pairs. Currently, only one key/value pair can
|
// metadata key/value pairs. Currently, only one key/value pair can
|
||||||
// be provided for filtering.
|
// be provided for filtering.
|
||||||
NodeMeta map[string]string
|
NodeMeta map[string]string
|
||||||
|
|
||||||
|
// RelayFactor is used in keyring operations to cause responses to be
|
||||||
|
// relayed back to the sender through N other random nodes. Must be
|
||||||
|
// a value from 0 to 5 (inclusive).
|
||||||
|
RelayFactor uint8
|
||||||
|
|
||||||
|
// ctx is an optional context pass through to the underlying HTTP
|
||||||
|
// request layer. Use Context() and WithContext() to manage this.
|
||||||
|
ctx context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *QueryOptions) Context() context.Context {
|
||||||
|
if o != nil && o.ctx != nil {
|
||||||
|
return o.ctx
|
||||||
|
}
|
||||||
|
return context.Background()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *QueryOptions) WithContext(ctx context.Context) *QueryOptions {
|
||||||
|
o2 := new(QueryOptions)
|
||||||
|
if o != nil {
|
||||||
|
*o2 = *o
|
||||||
|
}
|
||||||
|
o2.ctx = ctx
|
||||||
|
return o2
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteOptions are used to parameterize a write
|
// WriteOptions are used to parameterize a write
|
||||||
|
@ -90,6 +136,31 @@ type WriteOptions struct {
|
||||||
// Token is used to provide a per-request ACL token
|
// Token is used to provide a per-request ACL token
|
||||||
// which overrides the agent's default token.
|
// which overrides the agent's default token.
|
||||||
Token string
|
Token string
|
||||||
|
|
||||||
|
// RelayFactor is used in keyring operations to cause responses to be
|
||||||
|
// relayed back to the sender through N other random nodes. Must be
|
||||||
|
// a value from 0 to 5 (inclusive).
|
||||||
|
RelayFactor uint8
|
||||||
|
|
||||||
|
// ctx is an optional context pass through to the underlying HTTP
|
||||||
|
// request layer. Use Context() and WithContext() to manage this.
|
||||||
|
ctx context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *WriteOptions) Context() context.Context {
|
||||||
|
if o != nil && o.ctx != nil {
|
||||||
|
return o.ctx
|
||||||
|
}
|
||||||
|
return context.Background()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *WriteOptions) WithContext(ctx context.Context) *WriteOptions {
|
||||||
|
o2 := new(WriteOptions)
|
||||||
|
if o != nil {
|
||||||
|
*o2 = *o
|
||||||
|
}
|
||||||
|
o2.ctx = ctx
|
||||||
|
return o2
|
||||||
}
|
}
|
||||||
|
|
||||||
// QueryMeta is used to return meta data about a query
|
// QueryMeta is used to return meta data about a query
|
||||||
|
@ -138,6 +209,9 @@ type Config struct {
|
||||||
// Datacenter to use. If not provided, the default agent datacenter is used.
|
// Datacenter to use. If not provided, the default agent datacenter is used.
|
||||||
Datacenter string
|
Datacenter string
|
||||||
|
|
||||||
|
// Transport is the Transport to use for the http client.
|
||||||
|
Transport *http.Transport
|
||||||
|
|
||||||
// HttpClient is the client to use. Default will be
|
// HttpClient is the client to use. Default will be
|
||||||
// used if not provided.
|
// used if not provided.
|
||||||
HttpClient *http.Client
|
HttpClient *http.Client
|
||||||
|
@ -152,6 +226,8 @@ type Config struct {
|
||||||
// Token is used to provide a per-request ACL token
|
// Token is used to provide a per-request ACL token
|
||||||
// which overrides the agent's default token.
|
// which overrides the agent's default token.
|
||||||
Token string
|
Token string
|
||||||
|
|
||||||
|
TLSConfig TLSConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// TLSConfig is used to generate a TLSClientConfig that's useful for talking to
|
// TLSConfig is used to generate a TLSClientConfig that's useful for talking to
|
||||||
|
@ -166,6 +242,10 @@ type TLSConfig struct {
|
||||||
// communication, defaults to the system bundle if not specified.
|
// communication, defaults to the system bundle if not specified.
|
||||||
CAFile string
|
CAFile string
|
||||||
|
|
||||||
|
// CAPath is the optional path to a directory of CA certificates to use for
|
||||||
|
// Consul communication, defaults to the system bundle if not specified.
|
||||||
|
CAPath string
|
||||||
|
|
||||||
// CertFile is the optional path to the certificate for Consul
|
// CertFile is the optional path to the certificate for Consul
|
||||||
// communication. If this is set then you need to also set KeyFile.
|
// communication. If this is set then you need to also set KeyFile.
|
||||||
CertFile string
|
CertFile string
|
||||||
|
@ -203,9 +283,7 @@ func defaultConfig(transportFn func() *http.Transport) *Config {
|
||||||
config := &Config{
|
config := &Config{
|
||||||
Address: "127.0.0.1:8500",
|
Address: "127.0.0.1:8500",
|
||||||
Scheme: "http",
|
Scheme: "http",
|
||||||
HttpClient: &http.Client{
|
|
||||||
Transport: transportFn(),
|
Transport: transportFn(),
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if addr := os.Getenv(HTTPAddrEnvName); addr != "" {
|
if addr := os.Getenv(HTTPAddrEnvName); addr != "" {
|
||||||
|
@ -243,27 +321,28 @@ func defaultConfig(transportFn func() *http.Transport) *Config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if verify := os.Getenv(HTTPSSLVerifyEnvName); verify != "" {
|
if v := os.Getenv(HTTPTLSServerName); v != "" {
|
||||||
doVerify, err := strconv.ParseBool(verify)
|
config.TLSConfig.Address = v
|
||||||
|
}
|
||||||
|
if v := os.Getenv(HTTPCAFile); v != "" {
|
||||||
|
config.TLSConfig.CAFile = v
|
||||||
|
}
|
||||||
|
if v := os.Getenv(HTTPCAPath); v != "" {
|
||||||
|
config.TLSConfig.CAPath = v
|
||||||
|
}
|
||||||
|
if v := os.Getenv(HTTPClientCert); v != "" {
|
||||||
|
config.TLSConfig.CertFile = v
|
||||||
|
}
|
||||||
|
if v := os.Getenv(HTTPClientKey); v != "" {
|
||||||
|
config.TLSConfig.KeyFile = v
|
||||||
|
}
|
||||||
|
if v := os.Getenv(HTTPSSLVerifyEnvName); v != "" {
|
||||||
|
doVerify, err := strconv.ParseBool(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("[WARN] client: could not parse %s: %s", HTTPSSLVerifyEnvName, err)
|
log.Printf("[WARN] client: could not parse %s: %s", HTTPSSLVerifyEnvName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !doVerify {
|
if !doVerify {
|
||||||
tlsClientConfig, err := SetupTLSConfig(&TLSConfig{
|
config.TLSConfig.InsecureSkipVerify = true
|
||||||
InsecureSkipVerify: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
// We don't expect this to fail given that we aren't
|
|
||||||
// parsing any of the input, but we panic just in case
|
|
||||||
// since this doesn't have an error return.
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
transport := transportFn()
|
|
||||||
transport.TLSClientConfig = tlsClientConfig
|
|
||||||
config.HttpClient.Transport = transport
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,17 +377,14 @@ func SetupTLSConfig(tlsConfig *TLSConfig) (*tls.Config, error) {
|
||||||
tlsClientConfig.Certificates = []tls.Certificate{tlsCert}
|
tlsClientConfig.Certificates = []tls.Certificate{tlsCert}
|
||||||
}
|
}
|
||||||
|
|
||||||
if tlsConfig.CAFile != "" {
|
if tlsConfig.CAFile != "" || tlsConfig.CAPath != "" {
|
||||||
data, err := ioutil.ReadFile(tlsConfig.CAFile)
|
rootConfig := &rootcerts.Config{
|
||||||
if err != nil {
|
CAFile: tlsConfig.CAFile,
|
||||||
return nil, fmt.Errorf("failed to read CA file: %v", err)
|
CAPath: tlsConfig.CAPath,
|
||||||
}
|
}
|
||||||
|
if err := rootcerts.ConfigureTLS(tlsClientConfig, rootConfig); err != nil {
|
||||||
caPool := x509.NewCertPool()
|
return nil, err
|
||||||
if !caPool.AppendCertsFromPEM(data) {
|
|
||||||
return nil, fmt.Errorf("failed to parse CA certificate")
|
|
||||||
}
|
}
|
||||||
tlsClientConfig.RootCAs = caPool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return tlsClientConfig, nil
|
return tlsClientConfig, nil
|
||||||
|
@ -332,24 +408,95 @@ func NewClient(config *Config) (*Client, error) {
|
||||||
config.Scheme = defConfig.Scheme
|
config.Scheme = defConfig.Scheme
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.HttpClient == nil {
|
if config.Transport == nil {
|
||||||
config.HttpClient = defConfig.HttpClient
|
config.Transport = defConfig.Transport
|
||||||
}
|
}
|
||||||
|
|
||||||
if parts := strings.SplitN(config.Address, "unix://", 2); len(parts) == 2 {
|
if config.TLSConfig.Address == "" {
|
||||||
|
config.TLSConfig.Address = defConfig.TLSConfig.Address
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.TLSConfig.CAFile == "" {
|
||||||
|
config.TLSConfig.CAFile = defConfig.TLSConfig.CAFile
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.TLSConfig.CAPath == "" {
|
||||||
|
config.TLSConfig.CAPath = defConfig.TLSConfig.CAPath
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.TLSConfig.CertFile == "" {
|
||||||
|
config.TLSConfig.CertFile = defConfig.TLSConfig.CertFile
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.TLSConfig.KeyFile == "" {
|
||||||
|
config.TLSConfig.KeyFile = defConfig.TLSConfig.KeyFile
|
||||||
|
}
|
||||||
|
|
||||||
|
if !config.TLSConfig.InsecureSkipVerify {
|
||||||
|
config.TLSConfig.InsecureSkipVerify = defConfig.TLSConfig.InsecureSkipVerify
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.HttpClient == nil {
|
||||||
|
var err error
|
||||||
|
config.HttpClient, err = NewHttpClient(config.Transport, config.TLSConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.SplitN(config.Address, "://", 2)
|
||||||
|
if len(parts) == 2 {
|
||||||
|
switch parts[0] {
|
||||||
|
case "http":
|
||||||
|
config.Scheme = "http"
|
||||||
|
case "https":
|
||||||
|
config.Scheme = "https"
|
||||||
|
case "unix":
|
||||||
trans := cleanhttp.DefaultTransport()
|
trans := cleanhttp.DefaultTransport()
|
||||||
trans.Dial = func(_, _ string) (net.Conn, error) {
|
trans.DialContext = func(_ context.Context, _, _ string) (net.Conn, error) {
|
||||||
return net.Dial("unix", parts[1])
|
return net.Dial("unix", parts[1])
|
||||||
}
|
}
|
||||||
config.HttpClient = &http.Client{
|
config.HttpClient = &http.Client{
|
||||||
Transport: trans,
|
Transport: trans,
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("Unknown protocol scheme: %s", parts[0])
|
||||||
|
}
|
||||||
config.Address = parts[1]
|
config.Address = parts[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
client := &Client{
|
if config.Token == "" {
|
||||||
config: *config,
|
config.Token = defConfig.Token
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return &Client{config: *config}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHttpClient returns an http client configured with the given Transport and TLS
|
||||||
|
// config.
|
||||||
|
func NewHttpClient(transport *http.Transport, tlsConf TLSConfig) (*http.Client, error) {
|
||||||
|
client := &http.Client{
|
||||||
|
Transport: transport,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO (slackpad) - Once we get some run time on the HTTP/2 support we
|
||||||
|
// should turn it on by default if TLS is enabled. We would basically
|
||||||
|
// just need to call http2.ConfigureTransport(transport) here. We also
|
||||||
|
// don't want to introduce another external dependency on
|
||||||
|
// golang.org/x/net/http2 at this time. For a complete recipe for how
|
||||||
|
// to enable HTTP/2 support on a transport suitable for the API client
|
||||||
|
// library see agent/http_test.go:TestHTTPServer_H2.
|
||||||
|
|
||||||
|
if transport.TLSClientConfig == nil {
|
||||||
|
tlsClientConfig, err := SetupTLSConfig(&tlsConf)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
transport.TLSClientConfig = tlsClientConfig
|
||||||
|
}
|
||||||
|
|
||||||
return client, nil
|
return client, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -362,6 +509,7 @@ type request struct {
|
||||||
body io.Reader
|
body io.Reader
|
||||||
header http.Header
|
header http.Header
|
||||||
obj interface{}
|
obj interface{}
|
||||||
|
ctx context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
// setQueryOptions is used to annotate the request with
|
// setQueryOptions is used to annotate the request with
|
||||||
|
@ -396,6 +544,10 @@ func (r *request) setQueryOptions(q *QueryOptions) {
|
||||||
r.params.Add("node-meta", key+":"+value)
|
r.params.Add("node-meta", key+":"+value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if q.RelayFactor != 0 {
|
||||||
|
r.params.Set("relay-factor", strconv.Itoa(int(q.RelayFactor)))
|
||||||
|
}
|
||||||
|
r.ctx = q.ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
// durToMsec converts a duration to a millisecond specified string. If the
|
// durToMsec converts a duration to a millisecond specified string. If the
|
||||||
|
@ -413,13 +565,20 @@ func durToMsec(dur time.Duration) string {
|
||||||
// serverError is a string we look for to detect 500 errors.
|
// serverError is a string we look for to detect 500 errors.
|
||||||
const serverError = "Unexpected response code: 500"
|
const serverError = "Unexpected response code: 500"
|
||||||
|
|
||||||
// IsServerError returns true for 500 errors from the Consul servers, these are
|
// IsRetryableError returns true for 500 errors from the Consul servers, and
|
||||||
// usually retryable at a later time.
|
// network connection errors. These are usually retryable at a later time.
|
||||||
func IsServerError(err error) bool {
|
// This applies to reads but NOT to writes. This may return true for errors
|
||||||
|
// on writes that may have still gone through, so do not use this to retry
|
||||||
|
// any write operations.
|
||||||
|
func IsRetryableError(err error) bool {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, ok := err.(net.Error); ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// TODO (slackpad) - Make a real error type here instead of using
|
// TODO (slackpad) - Make a real error type here instead of using
|
||||||
// a string check.
|
// a string check.
|
||||||
return strings.Contains(err.Error(), serverError)
|
return strings.Contains(err.Error(), serverError)
|
||||||
|
@ -437,6 +596,10 @@ func (r *request) setWriteOptions(q *WriteOptions) {
|
||||||
if q.Token != "" {
|
if q.Token != "" {
|
||||||
r.header.Set("X-Consul-Token", q.Token)
|
r.header.Set("X-Consul-Token", q.Token)
|
||||||
}
|
}
|
||||||
|
if q.RelayFactor != 0 {
|
||||||
|
r.params.Set("relay-factor", strconv.Itoa(int(q.RelayFactor)))
|
||||||
|
}
|
||||||
|
r.ctx = q.ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
// toHTTP converts the request to an HTTP request
|
// toHTTP converts the request to an HTTP request
|
||||||
|
@ -446,11 +609,11 @@ func (r *request) toHTTP() (*http.Request, error) {
|
||||||
|
|
||||||
// Check if we should encode the body
|
// Check if we should encode the body
|
||||||
if r.body == nil && r.obj != nil {
|
if r.body == nil && r.obj != nil {
|
||||||
if b, err := encodeBody(r.obj); err != nil {
|
b, err := encodeBody(r.obj)
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
|
||||||
r.body = b
|
|
||||||
}
|
}
|
||||||
|
r.body = b
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the HTTP request
|
// Create the HTTP request
|
||||||
|
@ -468,6 +631,9 @@ func (r *request) toHTTP() (*http.Request, error) {
|
||||||
if r.config.HttpAuth != nil {
|
if r.config.HttpAuth != nil {
|
||||||
req.SetBasicAuth(r.config.HttpAuth.Username, r.config.HttpAuth.Password)
|
req.SetBasicAuth(r.config.HttpAuth.Username, r.config.HttpAuth.Password)
|
||||||
}
|
}
|
||||||
|
if r.ctx != nil {
|
||||||
|
return req.WithContext(r.ctx), nil
|
||||||
|
}
|
||||||
|
|
||||||
return req, nil
|
return req, nil
|
||||||
}
|
}
|
||||||
|
@ -505,7 +671,7 @@ func (c *Client) doRequest(r *request) (time.Duration, *http.Response, error) {
|
||||||
}
|
}
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
resp, err := c.config.HttpClient.Do(req)
|
resp, err := c.config.HttpClient.Do(req)
|
||||||
diff := time.Now().Sub(start)
|
diff := time.Since(start)
|
||||||
return diff, resp, err
|
return diff, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -548,6 +714,8 @@ func (c *Client) write(endpoint string, in, out interface{}, q *WriteOptions) (*
|
||||||
if err := decodeBody(resp, &out); err != nil {
|
if err := decodeBody(resp, &out); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
} else if _, err := ioutil.ReadAll(resp.Body); err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
return wm, nil
|
return wm, nil
|
||||||
}
|
}
|
||||||
|
|
11
vendor/github.com/hashicorp/consul/api/catalog.go
generated
vendored
11
vendor/github.com/hashicorp/consul/api/catalog.go
generated
vendored
|
@ -1,21 +1,28 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
type Node struct {
|
type Node struct {
|
||||||
|
ID string
|
||||||
Node string
|
Node string
|
||||||
Address string
|
Address string
|
||||||
|
Datacenter string
|
||||||
TaggedAddresses map[string]string
|
TaggedAddresses map[string]string
|
||||||
Meta map[string]string
|
Meta map[string]string
|
||||||
|
CreateIndex uint64
|
||||||
|
ModifyIndex uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
type CatalogService struct {
|
type CatalogService struct {
|
||||||
|
ID string
|
||||||
Node string
|
Node string
|
||||||
Address string
|
Address string
|
||||||
|
Datacenter string
|
||||||
TaggedAddresses map[string]string
|
TaggedAddresses map[string]string
|
||||||
NodeMeta map[string]string
|
NodeMeta map[string]string
|
||||||
ServiceID string
|
ServiceID string
|
||||||
ServiceName string
|
ServiceName string
|
||||||
ServiceAddress string
|
ServiceAddress string
|
||||||
ServiceTags []string
|
ServiceTags []string
|
||||||
|
ServiceMeta map[string]string
|
||||||
ServicePort int
|
ServicePort int
|
||||||
ServiceEnableTagOverride bool
|
ServiceEnableTagOverride bool
|
||||||
CreateIndex uint64
|
CreateIndex uint64
|
||||||
|
@ -28,6 +35,7 @@ type CatalogNode struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type CatalogRegistration struct {
|
type CatalogRegistration struct {
|
||||||
|
ID string
|
||||||
Node string
|
Node string
|
||||||
Address string
|
Address string
|
||||||
TaggedAddresses map[string]string
|
TaggedAddresses map[string]string
|
||||||
|
@ -35,11 +43,12 @@ type CatalogRegistration struct {
|
||||||
Datacenter string
|
Datacenter string
|
||||||
Service *AgentService
|
Service *AgentService
|
||||||
Check *AgentCheck
|
Check *AgentCheck
|
||||||
|
SkipNodeUpdate bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type CatalogDeregistration struct {
|
type CatalogDeregistration struct {
|
||||||
Node string
|
Node string
|
||||||
Address string
|
Address string // Obsolete.
|
||||||
Datacenter string
|
Datacenter string
|
||||||
ServiceID string
|
ServiceID string
|
||||||
CheckID string
|
CheckID string
|
||||||
|
|
44
vendor/github.com/hashicorp/consul/api/coordinate.go
generated
vendored
44
vendor/github.com/hashicorp/consul/api/coordinate.go
generated
vendored
|
@ -7,13 +7,15 @@ import (
|
||||||
// CoordinateEntry represents a node and its associated network coordinate.
|
// CoordinateEntry represents a node and its associated network coordinate.
|
||||||
type CoordinateEntry struct {
|
type CoordinateEntry struct {
|
||||||
Node string
|
Node string
|
||||||
|
Segment string
|
||||||
Coord *coordinate.Coordinate
|
Coord *coordinate.Coordinate
|
||||||
}
|
}
|
||||||
|
|
||||||
// CoordinateDatacenterMap represents a datacenter and its associated WAN
|
// CoordinateDatacenterMap has the coordinates for servers in a given datacenter
|
||||||
// nodes and their associates coordinates.
|
// and area. Network coordinates are only compatible within the same area.
|
||||||
type CoordinateDatacenterMap struct {
|
type CoordinateDatacenterMap struct {
|
||||||
Datacenter string
|
Datacenter string
|
||||||
|
AreaID string
|
||||||
Coordinates []CoordinateEntry
|
Coordinates []CoordinateEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,3 +66,41 @@ func (c *Coordinate) Nodes(q *QueryOptions) ([]*CoordinateEntry, *QueryMeta, err
|
||||||
}
|
}
|
||||||
return out, qm, nil
|
return out, qm, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update inserts or updates the LAN coordinate of a node.
|
||||||
|
func (c *Coordinate) Update(coord *CoordinateEntry, q *WriteOptions) (*WriteMeta, error) {
|
||||||
|
r := c.c.newRequest("PUT", "/v1/coordinate/update")
|
||||||
|
r.setWriteOptions(q)
|
||||||
|
r.obj = coord
|
||||||
|
rtt, resp, err := requireOK(c.c.doRequest(r))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
wm := &WriteMeta{}
|
||||||
|
wm.RequestTime = rtt
|
||||||
|
|
||||||
|
return wm, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Node is used to return the coordinates of a single in the LAN pool.
|
||||||
|
func (c *Coordinate) Node(node string, q *QueryOptions) ([]*CoordinateEntry, *QueryMeta, error) {
|
||||||
|
r := c.c.newRequest("GET", "/v1/coordinate/node/"+node)
|
||||||
|
r.setQueryOptions(q)
|
||||||
|
rtt, resp, err := requireOK(c.c.doRequest(r))
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
qm := &QueryMeta{}
|
||||||
|
parseQueryMeta(resp, qm)
|
||||||
|
qm.RequestTime = rtt
|
||||||
|
|
||||||
|
var out []*CoordinateEntry
|
||||||
|
if err := decodeBody(resp, &out); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return out, qm, nil
|
||||||
|
}
|
||||||
|
|
16
vendor/github.com/hashicorp/consul/api/health.go
generated
vendored
16
vendor/github.com/hashicorp/consul/api/health.go
generated
vendored
|
@ -33,6 +33,22 @@ type HealthCheck struct {
|
||||||
Output string
|
Output string
|
||||||
ServiceID string
|
ServiceID string
|
||||||
ServiceName string
|
ServiceName string
|
||||||
|
ServiceTags []string
|
||||||
|
|
||||||
|
Definition HealthCheckDefinition
|
||||||
|
}
|
||||||
|
|
||||||
|
// HealthCheckDefinition is used to store the details about
|
||||||
|
// a health check's execution.
|
||||||
|
type HealthCheckDefinition struct {
|
||||||
|
HTTP string
|
||||||
|
Header map[string][]string
|
||||||
|
Method string
|
||||||
|
TLSSkipVerify bool
|
||||||
|
TCP string
|
||||||
|
Interval ReadableDuration
|
||||||
|
Timeout ReadableDuration
|
||||||
|
DeregisterCriticalServiceAfter ReadableDuration
|
||||||
}
|
}
|
||||||
|
|
||||||
// HealthChecks is a collection of HealthCheck structs.
|
// HealthChecks is a collection of HealthCheck structs.
|
||||||
|
|
5
vendor/github.com/hashicorp/consul/api/kv.go
generated
vendored
5
vendor/github.com/hashicorp/consul/api/kv.go
generated
vendored
|
@ -60,6 +60,7 @@ const (
|
||||||
KVGetTree KVOp = "get-tree"
|
KVGetTree KVOp = "get-tree"
|
||||||
KVCheckSession KVOp = "check-session"
|
KVCheckSession KVOp = "check-session"
|
||||||
KVCheckIndex KVOp = "check-index"
|
KVCheckIndex KVOp = "check-index"
|
||||||
|
KVCheckNotExists KVOp = "check-not-exists"
|
||||||
)
|
)
|
||||||
|
|
||||||
// KVTxnOp defines a single operation inside a transaction.
|
// KVTxnOp defines a single operation inside a transaction.
|
||||||
|
@ -251,7 +252,7 @@ func (k *KV) put(key string, params map[string]string, body []byte, q *WriteOpti
|
||||||
if _, err := io.Copy(&buf, resp.Body); err != nil {
|
if _, err := io.Copy(&buf, resp.Body); err != nil {
|
||||||
return false, nil, fmt.Errorf("Failed to read response: %v", err)
|
return false, nil, fmt.Errorf("Failed to read response: %v", err)
|
||||||
}
|
}
|
||||||
res := strings.Contains(string(buf.Bytes()), "true")
|
res := strings.Contains(buf.String(), "true")
|
||||||
return res, qm, nil
|
return res, qm, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -295,7 +296,7 @@ func (k *KV) deleteInternal(key string, params map[string]string, q *WriteOption
|
||||||
if _, err := io.Copy(&buf, resp.Body); err != nil {
|
if _, err := io.Copy(&buf, resp.Body); err != nil {
|
||||||
return false, nil, fmt.Errorf("Failed to read response: %v", err)
|
return false, nil, fmt.Errorf("Failed to read response: %v", err)
|
||||||
}
|
}
|
||||||
res := strings.Contains(string(buf.Bytes()), "true")
|
res := strings.Contains(buf.String(), "true")
|
||||||
return res, qm, nil
|
return res, qm, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
11
vendor/github.com/hashicorp/consul/api/lock.go
generated
vendored
11
vendor/github.com/hashicorp/consul/api/lock.go
generated
vendored
|
@ -143,9 +143,11 @@ func (l *Lock) Lock(stopCh <-chan struct{}) (<-chan struct{}, error) {
|
||||||
// Check if we need to create a session first
|
// Check if we need to create a session first
|
||||||
l.lockSession = l.opts.Session
|
l.lockSession = l.opts.Session
|
||||||
if l.lockSession == "" {
|
if l.lockSession == "" {
|
||||||
if s, err := l.createSession(); err != nil {
|
s, err := l.createSession()
|
||||||
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create session: %v", err)
|
return nil, fmt.Errorf("failed to create session: %v", err)
|
||||||
} else {
|
}
|
||||||
|
|
||||||
l.sessionRenew = make(chan struct{})
|
l.sessionRenew = make(chan struct{})
|
||||||
l.lockSession = s
|
l.lockSession = s
|
||||||
session := l.c.Session()
|
session := l.c.Session()
|
||||||
|
@ -159,7 +161,6 @@ func (l *Lock) Lock(stopCh <-chan struct{}) (<-chan struct{}, error) {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Setup the query options
|
// Setup the query options
|
||||||
kv := l.c.KV()
|
kv := l.c.KV()
|
||||||
|
@ -179,7 +180,7 @@ WAIT:
|
||||||
|
|
||||||
// Handle the one-shot mode.
|
// Handle the one-shot mode.
|
||||||
if l.opts.LockTryOnce && attempts > 0 {
|
if l.opts.LockTryOnce && attempts > 0 {
|
||||||
elapsed := time.Now().Sub(start)
|
elapsed := time.Since(start)
|
||||||
if elapsed > qOpts.WaitTime {
|
if elapsed > qOpts.WaitTime {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
@ -369,7 +370,7 @@ RETRY:
|
||||||
// by doing retries. Note that we have to attempt the retry in a non-
|
// by doing retries. Note that we have to attempt the retry in a non-
|
||||||
// blocking fashion so that we have a clean place to reset the retry
|
// blocking fashion so that we have a clean place to reset the retry
|
||||||
// counter if service is restored.
|
// counter if service is restored.
|
||||||
if retries > 0 && IsServerError(err) {
|
if retries > 0 && IsRetryableError(err) {
|
||||||
time.Sleep(l.opts.MonitorRetryTime)
|
time.Sleep(l.opts.MonitorRetryTime)
|
||||||
retries--
|
retries--
|
||||||
opts.WaitIndex = 0
|
opts.WaitIndex = 0
|
||||||
|
|
152
vendor/github.com/hashicorp/consul/api/operator.go
generated
vendored
152
vendor/github.com/hashicorp/consul/api/operator.go
generated
vendored
|
@ -9,155 +9,3 @@ type Operator struct {
|
||||||
func (c *Client) Operator() *Operator {
|
func (c *Client) Operator() *Operator {
|
||||||
return &Operator{c}
|
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/<id>.
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
193
vendor/github.com/hashicorp/consul/api/operator_area.go
generated
vendored
Normal file
193
vendor/github.com/hashicorp/consul/api/operator_area.go
generated
vendored
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
// The /v1/operator/area endpoints are available only in Consul Enterprise and
|
||||||
|
// interact with its network area subsystem. Network areas are used to link
|
||||||
|
// together Consul servers in different Consul datacenters. With network areas,
|
||||||
|
// Consul datacenters can be linked together in ways other than a fully-connected
|
||||||
|
// mesh, as is required for Consul's WAN.
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Area defines a network area.
|
||||||
|
type Area struct {
|
||||||
|
// ID is this identifier for an area (a UUID). This must be left empty
|
||||||
|
// when creating a new area.
|
||||||
|
ID string
|
||||||
|
|
||||||
|
// PeerDatacenter is the peer Consul datacenter that will make up the
|
||||||
|
// other side of this network area. Network areas always involve a pair
|
||||||
|
// of datacenters: the datacenter where the area was created, and the
|
||||||
|
// peer datacenter. This is required.
|
||||||
|
PeerDatacenter string
|
||||||
|
|
||||||
|
// RetryJoin specifies the address of Consul servers to join to, such as
|
||||||
|
// an IPs or hostnames with an optional port number. This is optional.
|
||||||
|
RetryJoin []string
|
||||||
|
|
||||||
|
// UseTLS specifies whether gossip over this area should be encrypted with TLS
|
||||||
|
// if possible.
|
||||||
|
UseTLS bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// AreaJoinResponse is returned when a join occurs and gives the result for each
|
||||||
|
// address.
|
||||||
|
type AreaJoinResponse struct {
|
||||||
|
// The address that was joined.
|
||||||
|
Address string
|
||||||
|
|
||||||
|
// Whether or not the join was a success.
|
||||||
|
Joined bool
|
||||||
|
|
||||||
|
// If we couldn't join, this is the message with information.
|
||||||
|
Error string
|
||||||
|
}
|
||||||
|
|
||||||
|
// SerfMember is a generic structure for reporting information about members in
|
||||||
|
// a Serf cluster. This is only used by the area endpoints right now, but this
|
||||||
|
// could be expanded to other endpoints in the future.
|
||||||
|
type SerfMember struct {
|
||||||
|
// ID is the node identifier (a UUID).
|
||||||
|
ID string
|
||||||
|
|
||||||
|
// Name is the node name.
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// Addr has the IP address.
|
||||||
|
Addr net.IP
|
||||||
|
|
||||||
|
// Port is the RPC port.
|
||||||
|
Port uint16
|
||||||
|
|
||||||
|
// Datacenter is the DC name.
|
||||||
|
Datacenter string
|
||||||
|
|
||||||
|
// Role is "client", "server", or "unknown".
|
||||||
|
Role string
|
||||||
|
|
||||||
|
// Build has the version of the Consul agent.
|
||||||
|
Build string
|
||||||
|
|
||||||
|
// Protocol is the protocol of the Consul agent.
|
||||||
|
Protocol int
|
||||||
|
|
||||||
|
// Status is the Serf health status "none", "alive", "leaving", "left",
|
||||||
|
// or "failed".
|
||||||
|
Status string
|
||||||
|
|
||||||
|
// RTT is the estimated round trip time from the server handling the
|
||||||
|
// request to the this member. This will be negative if no RTT estimate
|
||||||
|
// is available.
|
||||||
|
RTT time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// AreaCreate will create a new network area. The ID in the given structure must
|
||||||
|
// be empty and a generated ID will be returned on success.
|
||||||
|
func (op *Operator) AreaCreate(area *Area, q *WriteOptions) (string, *WriteMeta, error) {
|
||||||
|
r := op.c.newRequest("POST", "/v1/operator/area")
|
||||||
|
r.setWriteOptions(q)
|
||||||
|
r.obj = area
|
||||||
|
rtt, resp, err := requireOK(op.c.doRequest(r))
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
wm := &WriteMeta{}
|
||||||
|
wm.RequestTime = rtt
|
||||||
|
|
||||||
|
var out struct{ ID string }
|
||||||
|
if err := decodeBody(resp, &out); err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
return out.ID, wm, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AreaUpdate will update the configuration of the network area with the given ID.
|
||||||
|
func (op *Operator) AreaUpdate(areaID string, area *Area, q *WriteOptions) (string, *WriteMeta, error) {
|
||||||
|
r := op.c.newRequest("PUT", "/v1/operator/area/"+areaID)
|
||||||
|
r.setWriteOptions(q)
|
||||||
|
r.obj = area
|
||||||
|
rtt, resp, err := requireOK(op.c.doRequest(r))
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
wm := &WriteMeta{}
|
||||||
|
wm.RequestTime = rtt
|
||||||
|
|
||||||
|
var out struct{ ID string }
|
||||||
|
if err := decodeBody(resp, &out); err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
return out.ID, wm, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AreaGet returns a single network area.
|
||||||
|
func (op *Operator) AreaGet(areaID string, q *QueryOptions) ([]*Area, *QueryMeta, error) {
|
||||||
|
var out []*Area
|
||||||
|
qm, err := op.c.query("/v1/operator/area/"+areaID, &out, q)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return out, qm, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AreaList returns all the available network areas.
|
||||||
|
func (op *Operator) AreaList(q *QueryOptions) ([]*Area, *QueryMeta, error) {
|
||||||
|
var out []*Area
|
||||||
|
qm, err := op.c.query("/v1/operator/area", &out, q)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return out, qm, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AreaDelete deletes the given network area.
|
||||||
|
func (op *Operator) AreaDelete(areaID string, q *WriteOptions) (*WriteMeta, error) {
|
||||||
|
r := op.c.newRequest("DELETE", "/v1/operator/area/"+areaID)
|
||||||
|
r.setWriteOptions(q)
|
||||||
|
rtt, resp, err := requireOK(op.c.doRequest(r))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
wm := &WriteMeta{}
|
||||||
|
wm.RequestTime = rtt
|
||||||
|
return wm, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AreaJoin attempts to join the given set of join addresses to the given
|
||||||
|
// network area. See the Area structure for details about join addresses.
|
||||||
|
func (op *Operator) AreaJoin(areaID string, addresses []string, q *WriteOptions) ([]*AreaJoinResponse, *WriteMeta, error) {
|
||||||
|
r := op.c.newRequest("PUT", "/v1/operator/area/"+areaID+"/join")
|
||||||
|
r.setWriteOptions(q)
|
||||||
|
r.obj = addresses
|
||||||
|
rtt, resp, err := requireOK(op.c.doRequest(r))
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
wm := &WriteMeta{}
|
||||||
|
wm.RequestTime = rtt
|
||||||
|
|
||||||
|
var out []*AreaJoinResponse
|
||||||
|
if err := decodeBody(resp, &out); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return out, wm, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AreaMembers lists the Serf information about the members in the given area.
|
||||||
|
func (op *Operator) AreaMembers(areaID string, q *QueryOptions) ([]*SerfMember, *QueryMeta, error) {
|
||||||
|
var out []*SerfMember
|
||||||
|
qm, err := op.c.query("/v1/operator/area/"+areaID+"/members", &out, q)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return out, qm, nil
|
||||||
|
}
|
219
vendor/github.com/hashicorp/consul/api/operator_autopilot.go
generated
vendored
Normal file
219
vendor/github.com/hashicorp/consul/api/operator_autopilot.go
generated
vendored
Normal file
|
@ -0,0 +1,219 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AutopilotConfiguration is used for querying/setting the Autopilot configuration.
|
||||||
|
// Autopilot helps manage operator tasks related to Consul servers like removing
|
||||||
|
// failed servers from the Raft quorum.
|
||||||
|
type AutopilotConfiguration struct {
|
||||||
|
// CleanupDeadServers controls whether to remove dead servers from the Raft
|
||||||
|
// peer list when a new server joins
|
||||||
|
CleanupDeadServers bool
|
||||||
|
|
||||||
|
// LastContactThreshold is the limit on the amount of time a server can go
|
||||||
|
// without leader contact before being considered unhealthy.
|
||||||
|
LastContactThreshold *ReadableDuration
|
||||||
|
|
||||||
|
// MaxTrailingLogs is the amount of entries in the Raft Log that a server can
|
||||||
|
// be behind before being considered unhealthy.
|
||||||
|
MaxTrailingLogs uint64
|
||||||
|
|
||||||
|
// ServerStabilizationTime is the minimum amount of time a server must be
|
||||||
|
// in a stable, healthy state before it can be added to the cluster. Only
|
||||||
|
// applicable with Raft protocol version 3 or higher.
|
||||||
|
ServerStabilizationTime *ReadableDuration
|
||||||
|
|
||||||
|
// (Enterprise-only) RedundancyZoneTag is the node tag to use for separating
|
||||||
|
// servers into zones for redundancy. If left blank, this feature will be disabled.
|
||||||
|
RedundancyZoneTag string
|
||||||
|
|
||||||
|
// (Enterprise-only) DisableUpgradeMigration will disable Autopilot's upgrade migration
|
||||||
|
// strategy of waiting until enough newer-versioned servers have been added to the
|
||||||
|
// cluster before promoting them to voters.
|
||||||
|
DisableUpgradeMigration bool
|
||||||
|
|
||||||
|
// (Enterprise-only) UpgradeVersionTag is the node tag to use for version info when
|
||||||
|
// performing upgrade migrations. If left blank, the Consul version will be used.
|
||||||
|
UpgradeVersionTag string
|
||||||
|
|
||||||
|
// CreateIndex holds the index corresponding the creation of this configuration.
|
||||||
|
// This is a read-only field.
|
||||||
|
CreateIndex uint64
|
||||||
|
|
||||||
|
// ModifyIndex will be set to the index of the last update when retrieving the
|
||||||
|
// Autopilot configuration. Resubmitting a configuration with
|
||||||
|
// AutopilotCASConfiguration will perform a check-and-set operation which ensures
|
||||||
|
// there hasn't been a subsequent update since the configuration was retrieved.
|
||||||
|
ModifyIndex uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerHealth is the health (from the leader's point of view) of a server.
|
||||||
|
type ServerHealth struct {
|
||||||
|
// ID is the raft ID of the server.
|
||||||
|
ID string
|
||||||
|
|
||||||
|
// Name is the node name of the server.
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// Address is the address of the server.
|
||||||
|
Address string
|
||||||
|
|
||||||
|
// The status of the SerfHealth check for the server.
|
||||||
|
SerfStatus string
|
||||||
|
|
||||||
|
// Version is the Consul version of the server.
|
||||||
|
Version string
|
||||||
|
|
||||||
|
// Leader is whether this server is currently the leader.
|
||||||
|
Leader bool
|
||||||
|
|
||||||
|
// LastContact is the time since this node's last contact with the leader.
|
||||||
|
LastContact *ReadableDuration
|
||||||
|
|
||||||
|
// LastTerm is the highest leader term this server has a record of in its Raft log.
|
||||||
|
LastTerm uint64
|
||||||
|
|
||||||
|
// LastIndex is the last log index this server has a record of in its Raft log.
|
||||||
|
LastIndex uint64
|
||||||
|
|
||||||
|
// Healthy is whether or not the server is healthy according to the current
|
||||||
|
// Autopilot config.
|
||||||
|
Healthy bool
|
||||||
|
|
||||||
|
// Voter is whether this is a voting server.
|
||||||
|
Voter bool
|
||||||
|
|
||||||
|
// StableSince is the last time this server's Healthy value changed.
|
||||||
|
StableSince time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// OperatorHealthReply is a representation of the overall health of the cluster
|
||||||
|
type OperatorHealthReply struct {
|
||||||
|
// Healthy is true if all the servers in the cluster are healthy.
|
||||||
|
Healthy bool
|
||||||
|
|
||||||
|
// FailureTolerance is the number of healthy servers that could be lost without
|
||||||
|
// an outage occurring.
|
||||||
|
FailureTolerance int
|
||||||
|
|
||||||
|
// Servers holds the health of each server.
|
||||||
|
Servers []ServerHealth
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadableDuration is a duration type that is serialized to JSON in human readable format.
|
||||||
|
type ReadableDuration time.Duration
|
||||||
|
|
||||||
|
func NewReadableDuration(dur time.Duration) *ReadableDuration {
|
||||||
|
d := ReadableDuration(dur)
|
||||||
|
return &d
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *ReadableDuration) String() string {
|
||||||
|
return d.Duration().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *ReadableDuration) Duration() time.Duration {
|
||||||
|
if d == nil {
|
||||||
|
return time.Duration(0)
|
||||||
|
}
|
||||||
|
return time.Duration(*d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *ReadableDuration) MarshalJSON() ([]byte, error) {
|
||||||
|
return []byte(fmt.Sprintf(`"%s"`, d.Duration().String())), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *ReadableDuration) UnmarshalJSON(raw []byte) error {
|
||||||
|
if d == nil {
|
||||||
|
return fmt.Errorf("cannot unmarshal to nil pointer")
|
||||||
|
}
|
||||||
|
|
||||||
|
str := string(raw)
|
||||||
|
if len(str) < 2 || str[0] != '"' || str[len(str)-1] != '"' {
|
||||||
|
return fmt.Errorf("must be enclosed with quotes: %s", str)
|
||||||
|
}
|
||||||
|
dur, err := time.ParseDuration(str[1 : len(str)-1])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*d = ReadableDuration(dur)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AutopilotGetConfiguration is used to query the current Autopilot configuration.
|
||||||
|
func (op *Operator) AutopilotGetConfiguration(q *QueryOptions) (*AutopilotConfiguration, error) {
|
||||||
|
r := op.c.newRequest("GET", "/v1/operator/autopilot/configuration")
|
||||||
|
r.setQueryOptions(q)
|
||||||
|
_, resp, err := requireOK(op.c.doRequest(r))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
var out AutopilotConfiguration
|
||||||
|
if err := decodeBody(resp, &out); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AutopilotSetConfiguration is used to set the current Autopilot configuration.
|
||||||
|
func (op *Operator) AutopilotSetConfiguration(conf *AutopilotConfiguration, q *WriteOptions) error {
|
||||||
|
r := op.c.newRequest("PUT", "/v1/operator/autopilot/configuration")
|
||||||
|
r.setWriteOptions(q)
|
||||||
|
r.obj = conf
|
||||||
|
_, resp, err := requireOK(op.c.doRequest(r))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
resp.Body.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AutopilotCASConfiguration is used to perform a Check-And-Set update on the
|
||||||
|
// Autopilot configuration. The ModifyIndex value will be respected. Returns
|
||||||
|
// true on success or false on failures.
|
||||||
|
func (op *Operator) AutopilotCASConfiguration(conf *AutopilotConfiguration, q *WriteOptions) (bool, error) {
|
||||||
|
r := op.c.newRequest("PUT", "/v1/operator/autopilot/configuration")
|
||||||
|
r.setWriteOptions(q)
|
||||||
|
r.params.Set("cas", strconv.FormatUint(conf.ModifyIndex, 10))
|
||||||
|
r.obj = conf
|
||||||
|
_, resp, err := requireOK(op.c.doRequest(r))
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if _, err := io.Copy(&buf, resp.Body); err != nil {
|
||||||
|
return false, fmt.Errorf("Failed to read response: %v", err)
|
||||||
|
}
|
||||||
|
res := strings.Contains(buf.String(), "true")
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AutopilotServerHealth
|
||||||
|
func (op *Operator) AutopilotServerHealth(q *QueryOptions) (*OperatorHealthReply, error) {
|
||||||
|
r := op.c.newRequest("GET", "/v1/operator/autopilot/health")
|
||||||
|
r.setQueryOptions(q)
|
||||||
|
_, resp, err := requireOK(op.c.doRequest(r))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
var out OperatorHealthReply
|
||||||
|
if err := decodeBody(resp, &out); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &out, nil
|
||||||
|
}
|
86
vendor/github.com/hashicorp/consul/api/operator_keyring.go
generated
vendored
Normal file
86
vendor/github.com/hashicorp/consul/api/operator_keyring.go
generated
vendored
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// Segment has the network segment this request corresponds to.
|
||||||
|
Segment 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
89
vendor/github.com/hashicorp/consul/api/operator_raft.go
generated
vendored
Normal file
89
vendor/github.com/hashicorp/consul/api/operator_raft.go
generated
vendored
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// Protocol version is the raft protocol version used by the server
|
||||||
|
ProtocolVersion string
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// RaftConfiguration 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
|
||||||
|
r.params.Set("address", string(address))
|
||||||
|
|
||||||
|
_, resp, err := requireOK(op.c.doRequest(r))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.Body.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RaftRemovePeerByID is used to kick a stale peer (one that it in the Raft
|
||||||
|
// quorum but no longer known to Serf or the catalog) by ID.
|
||||||
|
func (op *Operator) RaftRemovePeerByID(id string, q *WriteOptions) error {
|
||||||
|
r := op.c.newRequest("DELETE", "/v1/operator/raft/peer")
|
||||||
|
r.setWriteOptions(q)
|
||||||
|
|
||||||
|
r.params.Set("id", string(id))
|
||||||
|
|
||||||
|
_, resp, err := requireOK(op.c.doRequest(r))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.Body.Close()
|
||||||
|
return nil
|
||||||
|
}
|
11
vendor/github.com/hashicorp/consul/api/operator_segment.go
generated
vendored
Normal file
11
vendor/github.com/hashicorp/consul/api/operator_segment.go
generated
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
// SegmentList returns all the available LAN segments.
|
||||||
|
func (op *Operator) SegmentList(q *QueryOptions) ([]string, *QueryMeta, error) {
|
||||||
|
var out []string
|
||||||
|
qm, err := op.c.query("/v1/operator/segment", &out, q)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return out, qm, nil
|
||||||
|
}
|
13
vendor/github.com/hashicorp/consul/api/prepared_query.go
generated
vendored
13
vendor/github.com/hashicorp/consul/api/prepared_query.go
generated
vendored
|
@ -34,6 +34,12 @@ type ServiceQuery struct {
|
||||||
// local datacenter.
|
// local datacenter.
|
||||||
Failover QueryDatacenterOptions
|
Failover QueryDatacenterOptions
|
||||||
|
|
||||||
|
// IgnoreCheckIDs is an optional list of health check IDs to ignore when
|
||||||
|
// considering which nodes are healthy. It is useful as an emergency measure
|
||||||
|
// to temporarily override some health check that is producing false negatives
|
||||||
|
// for example.
|
||||||
|
IgnoreCheckIDs []string
|
||||||
|
|
||||||
// If OnlyPassing is true then we will only include nodes with passing
|
// If OnlyPassing is true then we will only include nodes with passing
|
||||||
// health checks (critical AND warning checks will cause a node to be
|
// health checks (critical AND warning checks will cause a node to be
|
||||||
// discarded)
|
// discarded)
|
||||||
|
@ -43,6 +49,11 @@ type ServiceQuery struct {
|
||||||
// this list it must be present. If the tag is preceded with "!" then
|
// this list it must be present. If the tag is preceded with "!" then
|
||||||
// it is disallowed.
|
// it is disallowed.
|
||||||
Tags []string
|
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.
|
// QueryTemplate carries the arguments for creating a templated query.
|
||||||
|
@ -56,7 +67,7 @@ type QueryTemplate struct {
|
||||||
Regexp string
|
Regexp string
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrepatedQueryDefinition defines a complete prepared query.
|
// PreparedQueryDefinition defines a complete prepared query.
|
||||||
type PreparedQueryDefinition struct {
|
type PreparedQueryDefinition struct {
|
||||||
// ID is this UUID-based ID for the query, always generated by Consul.
|
// ID is this UUID-based ID for the query, always generated by Consul.
|
||||||
ID string
|
ID string
|
||||||
|
|
11
vendor/github.com/hashicorp/consul/api/semaphore.go
generated
vendored
11
vendor/github.com/hashicorp/consul/api/semaphore.go
generated
vendored
|
@ -155,9 +155,11 @@ func (s *Semaphore) Acquire(stopCh <-chan struct{}) (<-chan struct{}, error) {
|
||||||
// Check if we need to create a session first
|
// Check if we need to create a session first
|
||||||
s.lockSession = s.opts.Session
|
s.lockSession = s.opts.Session
|
||||||
if s.lockSession == "" {
|
if s.lockSession == "" {
|
||||||
if sess, err := s.createSession(); err != nil {
|
sess, err := s.createSession()
|
||||||
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create session: %v", err)
|
return nil, fmt.Errorf("failed to create session: %v", err)
|
||||||
} else {
|
}
|
||||||
|
|
||||||
s.sessionRenew = make(chan struct{})
|
s.sessionRenew = make(chan struct{})
|
||||||
s.lockSession = sess
|
s.lockSession = sess
|
||||||
session := s.c.Session()
|
session := s.c.Session()
|
||||||
|
@ -171,7 +173,6 @@ func (s *Semaphore) Acquire(stopCh <-chan struct{}) (<-chan struct{}, error) {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Create the contender entry
|
// Create the contender entry
|
||||||
kv := s.c.KV()
|
kv := s.c.KV()
|
||||||
|
@ -197,7 +198,7 @@ WAIT:
|
||||||
|
|
||||||
// Handle the one-shot mode.
|
// Handle the one-shot mode.
|
||||||
if s.opts.SemaphoreTryOnce && attempts > 0 {
|
if s.opts.SemaphoreTryOnce && attempts > 0 {
|
||||||
elapsed := time.Now().Sub(start)
|
elapsed := time.Since(start)
|
||||||
if elapsed > qOpts.WaitTime {
|
if elapsed > qOpts.WaitTime {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
@ -491,7 +492,7 @@ RETRY:
|
||||||
// by doing retries. Note that we have to attempt the retry in a non-
|
// by doing retries. Note that we have to attempt the retry in a non-
|
||||||
// blocking fashion so that we have a clean place to reset the retry
|
// blocking fashion so that we have a clean place to reset the retry
|
||||||
// counter if service is restored.
|
// counter if service is restored.
|
||||||
if retries > 0 && IsServerError(err) {
|
if retries > 0 && IsRetryableError(err) {
|
||||||
time.Sleep(s.opts.MonitorRetryTime)
|
time.Sleep(s.opts.MonitorRetryTime)
|
||||||
retries--
|
retries--
|
||||||
opts.WaitIndex = 0
|
opts.WaitIndex = 0
|
||||||
|
|
9
vendor/github.com/hashicorp/consul/api/session.go
generated
vendored
9
vendor/github.com/hashicorp/consul/api/session.go
generated
vendored
|
@ -145,7 +145,9 @@ func (s *Session) Renew(id string, q *WriteOptions) (*SessionEntry, *WriteMeta,
|
||||||
// RenewPeriodic is used to periodically invoke Session.Renew on a
|
// RenewPeriodic is used to periodically invoke Session.Renew on a
|
||||||
// session until a doneCh is closed. This is meant to be used in a long running
|
// session until a doneCh is closed. This is meant to be used in a long running
|
||||||
// goroutine to ensure a session stays valid.
|
// goroutine to ensure a session stays valid.
|
||||||
func (s *Session) RenewPeriodic(initialTTL string, id string, q *WriteOptions, doneCh chan struct{}) error {
|
func (s *Session) RenewPeriodic(initialTTL string, id string, q *WriteOptions, doneCh <-chan struct{}) error {
|
||||||
|
ctx := q.Context()
|
||||||
|
|
||||||
ttl, err := time.ParseDuration(initialTTL)
|
ttl, err := time.ParseDuration(initialTTL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -179,6 +181,11 @@ func (s *Session) RenewPeriodic(initialTTL string, id string, q *WriteOptions, d
|
||||||
// Attempt a session destroy
|
// Attempt a session destroy
|
||||||
s.Destroy(id, q)
|
s.Destroy(id, q)
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
|
case <-ctx.Done():
|
||||||
|
// Bail immediately since attempting the destroy would
|
||||||
|
// use the canceled context in q, which would just bail.
|
||||||
|
return ctx.Err()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
363
vendor/github.com/hashicorp/go-rootcerts/LICENSE
generated
vendored
Normal file
363
vendor/github.com/hashicorp/go-rootcerts/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,363 @@
|
||||||
|
Mozilla Public License, version 2.0
|
||||||
|
|
||||||
|
1. Definitions
|
||||||
|
|
||||||
|
1.1. "Contributor"
|
||||||
|
|
||||||
|
means each individual or legal entity that creates, contributes to the
|
||||||
|
creation of, or owns Covered Software.
|
||||||
|
|
||||||
|
1.2. "Contributor Version"
|
||||||
|
|
||||||
|
means the combination of the Contributions of others (if any) used by a
|
||||||
|
Contributor and that particular Contributor's Contribution.
|
||||||
|
|
||||||
|
1.3. "Contribution"
|
||||||
|
|
||||||
|
means Covered Software of a particular Contributor.
|
||||||
|
|
||||||
|
1.4. "Covered Software"
|
||||||
|
|
||||||
|
means Source Code Form to which the initial Contributor has attached the
|
||||||
|
notice in Exhibit A, the Executable Form of such Source Code Form, and
|
||||||
|
Modifications of such Source Code Form, in each case including portions
|
||||||
|
thereof.
|
||||||
|
|
||||||
|
1.5. "Incompatible With Secondary Licenses"
|
||||||
|
means
|
||||||
|
|
||||||
|
a. that the initial Contributor has attached the notice described in
|
||||||
|
Exhibit B to the Covered Software; or
|
||||||
|
|
||||||
|
b. that the Covered Software was made available under the terms of
|
||||||
|
version 1.1 or earlier of the License, but not also under the terms of
|
||||||
|
a Secondary License.
|
||||||
|
|
||||||
|
1.6. "Executable Form"
|
||||||
|
|
||||||
|
means any form of the work other than Source Code Form.
|
||||||
|
|
||||||
|
1.7. "Larger Work"
|
||||||
|
|
||||||
|
means a work that combines Covered Software with other material, in a
|
||||||
|
separate file or files, that is not Covered Software.
|
||||||
|
|
||||||
|
1.8. "License"
|
||||||
|
|
||||||
|
means this document.
|
||||||
|
|
||||||
|
1.9. "Licensable"
|
||||||
|
|
||||||
|
means having the right to grant, to the maximum extent possible, whether
|
||||||
|
at the time of the initial grant or subsequently, any and all of the
|
||||||
|
rights conveyed by this License.
|
||||||
|
|
||||||
|
1.10. "Modifications"
|
||||||
|
|
||||||
|
means any of the following:
|
||||||
|
|
||||||
|
a. any file in Source Code Form that results from an addition to,
|
||||||
|
deletion from, or modification of the contents of Covered Software; or
|
||||||
|
|
||||||
|
b. any new file in Source Code Form that contains any Covered Software.
|
||||||
|
|
||||||
|
1.11. "Patent Claims" of a Contributor
|
||||||
|
|
||||||
|
means any patent claim(s), including without limitation, method,
|
||||||
|
process, and apparatus claims, in any patent Licensable by such
|
||||||
|
Contributor that would be infringed, but for the grant of the License,
|
||||||
|
by the making, using, selling, offering for sale, having made, import,
|
||||||
|
or transfer of either its Contributions or its Contributor Version.
|
||||||
|
|
||||||
|
1.12. "Secondary License"
|
||||||
|
|
||||||
|
means either the GNU General Public License, Version 2.0, the GNU Lesser
|
||||||
|
General Public License, Version 2.1, the GNU Affero General Public
|
||||||
|
License, Version 3.0, or any later versions of those licenses.
|
||||||
|
|
||||||
|
1.13. "Source Code Form"
|
||||||
|
|
||||||
|
means the form of the work preferred for making modifications.
|
||||||
|
|
||||||
|
1.14. "You" (or "Your")
|
||||||
|
|
||||||
|
means an individual or a legal entity exercising rights under this
|
||||||
|
License. For legal entities, "You" includes any entity that controls, is
|
||||||
|
controlled by, or is under common control with You. For purposes of this
|
||||||
|
definition, "control" means (a) the power, direct or indirect, to cause
|
||||||
|
the direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (b) ownership of more than fifty percent (50%) of the
|
||||||
|
outstanding shares or beneficial ownership of such entity.
|
||||||
|
|
||||||
|
|
||||||
|
2. License Grants and Conditions
|
||||||
|
|
||||||
|
2.1. Grants
|
||||||
|
|
||||||
|
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||||
|
non-exclusive license:
|
||||||
|
|
||||||
|
a. under intellectual property rights (other than patent or trademark)
|
||||||
|
Licensable by such Contributor to use, reproduce, make available,
|
||||||
|
modify, display, perform, distribute, and otherwise exploit its
|
||||||
|
Contributions, either on an unmodified basis, with Modifications, or
|
||||||
|
as part of a Larger Work; and
|
||||||
|
|
||||||
|
b. under Patent Claims of such Contributor to make, use, sell, offer for
|
||||||
|
sale, have made, import, and otherwise transfer either its
|
||||||
|
Contributions or its Contributor Version.
|
||||||
|
|
||||||
|
2.2. Effective Date
|
||||||
|
|
||||||
|
The licenses granted in Section 2.1 with respect to any Contribution
|
||||||
|
become effective for each Contribution on the date the Contributor first
|
||||||
|
distributes such Contribution.
|
||||||
|
|
||||||
|
2.3. Limitations on Grant Scope
|
||||||
|
|
||||||
|
The licenses granted in this Section 2 are the only rights granted under
|
||||||
|
this License. No additional rights or licenses will be implied from the
|
||||||
|
distribution or licensing of Covered Software under this License.
|
||||||
|
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||||
|
Contributor:
|
||||||
|
|
||||||
|
a. for any code that a Contributor has removed from Covered Software; or
|
||||||
|
|
||||||
|
b. for infringements caused by: (i) Your and any other third party's
|
||||||
|
modifications of Covered Software, or (ii) the combination of its
|
||||||
|
Contributions with other software (except as part of its Contributor
|
||||||
|
Version); or
|
||||||
|
|
||||||
|
c. under Patent Claims infringed by Covered Software in the absence of
|
||||||
|
its Contributions.
|
||||||
|
|
||||||
|
This License does not grant any rights in the trademarks, service marks,
|
||||||
|
or logos of any Contributor (except as may be necessary to comply with
|
||||||
|
the notice requirements in Section 3.4).
|
||||||
|
|
||||||
|
2.4. Subsequent Licenses
|
||||||
|
|
||||||
|
No Contributor makes additional grants as a result of Your choice to
|
||||||
|
distribute the Covered Software under a subsequent version of this
|
||||||
|
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||||
|
permitted under the terms of Section 3.3).
|
||||||
|
|
||||||
|
2.5. Representation
|
||||||
|
|
||||||
|
Each Contributor represents that the Contributor believes its
|
||||||
|
Contributions are its original creation(s) or it has sufficient rights to
|
||||||
|
grant the rights to its Contributions conveyed by this License.
|
||||||
|
|
||||||
|
2.6. Fair Use
|
||||||
|
|
||||||
|
This License is not intended to limit any rights You have under
|
||||||
|
applicable copyright doctrines of fair use, fair dealing, or other
|
||||||
|
equivalents.
|
||||||
|
|
||||||
|
2.7. Conditions
|
||||||
|
|
||||||
|
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
|
||||||
|
Section 2.1.
|
||||||
|
|
||||||
|
|
||||||
|
3. Responsibilities
|
||||||
|
|
||||||
|
3.1. Distribution of Source Form
|
||||||
|
|
||||||
|
All distribution of Covered Software in Source Code Form, including any
|
||||||
|
Modifications that You create or to which You contribute, must be under
|
||||||
|
the terms of this License. You must inform recipients that the Source
|
||||||
|
Code Form of the Covered Software is governed by the terms of this
|
||||||
|
License, and how they can obtain a copy of this License. You may not
|
||||||
|
attempt to alter or restrict the recipients' rights in the Source Code
|
||||||
|
Form.
|
||||||
|
|
||||||
|
3.2. Distribution of Executable Form
|
||||||
|
|
||||||
|
If You distribute Covered Software in Executable Form then:
|
||||||
|
|
||||||
|
a. such Covered Software must also be made available in Source Code Form,
|
||||||
|
as described in Section 3.1, and You must inform recipients of the
|
||||||
|
Executable Form how they can obtain a copy of such Source Code Form by
|
||||||
|
reasonable means in a timely manner, at a charge no more than the cost
|
||||||
|
of distribution to the recipient; and
|
||||||
|
|
||||||
|
b. You may distribute such Executable Form under the terms of this
|
||||||
|
License, or sublicense it under different terms, provided that the
|
||||||
|
license for the Executable Form does not attempt to limit or alter the
|
||||||
|
recipients' rights in the Source Code Form under this License.
|
||||||
|
|
||||||
|
3.3. Distribution of a Larger Work
|
||||||
|
|
||||||
|
You may create and distribute a Larger Work under terms of Your choice,
|
||||||
|
provided that You also comply with the requirements of this License for
|
||||||
|
the Covered Software. If the Larger Work is a combination of Covered
|
||||||
|
Software with a work governed by one or more Secondary Licenses, and the
|
||||||
|
Covered Software is not Incompatible With Secondary Licenses, this
|
||||||
|
License permits You to additionally distribute such Covered Software
|
||||||
|
under the terms of such Secondary License(s), so that the recipient of
|
||||||
|
the Larger Work may, at their option, further distribute the Covered
|
||||||
|
Software under the terms of either this License or such Secondary
|
||||||
|
License(s).
|
||||||
|
|
||||||
|
3.4. Notices
|
||||||
|
|
||||||
|
You may not remove or alter the substance of any license notices
|
||||||
|
(including copyright notices, patent notices, disclaimers of warranty, or
|
||||||
|
limitations of liability) contained within the Source Code Form of the
|
||||||
|
Covered Software, except that You may alter any license notices to the
|
||||||
|
extent required to remedy known factual inaccuracies.
|
||||||
|
|
||||||
|
3.5. Application of Additional Terms
|
||||||
|
|
||||||
|
You may choose to offer, and to charge a fee for, warranty, support,
|
||||||
|
indemnity or liability obligations to one or more recipients of Covered
|
||||||
|
Software. However, You may do so only on Your own behalf, and not on
|
||||||
|
behalf of any Contributor. You must make it absolutely clear that any
|
||||||
|
such warranty, support, indemnity, or liability obligation is offered by
|
||||||
|
You alone, and You hereby agree to indemnify every Contributor for any
|
||||||
|
liability incurred by such Contributor as a result of warranty, support,
|
||||||
|
indemnity or liability terms You offer. You may include additional
|
||||||
|
disclaimers of warranty and limitations of liability specific to any
|
||||||
|
jurisdiction.
|
||||||
|
|
||||||
|
4. Inability to Comply Due to Statute or Regulation
|
||||||
|
|
||||||
|
If it is impossible for You to comply with any of the terms of this License
|
||||||
|
with respect to some or all of the Covered Software due to statute,
|
||||||
|
judicial order, or regulation then You must: (a) comply with the terms of
|
||||||
|
this License to the maximum extent possible; and (b) describe the
|
||||||
|
limitations and the code they affect. Such description must be placed in a
|
||||||
|
text file included with all distributions of the Covered Software under
|
||||||
|
this License. Except to the extent prohibited by statute or regulation,
|
||||||
|
such description must be sufficiently detailed for a recipient of ordinary
|
||||||
|
skill to be able to understand it.
|
||||||
|
|
||||||
|
5. Termination
|
||||||
|
|
||||||
|
5.1. The rights granted under this License will terminate automatically if You
|
||||||
|
fail to comply with any of its terms. However, if You become compliant,
|
||||||
|
then the rights granted under this License from a particular Contributor
|
||||||
|
are reinstated (a) provisionally, unless and until such Contributor
|
||||||
|
explicitly and finally terminates Your grants, and (b) on an ongoing
|
||||||
|
basis, if such Contributor fails to notify You of the non-compliance by
|
||||||
|
some reasonable means prior to 60 days after You have come back into
|
||||||
|
compliance. Moreover, Your grants from a particular Contributor are
|
||||||
|
reinstated on an ongoing basis if such Contributor notifies You of the
|
||||||
|
non-compliance by some reasonable means, this is the first time You have
|
||||||
|
received notice of non-compliance with this License from such
|
||||||
|
Contributor, and You become compliant prior to 30 days after Your receipt
|
||||||
|
of the notice.
|
||||||
|
|
||||||
|
5.2. If You initiate litigation against any entity by asserting a patent
|
||||||
|
infringement claim (excluding declaratory judgment actions,
|
||||||
|
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||||
|
directly or indirectly infringes any patent, then the rights granted to
|
||||||
|
You by any and all Contributors for the Covered Software under Section
|
||||||
|
2.1 of this License shall terminate.
|
||||||
|
|
||||||
|
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
|
||||||
|
license agreements (excluding distributors and resellers) which have been
|
||||||
|
validly granted by You or Your distributors under this License prior to
|
||||||
|
termination shall survive termination.
|
||||||
|
|
||||||
|
6. Disclaimer of Warranty
|
||||||
|
|
||||||
|
Covered Software is provided under this License on an "as is" basis,
|
||||||
|
without warranty of any kind, either expressed, implied, or statutory,
|
||||||
|
including, without limitation, warranties that the Covered Software is free
|
||||||
|
of defects, merchantable, fit for a particular purpose or non-infringing.
|
||||||
|
The entire risk as to the quality and performance of the Covered Software
|
||||||
|
is with You. Should any Covered Software prove defective in any respect,
|
||||||
|
You (not any Contributor) assume the cost of any necessary servicing,
|
||||||
|
repair, or correction. This disclaimer of warranty constitutes an essential
|
||||||
|
part of this License. No use of any Covered Software is authorized under
|
||||||
|
this License except under this disclaimer.
|
||||||
|
|
||||||
|
7. Limitation of Liability
|
||||||
|
|
||||||
|
Under no circumstances and under no legal theory, whether tort (including
|
||||||
|
negligence), contract, or otherwise, shall any Contributor, or anyone who
|
||||||
|
distributes Covered Software as permitted above, be liable to You for any
|
||||||
|
direct, indirect, special, incidental, or consequential damages of any
|
||||||
|
character including, without limitation, damages for lost profits, loss of
|
||||||
|
goodwill, work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses, even if such party shall have been
|
||||||
|
informed of the possibility of such damages. This limitation of liability
|
||||||
|
shall not apply to liability for death or personal injury resulting from
|
||||||
|
such party's negligence to the extent applicable law prohibits such
|
||||||
|
limitation. Some jurisdictions do not allow the exclusion or limitation of
|
||||||
|
incidental or consequential damages, so this exclusion and limitation may
|
||||||
|
not apply to You.
|
||||||
|
|
||||||
|
8. Litigation
|
||||||
|
|
||||||
|
Any litigation relating to this License may be brought only in the courts
|
||||||
|
of a jurisdiction where the defendant maintains its principal place of
|
||||||
|
business and such litigation shall be governed by laws of that
|
||||||
|
jurisdiction, without reference to its conflict-of-law provisions. Nothing
|
||||||
|
in this Section shall prevent a party's ability to bring cross-claims or
|
||||||
|
counter-claims.
|
||||||
|
|
||||||
|
9. Miscellaneous
|
||||||
|
|
||||||
|
This License represents the complete agreement concerning the subject
|
||||||
|
matter hereof. If any provision of this License is held to be
|
||||||
|
unenforceable, such provision shall be reformed only to the extent
|
||||||
|
necessary to make it enforceable. Any law or regulation which provides that
|
||||||
|
the language of a contract shall be construed against the drafter shall not
|
||||||
|
be used to construe this License against a Contributor.
|
||||||
|
|
||||||
|
|
||||||
|
10. Versions of the License
|
||||||
|
|
||||||
|
10.1. New Versions
|
||||||
|
|
||||||
|
Mozilla Foundation is the license steward. Except as provided in Section
|
||||||
|
10.3, no one other than the license steward has the right to modify or
|
||||||
|
publish new versions of this License. Each version will be given a
|
||||||
|
distinguishing version number.
|
||||||
|
|
||||||
|
10.2. Effect of New Versions
|
||||||
|
|
||||||
|
You may distribute the Covered Software under the terms of the version
|
||||||
|
of the License under which You originally received the Covered Software,
|
||||||
|
or under the terms of any subsequent version published by the license
|
||||||
|
steward.
|
||||||
|
|
||||||
|
10.3. Modified Versions
|
||||||
|
|
||||||
|
If you create software not governed by this License, and you want to
|
||||||
|
create a new license for such software, you may create and use a
|
||||||
|
modified version of this License if you rename the license and remove
|
||||||
|
any references to the name of the license steward (except to note that
|
||||||
|
such modified license differs from this License).
|
||||||
|
|
||||||
|
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||||
|
Licenses If You choose to distribute Source Code Form that is
|
||||||
|
Incompatible With Secondary Licenses under the terms of this version of
|
||||||
|
the License, the notice described in Exhibit B of this License must be
|
||||||
|
attached.
|
||||||
|
|
||||||
|
Exhibit A - Source Code Form License Notice
|
||||||
|
|
||||||
|
This Source Code Form is subject to the
|
||||||
|
terms of the Mozilla Public License, v.
|
||||||
|
2.0. If a copy of the MPL was not
|
||||||
|
distributed with this file, You can
|
||||||
|
obtain one at
|
||||||
|
http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
If it is not possible or desirable to put the notice in a particular file,
|
||||||
|
then You may include the notice in a location (such as a LICENSE file in a
|
||||||
|
relevant directory) where a recipient would be likely to look for such a
|
||||||
|
notice.
|
||||||
|
|
||||||
|
You may add additional accurate notices of copyright ownership.
|
||||||
|
|
||||||
|
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||||
|
|
||||||
|
This Source Code Form is "Incompatible
|
||||||
|
With Secondary Licenses", as defined by
|
||||||
|
the Mozilla Public License, v. 2.0.
|
||||||
|
|
8
vendor/github.com/hashicorp/go-rootcerts/Makefile
generated
vendored
Normal file
8
vendor/github.com/hashicorp/go-rootcerts/Makefile
generated
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
TEST?=./...
|
||||||
|
|
||||||
|
test:
|
||||||
|
go test $(TEST) $(TESTARGS) -timeout=3s -parallel=4
|
||||||
|
go vet $(TEST)
|
||||||
|
go test $(TEST) -race
|
||||||
|
|
||||||
|
.PHONY: test
|
43
vendor/github.com/hashicorp/go-rootcerts/README.md
generated
vendored
Normal file
43
vendor/github.com/hashicorp/go-rootcerts/README.md
generated
vendored
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
# rootcerts
|
||||||
|
|
||||||
|
Functions for loading root certificates for TLS connections.
|
||||||
|
|
||||||
|
-----
|
||||||
|
|
||||||
|
Go's standard library `crypto/tls` provides a common mechanism for configuring
|
||||||
|
TLS connections in `tls.Config`. The `RootCAs` field on this struct is a pool
|
||||||
|
of certificates for the client to use as a trust store when verifying server
|
||||||
|
certificates.
|
||||||
|
|
||||||
|
This library contains utility functions for loading certificates destined for
|
||||||
|
that field, as well as one other important thing:
|
||||||
|
|
||||||
|
When the `RootCAs` field is `nil`, the standard library attempts to load the
|
||||||
|
host's root CA set. This behavior is OS-specific, and the Darwin
|
||||||
|
implementation contains [a bug that prevents trusted certificates from the
|
||||||
|
System and Login keychains from being loaded][1]. This library contains
|
||||||
|
Darwin-specific behavior that works around that bug.
|
||||||
|
|
||||||
|
[1]: https://github.com/golang/go/issues/14514
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
Here's a snippet demonstrating how this library is meant to be used:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func httpClient() (*http.Client, error)
|
||||||
|
tlsConfig := &tls.Config{}
|
||||||
|
err := rootcerts.ConfigureTLS(tlsConfig, &rootcerts.Config{
|
||||||
|
CAFile: os.Getenv("MYAPP_CAFILE"),
|
||||||
|
CAPath: os.Getenv("MYAPP_CAPATH"),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
c := cleanhttp.DefaultClient()
|
||||||
|
t := cleanhttp.DefaultTransport()
|
||||||
|
t.TLSClientConfig = tlsConfig
|
||||||
|
c.Transport = t
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
```
|
9
vendor/github.com/hashicorp/go-rootcerts/doc.go
generated
vendored
Normal file
9
vendor/github.com/hashicorp/go-rootcerts/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
// Package rootcerts contains functions to aid in loading CA certificates for
|
||||||
|
// TLS connections.
|
||||||
|
//
|
||||||
|
// In addition, its default behavior on Darwin works around an open issue [1]
|
||||||
|
// in Go's crypto/x509 that prevents certicates from being loaded from the
|
||||||
|
// System or Login keychains.
|
||||||
|
//
|
||||||
|
// [1] https://github.com/golang/go/issues/14514
|
||||||
|
package rootcerts
|
103
vendor/github.com/hashicorp/go-rootcerts/rootcerts.go
generated
vendored
Normal file
103
vendor/github.com/hashicorp/go-rootcerts/rootcerts.go
generated
vendored
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
package rootcerts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config determines where LoadCACerts will load certificates from. When both
|
||||||
|
// CAFile and CAPath are blank, this library's functions will either load
|
||||||
|
// system roots explicitly and return them, or set the CertPool to nil to allow
|
||||||
|
// Go's standard library to load system certs.
|
||||||
|
type Config struct {
|
||||||
|
// CAFile is a path to a PEM-encoded certificate file or bundle. Takes
|
||||||
|
// precedence over CAPath.
|
||||||
|
CAFile string
|
||||||
|
|
||||||
|
// CAPath is a path to a directory populated with PEM-encoded certificates.
|
||||||
|
CAPath string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigureTLS sets up the RootCAs on the provided tls.Config based on the
|
||||||
|
// Config specified.
|
||||||
|
func ConfigureTLS(t *tls.Config, c *Config) error {
|
||||||
|
if t == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
pool, err := LoadCACerts(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.RootCAs = pool
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadCACerts loads a CertPool based on the Config specified.
|
||||||
|
func LoadCACerts(c *Config) (*x509.CertPool, error) {
|
||||||
|
if c == nil {
|
||||||
|
c = &Config{}
|
||||||
|
}
|
||||||
|
if c.CAFile != "" {
|
||||||
|
return LoadCAFile(c.CAFile)
|
||||||
|
}
|
||||||
|
if c.CAPath != "" {
|
||||||
|
return LoadCAPath(c.CAPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
return LoadSystemCAs()
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadCAFile loads a single PEM-encoded file from the path specified.
|
||||||
|
func LoadCAFile(caFile string) (*x509.CertPool, error) {
|
||||||
|
pool := x509.NewCertPool()
|
||||||
|
|
||||||
|
pem, err := ioutil.ReadFile(caFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error loading CA File: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ok := pool.AppendCertsFromPEM(pem)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("Error loading CA File: Couldn't parse PEM in: %s", caFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
return pool, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadCAPath walks the provided path and loads all certificates encounted into
|
||||||
|
// a pool.
|
||||||
|
func LoadCAPath(caPath string) (*x509.CertPool, error) {
|
||||||
|
pool := x509.NewCertPool()
|
||||||
|
walkFn := func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
pem, err := ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error loading file from CAPath: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ok := pool.AppendCertsFromPEM(pem)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Error loading CA Path: Couldn't parse PEM in: %s", path)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := filepath.Walk(caPath, walkFn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return pool, nil
|
||||||
|
}
|
12
vendor/github.com/hashicorp/go-rootcerts/rootcerts_base.go
generated
vendored
Normal file
12
vendor/github.com/hashicorp/go-rootcerts/rootcerts_base.go
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
// +build !darwin
|
||||||
|
|
||||||
|
package rootcerts
|
||||||
|
|
||||||
|
import "crypto/x509"
|
||||||
|
|
||||||
|
// LoadSystemCAs does nothing on non-Darwin systems. We return nil so that
|
||||||
|
// default behavior of standard TLS config libraries is triggered, which is to
|
||||||
|
// load system certs.
|
||||||
|
func LoadSystemCAs() (*x509.CertPool, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
48
vendor/github.com/hashicorp/go-rootcerts/rootcerts_darwin.go
generated
vendored
Normal file
48
vendor/github.com/hashicorp/go-rootcerts/rootcerts_darwin.go
generated
vendored
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
package rootcerts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/x509"
|
||||||
|
"os/exec"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"github.com/mitchellh/go-homedir"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LoadSystemCAs has special behavior on Darwin systems to work around
|
||||||
|
func LoadSystemCAs() (*x509.CertPool, error) {
|
||||||
|
pool := x509.NewCertPool()
|
||||||
|
|
||||||
|
for _, keychain := range certKeychains() {
|
||||||
|
err := addCertsFromKeychain(pool, keychain)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pool, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func addCertsFromKeychain(pool *x509.CertPool, keychain string) error {
|
||||||
|
cmd := exec.Command("/usr/bin/security", "find-certificate", "-a", "-p", keychain)
|
||||||
|
data, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
pool.AppendCertsFromPEM(data)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func certKeychains() []string {
|
||||||
|
keychains := []string{
|
||||||
|
"/System/Library/Keychains/SystemRootCertificates.keychain",
|
||||||
|
"/Library/Keychains/System.keychain",
|
||||||
|
}
|
||||||
|
home, err := homedir.Dir()
|
||||||
|
if err == nil {
|
||||||
|
loginKeychain := path.Join(home, "Library", "Keychains", "login.keychain")
|
||||||
|
keychains = append(keychains, loginKeychain)
|
||||||
|
}
|
||||||
|
return keychains
|
||||||
|
}
|
21
vendor/github.com/mitchellh/go-homedir/LICENSE
generated
vendored
Normal file
21
vendor/github.com/mitchellh/go-homedir/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013 Mitchell Hashimoto
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
14
vendor/github.com/mitchellh/go-homedir/README.md
generated
vendored
Normal file
14
vendor/github.com/mitchellh/go-homedir/README.md
generated
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# go-homedir
|
||||||
|
|
||||||
|
This is a Go library for detecting the user's home directory without
|
||||||
|
the use of cgo, so the library can be used in cross-compilation environments.
|
||||||
|
|
||||||
|
Usage is incredibly simple, just call `homedir.Dir()` to get the home directory
|
||||||
|
for a user, and `homedir.Expand()` to expand the `~` in a path to the home
|
||||||
|
directory.
|
||||||
|
|
||||||
|
**Why not just use `os/user`?** The built-in `os/user` package requires
|
||||||
|
cgo on Darwin systems. This means that any Go code that uses that package
|
||||||
|
cannot cross compile. But 99% of the time the use for `os/user` is just to
|
||||||
|
retrieve the home directory, which we can do for the current user without
|
||||||
|
cgo. This library does that, enabling cross-compilation.
|
155
vendor/github.com/mitchellh/go-homedir/homedir.go
generated
vendored
Normal file
155
vendor/github.com/mitchellh/go-homedir/homedir.go
generated
vendored
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
package homedir
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DisableCache will disable caching of the home directory. Caching is enabled
|
||||||
|
// by default.
|
||||||
|
var DisableCache bool
|
||||||
|
|
||||||
|
var homedirCache string
|
||||||
|
var cacheLock sync.RWMutex
|
||||||
|
|
||||||
|
// Dir returns the home directory for the executing user.
|
||||||
|
//
|
||||||
|
// This uses an OS-specific method for discovering the home directory.
|
||||||
|
// An error is returned if a home directory cannot be detected.
|
||||||
|
func Dir() (string, error) {
|
||||||
|
if !DisableCache {
|
||||||
|
cacheLock.RLock()
|
||||||
|
cached := homedirCache
|
||||||
|
cacheLock.RUnlock()
|
||||||
|
if cached != "" {
|
||||||
|
return cached, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cacheLock.Lock()
|
||||||
|
defer cacheLock.Unlock()
|
||||||
|
|
||||||
|
var result string
|
||||||
|
var err error
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
result, err = dirWindows()
|
||||||
|
} else {
|
||||||
|
// Unix-like system, so just assume Unix
|
||||||
|
result, err = dirUnix()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
homedirCache = result
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expand expands the path to include the home directory if the path
|
||||||
|
// is prefixed with `~`. If it isn't prefixed with `~`, the path is
|
||||||
|
// returned as-is.
|
||||||
|
func Expand(path string) (string, error) {
|
||||||
|
if len(path) == 0 {
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if path[0] != '~' {
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(path) > 1 && path[1] != '/' && path[1] != '\\' {
|
||||||
|
return "", errors.New("cannot expand user-specific home dir")
|
||||||
|
}
|
||||||
|
|
||||||
|
dir, err := Dir()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return filepath.Join(dir, path[1:]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func dirUnix() (string, error) {
|
||||||
|
homeEnv := "HOME"
|
||||||
|
if runtime.GOOS == "plan9" {
|
||||||
|
// On plan9, env vars are lowercase.
|
||||||
|
homeEnv = "home"
|
||||||
|
}
|
||||||
|
|
||||||
|
// First prefer the HOME environmental variable
|
||||||
|
if home := os.Getenv(homeEnv); home != "" {
|
||||||
|
return home, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var stdout bytes.Buffer
|
||||||
|
|
||||||
|
// If that fails, try OS specific commands
|
||||||
|
if runtime.GOOS == "darwin" {
|
||||||
|
cmd := exec.Command("sh", "-c", `dscl -q . -read /Users/"$(whoami)" NFSHomeDirectory | sed 's/^[^ ]*: //'`)
|
||||||
|
cmd.Stdout = &stdout
|
||||||
|
if err := cmd.Run(); err == nil {
|
||||||
|
result := strings.TrimSpace(stdout.String())
|
||||||
|
if result != "" {
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cmd := exec.Command("getent", "passwd", strconv.Itoa(os.Getuid()))
|
||||||
|
cmd.Stdout = &stdout
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
// If the error is ErrNotFound, we ignore it. Otherwise, return it.
|
||||||
|
if err != exec.ErrNotFound {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if passwd := strings.TrimSpace(stdout.String()); passwd != "" {
|
||||||
|
// username:password:uid:gid:gecos:home:shell
|
||||||
|
passwdParts := strings.SplitN(passwd, ":", 7)
|
||||||
|
if len(passwdParts) > 5 {
|
||||||
|
return passwdParts[5], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If all else fails, try the shell
|
||||||
|
stdout.Reset()
|
||||||
|
cmd := exec.Command("sh", "-c", "cd && pwd")
|
||||||
|
cmd.Stdout = &stdout
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
result := strings.TrimSpace(stdout.String())
|
||||||
|
if result == "" {
|
||||||
|
return "", errors.New("blank output when reading home directory")
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func dirWindows() (string, error) {
|
||||||
|
// First prefer the HOME environmental variable
|
||||||
|
if home := os.Getenv("HOME"); home != "" {
|
||||||
|
return home, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
drive := os.Getenv("HOMEDRIVE")
|
||||||
|
path := os.Getenv("HOMEPATH")
|
||||||
|
home := drive + path
|
||||||
|
if drive == "" || path == "" {
|
||||||
|
home = os.Getenv("USERPROFILE")
|
||||||
|
}
|
||||||
|
if home == "" {
|
||||||
|
return "", errors.New("HOMEDRIVE, HOMEPATH, and USERPROFILE are blank")
|
||||||
|
}
|
||||||
|
|
||||||
|
return home, nil
|
||||||
|
}
|
18
vendor/vendor.json
vendored
18
vendor/vendor.json
vendored
|
@ -560,10 +560,10 @@
|
||||||
"versionExact": "v1.2.2"
|
"versionExact": "v1.2.2"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "/DReHn5j0caPm3thgFD9DmOmibQ=",
|
"checksumSHA1": "cZw5u6BGWe3gjJxmXT6J0Tfn6Mw=",
|
||||||
"path": "github.com/hashicorp/consul/api",
|
"path": "github.com/hashicorp/consul/api",
|
||||||
"revision": "23ce10f8891369f4c7758474c7c808f4e0262701",
|
"revision": "bed22a81e9fdd367171c14b2169f05e50e1cc905",
|
||||||
"revisionTime": "2017-01-12T01:29:24Z"
|
"revisionTime": "2018-06-15T16:10:29Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "Uzyon2091lmwacNsl1hCytjhHtg=",
|
"checksumSHA1": "Uzyon2091lmwacNsl1hCytjhHtg=",
|
||||||
|
@ -571,6 +571,12 @@
|
||||||
"revision": "ad28ea4487f05916463e2423a55166280e8254b5",
|
"revision": "ad28ea4487f05916463e2423a55166280e8254b5",
|
||||||
"revisionTime": "2016-04-07T17:41:26Z"
|
"revisionTime": "2016-04-07T17:41:26Z"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "A1PcINvF3UiwHRKn8UcgARgvGRs=",
|
||||||
|
"path": "github.com/hashicorp/go-rootcerts",
|
||||||
|
"revision": "6bb64b370b90e7ef1fa532be9e591a81c3493e00",
|
||||||
|
"revisionTime": "2016-05-03T14:34:40Z"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "E3Xcanc9ouQwL+CZGOUyA/+giLg=",
|
"checksumSHA1": "E3Xcanc9ouQwL+CZGOUyA/+giLg=",
|
||||||
"path": "github.com/hashicorp/serf/coordinate",
|
"path": "github.com/hashicorp/serf/coordinate",
|
||||||
|
@ -661,6 +667,12 @@
|
||||||
"version": "v1.0.4",
|
"version": "v1.0.4",
|
||||||
"versionExact": "v1.0.4"
|
"versionExact": "v1.0.4"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "SvPS4zqZYImMcBu+dLgp6+9eeUo=",
|
||||||
|
"path": "github.com/mitchellh/go-homedir",
|
||||||
|
"revision": "3864e76763d94a6df2f9960b16a20a33da9f9a66",
|
||||||
|
"revisionTime": "2018-05-23T09:45:22Z"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "ZYfqG6bNE3cRlbsvpJBL0bF6DSc=",
|
"checksumSHA1": "ZYfqG6bNE3cRlbsvpJBL0bF6DSc=",
|
||||||
"path": "github.com/mwitkow/go-conntrack",
|
"path": "github.com/mwitkow/go-conntrack",
|
||||||
|
|
Loading…
Reference in a new issue