github.com/sl1pm4t/consul@v1.4.5-0.20190325224627-74c31c540f9c/agent/structs/structs.go (about)

     1  package structs
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"math/rand"
     8  	"reflect"
     9  	"regexp"
    10  	"sort"
    11  	"strconv"
    12  	"strings"
    13  	"time"
    14  
    15  	"github.com/hashicorp/consul/agent/cache"
    16  	"github.com/hashicorp/consul/api"
    17  	"github.com/hashicorp/consul/types"
    18  	"github.com/hashicorp/go-msgpack/codec"
    19  	multierror "github.com/hashicorp/go-multierror"
    20  	"github.com/hashicorp/serf/coordinate"
    21  	"github.com/mitchellh/hashstructure"
    22  )
    23  
    24  type MessageType uint8
    25  
    26  // RaftIndex is used to track the index used while creating
    27  // or modifying a given struct type.
    28  type RaftIndex struct {
    29  	CreateIndex uint64
    30  	ModifyIndex uint64
    31  }
    32  
    33  // These are serialized between Consul servers and stored in Consul snapshots,
    34  // so entries must only ever be added.
    35  const (
    36  	RegisterRequestType        MessageType = 0
    37  	DeregisterRequestType                  = 1
    38  	KVSRequestType                         = 2
    39  	SessionRequestType                     = 3
    40  	ACLRequestType                         = 4 // DEPRECATED (ACL-Legacy-Compat)
    41  	TombstoneRequestType                   = 5
    42  	CoordinateBatchUpdateType              = 6
    43  	PreparedQueryRequestType               = 7
    44  	TxnRequestType                         = 8
    45  	AutopilotRequestType                   = 9
    46  	AreaRequestType                        = 10
    47  	ACLBootstrapRequestType                = 11
    48  	IntentionRequestType                   = 12
    49  	ConnectCARequestType                   = 13
    50  	ConnectCAProviderStateType             = 14
    51  	ConnectCAConfigType                    = 15 // FSM snapshots only.
    52  	IndexRequestType                       = 16 // FSM snapshots only.
    53  	ACLTokenSetRequestType                 = 17
    54  	ACLTokenDeleteRequestType              = 18
    55  	ACLPolicySetRequestType                = 19
    56  	ACLPolicyDeleteRequestType             = 20
    57  	ConnectCALeafRequestType               = 21
    58  )
    59  
    60  const (
    61  	// IgnoreUnknownTypeFlag is set along with a MessageType
    62  	// to indicate that the message type can be safely ignored
    63  	// if it is not recognized. This is for future proofing, so
    64  	// that new commands can be added in a way that won't cause
    65  	// old servers to crash when the FSM attempts to process them.
    66  	IgnoreUnknownTypeFlag MessageType = 128
    67  
    68  	// NodeMaint is the special key set by a node in maintenance mode.
    69  	NodeMaint = "_node_maintenance"
    70  
    71  	// ServiceMaintPrefix is the prefix for a service in maintenance mode.
    72  	ServiceMaintPrefix = "_service_maintenance:"
    73  
    74  	// The meta key prefix reserved for Consul's internal use
    75  	metaKeyReservedPrefix = "consul-"
    76  
    77  	// metaMaxKeyPairs is maximum number of metadata key pairs allowed to be registered
    78  	metaMaxKeyPairs = 64
    79  
    80  	// metaKeyMaxLength is the maximum allowed length of a metadata key
    81  	metaKeyMaxLength = 128
    82  
    83  	// metaValueMaxLength is the maximum allowed length of a metadata value
    84  	metaValueMaxLength = 512
    85  
    86  	// MetaSegmentKey is the node metadata key used to store the node's network segment
    87  	MetaSegmentKey = "consul-network-segment"
    88  
    89  	// MaxLockDelay provides a maximum LockDelay value for
    90  	// a session. Any value above this will not be respected.
    91  	MaxLockDelay = 60 * time.Second
    92  )
    93  
    94  // metaKeyFormat checks if a metadata key string is valid
    95  var metaKeyFormat = regexp.MustCompile(`^[a-zA-Z0-9_-]+$`).MatchString
    96  
    97  func ValidStatus(s string) bool {
    98  	return s == api.HealthPassing || s == api.HealthWarning || s == api.HealthCritical
    99  }
   100  
   101  // RPCInfo is used to describe common information about query
   102  type RPCInfo interface {
   103  	RequestDatacenter() string
   104  	IsRead() bool
   105  	AllowStaleRead() bool
   106  	TokenSecret() string
   107  }
   108  
   109  // QueryOptions is used to specify various flags for read queries
   110  type QueryOptions struct {
   111  	// Token is the ACL token ID. If not provided, the 'anonymous'
   112  	// token is assumed for backwards compatibility.
   113  	Token string
   114  
   115  	// If set, wait until query exceeds given index. Must be provided
   116  	// with MaxQueryTime.
   117  	MinQueryIndex uint64
   118  
   119  	// Provided with MinQueryIndex to wait for change.
   120  	MaxQueryTime time.Duration
   121  
   122  	// If set, any follower can service the request. Results
   123  	// may be arbitrarily stale.
   124  	AllowStale bool
   125  
   126  	// If set, the leader must verify leadership prior to
   127  	// servicing the request. Prevents a stale read.
   128  	RequireConsistent bool
   129  
   130  	// If set, the local agent may respond with an arbitrarily stale locally
   131  	// cached response. The semantics differ from AllowStale since the agent may
   132  	// be entirely partitioned from the servers and still considered "healthy" by
   133  	// operators. Stale responses from Servers are also arbitrarily stale, but can
   134  	// provide additional bounds on the last contact time from the leader. It's
   135  	// expected that servers that are partitioned are noticed and replaced in a
   136  	// timely way by operators while the same may not be true for client agents.
   137  	UseCache bool
   138  
   139  	// If set and AllowStale is true, will try first a stale
   140  	// read, and then will perform a consistent read if stale
   141  	// read is older than value.
   142  	MaxStaleDuration time.Duration
   143  
   144  	// MaxAge limits how old a cached value will be returned if UseCache is true.
   145  	// If there is a cached response that is older than the MaxAge, it is treated
   146  	// as a cache miss and a new fetch invoked. If the fetch fails, the error is
   147  	// returned. Clients that wish to allow for stale results on error can set
   148  	// StaleIfError to a longer duration to change this behavior. It is ignored
   149  	// if the endpoint supports background refresh caching. See
   150  	// https://www.consul.io/api/index.html#agent-caching for more details.
   151  	MaxAge time.Duration
   152  
   153  	// MustRevalidate forces the agent to fetch a fresh version of a cached
   154  	// resource or at least validate that the cached version is still fresh. It is
   155  	// implied by either max-age=0 or must-revalidate Cache-Control headers. It
   156  	// only makes sense when UseCache is true. We store it since MaxAge = 0 is the
   157  	// default unset value.
   158  	MustRevalidate bool
   159  
   160  	// StaleIfError specifies how stale the client will accept a cached response
   161  	// if the servers are unavailable to fetch a fresh one. Only makes sense when
   162  	// UseCache is true and MaxAge is set to a lower, non-zero value. It is
   163  	// ignored if the endpoint supports background refresh caching. See
   164  	// https://www.consul.io/api/index.html#agent-caching for more details.
   165  	StaleIfError time.Duration
   166  }
   167  
   168  // IsRead is always true for QueryOption.
   169  func (q QueryOptions) IsRead() bool {
   170  	return true
   171  }
   172  
   173  // ConsistencyLevel display the consistency required by a request
   174  func (q QueryOptions) ConsistencyLevel() string {
   175  	if q.RequireConsistent {
   176  		return "consistent"
   177  	} else if q.AllowStale {
   178  		return "stale"
   179  	} else {
   180  		return "leader"
   181  	}
   182  }
   183  
   184  func (q QueryOptions) AllowStaleRead() bool {
   185  	return q.AllowStale
   186  }
   187  
   188  func (q QueryOptions) TokenSecret() string {
   189  	return q.Token
   190  }
   191  
   192  type WriteRequest struct {
   193  	// Token is the ACL token ID. If not provided, the 'anonymous'
   194  	// token is assumed for backwards compatibility.
   195  	Token string
   196  }
   197  
   198  // WriteRequest only applies to writes, always false
   199  func (w WriteRequest) IsRead() bool {
   200  	return false
   201  }
   202  
   203  func (w WriteRequest) AllowStaleRead() bool {
   204  	return false
   205  }
   206  
   207  func (w WriteRequest) TokenSecret() string {
   208  	return w.Token
   209  }
   210  
   211  // QueryMeta allows a query response to include potentially
   212  // useful metadata about a query
   213  type QueryMeta struct {
   214  	// This is the index associated with the read
   215  	Index uint64
   216  
   217  	// If AllowStale is used, this is time elapsed since
   218  	// last contact between the follower and leader. This
   219  	// can be used to gauge staleness.
   220  	LastContact time.Duration
   221  
   222  	// Used to indicate if there is a known leader node
   223  	KnownLeader bool
   224  
   225  	// Consistencylevel returns the consistency used to serve the query
   226  	// Having `discovery_max_stale` on the agent can affect whether
   227  	// the request was served by a leader.
   228  	ConsistencyLevel string
   229  }
   230  
   231  // RegisterRequest is used for the Catalog.Register endpoint
   232  // to register a node as providing a service. If no service
   233  // is provided, the node is registered.
   234  type RegisterRequest struct {
   235  	Datacenter      string
   236  	ID              types.NodeID
   237  	Node            string
   238  	Address         string
   239  	TaggedAddresses map[string]string
   240  	NodeMeta        map[string]string
   241  	Service         *NodeService
   242  	Check           *HealthCheck
   243  	Checks          HealthChecks
   244  
   245  	// SkipNodeUpdate can be used when a register request is intended for
   246  	// updating a service and/or checks, but doesn't want to overwrite any
   247  	// node information if the node is already registered. If the node
   248  	// doesn't exist, it will still be created, but if the node exists, any
   249  	// node portion of this update will not apply.
   250  	SkipNodeUpdate bool
   251  
   252  	WriteRequest
   253  }
   254  
   255  func (r *RegisterRequest) RequestDatacenter() string {
   256  	return r.Datacenter
   257  }
   258  
   259  // ChangesNode returns true if the given register request changes the given
   260  // node, which can be nil. This only looks for changes to the node record itself,
   261  // not any of the health checks.
   262  func (r *RegisterRequest) ChangesNode(node *Node) bool {
   263  	// This means it's creating the node.
   264  	if node == nil {
   265  		return true
   266  	}
   267  
   268  	// If we've been asked to skip the node update, then say there are no
   269  	// changes.
   270  	if r.SkipNodeUpdate {
   271  		return false
   272  	}
   273  
   274  	// Check if any of the node-level fields are being changed.
   275  	if r.ID != node.ID ||
   276  		r.Node != node.Node ||
   277  		r.Address != node.Address ||
   278  		r.Datacenter != node.Datacenter ||
   279  		!reflect.DeepEqual(r.TaggedAddresses, node.TaggedAddresses) ||
   280  		!reflect.DeepEqual(r.NodeMeta, node.Meta) {
   281  		return true
   282  	}
   283  
   284  	return false
   285  }
   286  
   287  // DeregisterRequest is used for the Catalog.Deregister endpoint
   288  // to deregister a node as providing a service. If no service is
   289  // provided the entire node is deregistered.
   290  type DeregisterRequest struct {
   291  	Datacenter string
   292  	Node       string
   293  	ServiceID  string
   294  	CheckID    types.CheckID
   295  	WriteRequest
   296  }
   297  
   298  func (r *DeregisterRequest) RequestDatacenter() string {
   299  	return r.Datacenter
   300  }
   301  
   302  // QuerySource is used to pass along information about the source node
   303  // in queries so that we can adjust the response based on its network
   304  // coordinates.
   305  type QuerySource struct {
   306  	Datacenter string
   307  	Segment    string
   308  	Node       string
   309  	Ip         string
   310  }
   311  
   312  // DCSpecificRequest is used to query about a specific DC
   313  type DCSpecificRequest struct {
   314  	Datacenter      string
   315  	NodeMetaFilters map[string]string
   316  	Source          QuerySource
   317  	QueryOptions
   318  }
   319  
   320  func (r *DCSpecificRequest) RequestDatacenter() string {
   321  	return r.Datacenter
   322  }
   323  
   324  func (r *DCSpecificRequest) CacheInfo() cache.RequestInfo {
   325  	info := cache.RequestInfo{
   326  		Token:          r.Token,
   327  		Datacenter:     r.Datacenter,
   328  		MinIndex:       r.MinQueryIndex,
   329  		Timeout:        r.MaxQueryTime,
   330  		MaxAge:         r.MaxAge,
   331  		MustRevalidate: r.MustRevalidate,
   332  	}
   333  
   334  	// To calculate the cache key we only hash the node filters. The
   335  	// datacenter is handled by the cache framework. The other fields are
   336  	// not, but should not be used in any cache types.
   337  	v, err := hashstructure.Hash(r.NodeMetaFilters, nil)
   338  	if err == nil {
   339  		// If there is an error, we don't set the key. A blank key forces
   340  		// no cache for this request so the request is forwarded directly
   341  		// to the server.
   342  		info.Key = strconv.FormatUint(v, 10)
   343  	}
   344  
   345  	return info
   346  }
   347  
   348  func (r *DCSpecificRequest) CacheMinIndex() uint64 {
   349  	return r.QueryOptions.MinQueryIndex
   350  }
   351  
   352  // ServiceSpecificRequest is used to query about a specific service
   353  type ServiceSpecificRequest struct {
   354  	Datacenter      string
   355  	NodeMetaFilters map[string]string
   356  	ServiceName     string
   357  	// DEPRECATED (singular-service-tag) - remove this when backwards RPC compat
   358  	// with 1.2.x is not required.
   359  	ServiceTag     string
   360  	ServiceTags    []string
   361  	ServiceAddress string
   362  	TagFilter      bool // Controls tag filtering
   363  	Source         QuerySource
   364  
   365  	// Connect if true will only search for Connect-compatible services.
   366  	Connect bool
   367  
   368  	QueryOptions
   369  }
   370  
   371  func (r *ServiceSpecificRequest) RequestDatacenter() string {
   372  	return r.Datacenter
   373  }
   374  
   375  func (r *ServiceSpecificRequest) CacheInfo() cache.RequestInfo {
   376  	info := cache.RequestInfo{
   377  		Token:          r.Token,
   378  		Datacenter:     r.Datacenter,
   379  		MinIndex:       r.MinQueryIndex,
   380  		Timeout:        r.MaxQueryTime,
   381  		MaxAge:         r.MaxAge,
   382  		MustRevalidate: r.MustRevalidate,
   383  	}
   384  
   385  	// To calculate the cache key we hash over all the fields that affect the
   386  	// output other than Datacenter and Token which are dealt with in the cache
   387  	// framework already. Note the order here is important for the outcome - if we
   388  	// ever care about cache-invalidation on updates e.g. because we persist
   389  	// cached results, we need to be careful we maintain the same order of fields
   390  	// here. We could alternatively use `hash:set` struct tag on an anonymous
   391  	// struct to make it more robust if it becomes significant.
   392  	sort.Strings(r.ServiceTags)
   393  	v, err := hashstructure.Hash([]interface{}{
   394  		r.NodeMetaFilters,
   395  		r.ServiceName,
   396  		// DEPRECATED (singular-service-tag) - remove this when upgrade RPC compat
   397  		// with 1.2.x is not required. We still need this in because <1.3 agents
   398  		// might still send RPCs with singular tag set. In fact the only place we
   399  		// use this method is in agent cache so if the agent is new enough to have
   400  		// this code this should never be set, but it's safer to include it until we
   401  		// completely remove this field just in case it's erroneously used anywhere
   402  		// (e.g. until this change DNS still used it).
   403  		r.ServiceTag,
   404  		r.ServiceTags,
   405  		r.ServiceAddress,
   406  		r.TagFilter,
   407  		r.Connect,
   408  	}, nil)
   409  	if err == nil {
   410  		// If there is an error, we don't set the key. A blank key forces
   411  		// no cache for this request so the request is forwarded directly
   412  		// to the server.
   413  		info.Key = strconv.FormatUint(v, 10)
   414  	}
   415  
   416  	return info
   417  }
   418  
   419  func (r *ServiceSpecificRequest) CacheMinIndex() uint64 {
   420  	return r.QueryOptions.MinQueryIndex
   421  }
   422  
   423  // NodeSpecificRequest is used to request the information about a single node
   424  type NodeSpecificRequest struct {
   425  	Datacenter string
   426  	Node       string
   427  	QueryOptions
   428  }
   429  
   430  func (r *NodeSpecificRequest) RequestDatacenter() string {
   431  	return r.Datacenter
   432  }
   433  
   434  func (r *NodeSpecificRequest) CacheInfo() cache.RequestInfo {
   435  	info := cache.RequestInfo{
   436  		Token:          r.Token,
   437  		Datacenter:     r.Datacenter,
   438  		MinIndex:       r.MinQueryIndex,
   439  		Timeout:        r.MaxQueryTime,
   440  		MaxAge:         r.MaxAge,
   441  		MustRevalidate: r.MustRevalidate,
   442  	}
   443  
   444  	v, err := hashstructure.Hash([]interface{}{
   445  		r.Node,
   446  	}, nil)
   447  	if err == nil {
   448  		// If there is an error, we don't set the key. A blank key forces
   449  		// no cache for this request so the request is forwarded directly
   450  		// to the server.
   451  		info.Key = strconv.FormatUint(v, 10)
   452  	}
   453  
   454  	return info
   455  }
   456  
   457  // ChecksInStateRequest is used to query for nodes in a state
   458  type ChecksInStateRequest struct {
   459  	Datacenter      string
   460  	NodeMetaFilters map[string]string
   461  	State           string
   462  	Source          QuerySource
   463  	QueryOptions
   464  }
   465  
   466  func (r *ChecksInStateRequest) RequestDatacenter() string {
   467  	return r.Datacenter
   468  }
   469  
   470  // Used to return information about a node
   471  type Node struct {
   472  	ID              types.NodeID
   473  	Node            string
   474  	Address         string
   475  	Datacenter      string
   476  	TaggedAddresses map[string]string
   477  	Meta            map[string]string
   478  
   479  	RaftIndex
   480  }
   481  type Nodes []*Node
   482  
   483  // IsSame return whether nodes are similar without taking into account
   484  // RaftIndex fields.
   485  func (n *Node) IsSame(other *Node) bool {
   486  	return n.ID == other.ID &&
   487  		n.Node == other.Node &&
   488  		n.Address == other.Address &&
   489  		n.Datacenter == other.Datacenter &&
   490  		reflect.DeepEqual(n.TaggedAddresses, other.TaggedAddresses) &&
   491  		reflect.DeepEqual(n.Meta, other.Meta)
   492  }
   493  
   494  // ValidateMeta validates a set of key/value pairs from the agent config
   495  func ValidateMetadata(meta map[string]string, allowConsulPrefix bool) error {
   496  	if len(meta) > metaMaxKeyPairs {
   497  		return fmt.Errorf("Node metadata cannot contain more than %d key/value pairs", metaMaxKeyPairs)
   498  	}
   499  
   500  	for key, value := range meta {
   501  		if err := validateMetaPair(key, value, allowConsulPrefix); err != nil {
   502  			return fmt.Errorf("Couldn't load metadata pair ('%s', '%s'): %s", key, value, err)
   503  		}
   504  	}
   505  
   506  	return nil
   507  }
   508  
   509  // ValidateWeights checks the definition of DNS weight is valid
   510  func ValidateWeights(weights *Weights) error {
   511  	if weights == nil {
   512  		return nil
   513  	}
   514  	if weights.Passing < 1 {
   515  		return fmt.Errorf("Passing must be greater than 0")
   516  	}
   517  	if weights.Warning < 0 {
   518  		return fmt.Errorf("Warning must be greater or equal than 0")
   519  	}
   520  	if weights.Passing > 65535 || weights.Warning > 65535 {
   521  		return fmt.Errorf("DNS Weight must be between 0 and 65535")
   522  	}
   523  	return nil
   524  }
   525  
   526  // validateMetaPair checks that the given key/value pair is in a valid format
   527  func validateMetaPair(key, value string, allowConsulPrefix bool) error {
   528  	if key == "" {
   529  		return fmt.Errorf("Key cannot be blank")
   530  	}
   531  	if !metaKeyFormat(key) {
   532  		return fmt.Errorf("Key contains invalid characters")
   533  	}
   534  	if len(key) > metaKeyMaxLength {
   535  		return fmt.Errorf("Key is too long (limit: %d characters)", metaKeyMaxLength)
   536  	}
   537  	if strings.HasPrefix(key, metaKeyReservedPrefix) && !allowConsulPrefix {
   538  		return fmt.Errorf("Key prefix '%s' is reserved for internal use", metaKeyReservedPrefix)
   539  	}
   540  	if len(value) > metaValueMaxLength {
   541  		return fmt.Errorf("Value is too long (limit: %d characters)", metaValueMaxLength)
   542  	}
   543  	return nil
   544  }
   545  
   546  // SatisfiesMetaFilters returns true if the metadata map contains the given filters
   547  func SatisfiesMetaFilters(meta map[string]string, filters map[string]string) bool {
   548  	for key, value := range filters {
   549  		if v, ok := meta[key]; !ok || v != value {
   550  			return false
   551  		}
   552  	}
   553  	return true
   554  }
   555  
   556  // Used to return information about a provided services.
   557  // Maps service name to available tags
   558  type Services map[string][]string
   559  
   560  // ServiceNode represents a node that is part of a service. ID, Address,
   561  // TaggedAddresses, and NodeMeta are node-related fields that are always empty
   562  // in the state store and are filled in on the way out by parseServiceNodes().
   563  // This is also why PartialClone() skips them, because we know they are blank
   564  // already so it would be a waste of time to copy them.
   565  type ServiceNode struct {
   566  	ID                       types.NodeID
   567  	Node                     string
   568  	Address                  string
   569  	Datacenter               string
   570  	TaggedAddresses          map[string]string
   571  	NodeMeta                 map[string]string
   572  	ServiceKind              ServiceKind
   573  	ServiceID                string
   574  	ServiceName              string
   575  	ServiceTags              []string
   576  	ServiceAddress           string
   577  	ServiceWeights           Weights
   578  	ServiceMeta              map[string]string
   579  	ServicePort              int
   580  	ServiceEnableTagOverride bool
   581  	// DEPRECATED (ProxyDestination) - remove this when removing ProxyDestination
   582  	ServiceProxyDestination string
   583  	ServiceProxy            ConnectProxyConfig
   584  	ServiceConnect          ServiceConnect
   585  
   586  	RaftIndex
   587  }
   588  
   589  // PartialClone() returns a clone of the given service node, minus the node-
   590  // related fields that get filled in later, Address and TaggedAddresses.
   591  func (s *ServiceNode) PartialClone() *ServiceNode {
   592  	tags := make([]string, len(s.ServiceTags))
   593  	copy(tags, s.ServiceTags)
   594  	nsmeta := make(map[string]string)
   595  	for k, v := range s.ServiceMeta {
   596  		nsmeta[k] = v
   597  	}
   598  
   599  	return &ServiceNode{
   600  		// Skip ID, see above.
   601  		Node: s.Node,
   602  		// Skip Address, see above.
   603  		// Skip TaggedAddresses, see above.
   604  		ServiceKind:              s.ServiceKind,
   605  		ServiceID:                s.ServiceID,
   606  		ServiceName:              s.ServiceName,
   607  		ServiceTags:              tags,
   608  		ServiceAddress:           s.ServiceAddress,
   609  		ServicePort:              s.ServicePort,
   610  		ServiceMeta:              nsmeta,
   611  		ServiceWeights:           s.ServiceWeights,
   612  		ServiceEnableTagOverride: s.ServiceEnableTagOverride,
   613  		// DEPRECATED (ProxyDestination) - remove this when removing ProxyDestination
   614  		ServiceProxyDestination: s.ServiceProxyDestination,
   615  		ServiceProxy:            s.ServiceProxy,
   616  		ServiceConnect:          s.ServiceConnect,
   617  		RaftIndex: RaftIndex{
   618  			CreateIndex: s.CreateIndex,
   619  			ModifyIndex: s.ModifyIndex,
   620  		},
   621  	}
   622  }
   623  
   624  // ToNodeService converts the given service node to a node service.
   625  func (s *ServiceNode) ToNodeService() *NodeService {
   626  	return &NodeService{
   627  		Kind:              s.ServiceKind,
   628  		ID:                s.ServiceID,
   629  		Service:           s.ServiceName,
   630  		Tags:              s.ServiceTags,
   631  		Address:           s.ServiceAddress,
   632  		Port:              s.ServicePort,
   633  		Meta:              s.ServiceMeta,
   634  		Weights:           &s.ServiceWeights,
   635  		EnableTagOverride: s.ServiceEnableTagOverride,
   636  		Proxy:             s.ServiceProxy,
   637  		Connect:           s.ServiceConnect,
   638  		RaftIndex: RaftIndex{
   639  			CreateIndex: s.CreateIndex,
   640  			ModifyIndex: s.ModifyIndex,
   641  		},
   642  	}
   643  }
   644  
   645  // Weights represent the weight used by DNS for a given status
   646  type Weights struct {
   647  	Passing int
   648  	Warning int
   649  }
   650  
   651  type ServiceNodes []*ServiceNode
   652  
   653  // ServiceKind is the kind of service being registered.
   654  type ServiceKind string
   655  
   656  const (
   657  	// ServiceKindTypical is a typical, classic Consul service. This is
   658  	// represented by the absence of a value. This was chosen for ease of
   659  	// backwards compatibility: existing services in the catalog would
   660  	// default to the typical service.
   661  	ServiceKindTypical ServiceKind = ""
   662  
   663  	// ServiceKindConnectProxy is a proxy for the Connect feature. This
   664  	// service proxies another service within Consul and speaks the connect
   665  	// protocol.
   666  	ServiceKindConnectProxy ServiceKind = "connect-proxy"
   667  )
   668  
   669  // NodeService is a service provided by a node
   670  type NodeService struct {
   671  	// Kind is the kind of service this is. Different kinds of services may
   672  	// have differing validation, DNS behavior, etc. An empty kind will default
   673  	// to the Default kind. See ServiceKind for the full list of kinds.
   674  	Kind ServiceKind `json:",omitempty"`
   675  
   676  	ID                string
   677  	Service           string
   678  	Tags              []string
   679  	Address           string
   680  	Meta              map[string]string
   681  	Port              int
   682  	Weights           *Weights
   683  	EnableTagOverride bool
   684  
   685  	// ProxyDestination is DEPRECATED in favor of Proxy.DestinationServiceName.
   686  	// It's retained since this struct is used to parse input for
   687  	// /catalog/register but nothing else internal should use it - once
   688  	// request/config definitions are passes all internal uses of NodeService
   689  	// should have this empty and use the Proxy.DestinationServiceNames field
   690  	// below.
   691  	//
   692  	// It used to store the name of the service that this service is a Connect
   693  	// proxy for. This is only valid if Kind is "connect-proxy". The destination
   694  	// may be a service that isn't present in the catalog. This is expected and
   695  	// allowed to allow for proxies to come up earlier than their target services.
   696  	// DEPRECATED (ProxyDestination) - remove this when removing ProxyDestination
   697  	ProxyDestination string
   698  
   699  	// Proxy is the configuration set for Kind = connect-proxy. It is mandatory in
   700  	// that case and an error to be set for any other kind. This config is part of
   701  	// a proxy service definition and is distinct from but shares some fields with
   702  	// the Connect.Proxy which configures a managed proxy as part of the actual
   703  	// service's definition. This duplication is ugly but seemed better than the
   704  	// alternative which was to re-use the same struct fields for both cases even
   705  	// though the semantics are different and the non-shred fields make no sense
   706  	// in the other case. ProxyConfig may be a more natural name here, but it's
   707  	// confusing for the UX because one of the fields in ConnectProxyConfig is
   708  	// also called just "Config"
   709  	Proxy ConnectProxyConfig
   710  
   711  	// Connect are the Connect settings for a service. This is purposely NOT
   712  	// a pointer so that we never have to nil-check this.
   713  	Connect ServiceConnect
   714  
   715  	// LocallyRegisteredAsSidecar is private as it is only used by a local agent
   716  	// state to track if the service was registered from a nested sidecar_service
   717  	// block. We need to track that so we can know whether we need to deregister
   718  	// it automatically too if it's removed from the service definition or if the
   719  	// parent service is deregistered. Relying only on ID would cause us to
   720  	// deregister regular services if they happen to be registered using the same
   721  	// ID scheme as our sidecars do by default. We could use meta but that gets
   722  	// unpleasant because we can't use the consul- prefix from an agent (reserved
   723  	// for use internally but in practice that means within the state store or in
   724  	// responses only), and it leaks the detail publicly which people might rely
   725  	// on which is a bit unpleasant for something that is meant to be config-file
   726  	// syntax sugar. Note this is not translated to ServiceNode and friends and
   727  	// may not be set on a NodeService that isn't the one the agent registered and
   728  	// keeps in it's local state. We never want this rendered in JSON as it's
   729  	// internal only. Right now our agent endpoints return api structs which don't
   730  	// include it but this is a safety net incase we change that or there is
   731  	// somewhere this is used in API output.
   732  	LocallyRegisteredAsSidecar bool `json:"-"`
   733  
   734  	RaftIndex
   735  }
   736  
   737  // ServiceConnect are the shared Connect settings between all service
   738  // definitions from the agent to the state store.
   739  type ServiceConnect struct {
   740  	// Native is true when this service can natively understand Connect.
   741  	Native bool `json:",omitempty"`
   742  
   743  	// Proxy configures a connect proxy instance for the service. This is
   744  	// only used for agent service definitions and is invalid for non-agent
   745  	// (catalog API) definitions.
   746  	Proxy *ServiceDefinitionConnectProxy `json:",omitempty"`
   747  
   748  	// SidecarService is a nested Service Definition to register at the same time.
   749  	// It's purely a convenience mechanism to allow specifying a sidecar service
   750  	// along with the application service definition. It's nested nature allows
   751  	// all of the fields to be defaulted which can reduce the amount of
   752  	// boilerplate needed to register a sidecar service separately, but the end
   753  	// result is identical to just making a second service registration via any
   754  	// other means.
   755  	SidecarService *ServiceDefinition `json:",omitempty"`
   756  }
   757  
   758  // Validate validates the node service configuration.
   759  //
   760  // NOTE(mitchellh): This currently only validates fields for a ConnectProxy.
   761  // Historically validation has been directly in the Catalog.Register RPC.
   762  // ConnectProxy validation was moved here for easier table testing, but
   763  // other validation still exists in Catalog.Register.
   764  func (s *NodeService) Validate() error {
   765  	var result error
   766  
   767  	// ConnectProxy validation
   768  	if s.Kind == ServiceKindConnectProxy {
   769  		// DEPRECATED (ProxyDestination) - remove this when removing ProxyDestination
   770  		// Fixup legacy requests that specify the ProxyDestination still
   771  		if s.ProxyDestination != "" && s.Proxy.DestinationServiceName == "" {
   772  			s.Proxy.DestinationServiceName = s.ProxyDestination
   773  			s.ProxyDestination = ""
   774  		}
   775  
   776  		if strings.TrimSpace(s.Proxy.DestinationServiceName) == "" {
   777  			result = multierror.Append(result, fmt.Errorf(
   778  				"Proxy.DestinationServiceName must be non-empty for Connect proxy "+
   779  					"services"))
   780  		}
   781  
   782  		if s.Port == 0 {
   783  			result = multierror.Append(result, fmt.Errorf(
   784  				"Port must be set for a Connect proxy"))
   785  		}
   786  
   787  		if s.Connect.Native {
   788  			result = multierror.Append(result, fmt.Errorf(
   789  				"A Proxy cannot also be Connect Native, only typical services"))
   790  		}
   791  	}
   792  
   793  	// Nested sidecar validation
   794  	if s.Connect.SidecarService != nil {
   795  		if s.Connect.SidecarService.ID != "" {
   796  			result = multierror.Append(result, fmt.Errorf(
   797  				"A SidecarService cannot specify an ID as this is managed by the "+
   798  					"agent"))
   799  		}
   800  		if s.Connect.SidecarService.Connect != nil {
   801  			if s.Connect.SidecarService.Connect.SidecarService != nil {
   802  				result = multierror.Append(result, fmt.Errorf(
   803  					"A SidecarService cannot have a nested SidecarService"))
   804  			}
   805  			if s.Connect.SidecarService.Connect.Proxy != nil {
   806  				result = multierror.Append(result, fmt.Errorf(
   807  					"A SidecarService cannot have a managed proxy"))
   808  			}
   809  		}
   810  	}
   811  
   812  	return result
   813  }
   814  
   815  // IsSame checks if one NodeService is the same as another, without looking
   816  // at the Raft information (that's why we didn't call it IsEqual). This is
   817  // useful for seeing if an update would be idempotent for all the functional
   818  // parts of the structure.
   819  func (s *NodeService) IsSame(other *NodeService) bool {
   820  	if s.ID != other.ID ||
   821  		s.Service != other.Service ||
   822  		!reflect.DeepEqual(s.Tags, other.Tags) ||
   823  		s.Address != other.Address ||
   824  		s.Port != other.Port ||
   825  		!reflect.DeepEqual(s.Weights, other.Weights) ||
   826  		!reflect.DeepEqual(s.Meta, other.Meta) ||
   827  		s.EnableTagOverride != other.EnableTagOverride ||
   828  		s.Kind != other.Kind ||
   829  		!reflect.DeepEqual(s.Proxy, other.Proxy) ||
   830  		s.Connect != other.Connect {
   831  		return false
   832  	}
   833  
   834  	return true
   835  }
   836  
   837  // IsSameService checks if one Service of a ServiceNode is the same as another,
   838  // without looking at the Raft information or Node information (that's why we
   839  // didn't call it IsEqual).
   840  // This is useful for seeing if an update would be idempotent for all the functional
   841  // parts of the structure.
   842  // In a similar fashion as ToNodeService(), fields related to Node are ignored
   843  // see ServiceNode for more information.
   844  func (s *ServiceNode) IsSameService(other *ServiceNode) bool {
   845  	// Skip the following fields, see ServiceNode definition
   846  	// Address                  string
   847  	// Datacenter               string
   848  	// TaggedAddresses          map[string]string
   849  	// NodeMeta                 map[string]string
   850  	if s.ID != other.ID ||
   851  		s.Node != other.Node ||
   852  		s.ServiceKind != other.ServiceKind ||
   853  		s.ServiceID != other.ServiceID ||
   854  		s.ServiceName != other.ServiceName ||
   855  		!reflect.DeepEqual(s.ServiceTags, other.ServiceTags) ||
   856  		s.ServiceAddress != other.ServiceAddress ||
   857  		s.ServicePort != other.ServicePort ||
   858  		!reflect.DeepEqual(s.ServiceMeta, other.ServiceMeta) ||
   859  		!reflect.DeepEqual(s.ServiceWeights, other.ServiceWeights) ||
   860  		s.ServiceEnableTagOverride != other.ServiceEnableTagOverride ||
   861  		s.ServiceProxyDestination != other.ServiceProxyDestination ||
   862  		!reflect.DeepEqual(s.ServiceProxy, other.ServiceProxy) ||
   863  		!reflect.DeepEqual(s.ServiceConnect, other.ServiceConnect) {
   864  		return false
   865  	}
   866  
   867  	return true
   868  }
   869  
   870  // ToServiceNode converts the given node service to a service node.
   871  func (s *NodeService) ToServiceNode(node string) *ServiceNode {
   872  	theWeights := Weights{
   873  		Passing: 1,
   874  		Warning: 1,
   875  	}
   876  	if s.Weights != nil {
   877  		if err := ValidateWeights(s.Weights); err == nil {
   878  			theWeights = *s.Weights
   879  		}
   880  	}
   881  	// DEPRECATED (ProxyDestination) - remove this when removing ProxyDestination
   882  	legacyProxyDest := s.Proxy.DestinationServiceName
   883  	if legacyProxyDest == "" {
   884  		legacyProxyDest = s.ProxyDestination
   885  	}
   886  	return &ServiceNode{
   887  		// Skip ID, see ServiceNode definition.
   888  		Node: node,
   889  		// Skip Address, see ServiceNode definition.
   890  		// Skip TaggedAddresses, see ServiceNode definition.
   891  		ServiceKind:              s.Kind,
   892  		ServiceID:                s.ID,
   893  		ServiceName:              s.Service,
   894  		ServiceTags:              s.Tags,
   895  		ServiceAddress:           s.Address,
   896  		ServicePort:              s.Port,
   897  		ServiceMeta:              s.Meta,
   898  		ServiceWeights:           theWeights,
   899  		ServiceEnableTagOverride: s.EnableTagOverride,
   900  		ServiceProxy:             s.Proxy,
   901  		ServiceProxyDestination:  legacyProxyDest,
   902  		ServiceConnect:           s.Connect,
   903  		RaftIndex: RaftIndex{
   904  			CreateIndex: s.CreateIndex,
   905  			ModifyIndex: s.ModifyIndex,
   906  		},
   907  	}
   908  }
   909  
   910  type NodeServices struct {
   911  	Node     *Node
   912  	Services map[string]*NodeService
   913  }
   914  
   915  // HealthCheck represents a single check on a given node
   916  type HealthCheck struct {
   917  	Node        string
   918  	CheckID     types.CheckID // Unique per-node ID
   919  	Name        string        // Check name
   920  	Status      string        // The current check status
   921  	Notes       string        // Additional notes with the status
   922  	Output      string        // Holds output of script runs
   923  	ServiceID   string        // optional associated service
   924  	ServiceName string        // optional service name
   925  	ServiceTags []string      // optional service tags
   926  
   927  	Definition HealthCheckDefinition
   928  
   929  	RaftIndex
   930  }
   931  
   932  type HealthCheckDefinition struct {
   933  	HTTP                           string              `json:",omitempty"`
   934  	TLSSkipVerify                  bool                `json:",omitempty"`
   935  	Header                         map[string][]string `json:",omitempty"`
   936  	Method                         string              `json:",omitempty"`
   937  	TCP                            string              `json:",omitempty"`
   938  	Interval                       time.Duration       `json:",omitempty"`
   939  	Timeout                        time.Duration       `json:",omitempty"`
   940  	DeregisterCriticalServiceAfter time.Duration       `json:",omitempty"`
   941  }
   942  
   943  func (d *HealthCheckDefinition) MarshalJSON() ([]byte, error) {
   944  	type Alias HealthCheckDefinition
   945  	exported := &struct {
   946  		Interval                       string `json:",omitempty"`
   947  		Timeout                        string `json:",omitempty"`
   948  		DeregisterCriticalServiceAfter string `json:",omitempty"`
   949  		*Alias
   950  	}{
   951  		Interval:                       d.Interval.String(),
   952  		Timeout:                        d.Timeout.String(),
   953  		DeregisterCriticalServiceAfter: d.DeregisterCriticalServiceAfter.String(),
   954  		Alias:                          (*Alias)(d),
   955  	}
   956  	if d.Interval == 0 {
   957  		exported.Interval = ""
   958  	}
   959  	if d.Timeout == 0 {
   960  		exported.Timeout = ""
   961  	}
   962  	if d.DeregisterCriticalServiceAfter == 0 {
   963  		exported.DeregisterCriticalServiceAfter = ""
   964  	}
   965  
   966  	return json.Marshal(exported)
   967  }
   968  
   969  func (d *HealthCheckDefinition) UnmarshalJSON(data []byte) error {
   970  	type Alias HealthCheckDefinition
   971  	aux := &struct {
   972  		Interval                       string
   973  		Timeout                        string
   974  		DeregisterCriticalServiceAfter string
   975  		*Alias
   976  	}{
   977  		Alias: (*Alias)(d),
   978  	}
   979  	if err := json.Unmarshal(data, &aux); err != nil {
   980  		return err
   981  	}
   982  	var err error
   983  	if aux.Interval != "" {
   984  		if d.Interval, err = time.ParseDuration(aux.Interval); err != nil {
   985  			return err
   986  		}
   987  	}
   988  	if aux.Timeout != "" {
   989  		if d.Timeout, err = time.ParseDuration(aux.Timeout); err != nil {
   990  			return err
   991  		}
   992  	}
   993  	if aux.DeregisterCriticalServiceAfter != "" {
   994  		if d.DeregisterCriticalServiceAfter, err = time.ParseDuration(aux.DeregisterCriticalServiceAfter); err != nil {
   995  			return err
   996  		}
   997  	}
   998  	return nil
   999  }
  1000  
  1001  // IsSame checks if one HealthCheck is the same as another, without looking
  1002  // at the Raft information (that's why we didn't call it IsEqual). This is
  1003  // useful for seeing if an update would be idempotent for all the functional
  1004  // parts of the structure.
  1005  func (c *HealthCheck) IsSame(other *HealthCheck) bool {
  1006  	if c.Node != other.Node ||
  1007  		c.CheckID != other.CheckID ||
  1008  		c.Name != other.Name ||
  1009  		c.Status != other.Status ||
  1010  		c.Notes != other.Notes ||
  1011  		c.Output != other.Output ||
  1012  		c.ServiceID != other.ServiceID ||
  1013  		c.ServiceName != other.ServiceName ||
  1014  		!reflect.DeepEqual(c.ServiceTags, other.ServiceTags) ||
  1015  		!reflect.DeepEqual(c.Definition, other.Definition) {
  1016  		return false
  1017  	}
  1018  
  1019  	return true
  1020  }
  1021  
  1022  // Clone returns a distinct clone of the HealthCheck.
  1023  func (c *HealthCheck) Clone() *HealthCheck {
  1024  	clone := new(HealthCheck)
  1025  	*clone = *c
  1026  	return clone
  1027  }
  1028  
  1029  // HealthChecks is a collection of HealthCheck structs.
  1030  type HealthChecks []*HealthCheck
  1031  
  1032  // CheckServiceNode is used to provide the node, its service
  1033  // definition, as well as a HealthCheck that is associated.
  1034  type CheckServiceNode struct {
  1035  	Node    *Node
  1036  	Service *NodeService
  1037  	Checks  HealthChecks
  1038  }
  1039  type CheckServiceNodes []CheckServiceNode
  1040  
  1041  // Shuffle does an in-place random shuffle using the Fisher-Yates algorithm.
  1042  func (nodes CheckServiceNodes) Shuffle() {
  1043  	for i := len(nodes) - 1; i > 0; i-- {
  1044  		j := rand.Int31n(int32(i + 1))
  1045  		nodes[i], nodes[j] = nodes[j], nodes[i]
  1046  	}
  1047  }
  1048  
  1049  // Filter removes nodes that are failing health checks (and any non-passing
  1050  // check if that option is selected). Note that this returns the filtered
  1051  // results AND modifies the receiver for performance.
  1052  func (nodes CheckServiceNodes) Filter(onlyPassing bool) CheckServiceNodes {
  1053  	return nodes.FilterIgnore(onlyPassing, nil)
  1054  }
  1055  
  1056  // FilterIgnore removes nodes that are failing health checks just like Filter.
  1057  // It also ignores the status of any check with an ID present in ignoreCheckIDs
  1058  // as if that check didn't exist. Note that this returns the filtered results
  1059  // AND modifies the receiver for performance.
  1060  func (nodes CheckServiceNodes) FilterIgnore(onlyPassing bool,
  1061  	ignoreCheckIDs []types.CheckID) CheckServiceNodes {
  1062  	n := len(nodes)
  1063  OUTER:
  1064  	for i := 0; i < n; i++ {
  1065  		node := nodes[i]
  1066  	INNER:
  1067  		for _, check := range node.Checks {
  1068  			for _, ignore := range ignoreCheckIDs {
  1069  				if check.CheckID == ignore {
  1070  					// Skip this _check_ but keep looking at other checks for this node.
  1071  					continue INNER
  1072  				}
  1073  			}
  1074  			if check.Status == api.HealthCritical ||
  1075  				(onlyPassing && check.Status != api.HealthPassing) {
  1076  				nodes[i], nodes[n-1] = nodes[n-1], CheckServiceNode{}
  1077  				n--
  1078  				i--
  1079  				// Skip this _node_ now we've swapped it off the end of the list.
  1080  				continue OUTER
  1081  			}
  1082  		}
  1083  	}
  1084  	return nodes[:n]
  1085  }
  1086  
  1087  // NodeInfo is used to dump all associated information about
  1088  // a node. This is currently used for the UI only, as it is
  1089  // rather expensive to generate.
  1090  type NodeInfo struct {
  1091  	ID              types.NodeID
  1092  	Node            string
  1093  	Address         string
  1094  	TaggedAddresses map[string]string
  1095  	Meta            map[string]string
  1096  	Services        []*NodeService
  1097  	Checks          HealthChecks
  1098  }
  1099  
  1100  // NodeDump is used to dump all the nodes with all their
  1101  // associated data. This is currently used for the UI only,
  1102  // as it is rather expensive to generate.
  1103  type NodeDump []*NodeInfo
  1104  
  1105  type IndexedNodes struct {
  1106  	Nodes Nodes
  1107  	QueryMeta
  1108  }
  1109  
  1110  type IndexedServices struct {
  1111  	Services Services
  1112  	QueryMeta
  1113  }
  1114  
  1115  type IndexedServiceNodes struct {
  1116  	ServiceNodes ServiceNodes
  1117  	QueryMeta
  1118  }
  1119  
  1120  type IndexedNodeServices struct {
  1121  	// TODO: This should not be a pointer, see comments in
  1122  	// agent/catalog_endpoint.go.
  1123  	NodeServices *NodeServices
  1124  	QueryMeta
  1125  }
  1126  
  1127  type IndexedHealthChecks struct {
  1128  	HealthChecks HealthChecks
  1129  	QueryMeta
  1130  }
  1131  
  1132  type IndexedCheckServiceNodes struct {
  1133  	Nodes CheckServiceNodes
  1134  	QueryMeta
  1135  }
  1136  
  1137  type IndexedNodeDump struct {
  1138  	Dump NodeDump
  1139  	QueryMeta
  1140  }
  1141  
  1142  // DirEntry is used to represent a directory entry. This is
  1143  // used for values in our Key-Value store.
  1144  type DirEntry struct {
  1145  	LockIndex uint64
  1146  	Key       string
  1147  	Flags     uint64
  1148  	Value     []byte
  1149  	Session   string `json:",omitempty"`
  1150  
  1151  	RaftIndex
  1152  }
  1153  
  1154  // Returns a clone of the given directory entry.
  1155  func (d *DirEntry) Clone() *DirEntry {
  1156  	return &DirEntry{
  1157  		LockIndex: d.LockIndex,
  1158  		Key:       d.Key,
  1159  		Flags:     d.Flags,
  1160  		Value:     d.Value,
  1161  		Session:   d.Session,
  1162  		RaftIndex: RaftIndex{
  1163  			CreateIndex: d.CreateIndex,
  1164  			ModifyIndex: d.ModifyIndex,
  1165  		},
  1166  	}
  1167  }
  1168  
  1169  type DirEntries []*DirEntry
  1170  
  1171  // KVSRequest is used to operate on the Key-Value store
  1172  type KVSRequest struct {
  1173  	Datacenter string
  1174  	Op         api.KVOp // Which operation are we performing
  1175  	DirEnt     DirEntry // Which directory entry
  1176  	WriteRequest
  1177  }
  1178  
  1179  func (r *KVSRequest) RequestDatacenter() string {
  1180  	return r.Datacenter
  1181  }
  1182  
  1183  // KeyRequest is used to request a key, or key prefix
  1184  type KeyRequest struct {
  1185  	Datacenter string
  1186  	Key        string
  1187  	QueryOptions
  1188  }
  1189  
  1190  func (r *KeyRequest) RequestDatacenter() string {
  1191  	return r.Datacenter
  1192  }
  1193  
  1194  // KeyListRequest is used to list keys
  1195  type KeyListRequest struct {
  1196  	Datacenter string
  1197  	Prefix     string
  1198  	Seperator  string
  1199  	QueryOptions
  1200  }
  1201  
  1202  func (r *KeyListRequest) RequestDatacenter() string {
  1203  	return r.Datacenter
  1204  }
  1205  
  1206  type IndexedDirEntries struct {
  1207  	Entries DirEntries
  1208  	QueryMeta
  1209  }
  1210  
  1211  type IndexedKeyList struct {
  1212  	Keys []string
  1213  	QueryMeta
  1214  }
  1215  
  1216  type SessionBehavior string
  1217  
  1218  const (
  1219  	SessionKeysRelease SessionBehavior = "release"
  1220  	SessionKeysDelete                  = "delete"
  1221  )
  1222  
  1223  const (
  1224  	SessionTTLMax        = 24 * time.Hour
  1225  	SessionTTLMultiplier = 2
  1226  )
  1227  
  1228  // Session is used to represent an open session in the KV store.
  1229  // This issued to associate node checks with acquired locks.
  1230  type Session struct {
  1231  	ID        string
  1232  	Name      string
  1233  	Node      string
  1234  	Checks    []types.CheckID
  1235  	LockDelay time.Duration
  1236  	Behavior  SessionBehavior // What to do when session is invalidated
  1237  	TTL       string
  1238  
  1239  	RaftIndex
  1240  }
  1241  type Sessions []*Session
  1242  
  1243  type SessionOp string
  1244  
  1245  const (
  1246  	SessionCreate  SessionOp = "create"
  1247  	SessionDestroy           = "destroy"
  1248  )
  1249  
  1250  // SessionRequest is used to operate on sessions
  1251  type SessionRequest struct {
  1252  	Datacenter string
  1253  	Op         SessionOp // Which operation are we performing
  1254  	Session    Session   // Which session
  1255  	WriteRequest
  1256  }
  1257  
  1258  func (r *SessionRequest) RequestDatacenter() string {
  1259  	return r.Datacenter
  1260  }
  1261  
  1262  // SessionSpecificRequest is used to request a session by ID
  1263  type SessionSpecificRequest struct {
  1264  	Datacenter string
  1265  	Session    string
  1266  	QueryOptions
  1267  }
  1268  
  1269  func (r *SessionSpecificRequest) RequestDatacenter() string {
  1270  	return r.Datacenter
  1271  }
  1272  
  1273  type IndexedSessions struct {
  1274  	Sessions Sessions
  1275  	QueryMeta
  1276  }
  1277  
  1278  // Coordinate stores a node name with its associated network coordinate.
  1279  type Coordinate struct {
  1280  	Node    string
  1281  	Segment string
  1282  	Coord   *coordinate.Coordinate
  1283  }
  1284  
  1285  type Coordinates []*Coordinate
  1286  
  1287  // IndexedCoordinate is used to represent a single node's coordinate from the state
  1288  // store.
  1289  type IndexedCoordinate struct {
  1290  	Coord *coordinate.Coordinate
  1291  	QueryMeta
  1292  }
  1293  
  1294  // IndexedCoordinates is used to represent a list of nodes and their
  1295  // corresponding raw coordinates.
  1296  type IndexedCoordinates struct {
  1297  	Coordinates Coordinates
  1298  	QueryMeta
  1299  }
  1300  
  1301  // DatacenterMap is used to represent a list of nodes with their raw coordinates,
  1302  // associated with a datacenter. Coordinates are only compatible between nodes in
  1303  // the same area.
  1304  type DatacenterMap struct {
  1305  	Datacenter  string
  1306  	AreaID      types.AreaID
  1307  	Coordinates Coordinates
  1308  }
  1309  
  1310  // CoordinateUpdateRequest is used to update the network coordinate of a given
  1311  // node.
  1312  type CoordinateUpdateRequest struct {
  1313  	Datacenter string
  1314  	Node       string
  1315  	Segment    string
  1316  	Coord      *coordinate.Coordinate
  1317  	WriteRequest
  1318  }
  1319  
  1320  // RequestDatacenter returns the datacenter for a given update request.
  1321  func (c *CoordinateUpdateRequest) RequestDatacenter() string {
  1322  	return c.Datacenter
  1323  }
  1324  
  1325  // EventFireRequest is used to ask a server to fire
  1326  // a Serf event. It is a bit odd, since it doesn't depend on
  1327  // the catalog or leader. Any node can respond, so it's not quite
  1328  // like a standard write request. This is used only internally.
  1329  type EventFireRequest struct {
  1330  	Datacenter string
  1331  	Name       string
  1332  	Payload    []byte
  1333  
  1334  	// Not using WriteRequest so that any server can process
  1335  	// the request. It is a bit unusual...
  1336  	QueryOptions
  1337  }
  1338  
  1339  func (r *EventFireRequest) RequestDatacenter() string {
  1340  	return r.Datacenter
  1341  }
  1342  
  1343  // EventFireResponse is used to respond to a fire request.
  1344  type EventFireResponse struct {
  1345  	QueryMeta
  1346  }
  1347  
  1348  type TombstoneOp string
  1349  
  1350  const (
  1351  	TombstoneReap TombstoneOp = "reap"
  1352  )
  1353  
  1354  // TombstoneRequest is used to trigger a reaping of the tombstones
  1355  type TombstoneRequest struct {
  1356  	Datacenter string
  1357  	Op         TombstoneOp
  1358  	ReapIndex  uint64
  1359  	WriteRequest
  1360  }
  1361  
  1362  func (r *TombstoneRequest) RequestDatacenter() string {
  1363  	return r.Datacenter
  1364  }
  1365  
  1366  // msgpackHandle is a shared handle for encoding/decoding of structs
  1367  var msgpackHandle = &codec.MsgpackHandle{}
  1368  
  1369  // Decode is used to decode a MsgPack encoded object
  1370  func Decode(buf []byte, out interface{}) error {
  1371  	return codec.NewDecoder(bytes.NewReader(buf), msgpackHandle).Decode(out)
  1372  }
  1373  
  1374  // Encode is used to encode a MsgPack object with type prefix
  1375  func Encode(t MessageType, msg interface{}) ([]byte, error) {
  1376  	var buf bytes.Buffer
  1377  	buf.WriteByte(uint8(t))
  1378  	err := codec.NewEncoder(&buf, msgpackHandle).Encode(msg)
  1379  	return buf.Bytes(), err
  1380  }
  1381  
  1382  // CompoundResponse is an interface for gathering multiple responses. It is
  1383  // used in cross-datacenter RPC calls where more than 1 datacenter is
  1384  // expected to reply.
  1385  type CompoundResponse interface {
  1386  	// Add adds a new response to the compound response
  1387  	Add(interface{})
  1388  
  1389  	// New returns an empty response object which can be passed around by
  1390  	// reference, and then passed to Add() later on.
  1391  	New() interface{}
  1392  }
  1393  
  1394  type KeyringOp string
  1395  
  1396  const (
  1397  	KeyringList    KeyringOp = "list"
  1398  	KeyringInstall           = "install"
  1399  	KeyringUse               = "use"
  1400  	KeyringRemove            = "remove"
  1401  )
  1402  
  1403  // KeyringRequest encapsulates a request to modify an encryption keyring.
  1404  // It can be used for install, remove, or use key type operations.
  1405  type KeyringRequest struct {
  1406  	Operation   KeyringOp
  1407  	Key         string
  1408  	Datacenter  string
  1409  	Forwarded   bool
  1410  	RelayFactor uint8
  1411  	QueryOptions
  1412  }
  1413  
  1414  func (r *KeyringRequest) RequestDatacenter() string {
  1415  	return r.Datacenter
  1416  }
  1417  
  1418  // KeyringResponse is a unified key response and can be used for install,
  1419  // remove, use, as well as listing key queries.
  1420  type KeyringResponse struct {
  1421  	WAN        bool
  1422  	Datacenter string
  1423  	Segment    string
  1424  	Messages   map[string]string `json:",omitempty"`
  1425  	Keys       map[string]int
  1426  	NumNodes   int
  1427  	Error      string `json:",omitempty"`
  1428  }
  1429  
  1430  // KeyringResponses holds multiple responses to keyring queries. Each
  1431  // datacenter replies independently, and KeyringResponses is used as a
  1432  // container for the set of all responses.
  1433  type KeyringResponses struct {
  1434  	Responses []*KeyringResponse
  1435  	QueryMeta
  1436  }
  1437  
  1438  func (r *KeyringResponses) Add(v interface{}) {
  1439  	val := v.(*KeyringResponses)
  1440  	r.Responses = append(r.Responses, val.Responses...)
  1441  }
  1442  
  1443  func (r *KeyringResponses) New() interface{} {
  1444  	return new(KeyringResponses)
  1445  }