Update `github.com/hashicorp/consul/api` to the latest and greatest.

This commit is contained in:
Sean Chittenden 2017-02-07 14:46:10 -08:00
parent 77d7e25018
commit 7063ee1af2
No known key found for this signature in database
GPG Key ID: 4EBC9DC16C2E5E16
10 changed files with 481 additions and 62 deletions

View File

@ -1,6 +1,7 @@
package api package api
import ( import (
"bufio"
"fmt" "fmt"
) )
@ -62,8 +63,7 @@ type AgentCheckRegistration struct {
AgentServiceCheck AgentServiceCheck
} }
// AgentServiceCheck is used to create an associated // AgentServiceCheck is used to define a node or service level check
// check for a service
type AgentServiceCheck struct { type AgentServiceCheck struct {
Script string `json:",omitempty"` Script string `json:",omitempty"`
DockerContainerID string `json:",omitempty"` DockerContainerID string `json:",omitempty"`
@ -74,6 +74,16 @@ type AgentServiceCheck struct {
HTTP string `json:",omitempty"` HTTP string `json:",omitempty"`
TCP string `json:",omitempty"` TCP string `json:",omitempty"`
Status string `json:",omitempty"` Status string `json:",omitempty"`
Notes string `json:",omitempty"`
TLSSkipVerify bool `json:",omitempty"`
// In Consul 0.7 and later, checks that are associated with a service
// may also contain this optional DeregisterCriticalServiceAfter field,
// which is a timeout in the same Go time format as Interval and TTL. If
// a check is in the critical state for more than this configured value,
// then its associated service (and all of its associated checks) will
// automatically be deregistered.
DeregisterCriticalServiceAfter string `json:",omitempty"`
} }
type AgentServiceChecks []*AgentServiceCheck type AgentServiceChecks []*AgentServiceCheck
@ -107,6 +117,17 @@ func (a *Agent) Self() (map[string]map[string]interface{}, error) {
return out, nil return out, nil
} }
// Reload triggers a configuration reload for the agent we are connected to.
func (a *Agent) Reload() error {
r := a.c.newRequest("PUT", "/v1/agent/reload")
_, resp, err := requireOK(a.c.doRequest(r))
if err != nil {
return err
}
resp.Body.Close()
return nil
}
// NodeName is used to get the node name of the agent // NodeName is used to get the node name of the agent
func (a *Agent) NodeName() (string, error) { func (a *Agent) NodeName() (string, error) {
if a.nodeName != "" { if a.nodeName != "" {
@ -338,6 +359,17 @@ func (a *Agent) Join(addr string, wan bool) error {
return nil return nil
} }
// Leave is used to have the agent gracefully leave the cluster and shutdown
func (a *Agent) Leave() error {
r := a.c.newRequest("PUT", "/v1/agent/leave")
_, resp, err := requireOK(a.c.doRequest(r))
if err != nil {
return err
}
resp.Body.Close()
return nil
}
// ForceLeave is used to have the agent eject a failed node // ForceLeave is used to have the agent eject a failed node
func (a *Agent) ForceLeave(node string) error { func (a *Agent) ForceLeave(node string) error {
r := a.c.newRequest("PUT", "/v1/agent/force-leave/"+node) r := a.c.newRequest("PUT", "/v1/agent/force-leave/"+node)
@ -402,3 +434,38 @@ func (a *Agent) DisableNodeMaintenance() error {
resp.Body.Close() resp.Body.Close()
return nil return nil
} }
// Monitor returns a channel which will receive streaming logs from the agent
// Providing a non-nil stopCh can be used to close the connection and stop the
// log stream
func (a *Agent) Monitor(loglevel string, stopCh chan struct{}, q *QueryOptions) (chan string, error) {
r := a.c.newRequest("GET", "/v1/agent/monitor")
r.setQueryOptions(q)
if loglevel != "" {
r.params.Add("loglevel", loglevel)
}
_, resp, err := requireOK(a.c.doRequest(r))
if err != nil {
return nil, err
}
logCh := make(chan string, 64)
go func() {
defer resp.Body.Close()
scanner := bufio.NewScanner(resp.Body)
for {
select {
case <-stopCh:
close(logCh)
return
default:
}
if scanner.Scan() {
logCh <- scanner.Text()
}
}
}()
return logCh, nil
}

View File

@ -20,6 +20,28 @@ import (
"github.com/hashicorp/go-cleanhttp" "github.com/hashicorp/go-cleanhttp"
) )
const (
// HTTPAddrEnvName defines an environment variable name which sets
// the HTTP address if there is no -http-addr specified.
HTTPAddrEnvName = "CONSUL_HTTP_ADDR"
// HTTPTokenEnvName defines an environment variable name which sets
// the HTTP token.
HTTPTokenEnvName = "CONSUL_HTTP_TOKEN"
// HTTPAuthEnvName defines an environment variable name which sets
// the HTTP authentication header.
HTTPAuthEnvName = "CONSUL_HTTP_AUTH"
// HTTPSSLEnvName defines an environment variable name which sets
// whether or not to use HTTPS.
HTTPSSLEnvName = "CONSUL_HTTP_SSL"
// HTTPSSLVerifyEnvName defines an environment variable name which sets
// whether or not to disable certificate checking.
HTTPSSLVerifyEnvName = "CONSUL_HTTP_SSL_VERIFY"
)
// QueryOptions are used to parameterize a query // QueryOptions are used to parameterize a query
type QueryOptions struct { type QueryOptions struct {
// Providing a datacenter overwrites the DC provided // Providing a datacenter overwrites the DC provided
@ -52,6 +74,11 @@ type QueryOptions struct {
// that node. Setting this to "_agent" will use the agent's node // that node. Setting this to "_agent" will use the agent's node
// for the sort. // for the sort.
Near string Near string
// NodeMeta is used to filter results by nodes with the given
// metadata key/value pairs. Currently, only one key/value pair can
// be provided for filtering.
NodeMeta map[string]string
} }
// WriteOptions are used to parameterize a write // WriteOptions are used to parameterize a write
@ -80,6 +107,9 @@ type QueryMeta struct {
// How long did the request take // How long did the request take
RequestTime time.Duration RequestTime time.Duration
// Is address translation enabled for HTTP responses on this agent
AddressTranslationEnabled bool
} }
// WriteMeta is used to return meta data about a write // WriteMeta is used to return meta data about a write
@ -178,15 +208,15 @@ func defaultConfig(transportFn func() *http.Transport) *Config {
}, },
} }
if addr := os.Getenv("CONSUL_HTTP_ADDR"); addr != "" { if addr := os.Getenv(HTTPAddrEnvName); addr != "" {
config.Address = addr config.Address = addr
} }
if token := os.Getenv("CONSUL_HTTP_TOKEN"); token != "" { if token := os.Getenv(HTTPTokenEnvName); token != "" {
config.Token = token config.Token = token
} }
if auth := os.Getenv("CONSUL_HTTP_AUTH"); auth != "" { if auth := os.Getenv(HTTPAuthEnvName); auth != "" {
var username, password string var username, password string
if strings.Contains(auth, ":") { if strings.Contains(auth, ":") {
split := strings.SplitN(auth, ":", 2) split := strings.SplitN(auth, ":", 2)
@ -202,10 +232,10 @@ func defaultConfig(transportFn func() *http.Transport) *Config {
} }
} }
if ssl := os.Getenv("CONSUL_HTTP_SSL"); ssl != "" { if ssl := os.Getenv(HTTPSSLEnvName); ssl != "" {
enabled, err := strconv.ParseBool(ssl) enabled, err := strconv.ParseBool(ssl)
if err != nil { if err != nil {
log.Printf("[WARN] client: could not parse CONSUL_HTTP_SSL: %s", err) log.Printf("[WARN] client: could not parse %s: %s", HTTPSSLEnvName, err)
} }
if enabled { if enabled {
@ -213,10 +243,10 @@ func defaultConfig(transportFn func() *http.Transport) *Config {
} }
} }
if verify := os.Getenv("CONSUL_HTTP_SSL_VERIFY"); verify != "" { if verify := os.Getenv(HTTPSSLVerifyEnvName); verify != "" {
doVerify, err := strconv.ParseBool(verify) doVerify, err := strconv.ParseBool(verify)
if err != nil { if err != nil {
log.Printf("[WARN] client: could not parse CONSUL_HTTP_SSL_VERIFY: %s", err) log.Printf("[WARN] client: could not parse %s: %s", HTTPSSLVerifyEnvName, err)
} }
if !doVerify { if !doVerify {
@ -330,6 +360,7 @@ type request struct {
url *url.URL url *url.URL
params url.Values params url.Values
body io.Reader body io.Reader
header http.Header
obj interface{} obj interface{}
} }
@ -355,11 +386,16 @@ func (r *request) setQueryOptions(q *QueryOptions) {
r.params.Set("wait", durToMsec(q.WaitTime)) r.params.Set("wait", durToMsec(q.WaitTime))
} }
if q.Token != "" { if q.Token != "" {
r.params.Set("token", q.Token) r.header.Set("X-Consul-Token", q.Token)
} }
if q.Near != "" { if q.Near != "" {
r.params.Set("near", q.Near) r.params.Set("near", q.Near)
} }
if len(q.NodeMeta) > 0 {
for key, value := range q.NodeMeta {
r.params.Add("node-meta", key+":"+value)
}
}
} }
// durToMsec converts a duration to a millisecond specified string. If the // durToMsec converts a duration to a millisecond specified string. If the
@ -399,7 +435,7 @@ func (r *request) setWriteOptions(q *WriteOptions) {
r.params.Set("dc", q.Datacenter) r.params.Set("dc", q.Datacenter)
} }
if q.Token != "" { if q.Token != "" {
r.params.Set("token", q.Token) r.header.Set("X-Consul-Token", q.Token)
} }
} }
@ -426,6 +462,7 @@ func (r *request) toHTTP() (*http.Request, error) {
req.URL.Host = r.url.Host req.URL.Host = r.url.Host
req.URL.Scheme = r.url.Scheme req.URL.Scheme = r.url.Scheme
req.Host = r.url.Host req.Host = r.url.Host
req.Header = r.header
// Setup auth // Setup auth
if r.config.HttpAuth != nil { if r.config.HttpAuth != nil {
@ -446,6 +483,7 @@ func (c *Client) newRequest(method, path string) *request {
Path: path, Path: path,
}, },
params: make(map[string][]string), params: make(map[string][]string),
header: make(http.Header),
} }
if c.config.Datacenter != "" { if c.config.Datacenter != "" {
r.params.Set("dc", c.config.Datacenter) r.params.Set("dc", c.config.Datacenter)
@ -454,7 +492,7 @@ func (c *Client) newRequest(method, path string) *request {
r.params.Set("wait", durToMsec(r.config.WaitTime)) r.params.Set("wait", durToMsec(r.config.WaitTime))
} }
if c.config.Token != "" { if c.config.Token != "" {
r.params.Set("token", r.config.Token) r.header.Set("X-Consul-Token", r.config.Token)
} }
return r return r
} }
@ -539,6 +577,15 @@ func parseQueryMeta(resp *http.Response, q *QueryMeta) error {
default: default:
q.KnownLeader = false q.KnownLeader = false
} }
// Parse X-Consul-Translate-Addresses
switch header.Get("X-Consul-Translate-Addresses") {
case "true":
q.AddressTranslationEnabled = true
default:
q.AddressTranslationEnabled = false
}
return nil return nil
} }

