2015-11-14 02:07:35 +00:00
|
|
|
package api
|
|
|
|
|
|
|
|
// QueryDatacenterOptions sets options about how we fail over if there are no
|
|
|
|
// healthy nodes in the local datacenter.
|
|
|
|
type QueryDatacenterOptions struct {
|
|
|
|
// NearestN is set to the number of remote datacenters to try, based on
|
|
|
|
// network coordinates.
|
|
|
|
NearestN int
|
|
|
|
|
|
|
|
// Datacenters is a fixed list of datacenters to try after NearestN. We
|
|
|
|
// never try a datacenter multiple times, so those are subtracted from
|
|
|
|
// this list before proceeding.
|
|
|
|
Datacenters []string
|
|
|
|
}
|
|
|
|
|
|
|
|
// QueryDNSOptions controls settings when query results are served over DNS.
|
|
|
|
type QueryDNSOptions struct {
|
|
|
|
// TTL is the time to live for the served DNS results.
|
|
|
|
TTL string
|
|
|
|
}
|
|
|
|
|
|
|
|
// ServiceQuery is used to query for a set of healthy nodes offering a specific
|
|
|
|
// service.
|
|
|
|
type ServiceQuery struct {
|
|
|
|
// Service is the service to query.
|
|
|
|
Service string
|
|
|
|
|
2020-02-10 15:40:44 +00:00
|
|
|
// Namespace of the service to query
|
|
|
|
Namespace string `json:",omitempty"`
|
|
|
|
|
2016-07-01 23:41:46 +00:00
|
|
|
// Near allows baking in the name of a node to automatically distance-
|
|
|
|
// sort from. The magic "_agent" value is supported, which sorts near
|
|
|
|
// the agent which initiated the request by default.
|
|
|
|
Near string
|
|
|
|
|
2015-11-14 02:07:35 +00:00
|
|
|
// Failover controls what we do if there are no healthy nodes in the
|
|
|
|
// local datacenter.
|
|
|
|
Failover QueryDatacenterOptions
|
|
|
|
|
2018-04-10 12:28:27 +00:00
|
|
|
// 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
|
|
|
|
|
2015-11-14 02:07:35 +00:00
|
|
|
// If OnlyPassing is true then we will only include nodes with passing
|
|
|
|
// health checks (critical AND warning checks will cause a node to be
|
|
|
|
// discarded)
|
|
|
|
OnlyPassing bool
|
|
|
|
|
|
|
|
// Tags are a set of required and/or disallowed tags. If a tag is in
|
2015-11-17 16:29:20 +00:00
|
|
|
// this list it must be present. If the tag is preceded with "!" then
|
2015-11-14 02:07:35 +00:00
|
|
|
// it is disallowed.
|
|
|
|
Tags []string
|
2017-01-23 23:53:45 +00:00
|
|
|
|
|
|
|
// 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
|
2018-06-05 23:15:59 +00:00
|
|
|
|
Improve Connect with Prepared Queries (#5291)
Given a query like:
```
{
"Name": "tagged-connect-query",
"Service": {
"Service": "foo",
"Tags": ["tag"],
"Connect": true
}
}
```
And a Consul configuration like:
```
{
"services": [
"name": "foo",
"port": 8080,
"connect": { "sidecar_service": {} },
"tags": ["tag"]
]
}
```
If you executed the query it would always turn up with 0 results. This was because the sidecar service was being created without any tags. You could instead make your config look like:
```
{
"services": [
"name": "foo",
"port": 8080,
"connect": { "sidecar_service": {
"tags": ["tag"]
} },
"tags": ["tag"]
]
}
```
However that is a bit redundant for most cases. This PR ensures that the tags and service meta of the parent service get copied to the sidecar service. If there are any tags or service meta set in the sidecar service definition then this copying does not take place. After the changes, the query will now return the expected results.
A second change was made to prepared queries in this PR which is to allow filtering on ServiceMeta just like we allow for filtering on NodeMeta.
2019-02-04 14:36:51 +00:00
|
|
|
// ServiceMeta is a map of required service 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.
|
|
|
|
ServiceMeta map[string]string
|
|
|
|
|
2018-06-05 23:15:59 +00:00
|
|
|
// Connect if true will filter the prepared query results to only
|
|
|
|
// include Connect-capable services. These include both native services
|
|
|
|
// and proxies for matching services. Note that if a proxy matches,
|
|
|
|
// the constraints in the query above (Near, OnlyPassing, etc.) apply
|
|
|
|
// to the _proxy_ and not the service being proxied. In practice, proxies
|
|
|
|
// should be directly next to their services so this isn't an issue.
|
|
|
|
Connect bool
|
2015-11-14 02:07:35 +00:00
|
|
|
}
|
|
|
|
|
2016-07-02 23:05:41 +00:00
|
|
|
// QueryTemplate carries the arguments for creating a templated query.
|
|
|
|
type QueryTemplate struct {
|
|
|
|
// Type specifies the type of the query template. Currently only
|
|
|
|
// "name_prefix_match" is supported. This field is required.
|
|
|
|
Type string
|
|
|
|
|
|
|
|
// Regexp allows specifying a regex pattern to match against the name
|
|
|
|
// of the query being executed.
|
|
|
|
Regexp string
|
|
|
|
}
|
|
|
|
|
2018-03-19 16:56:00 +00:00
|
|
|
// PreparedQueryDefinition defines a complete prepared query.
|
2015-11-14 02:07:35 +00:00
|
|
|
type PreparedQueryDefinition struct {
|
|
|
|
// ID is this UUID-based ID for the query, always generated by Consul.
|
|
|
|
ID string
|
|
|
|
|
|
|
|
// Name is an optional friendly name for the query supplied by the
|
|
|
|
// user. NOTE - if this feature is used then it will reduce the security
|
|
|
|
// of any read ACL associated with this query/service since this name
|
|
|
|
// can be used to locate nodes with supplying any ACL.
|
|
|
|
Name string
|
|
|
|
|
|
|
|
// Session is an optional session to tie this query's lifetime to. If
|
|
|
|
// this is omitted then the query will not expire.
|
|
|
|
Session string
|
|
|
|
|
|
|
|
// Token is the ACL token used when the query was created, and it is
|
|
|
|
// used when a query is subsequently executed. This token, or a token
|
|
|
|
// with management privileges, must be used to change the query later.
|
|
|
|
Token string
|
|
|
|
|
|
|
|
// Service defines a service query (leaving things open for other types
|
|
|
|
// later).
|
|
|
|
Service ServiceQuery
|
|
|
|
|
|
|
|
// DNS has options that control how the results of this query are
|
|
|
|
// served over DNS.
|
|
|
|
DNS QueryDNSOptions
|
2016-07-02 23:05:41 +00:00
|
|
|
|
|
|
|
// Template is used to pass through the arguments for creating a
|
|
|
|
// prepared query with an attached template. If a template is given,
|
|
|
|
// interpolations are possible in other struct fields.
|
|
|
|
Template QueryTemplate
|
2015-11-14 02:07:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// PreparedQueryExecuteResponse has the results of executing a query.
|
|
|
|
type PreparedQueryExecuteResponse struct {
|
|
|
|
// Service is the service that was queried.
|
|
|
|
Service string
|
|
|
|
|
2020-02-10 15:40:44 +00:00
|
|
|
// Namespace of the service that was queried
|
|
|
|
Namespace string `json:",omitempty"`
|
|
|
|
|
2015-11-14 02:07:35 +00:00
|
|
|
// Nodes has the nodes that were output by the query.
|
|
|
|
Nodes []ServiceEntry
|
|
|
|
|
|
|
|
// DNS has the options for serving these results over DNS.
|
|
|
|
DNS QueryDNSOptions
|
|
|
|
|
|
|
|
// Datacenter is the datacenter that these results came from.
|
|
|
|
Datacenter string
|
|
|
|
|
|
|
|
// Failovers is a count of how many times we had to query a remote
|
|
|
|
// datacenter.
|
|
|
|
Failovers int
|
|
|
|
}
|
|
|
|
|
|
|
|
// PreparedQuery can be used to query the prepared query endpoints.
|
|
|
|
type PreparedQuery struct {
|
|
|
|
c *Client
|
|
|
|
}
|
|
|
|
|
|
|
|
// PreparedQuery returns a handle to the prepared query endpoints.
|
|
|
|
func (c *Client) PreparedQuery() *PreparedQuery {
|
|
|
|
return &PreparedQuery{c}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create makes a new prepared query. The ID of the new query is returned.
|
|
|
|
func (c *PreparedQuery) Create(query *PreparedQueryDefinition, q *WriteOptions) (string, *WriteMeta, error) {
|
|
|
|
r := c.c.newRequest("POST", "/v1/query")
|
|
|
|
r.setWriteOptions(q)
|
|
|
|
r.obj = query
|
|
|
|
rtt, resp, err := requireOK(c.c.doRequest(r))
|
|
|
|
if err != nil {
|
|
|
|
return "", nil, err
|
|
|
|
}
|
2021-06-15 15:50:29 +00:00
|
|
|
defer closeResponseBody(resp)
|
2015-11-14 02:07:35 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update makes updates to an existing prepared query.
|
|
|
|
func (c *PreparedQuery) Update(query *PreparedQueryDefinition, q *WriteOptions) (*WriteMeta, error) {
|
2015-11-17 07:12:44 +00:00
|
|
|
return c.c.write("/v1/query/"+query.ID, query, nil, q)
|
2015-11-14 02:07:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// List is used to fetch all the prepared queries (always requires a management
|
|
|
|
// token).
|
|
|
|
func (c *PreparedQuery) List(q *QueryOptions) ([]*PreparedQueryDefinition, *QueryMeta, error) {
|
|
|
|
var out []*PreparedQueryDefinition
|
2015-11-17 07:12:44 +00:00
|
|
|
qm, err := c.c.query("/v1/query", &out, q)
|
|
|
|
if err != nil {
|
2015-11-14 02:07:35 +00:00
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
return out, qm, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get is used to fetch a specific prepared query.
|
|
|
|
func (c *PreparedQuery) Get(queryID string, q *QueryOptions) ([]*PreparedQueryDefinition, *QueryMeta, error) {
|
|
|
|
var out []*PreparedQueryDefinition
|
2015-11-17 07:12:44 +00:00
|
|
|
qm, err := c.c.query("/v1/query/"+queryID, &out, q)
|
|
|
|
if err != nil {
|
2015-11-14 02:07:35 +00:00
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
return out, qm, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete is used to delete a specific prepared query.
|
2016-11-03 19:54:07 +00:00
|
|
|
func (c *PreparedQuery) Delete(queryID string, q *WriteOptions) (*WriteMeta, error) {
|
2015-11-14 02:07:35 +00:00
|
|
|
r := c.c.newRequest("DELETE", "/v1/query/"+queryID)
|
2016-11-03 19:54:07 +00:00
|
|
|
r.setWriteOptions(q)
|
2015-11-14 02:07:35 +00:00
|
|
|
rtt, resp, err := requireOK(c.c.doRequest(r))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-06-15 15:50:29 +00:00
|
|
|
defer closeResponseBody(resp)
|
2015-11-14 02:07:35 +00:00
|
|
|
|
2016-11-03 19:54:07 +00:00
|
|
|
wm := &WriteMeta{}
|
|
|
|
wm.RequestTime = rtt
|
|
|
|
return wm, nil
|
2015-11-14 02:07:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Execute is used to execute a specific prepared query. You can execute using
|
|
|
|
// a query ID or name.
|
|
|
|
func (c *PreparedQuery) Execute(queryIDOrName string, q *QueryOptions) (*PreparedQueryExecuteResponse, *QueryMeta, error) {
|
|
|
|
var out *PreparedQueryExecuteResponse
|
2015-11-17 07:12:44 +00:00
|
|
|
qm, err := c.c.query("/v1/query/"+queryIDOrName+"/execute", &out, q)
|
|
|
|
if err != nil {
|
2015-11-14 02:07:35 +00:00
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
return out, qm, nil
|
|
|
|
}
|