VOL-1497 : Add more control to kv/memory access
- Added kv locking mechanism (etcd only)
- (watch) control path access whenever possible
- (watch) use a transaction for updates and merge with memory
- cleaned up vendoring
- misc changes to fix exceptions found along the way
Amendments:
- Copyright header got removed in auto-generated file
- Changed default locking to false for KV list operation
- Updated backend api to allow the passing of locking parameter
Change-Id: Ie1a55d3ca8b9d92ae71a85ce42bb22fcf1419e2c
diff --git a/vendor/github.com/hashicorp/consul/api/agent.go b/vendor/github.com/hashicorp/consul/api/agent.go
index 8e5ffde..6a3fb27 100644
--- a/vendor/github.com/hashicorp/consul/api/agent.go
+++ b/vendor/github.com/hashicorp/consul/api/agent.go
@@ -3,6 +3,8 @@
import (
"bufio"
"fmt"
+ "net/http"
+ "net/url"
)
// ServiceKind is the kind of service being registered.
@@ -89,6 +91,13 @@
Connect *AgentServiceConnect `json:",omitempty"`
}
+// AgentServiceChecksInfo returns information about a Service and its checks
+type AgentServiceChecksInfo struct {
+ AggregatedStatus string
+ Service *AgentService
+ Checks HealthChecks
+}
+
// AgentServiceConnect represents the Connect configuration of a service.
type AgentServiceConnect struct {
Native bool `json:",omitempty"`
@@ -407,6 +416,73 @@
return out, nil
}
+// AgentHealthServiceByID returns for a given serviceID: the aggregated health status, the service definition or an error if any
+// - If the service is not found, will return status (critical, nil, nil)
+// - If the service is found, will return (critical|passing|warning), AgentServiceChecksInfo, nil)
+// - In all other cases, will return an error
+func (a *Agent) AgentHealthServiceByID(serviceID string) (string, *AgentServiceChecksInfo, error) {
+ path := fmt.Sprintf("/v1/agent/health/service/id/%v", url.PathEscape(serviceID))
+ r := a.c.newRequest("GET", path)
+ r.params.Add("format", "json")
+ r.header.Set("Accept", "application/json")
+ _, resp, err := a.c.doRequest(r)
+ if err != nil {
+ return "", nil, err
+ }
+ defer resp.Body.Close()
+ // Service not Found
+ if resp.StatusCode == http.StatusNotFound {
+ return HealthCritical, nil, nil
+ }
+ var out *AgentServiceChecksInfo
+ if err := decodeBody(resp, &out); err != nil {
+ return HealthCritical, out, err
+ }
+ switch resp.StatusCode {
+ case http.StatusOK:
+ return HealthPassing, out, nil
+ case http.StatusTooManyRequests:
+ return HealthWarning, out, nil
+ case http.StatusServiceUnavailable:
+ return HealthCritical, out, nil
+ }
+ return HealthCritical, out, fmt.Errorf("Unexpected Error Code %v for %s", resp.StatusCode, path)
+}
+
+// AgentHealthServiceByName returns for a given service name: the aggregated health status for all services
+// having the specified name.
+// - If no service is not found, will return status (critical, [], nil)
+// - If the service is found, will return (critical|passing|warning), []api.AgentServiceChecksInfo, nil)
+// - In all other cases, will return an error
+func (a *Agent) AgentHealthServiceByName(service string) (string, []AgentServiceChecksInfo, error) {
+ path := fmt.Sprintf("/v1/agent/health/service/name/%v", url.PathEscape(service))
+ r := a.c.newRequest("GET", path)
+ r.params.Add("format", "json")
+ r.header.Set("Accept", "application/json")
+ _, resp, err := a.c.doRequest(r)
+ if err != nil {
+ return "", nil, err
+ }
+ defer resp.Body.Close()
+ // Service not Found
+ if resp.StatusCode == http.StatusNotFound {
+ return HealthCritical, nil, nil
+ }
+ var out []AgentServiceChecksInfo
+ if err := decodeBody(resp, &out); err != nil {
+ return HealthCritical, out, err
+ }
+ switch resp.StatusCode {
+ case http.StatusOK:
+ return HealthPassing, out, nil
+ case http.StatusTooManyRequests:
+ return HealthWarning, out, nil
+ case http.StatusServiceUnavailable:
+ return HealthCritical, out, nil
+ }
+ return HealthCritical, out, fmt.Errorf("Unexpected Error Code %v for %s", resp.StatusCode, path)
+}
+
// Service returns a locally registered service instance and allows for
// hash-based blocking.
//
diff --git a/vendor/github.com/hashicorp/consul/api/api.go b/vendor/github.com/hashicorp/consul/api/api.go
index cf8bad2..b913fa3 100644
--- a/vendor/github.com/hashicorp/consul/api/api.go
+++ b/vendor/github.com/hashicorp/consul/api/api.go
@@ -773,7 +773,7 @@
func (c *Client) query(endpoint string, out interface{}, q *QueryOptions) (*QueryMeta, error) {
r := c.newRequest("GET", endpoint)
r.setQueryOptions(q)
- rtt, resp, err := requireOK(c.doRequest(r))
+ rtt, resp, err := c.doRequest(r)
if err != nil {
return nil, err
}
diff --git a/vendor/github.com/hashicorp/consul/api/connect_ca.go b/vendor/github.com/hashicorp/consul/api/connect_ca.go
index a863d21..600a3e0 100644
--- a/vendor/github.com/hashicorp/consul/api/connect_ca.go
+++ b/vendor/github.com/hashicorp/consul/api/connect_ca.go
@@ -23,7 +23,10 @@
// CommonCAProviderConfig is the common options available to all CA providers.
type CommonCAProviderConfig struct {
- LeafCertTTL time.Duration
+ LeafCertTTL time.Duration
+ SkipValidate bool
+ CSRMaxPerSecond float32
+ CSRMaxConcurrent int
}
// ConsulCAProviderConfig is the config for the built-in Consul CA provider.
@@ -41,7 +44,6 @@
var config ConsulCAProviderConfig
decodeConf := &mapstructure.DecoderConfig{
DecodeHook: mapstructure.StringToTimeDurationHookFunc(),
- ErrorUnused: true,
Result: &config,
WeaklyTypedInput: true,
}
diff --git a/vendor/github.com/hashicorp/consul/api/health.go b/vendor/github.com/hashicorp/consul/api/health.go
index eae6a01..9faf6b6 100644
--- a/vendor/github.com/hashicorp/consul/api/health.go
+++ b/vendor/github.com/hashicorp/consul/api/health.go
@@ -1,8 +1,10 @@
package api
import (
+ "encoding/json"
"fmt"
"strings"
+ "time"
)
const (
@@ -36,21 +38,99 @@
ServiceTags []string
Definition HealthCheckDefinition
+
+ CreateIndex uint64
+ ModifyIndex uint64
}
// 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
+ HTTP string
+ Header map[string][]string
+ Method string
+ TLSSkipVerify bool
+ TCP string
+ IntervalDuration time.Duration `json:"-"`
+ TimeoutDuration time.Duration `json:"-"`
+ DeregisterCriticalServiceAfterDuration time.Duration `json:"-"`
+
+ // DEPRECATED in Consul 1.4.1. Use the above time.Duration fields instead.
Interval ReadableDuration
Timeout ReadableDuration
DeregisterCriticalServiceAfter ReadableDuration
}
+func (d *HealthCheckDefinition) MarshalJSON() ([]byte, error) {
+ type Alias HealthCheckDefinition
+ out := &struct {
+ Interval string
+ Timeout string
+ DeregisterCriticalServiceAfter string
+ *Alias
+ }{
+ Interval: d.Interval.String(),
+ Timeout: d.Timeout.String(),
+ DeregisterCriticalServiceAfter: d.DeregisterCriticalServiceAfter.String(),
+ Alias: (*Alias)(d),
+ }
+
+ if d.IntervalDuration != 0 {
+ out.Interval = d.IntervalDuration.String()
+ } else if d.Interval != 0 {
+ out.Interval = d.Interval.String()
+ }
+ if d.TimeoutDuration != 0 {
+ out.Timeout = d.TimeoutDuration.String()
+ } else if d.Timeout != 0 {
+ out.Timeout = d.Timeout.String()
+ }
+ if d.DeregisterCriticalServiceAfterDuration != 0 {
+ out.DeregisterCriticalServiceAfter = d.DeregisterCriticalServiceAfterDuration.String()
+ } else if d.DeregisterCriticalServiceAfter != 0 {
+ out.DeregisterCriticalServiceAfter = d.DeregisterCriticalServiceAfter.String()
+ }
+
+ return json.Marshal(out)
+}
+
+func (d *HealthCheckDefinition) UnmarshalJSON(data []byte) error {
+ type Alias HealthCheckDefinition
+ aux := &struct {
+ Interval string
+ Timeout string
+ DeregisterCriticalServiceAfter string
+ *Alias
+ }{
+ Alias: (*Alias)(d),
+ }
+ if err := json.Unmarshal(data, &aux); err != nil {
+ return err
+ }
+
+ // Parse the values into both the time.Duration and old ReadableDuration fields.
+ var err error
+ if aux.Interval != "" {
+ if d.IntervalDuration, err = time.ParseDuration(aux.Interval); err != nil {
+ return err
+ }
+ d.Interval = ReadableDuration(d.IntervalDuration)
+ }
+ if aux.Timeout != "" {
+ if d.TimeoutDuration, err = time.ParseDuration(aux.Timeout); err != nil {
+ return err
+ }
+ d.Timeout = ReadableDuration(d.TimeoutDuration)
+ }
+ if aux.DeregisterCriticalServiceAfter != "" {
+ if d.DeregisterCriticalServiceAfterDuration, err = time.ParseDuration(aux.DeregisterCriticalServiceAfter); err != nil {
+ return err
+ }
+ d.DeregisterCriticalServiceAfter = ReadableDuration(d.DeregisterCriticalServiceAfterDuration)
+ }
+ return nil
+}
+
// HealthChecks is a collection of HealthCheck structs.
type HealthChecks []*HealthCheck
diff --git a/vendor/github.com/hashicorp/consul/api/kv.go b/vendor/github.com/hashicorp/consul/api/kv.go
index 97f5156..bd45a06 100644
--- a/vendor/github.com/hashicorp/consul/api/kv.go
+++ b/vendor/github.com/hashicorp/consul/api/kv.go
@@ -45,44 +45,6 @@
// KVPairs is a list of KVPair objects
type KVPairs []*KVPair
-// KVOp constants give possible operations available in a KVTxn.
-type KVOp string
-
-const (
- KVSet KVOp = "set"
- KVDelete KVOp = "delete"
- KVDeleteCAS KVOp = "delete-cas"
- KVDeleteTree KVOp = "delete-tree"
- KVCAS KVOp = "cas"
- KVLock KVOp = "lock"
- KVUnlock KVOp = "unlock"
- KVGet KVOp = "get"
- KVGetTree KVOp = "get-tree"
- KVCheckSession KVOp = "check-session"
- KVCheckIndex KVOp = "check-index"
- KVCheckNotExists KVOp = "check-not-exists"
-)
-
-// KVTxnOp defines a single operation inside a transaction.
-type KVTxnOp struct {
- Verb KVOp
- Key string
- Value []byte
- Flags uint64
- Index uint64
- Session string
-}
-
-// KVTxnOps defines a set of operations to be performed inside a single
-// transaction.
-type KVTxnOps []*KVTxnOp
-
-// KVTxnResponse has the outcome of a transaction.
-type KVTxnResponse struct {
- Results []*KVPair
- Errors TxnErrors
-}
-
// KV is used to manipulate the K/V API
type KV struct {
c *Client
@@ -300,121 +262,25 @@
return res, qm, nil
}
-// TxnOp is the internal format we send to Consul. It's not specific to KV,
-// though currently only KV operations are supported.
-type TxnOp struct {
- KV *KVTxnOp
-}
-
-// TxnOps is a list of transaction operations.
-type TxnOps []*TxnOp
-
-// TxnResult is the internal format we receive from Consul.
-type TxnResult struct {
- KV *KVPair
-}
-
-// TxnResults is a list of TxnResult objects.
-type TxnResults []*TxnResult
-
-// TxnError is used to return information about an operation in a transaction.
-type TxnError struct {
- OpIndex int
- What string
-}
-
-// TxnErrors is a list of TxnError objects.
-type TxnErrors []*TxnError
-
-// TxnResponse is the internal format we receive from Consul.
-type TxnResponse struct {
- Results TxnResults
- Errors TxnErrors
-}
-
-// Txn is used to apply multiple KV operations in a single, atomic transaction.
-//
-// Note that Go will perform the required base64 encoding on the values
-// automatically because the type is a byte slice. Transactions are defined as a
-// list of operations to perform, using the KVOp constants and KVTxnOp structure
-// to define operations. If any operation fails, none of the changes are applied
-// to the state store. Note that this hides the internal raw transaction interface
-// and munges the input and output types into KV-specific ones for ease of use.
-// If there are more non-KV operations in the future we may break out a new
-// transaction API client, but it will be easy to keep this KV-specific variant
-// supported.
-//
-// Even though this is generally a write operation, we take a QueryOptions input
-// and return a QueryMeta output. If the transaction contains only read ops, then
-// Consul will fast-path it to a different endpoint internally which supports
-// consistency controls, but not blocking. If there are write operations then
-// the request will always be routed through raft and any consistency settings
-// will be ignored.
-//
-// Here's an example:
-//
-// ops := KVTxnOps{
-// &KVTxnOp{
-// Verb: KVLock,
-// Key: "test/lock",
-// Session: "adf4238a-882b-9ddc-4a9d-5b6758e4159e",
-// Value: []byte("hello"),
-// },
-// &KVTxnOp{
-// Verb: KVGet,
-// Key: "another/key",
-// },
-// }
-// ok, response, _, err := kv.Txn(&ops, nil)
-//
-// If there is a problem making the transaction request then an error will be
-// returned. Otherwise, the ok value will be true if the transaction succeeded
-// or false if it was rolled back. The response is a structured return value which
-// will have the outcome of the transaction. Its Results member will have entries
-// for each operation. Deleted keys will have a nil entry in the, and to save
-// space, the Value of each key in the Results will be nil unless the operation
-// is a KVGet. If the transaction was rolled back, the Errors member will have
-// entries referencing the index of the operation that failed along with an error
-// message.
+// The Txn function has been deprecated from the KV object; please see the Txn
+// object for more information about Transactions.
func (k *KV) Txn(txn KVTxnOps, q *QueryOptions) (bool, *KVTxnResponse, *QueryMeta, error) {
- r := k.c.newRequest("PUT", "/v1/txn")
- r.setQueryOptions(q)
-
- // Convert into the internal format since this is an all-KV txn.
- ops := make(TxnOps, 0, len(txn))
- for _, kvOp := range txn {
- ops = append(ops, &TxnOp{KV: kvOp})
+ var ops TxnOps
+ for _, op := range txn {
+ ops = append(ops, &TxnOp{KV: op})
}
- r.obj = ops
- rtt, resp, err := k.c.doRequest(r)
+
+ respOk, txnResp, qm, err := k.c.txn(ops, q)
if err != nil {
return false, nil, nil, err
}
- defer resp.Body.Close()
- qm := &QueryMeta{}
- parseQueryMeta(resp, qm)
- qm.RequestTime = rtt
-
- if resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusConflict {
- var txnResp TxnResponse
- if err := decodeBody(resp, &txnResp); err != nil {
- return false, nil, nil, err
- }
-
- // Convert from the internal format.
- kvResp := KVTxnResponse{
- Errors: txnResp.Errors,
- }
- for _, result := range txnResp.Results {
- kvResp.Results = append(kvResp.Results, result.KV)
- }
- return resp.StatusCode == http.StatusOK, &kvResp, qm, nil
+ // Convert from the internal format.
+ kvResp := KVTxnResponse{
+ Errors: txnResp.Errors,
}
-
- var buf bytes.Buffer
- if _, err := io.Copy(&buf, resp.Body); err != nil {
- return false, nil, nil, fmt.Errorf("Failed to read response: %v", err)
+ for _, result := range txnResp.Results {
+ kvResp.Results = append(kvResp.Results, result.KV)
}
- return false, nil, nil, fmt.Errorf("Failed request: %s", buf.String())
+ return respOk, &kvResp, qm, nil
}
diff --git a/vendor/github.com/hashicorp/consul/api/operator_keyring.go b/vendor/github.com/hashicorp/consul/api/operator_keyring.go
index 6b61429..038d5d5 100644
--- a/vendor/github.com/hashicorp/consul/api/operator_keyring.go
+++ b/vendor/github.com/hashicorp/consul/api/operator_keyring.go
@@ -16,6 +16,9 @@
// Segment has the network segment this request corresponds to.
Segment string
+ // Messages has information or errors from serf
+ Messages map[string]string `json:",omitempty"`
+
// A map of the encryption keys to the number of nodes they're installed on
Keys map[string]int
diff --git a/vendor/github.com/hashicorp/consul/api/txn.go b/vendor/github.com/hashicorp/consul/api/txn.go
new file mode 100644
index 0000000..65d7a16
--- /dev/null
+++ b/vendor/github.com/hashicorp/consul/api/txn.go
@@ -0,0 +1,230 @@
+package api
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "net/http"
+)
+
+// Txn is used to manipulate the Txn API
+type Txn struct {
+ c *Client
+}
+
+// Txn is used to return a handle to the K/V apis
+func (c *Client) Txn() *Txn {
+ return &Txn{c}
+}
+
+// TxnOp is the internal format we send to Consul. Currently only K/V and
+// check operations are supported.
+type TxnOp struct {
+ KV *KVTxnOp
+ Node *NodeTxnOp
+ Service *ServiceTxnOp
+ Check *CheckTxnOp
+}
+
+// TxnOps is a list of transaction operations.
+type TxnOps []*TxnOp
+
+// TxnResult is the internal format we receive from Consul.
+type TxnResult struct {
+ KV *KVPair
+ Node *Node
+ Service *CatalogService
+ Check *HealthCheck
+}
+
+// TxnResults is a list of TxnResult objects.
+type TxnResults []*TxnResult
+
+// TxnError is used to return information about an operation in a transaction.
+type TxnError struct {
+ OpIndex int
+ What string
+}
+
+// TxnErrors is a list of TxnError objects.
+type TxnErrors []*TxnError
+
+// TxnResponse is the internal format we receive from Consul.
+type TxnResponse struct {
+ Results TxnResults
+ Errors TxnErrors
+}
+
+// KVOp constants give possible operations available in a transaction.
+type KVOp string
+
+const (
+ KVSet KVOp = "set"
+ KVDelete KVOp = "delete"
+ KVDeleteCAS KVOp = "delete-cas"
+ KVDeleteTree KVOp = "delete-tree"
+ KVCAS KVOp = "cas"
+ KVLock KVOp = "lock"
+ KVUnlock KVOp = "unlock"
+ KVGet KVOp = "get"
+ KVGetTree KVOp = "get-tree"
+ KVCheckSession KVOp = "check-session"
+ KVCheckIndex KVOp = "check-index"
+ KVCheckNotExists KVOp = "check-not-exists"
+)
+
+// KVTxnOp defines a single operation inside a transaction.
+type KVTxnOp struct {
+ Verb KVOp
+ Key string
+ Value []byte
+ Flags uint64
+ Index uint64
+ Session string
+}
+
+// KVTxnOps defines a set of operations to be performed inside a single
+// transaction.
+type KVTxnOps []*KVTxnOp
+
+// KVTxnResponse has the outcome of a transaction.
+type KVTxnResponse struct {
+ Results []*KVPair
+ Errors TxnErrors
+}
+
+// NodeOp constants give possible operations available in a transaction.
+type NodeOp string
+
+const (
+ NodeGet NodeOp = "get"
+ NodeSet NodeOp = "set"
+ NodeCAS NodeOp = "cas"
+ NodeDelete NodeOp = "delete"
+ NodeDeleteCAS NodeOp = "delete-cas"
+)
+
+// NodeTxnOp defines a single operation inside a transaction.
+type NodeTxnOp struct {
+ Verb NodeOp
+ Node Node
+}
+
+// ServiceOp constants give possible operations available in a transaction.
+type ServiceOp string
+
+const (
+ ServiceGet ServiceOp = "get"
+ ServiceSet ServiceOp = "set"
+ ServiceCAS ServiceOp = "cas"
+ ServiceDelete ServiceOp = "delete"
+ ServiceDeleteCAS ServiceOp = "delete-cas"
+)
+
+// ServiceTxnOp defines a single operation inside a transaction.
+type ServiceTxnOp struct {
+ Verb ServiceOp
+ Node string
+ Service AgentService
+}
+
+// CheckOp constants give possible operations available in a transaction.
+type CheckOp string
+
+const (
+ CheckGet CheckOp = "get"
+ CheckSet CheckOp = "set"
+ CheckCAS CheckOp = "cas"
+ CheckDelete CheckOp = "delete"
+ CheckDeleteCAS CheckOp = "delete-cas"
+)
+
+// CheckTxnOp defines a single operation inside a transaction.
+type CheckTxnOp struct {
+ Verb CheckOp
+ Check HealthCheck
+}
+
+// Txn is used to apply multiple Consul operations in a single, atomic transaction.
+//
+// Note that Go will perform the required base64 encoding on the values
+// automatically because the type is a byte slice. Transactions are defined as a
+// list of operations to perform, using the different fields in the TxnOp structure
+// to define operations. If any operation fails, none of the changes are applied
+// to the state store.
+//
+// Even though this is generally a write operation, we take a QueryOptions input
+// and return a QueryMeta output. If the transaction contains only read ops, then
+// Consul will fast-path it to a different endpoint internally which supports
+// consistency controls, but not blocking. If there are write operations then
+// the request will always be routed through raft and any consistency settings
+// will be ignored.
+//
+// Here's an example:
+//
+// ops := KVTxnOps{
+// &KVTxnOp{
+// Verb: KVLock,
+// Key: "test/lock",
+// Session: "adf4238a-882b-9ddc-4a9d-5b6758e4159e",
+// Value: []byte("hello"),
+// },
+// &KVTxnOp{
+// Verb: KVGet,
+// Key: "another/key",
+// },
+// &CheckTxnOp{
+// Verb: CheckSet,
+// HealthCheck: HealthCheck{
+// Node: "foo",
+// CheckID: "redis:a",
+// Name: "Redis Health Check",
+// Status: "passing",
+// },
+// }
+// }
+// ok, response, _, err := kv.Txn(&ops, nil)
+//
+// If there is a problem making the transaction request then an error will be
+// returned. Otherwise, the ok value will be true if the transaction succeeded
+// or false if it was rolled back. The response is a structured return value which
+// will have the outcome of the transaction. Its Results member will have entries
+// for each operation. For KV operations, Deleted keys will have a nil entry in the
+// results, and to save space, the Value of each key in the Results will be nil
+// unless the operation is a KVGet. If the transaction was rolled back, the Errors
+// member will have entries referencing the index of the operation that failed
+// along with an error message.
+func (t *Txn) Txn(txn TxnOps, q *QueryOptions) (bool, *TxnResponse, *QueryMeta, error) {
+ return t.c.txn(txn, q)
+}
+
+func (c *Client) txn(txn TxnOps, q *QueryOptions) (bool, *TxnResponse, *QueryMeta, error) {
+ r := c.newRequest("PUT", "/v1/txn")
+ r.setQueryOptions(q)
+
+ r.obj = txn
+ rtt, resp, err := c.doRequest(r)
+ if err != nil {
+ return false, nil, nil, err
+ }
+ defer resp.Body.Close()
+
+ qm := &QueryMeta{}
+ parseQueryMeta(resp, qm)
+ qm.RequestTime = rtt
+
+ if resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusConflict {
+ var txnResp TxnResponse
+ if err := decodeBody(resp, &txnResp); err != nil {
+ return false, nil, nil, err
+ }
+
+ return resp.StatusCode == http.StatusOK, &txnResp, qm, nil
+ }
+
+ var buf bytes.Buffer
+ if _, err := io.Copy(&buf, resp.Body); err != nil {
+ return false, nil, nil, fmt.Errorf("Failed to read response: %v", err)
+ }
+ return false, nil, nil, fmt.Errorf("Failed request: %s", buf.String())
+}