View File

@ -1,19 +1,27 @@
package api package api
type Node struct { type Node struct {
ID string
Node string Node string
Address string Address string
TaggedAddresses map[string]string
Meta map[string]string
} }
type CatalogService struct { type CatalogService struct {
ID string
Node string Node string
Address string Address string
TaggedAddresses map[string]string
NodeMeta map[string]string
ServiceID string ServiceID string
ServiceName string ServiceName string
ServiceAddress string ServiceAddress string
ServiceTags []string ServiceTags []string
ServicePort int ServicePort int
ServiceEnableTagOverride bool ServiceEnableTagOverride bool
CreateIndex uint64
ModifyIndex uint64
} }
type CatalogNode struct { type CatalogNode struct {
@ -22,8 +30,11 @@ type CatalogNode struct {
} }
type CatalogRegistration struct { type CatalogRegistration struct {
ID string
Node string Node string
Address string Address string
TaggedAddresses map[string]string
NodeMeta map[string]string
Datacenter string Datacenter string
Service *AgentService Service *AgentService
Check *AgentCheck Check *AgentCheck
@ -31,7 +42,7 @@ type CatalogRegistration struct {
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

View File

@ -2,16 +2,25 @@ package api
import ( import (
"fmt" "fmt"
"strings"
) )
const ( const (
// HealthAny is special, and is used as a wild card, // HealthAny is special, and is used as a wild card,
// not as a specific state. // not as a specific state.
HealthAny = "any" HealthAny = "any"
HealthUnknown = "unknown"
HealthPassing = "passing" HealthPassing = "passing"
HealthWarning = "warning" HealthWarning = "warning"
HealthCritical = "critical" HealthCritical = "critical"
HealthMaint = "maintenance"
)
const (
// NodeMaint is the special key set by a node in maintenance mode.
NodeMaint = "_node_maintenance"
// ServiceMaintPrefix is the prefix for a service in maintenance mode.
ServiceMaintPrefix = "_service_maintenance:"
) )
// HealthCheck is used to represent a single check // HealthCheck is used to represent a single check
@ -26,11 +35,56 @@ type HealthCheck struct {
ServiceName string ServiceName string
} }
// HealthChecks is a collection of HealthCheck structs.
type HealthChecks []*HealthCheck
// AggregatedStatus returns the "best" status for the list of health checks.
// Because a given entry may have many service and node-level health checks
// attached, this function determines the best representative of the status as
// as single string using the following heuristic:
//
// maintenance > critical > warning > passing
//
func (c HealthChecks) AggregatedStatus() string {
var passing, warning, critical, maintenance bool
for _, check := range c {
id := string(check.CheckID)
if id == NodeMaint || strings.HasPrefix(id, ServiceMaintPrefix) {
maintenance = true
continue
}
switch check.Status {
case HealthPassing:
passing = true
case HealthWarning:
warning = true
case HealthCritical:
critical = true
default:
return ""
}
}
switch {
case maintenance:
return HealthMaint
case critical:
return HealthCritical
case warning:
return HealthWarning
case passing:
return HealthPassing
default:
return HealthPassing
}
}
// ServiceEntry is used for the health service endpoint // ServiceEntry is used for the health service endpoint
type ServiceEntry struct { type ServiceEntry struct {
Node *Node Node *Node
Service *AgentService Service *AgentService
Checks []*HealthCheck Checks HealthChecks
} }
// Health can be used to query the Health endpoints // Health can be used to query the Health endpoints
@ -44,7 +98,7 @@ func (c *Client) Health() *Health {
} }
// Node is used to query for checks belonging to a given node // Node is used to query for checks belonging to a given node
func (h *Health) Node(node string, q *QueryOptions) ([]*HealthCheck, *QueryMeta, error) { func (h *Health) Node(node string, q *QueryOptions) (HealthChecks, *QueryMeta, error) {
r := h.c.newRequest("GET", "/v1/health/node/"+node) r := h.c.newRequest("GET", "/v1/health/node/"+node)
r.setQueryOptions(q) r.setQueryOptions(q)
rtt, resp, err := requireOK(h.c.doRequest(r)) rtt, resp, err := requireOK(h.c.doRequest(r))
@ -57,7 +111,7 @@ func (h *Health) Node(node string, q *QueryOptions) ([]*HealthCheck, *QueryMeta,
parseQueryMeta(resp, qm) parseQueryMeta(resp, qm)
qm.RequestTime = rtt qm.RequestTime = rtt
var out []*HealthCheck var out HealthChecks
if err := decodeBody(resp, &out); err != nil { if err := decodeBody(resp, &out); err != nil {
return nil, nil, err return nil, nil, err
} }
@ -65,7 +119,7 @@ func (h *Health) Node(node string, q *QueryOptions) ([]*HealthCheck, *QueryMeta,
} }
// Checks is used to return the checks associated with a service // Checks is used to return the checks associated with a service
func (h *Health) Checks(service string, q *QueryOptions) ([]*HealthCheck, *QueryMeta, error) { func (h *Health) Checks(service string, q *QueryOptions) (HealthChecks, *QueryMeta, error) {
r := h.c.newRequest("GET", "/v1/health/checks/"+service) r := h.c.newRequest("GET", "/v1/health/checks/"+service)
r.setQueryOptions(q) r.setQueryOptions(q)
rtt, resp, err := requireOK(h.c.doRequest(r)) rtt, resp, err := requireOK(h.c.doRequest(r))
@ -78,7 +132,7 @@ func (h *Health) Checks(service string, q *QueryOptions) ([]*HealthCheck, *Query
parseQueryMeta(resp, qm) parseQueryMeta(resp, qm)
qm.RequestTime = rtt qm.RequestTime = rtt
var out []*HealthCheck var out HealthChecks
if err := decodeBody(resp, &out); err != nil { if err := decodeBody(resp, &out); err != nil {
return nil, nil, err return nil, nil, err
} }
@ -116,13 +170,12 @@ func (h *Health) Service(service, tag string, passingOnly bool, q *QueryOptions)
// State is used to retrieve all the checks in a given state. // State is used to retrieve all the checks in a given state.
// The wildcard "any" state can also be used for all checks. // The wildcard "any" state can also be used for all checks.
func (h *Health) State(state string, q *QueryOptions) ([]*HealthCheck, *QueryMeta, error) { func (h *Health) State(state string, q *QueryOptions) (HealthChecks, *QueryMeta, error) {
switch state { switch state {
case HealthAny: case HealthAny:
case HealthWarning: case HealthWarning:
case HealthCritical: case HealthCritical:
case HealthPassing: case HealthPassing:
case HealthUnknown:
default: default:
return nil, nil, fmt.Errorf("Unsupported state: %v", state) return nil, nil, fmt.Errorf("Unsupported state: %v", state)
} }
@ -138,7 +191,7 @@ func (h *Health) State(state string, q *QueryOptions) ([]*HealthCheck, *QueryMet
parseQueryMeta(resp, qm) parseQueryMeta(resp, qm)
qm.RequestTime = rtt qm.RequestTime = rtt
var out []*HealthCheck var out HealthChecks
if err := decodeBody(resp, &out); err != nil { if err := decodeBody(resp, &out); err != nil {
return nil, nil, err return nil, nil, err
} }

View File

@ -11,12 +11,34 @@ import (
// KVPair is used to represent a single K/V entry // KVPair is used to represent a single K/V entry
type KVPair struct { type KVPair struct {
// Key is the name of the key. It is also part of the URL path when accessed
// via the API.
Key string Key string
// CreateIndex holds the index corresponding the creation of this KVPair. This
// is a read-only field.
CreateIndex uint64 CreateIndex uint64
// ModifyIndex is used for the Check-And-Set operations and can also be fed
// back into the WaitIndex of the QueryOptions in order to perform blocking
// queries.
ModifyIndex uint64 ModifyIndex uint64
// LockIndex holds the index corresponding to a lock on this key, if any. This
// is a read-only field.
LockIndex uint64 LockIndex uint64
// Flags are any user-defined flags on the key. It is up to the implementer
// to check these values, since Consul does not treat them specially.
Flags uint64 Flags uint64
// Value is the value for the key. This can be any value, but it will be
// base64 encoded upon transport.
Value []byte Value []byte
// Session is a string representing the ID of the session. Any other
// interactions with this key over the same session must specify the same
// session ID.
Session string Session string
} }
@ -28,21 +50,21 @@ type KVOp string
const ( const (
KVSet KVOp = "set" KVSet KVOp = "set"
KVDelete = "delete" KVDelete KVOp = "delete"
KVDeleteCAS = "delete-cas" KVDeleteCAS KVOp = "delete-cas"
KVDeleteTree = "delete-tree" KVDeleteTree KVOp = "delete-tree"
KVCAS = "cas" KVCAS KVOp = "cas"
KVLock = "lock" KVLock KVOp = "lock"
KVUnlock = "unlock" KVUnlock KVOp = "unlock"
KVGet = "get" KVGet KVOp = "get"
KVGetTree = "get-tree" KVGetTree KVOp = "get-tree"
KVCheckSession = "check-session" KVCheckSession KVOp = "check-session"
KVCheckIndex = "check-index" KVCheckIndex KVOp = "check-index"
) )
// KVTxnOp defines a single operation inside a transaction. // KVTxnOp defines a single operation inside a transaction.
type KVTxnOp struct { type KVTxnOp struct {
Verb string Verb KVOp
Key string Key string
Value []byte Value []byte
Flags uint64 Flags uint64
@ -70,7 +92,8 @@ func (c *Client) KV() *KV {
return &KV{c} return &KV{c}
} }
// Get is used to lookup a single key // Get is used to lookup a single key. The returned pointer
// to the KVPair will be nil if the key does not exist.
func (k *KV) Get(key string, q *QueryOptions) (*KVPair, *QueryMeta, error) { func (k *KV) Get(key string, q *QueryOptions) (*KVPair, *QueryMeta, error) {
resp, qm, err := k.getInternal(key, nil, q) resp, qm, err := k.getInternal(key, nil, q)
if err != nil { if err != nil {
@ -133,7 +156,7 @@ func (k *KV) Keys(prefix, separator string, q *QueryOptions) ([]string, *QueryMe
} }
func (k *KV) getInternal(key string, params map[string]string, q *QueryOptions) (*http.Response, *QueryMeta, error) { func (k *KV) getInternal(key string, params map[string]string, q *QueryOptions) (*http.Response, *QueryMeta, error) {
r := k.c.newRequest("GET", "/v1/kv/"+key) r := k.c.newRequest("GET", "/v1/kv/"+strings.TrimPrefix(key, "/"))
r.setQueryOptions(q) r.setQueryOptions(q)
for param, val := range params { for param, val := range params {
r.params.Set(param, val) r.params.Set(param, val)
@ -254,7 +277,7 @@ func (k *KV) DeleteTree(prefix string, w *WriteOptions) (*WriteMeta, error) {
} }
func (k *KV) deleteInternal(key string, params map[string]string, q *WriteOptions) (bool, *WriteMeta, error) { func (k *KV) deleteInternal(key string, params map[string]string, q *WriteOptions) (bool, *WriteMeta, error) {
r := k.c.newRequest("DELETE", "/v1/kv/"+key) r := k.c.newRequest("DELETE", "/v1/kv/"+strings.TrimPrefix(key, "/"))
r.setWriteOptions(q) r.setWriteOptions(q)
for param, val := range params { for param, val := range params {
r.params.Set(param, val) r.params.Set(param, val)

View File

@ -72,8 +72,9 @@ type LockOptions struct {
Key string // Must be set and have write permissions Key string // Must be set and have write permissions
Value []byte // Optional, value to associate with the lock Value []byte // Optional, value to associate with the lock
Session string // Optional, created if not specified Session string // Optional, created if not specified
SessionName string // Optional, defaults to DefaultLockSessionName SessionOpts *SessionEntry // Optional, options to use when creating a session
SessionTTL string // Optional, defaults to DefaultLockSessionTTL SessionName string // Optional, defaults to DefaultLockSessionName (ignored if SessionOpts is given)
SessionTTL string // Optional, defaults to DefaultLockSessionTTL (ignored if SessionOpts is given)
MonitorRetries int // Optional, defaults to 0 which means no retries MonitorRetries int // Optional, defaults to 0 which means no retries
MonitorRetryTime time.Duration // Optional, defaults to DefaultMonitorRetryTime MonitorRetryTime time.Duration // Optional, defaults to DefaultMonitorRetryTime
LockWaitTime time.Duration // Optional, defaults to DefaultLockWaitTime LockWaitTime time.Duration // Optional, defaults to DefaultLockWaitTime
@ -329,10 +330,13 @@ func (l *Lock) Destroy() error {
// createSession is used to create a new managed session // createSession is used to create a new managed session
func (l *Lock) createSession() (string, error) { func (l *Lock) createSession() (string, error) {
session := l.c.Session() session := l.c.Session()
se := &SessionEntry{ se := l.opts.SessionOpts
if se == nil {
se = &SessionEntry{
Name: l.opts.SessionName, Name: l.opts.SessionName,
TTL: l.opts.SessionTTL, TTL: l.opts.SessionTTL,
} }
}
id, _, err := session.Create(se, nil) id, _, err := session.Create(se, nil)
if err != nil { if err != nil {
return "", err return "", err

163
vendor/github.com/hashicorp/consul/api/operator.go generated vendored Normal file
View File

@ -0,0 +1,163 @@
package api
// Operator can be used to perform low-level operator tasks for Consul.
type Operator struct {
c *Client
}
// Operator returns a handle to the operator endpoints.
func (c *Client) Operator() *Operator {
return &Operator{c}
}
// RaftServer has information about a server in the Raft configuration.
type RaftServer struct {
// ID is the unique ID for the server. These are currently the same
// as the address, but they will be changed to a real GUID in a future
// release of Consul.
ID string
// Node is the node name of the server, as known by Consul, or this
// will be set to "(unknown)" otherwise.
Node string
// Address is the IP:port of the server, used for Raft communications.
Address string
// Leader is true if this server is the current cluster leader.
Leader bool
// Voter is true if this server has a vote in the cluster. This might
// be false if the server is staging and still coming online, or if
// it's a non-voting server, which will be added in a future release of
// Consul.
Voter bool
}
// RaftConfigration is returned when querying for the current Raft configuration.
type RaftConfiguration struct {
// Servers has the list of servers in the Raft configuration.
Servers []*RaftServer
// Index has the Raft index of this configuration.
Index uint64
}
// keyringRequest is used for performing Keyring operations
type keyringRequest struct {
Key string
}
// KeyringResponse is returned when listing the gossip encryption keys
type KeyringResponse struct {
// Whether this response is for a WAN ring
WAN bool
// The datacenter name this request corresponds to
Datacenter string
// A map of the encryption keys to the number of nodes they're installed on
Keys map[string]int
// The total number of nodes in this ring
NumNodes int
}
// RaftGetConfiguration is used to query the current Raft peer set.
func (op *Operator) RaftGetConfiguration(q *QueryOptions) (*RaftConfiguration, error) {
r := op.c.newRequest("GET", "/v1/operator/raft/configuration")
r.setQueryOptions(q)
_, resp, err := requireOK(op.c.doRequest(r))
if err != nil {
return nil, err
}
defer resp.Body.Close()
var out RaftConfiguration
if err := decodeBody(resp, &out); err != nil {
return nil, err
}
return &out, nil
}
// RaftRemovePeerByAddress is used to kick a stale peer (one that it in the Raft
// quorum but no longer known to Serf or the catalog) by address in the form of
// "IP:port".
func (op *Operator) RaftRemovePeerByAddress(address string, q *WriteOptions) error {
r := op.c.newRequest("DELETE", "/v1/operator/raft/peer")
r.setWriteOptions(q)
// TODO (slackpad) Currently we made address a query parameter. Once
// IDs are in place this will be DELETE /v1/operator/raft/peer/<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
}

View File

@ -43,6 +43,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.
@ -167,19 +172,18 @@ func (c *PreparedQuery) Get(queryID string, q *QueryOptions) ([]*PreparedQueryDe
} }
// Delete is used to delete a specific prepared query. // Delete is used to delete a specific prepared query.
func (c *PreparedQuery) Delete(queryID string, q *QueryOptions) (*QueryMeta, error) { func (c *PreparedQuery) Delete(queryID string, q *WriteOptions) (*WriteMeta, error) {
r := c.c.newRequest("DELETE", "/v1/query/"+queryID) r := c.c.newRequest("DELETE", "/v1/query/"+queryID)
r.setQueryOptions(q) r.setWriteOptions(q)
rtt, resp, err := requireOK(c.c.doRequest(r)) rtt, resp, err := requireOK(c.c.doRequest(r))
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer resp.Body.Close() defer resp.Body.Close()
qm := &QueryMeta{} wm := &WriteMeta{}
parseQueryMeta(resp, qm) wm.RequestTime = rtt
qm.RequestTime = rtt return wm, nil
return qm, nil
} }
// Execute is used to execute a specific prepared query. You can execute using // Execute is used to execute a specific prepared query. You can execute using

47
vendor/github.com/hashicorp/consul/api/snapshot.go generated vendored Normal file
View File

@ -0,0 +1,47 @@
package api
import (
"io"
)
// Snapshot can be used to query the /v1/snapshot endpoint to take snapshots of
// Consul's internal state and restore snapshots for disaster recovery.
type Snapshot struct {
c *Client
}
// Snapshot returns a handle that exposes the snapshot endpoints.
func (c *Client) Snapshot() *Snapshot {
return &Snapshot{c}
}
// Save requests a new snapshot and provides an io.ReadCloser with the snapshot
// data to save. If this doesn't return an error, then it's the responsibility
// of the caller to close it. Only a subset of the QueryOptions are supported:
// Datacenter, AllowStale, and Token.
func (s *Snapshot) Save(q *QueryOptions) (io.ReadCloser, *QueryMeta, error) {
r := s.c.newRequest("GET", "/v1/snapshot")
r.setQueryOptions(q)
rtt, resp, err := requireOK(s.c.doRequest(r))
if err != nil {
return nil, nil, err
}
qm := &QueryMeta{}
parseQueryMeta(resp, qm)
qm.RequestTime = rtt
return resp.Body, qm, nil
}
// Restore streams in an existing snapshot and attempts to restore it.
func (s *Snapshot) Restore(q *WriteOptions, in io.Reader) error {
r := s.c.newRequest("PUT", "/v1/snapshot")
r.body = in
r.setWriteOptions(q)
_, _, err := requireOK(s.c.doRequest(r))
if err != nil {
return err
}
return nil
}

6
vendor/vendor.json vendored
View File

@ -1634,11 +1634,11 @@
"revisionTime": "2016-11-07T20:49:10Z" "revisionTime": "2016-11-07T20:49:10Z"
}, },
{ {
"checksumSHA1": "ZY6NCrR80zUmtOtPtKffbmFxRWw=", "checksumSHA1": "ygEjA1d52B1RDmZu8+1WTwkrYDQ=",
"comment": "v0.6.3-28-g3215b87", "comment": "v0.6.3-28-g3215b87",
"path": "github.com/hashicorp/consul/api", "path": "github.com/hashicorp/consul/api",
"revision": "6e061b2d580d80347b7c5c4dfc8730de7403a145", "revision": "48d7b069ad443a48ffa93364048ff8909b5d1fa2",
"revisionTime": "2016-07-03T02:45:54Z" "revisionTime": "2017-02-07T15:38:46Z"
}, },
{ {
"checksumSHA1": "cdOCt0Yb+hdErz8NAQqayxPmRsY=", "checksumSHA1": "cdOCt0Yb+hdErz8NAQqayxPmRsY=",