40dd8ce65b
* added method for converting SamenessGroupConfigEntry - added new method `ToQueryFailoverTargets` for converting a SamenessGroupConfigEntry's members to a list of QueryFailoverTargets - renamed `ToFailoverTargets` ToServiceResolverFailoverTargets to distinguish it from `ToQueryFailoverTargets` * Added SamenessGroup to PreparedQuery - exposed Service.Partition to API when defining a prepared query - added a method for determining if a QueryFailoverOptions is empty - This will be useful for validation - added unit tests * added method for retrieving a SamenessGroup to state store * added logic for using PQ with SamenessGroup - added branching path for SamenessGroup handling in execute. It will be handled separate from the normal PQ case - added a new interface so that the `GetSamenessGroupFailoverTargets` can be properly tested - separated the execute logic into a `targetSelector` function so that it can be used for both failover and sameness group PQs - split OSS only methods into new PQ OSS files - added validation that `samenessGroup` is an enterprise only feature * added documentation for PQ SamenessGroup
389 lines
12 KiB
Go
389 lines
12 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package structs
|
|
|
|
import (
|
|
"strconv"
|
|
|
|
"github.com/mitchellh/hashstructure"
|
|
|
|
"github.com/hashicorp/consul/acl"
|
|
"github.com/hashicorp/consul/agent/cache"
|
|
"github.com/hashicorp/consul/types"
|
|
)
|
|
|
|
// QueryFailoverOptions sets options about how we fail over if there are no
|
|
// healthy nodes in the local datacenter.
|
|
type QueryFailoverOptions 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
|
|
|
|
// Targets is a fixed list of datacenters and peers to try. This field cannot
|
|
// be populated with NearestN or Datacenters.
|
|
Targets []QueryFailoverTarget
|
|
}
|
|
|
|
// AsTargets either returns Targets as is or Datacenters converted into
|
|
// Targets.
|
|
func (f *QueryFailoverOptions) AsTargets() []QueryFailoverTarget {
|
|
if dcs := f.Datacenters; len(dcs) > 0 {
|
|
var targets []QueryFailoverTarget
|
|
for _, dc := range dcs {
|
|
targets = append(targets, QueryFailoverTarget{Datacenter: dc})
|
|
}
|
|
return targets
|
|
}
|
|
|
|
return f.Targets
|
|
}
|
|
|
|
// IsEmpty returns true if the QueryFailoverOptions are empty (not set), false otherwise
|
|
func (f *QueryFailoverOptions) IsEmpty() bool {
|
|
if f == nil || (f.NearestN == 0 && len(f.Datacenters) == 0 && len(f.Targets) == 0) {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
type QueryFailoverTarget struct {
|
|
// Peer specifies a peer to try during failover.
|
|
Peer string
|
|
|
|
// Datacenter specifies a datacenter to try during failover.
|
|
Datacenter string
|
|
|
|
acl.EnterpriseMeta
|
|
}
|
|
|
|
// 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
|
|
|
|
// SamenessGroup specifies a sameness group to query. The first member of the Sameness Group will
|
|
// be targeted first on PQ execution and subsequent members will be targeted during failover scenarios.
|
|
// This field is mutually exclusive with Failover.
|
|
SamenessGroup string
|
|
|
|
// Failover controls what we do if there are no healthy nodes in the
|
|
// local datacenter.
|
|
Failover QueryFailoverOptions
|
|
|
|
// 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
|
|
|
|
// 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 []types.CheckID
|
|
|
|
// Near allows the query to always prefer the node nearest the given
|
|
// node. If the node does not exist, results are returned in their
|
|
// normal randomly-shuffled order. Supplying the magic "_agent" value
|
|
// is supported to sort near the agent which initiated the request.
|
|
Near string
|
|
|
|
// Tags are a set of required and/or disallowed tags. If a tag is in
|
|
// this list it must be present. If the tag is preceded with "!" then
|
|
// it is disallowed.
|
|
Tags []string
|
|
|
|
// NodeMeta is a map of required node metadata fields. If a key/value
|
|
// pair is in this map it must be present on the node in order for the
|
|
// service entry to be returned.
|
|
NodeMeta map[string]string
|
|
|
|
// 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
|
|
|
|
// 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
|
|
|
|
// If not empty, Peer represents the peer that the service
|
|
// was imported from.
|
|
Peer string
|
|
|
|
// EnterpriseMeta is the embedded enterprise metadata
|
|
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
|
|
}
|
|
|
|
const (
|
|
// QueryTemplateTypeNamePrefixMatch uses the Name field of the query as
|
|
// a prefix to select the template.
|
|
QueryTemplateTypeNamePrefixMatch = "name_prefix_match"
|
|
)
|
|
|
|
// QueryTemplateOptions controls settings if this query is a template.
|
|
type QueryTemplateOptions struct {
|
|
// Type, if non-empty, means that this query is a template. This is
|
|
// set to one of the QueryTemplateType* constants above.
|
|
Type string
|
|
|
|
// Regexp is an optional regular expression to use to parse the full
|
|
// name, once the prefix match has selected a template. This can be
|
|
// used to extract parts of the name and choose a service name, set
|
|
// tags, etc.
|
|
Regexp string
|
|
|
|
// RemoveEmptyTags, if true, removes empty tags from matched tag list
|
|
RemoveEmptyTags bool
|
|
}
|
|
|
|
// PreparedQuery defines a complete prepared query, and is the structure we
|
|
// maintain in the state store.
|
|
type PreparedQuery 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
|
|
|
|
// Template is used to configure this query as a template, which will
|
|
// respond to queries based on the Name, and then will be rendered
|
|
// before it is executed.
|
|
Template QueryTemplateOptions
|
|
|
|
// 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
|
|
|
|
RaftIndex
|
|
}
|
|
|
|
// GetACLPrefix returns the prefix to look up the prepared_query ACL policy for
|
|
// this query, and whether the prefix applies to this query. You always need to
|
|
// check the ok value before using the prefix.
|
|
func (pq *PreparedQuery) GetACLPrefix() (string, bool) {
|
|
if pq.Name != "" || pq.Template.Type != "" {
|
|
return pq.Name, true
|
|
}
|
|
|
|
return "", false
|
|
}
|
|
|
|
type PreparedQueries []*PreparedQuery
|
|
|
|
type IndexedPreparedQueries struct {
|
|
Queries PreparedQueries
|
|
QueryMeta
|
|
}
|
|
|
|
type PreparedQueryOp string
|
|
|
|
const (
|
|
PreparedQueryCreate PreparedQueryOp = "create"
|
|
PreparedQueryUpdate PreparedQueryOp = "update"
|
|
PreparedQueryDelete PreparedQueryOp = "delete"
|
|
)
|
|
|
|
// QueryRequest is used to create or change prepared queries.
|
|
type PreparedQueryRequest struct {
|
|
// Datacenter is the target this request is intended for.
|
|
Datacenter string
|
|
|
|
// Op is the operation to apply.
|
|
Op PreparedQueryOp
|
|
|
|
// Query is the query itself.
|
|
Query *PreparedQuery
|
|
|
|
// WriteRequest holds the ACL token to go along with this request.
|
|
WriteRequest
|
|
}
|
|
|
|
// RequestDatacenter returns the datacenter for a given request.
|
|
func (q *PreparedQueryRequest) RequestDatacenter() string {
|
|
return q.Datacenter
|
|
}
|
|
|
|
// PreparedQuerySpecificRequest is used to get information about a prepared
|
|
// query.
|
|
type PreparedQuerySpecificRequest struct {
|
|
// Datacenter is the target this request is intended for.
|
|
Datacenter string
|
|
|
|
// QueryID is the ID of a query.
|
|
QueryID string
|
|
|
|
// QueryOptions (unfortunately named here) controls the consistency
|
|
// settings for the query lookup itself, as well as the service lookups.
|
|
QueryOptions
|
|
}
|
|
|
|
// RequestDatacenter returns the datacenter for a given request.
|
|
func (q *PreparedQuerySpecificRequest) RequestDatacenter() string {
|
|
return q.Datacenter
|
|
}
|
|
|
|
// PreparedQueryExecuteRequest is used to execute a prepared query.
|
|
type PreparedQueryExecuteRequest struct {
|
|
// Datacenter is the target this request is intended for.
|
|
Datacenter string
|
|
|
|
// QueryIDOrName is the ID of a query _or_ the name of one, either can
|
|
// be provided.
|
|
QueryIDOrName string
|
|
|
|
// Limit will trim the resulting list down to the given limit.
|
|
Limit int
|
|
|
|
// Connect will force results to be Connect-enabled nodes for the
|
|
// matching services. This is equivalent in semantics exactly to
|
|
// setting "Connect" in the query template itself, but allows callers
|
|
// to use any prepared query in a Connect setting.
|
|
Connect bool
|
|
|
|
// Source is used to sort the results relative to a given node using
|
|
// network coordinates.
|
|
Source QuerySource
|
|
|
|
// Agent is used to carry around a reference to the agent which initiated
|
|
// the execute request. Used to distance-sort relative to the local node.
|
|
Agent QuerySource
|
|
|
|
// QueryOptions (unfortunately named here) controls the consistency
|
|
// settings for the query lookup itself, as well as the service lookups.
|
|
QueryOptions
|
|
}
|
|
|
|
// RequestDatacenter returns the datacenter for a given request.
|
|
func (q *PreparedQueryExecuteRequest) RequestDatacenter() string {
|
|
return q.Datacenter
|
|
}
|
|
|
|
// CacheInfo implements cache.Request allowing requests to be cached on agent.
|
|
func (q *PreparedQueryExecuteRequest) CacheInfo() cache.RequestInfo {
|
|
info := cache.RequestInfo{
|
|
Token: q.Token,
|
|
Datacenter: q.Datacenter,
|
|
MinIndex: q.MinQueryIndex,
|
|
Timeout: q.MaxQueryTime,
|
|
MaxAge: q.MaxAge,
|
|
MustRevalidate: q.MustRevalidate,
|
|
}
|
|
|
|
// To calculate the cache key we hash over all the fields that affect the
|
|
// output other than Datacenter and Token which are dealt with in the cache
|
|
// framework already. Note the order here is important for the outcome - if we
|
|
// ever care about cache-invalidation on updates e.g. because we persist
|
|
// cached results, we need to be careful we maintain the same order of fields
|
|
// here. We could alternatively use `hash:set` struct tag on an anonymous
|
|
// struct to make it more robust if it becomes significant.
|
|
v, err := hashstructure.Hash([]interface{}{
|
|
q.QueryIDOrName,
|
|
q.Limit,
|
|
q.Connect,
|
|
}, nil)
|
|
if err == nil {
|
|
// If there is an error, we don't set the key. A blank key forces
|
|
// no cache for this request so the request is forwarded directly
|
|
// to the server.
|
|
info.Key = strconv.FormatUint(v, 10)
|
|
}
|
|
|
|
return info
|
|
}
|
|
|
|
// PreparedQueryExecuteRemoteRequest is used when running a local query in a
|
|
// remote datacenter.
|
|
type PreparedQueryExecuteRemoteRequest struct {
|
|
// Datacenter is the target this request is intended for.
|
|
Datacenter string
|
|
|
|
// Query is a copy of the query to execute. We have to ship the entire
|
|
// query over since it won't be present in the remote state store.
|
|
Query PreparedQuery
|
|
|
|
// Limit will trim the resulting list down to the given limit.
|
|
Limit int
|
|
|
|
// Connect is the same as ExecuteRequest.
|
|
Connect bool
|
|
|
|
// QueryOptions (unfortunately named here) controls the consistency
|
|
// settings for the the service lookups.
|
|
QueryOptions
|
|
}
|
|
|
|
// RequestDatacenter returns the datacenter for a given request.
|
|
func (q *PreparedQueryExecuteRemoteRequest) RequestDatacenter() string {
|
|
return q.Datacenter
|
|
}
|
|
|
|
// PreparedQueryExecuteResponse has the results of executing a query.
|
|
type PreparedQueryExecuteResponse struct {
|
|
// Service is the service that was queried.
|
|
Service string
|
|
|
|
// EnterpriseMeta of the service that was queried.
|
|
acl.EnterpriseMeta
|
|
|
|
// Nodes has the nodes that were output by the query.
|
|
Nodes CheckServiceNodes
|
|
|
|
// DNS has the options for serving these results over DNS.
|
|
DNS QueryDNSOptions
|
|
|
|
// Datacenter is the datacenter that these results came from.
|
|
Datacenter string
|
|
|
|
// PeerName specifies the cluster peer that these results came from.
|
|
PeerName string
|
|
|
|
// Failovers is a count of how many times we had to query a remote
|
|
// datacenter.
|
|
Failovers int
|
|
|
|
// QueryMeta has freshness information about the query.
|
|
QueryMeta
|
|
}
|
|
|
|
// PreparedQueryExplainResponse has the results when explaining a query/
|
|
type PreparedQueryExplainResponse struct {
|
|
// Query has the fully-rendered query.
|
|
Query PreparedQuery
|
|
|
|
// QueryMeta has freshness information about the query.
|
|
QueryMeta
|
|
}
|