1package structs
2
3import (
4	"bytes"
5	"crypto/md5"
6	"encoding/json"
7	"fmt"
8	"math/rand"
9	"reflect"
10	"regexp"
11	"sort"
12	"strconv"
13	"strings"
14	"time"
15
16	"github.com/hashicorp/go-msgpack/codec"
17	"github.com/hashicorp/go-multierror"
18	"github.com/hashicorp/serf/coordinate"
19	"github.com/mitchellh/hashstructure"
20
21	"github.com/hashicorp/consul/acl"
22	"github.com/hashicorp/consul/agent/cache"
23	"github.com/hashicorp/consul/api"
24	"github.com/hashicorp/consul/lib"
25	"github.com/hashicorp/consul/types"
26)
27
28type MessageType uint8
29
30// RaftIndex is used to track the index used while creating
31// or modifying a given struct type.
32type RaftIndex struct {
33	CreateIndex uint64 `bexpr:"-"`
34	ModifyIndex uint64 `bexpr:"-"`
35}
36
37// These are serialized between Consul servers and stored in Consul snapshots,
38// so entries must only ever be added.
39const (
40	RegisterRequestType             MessageType = 0
41	DeregisterRequestType                       = 1
42	KVSRequestType                              = 2
43	SessionRequestType                          = 3
44	ACLRequestType                              = 4 // DEPRECATED (ACL-Legacy-Compat)
45	TombstoneRequestType                        = 5
46	CoordinateBatchUpdateType                   = 6
47	PreparedQueryRequestType                    = 7
48	TxnRequestType                              = 8
49	AutopilotRequestType                        = 9
50	AreaRequestType                             = 10
51	ACLBootstrapRequestType                     = 11
52	IntentionRequestType                        = 12
53	ConnectCARequestType                        = 13
54	ConnectCAProviderStateType                  = 14
55	ConnectCAConfigType                         = 15 // FSM snapshots only.
56	IndexRequestType                            = 16 // FSM snapshots only.
57	ACLTokenSetRequestType                      = 17
58	ACLTokenDeleteRequestType                   = 18
59	ACLPolicySetRequestType                     = 19
60	ACLPolicyDeleteRequestType                  = 20
61	ConnectCALeafRequestType                    = 21
62	ConfigEntryRequestType                      = 22
63	ACLRoleSetRequestType                       = 23
64	ACLRoleDeleteRequestType                    = 24
65	ACLBindingRuleSetRequestType                = 25
66	ACLBindingRuleDeleteRequestType             = 26
67	ACLAuthMethodSetRequestType                 = 27
68	ACLAuthMethodDeleteRequestType              = 28
69	ChunkingStateType                           = 29
70	FederationStateRequestType                  = 30
71	SystemMetadataRequestType                   = 31
72)
73
74// if a new request type is added above it must be
75// added to the map below
76
77// requestTypeStrings is used for snapshot enhance
78// any new request types added must be placed here
79var requestTypeStrings = map[MessageType]string{
80	RegisterRequestType:             "Register",
81	DeregisterRequestType:           "Deregister",
82	KVSRequestType:                  "KVS",
83	SessionRequestType:              "Session",
84	ACLRequestType:                  "ACL", // DEPRECATED (ACL-Legacy-Compat)
85	TombstoneRequestType:            "Tombstone",
86	CoordinateBatchUpdateType:       "CoordinateBatchUpdate",
87	PreparedQueryRequestType:        "PreparedQuery",
88	TxnRequestType:                  "Txn",
89	AutopilotRequestType:            "Autopilot",
90	AreaRequestType:                 "Area",
91	ACLBootstrapRequestType:         "ACLBootstrap",
92	IntentionRequestType:            "Intention",
93	ConnectCARequestType:            "ConnectCA",
94	ConnectCAProviderStateType:      "ConnectCAProviderState",
95	ConnectCAConfigType:             "ConnectCAConfig", // FSM snapshots only.
96	IndexRequestType:                "Index",           // FSM snapshots only.
97	ACLTokenSetRequestType:          "ACLToken",
98	ACLTokenDeleteRequestType:       "ACLTokenDelete",
99	ACLPolicySetRequestType:         "ACLPolicy",
100	ACLPolicyDeleteRequestType:      "ACLPolicyDelete",
101	ConnectCALeafRequestType:        "ConnectCALeaf",
102	ConfigEntryRequestType:          "ConfigEntry",
103	ACLRoleSetRequestType:           "ACLRole",
104	ACLRoleDeleteRequestType:        "ACLRoleDelete",
105	ACLBindingRuleSetRequestType:    "ACLBindingRule",
106	ACLBindingRuleDeleteRequestType: "ACLBindingRuleDelete",
107	ACLAuthMethodSetRequestType:     "ACLAuthMethod",
108	ACLAuthMethodDeleteRequestType:  "ACLAuthMethodDelete",
109	ChunkingStateType:               "ChunkingState",
110	FederationStateRequestType:      "FederationState",
111	SystemMetadataRequestType:       "SystemMetadata",
112}
113
114const (
115	// IgnoreUnknownTypeFlag is set along with a MessageType
116	// to indicate that the message type can be safely ignored
117	// if it is not recognized. This is for future proofing, so
118	// that new commands can be added in a way that won't cause
119	// old servers to crash when the FSM attempts to process them.
120	IgnoreUnknownTypeFlag MessageType = 128
121
122	// NodeMaint is the special key set by a node in maintenance mode.
123	NodeMaint = "_node_maintenance"
124
125	// ServiceMaintPrefix is the prefix for a service in maintenance mode.
126	ServiceMaintPrefix = "_service_maintenance:"
127
128	// The meta key prefix reserved for Consul's internal use
129	metaKeyReservedPrefix = "consul-"
130
131	// metaMaxKeyPairs is maximum number of metadata key pairs allowed to be registered
132	metaMaxKeyPairs = 64
133
134	// metaKeyMaxLength is the maximum allowed length of a metadata key
135	metaKeyMaxLength = 128
136
137	// metaValueMaxLength is the maximum allowed length of a metadata value
138	metaValueMaxLength = 512
139
140	// MetaSegmentKey is the node metadata key used to store the node's network segment
141	MetaSegmentKey = "consul-network-segment"
142
143	// MetaWANFederationKey is the mesh gateway metadata key that indicates a
144	// mesh gateway is usable for wan federation.
145	MetaWANFederationKey = "consul-wan-federation"
146
147	// MetaExternalSource is the metadata key used when a resource is managed by a source outside Consul like nomad/k8s
148	MetaExternalSource = "external-source"
149
150	// MaxLockDelay provides a maximum LockDelay value for
151	// a session. Any value above this will not be respected.
152	MaxLockDelay = 60 * time.Second
153
154	// lockDelayMinThreshold is used in JSON decoding to convert a
155	// numeric lockdelay value from nanoseconds to seconds if it is
156	// below thisthreshold. Users often send a value like 5, which
157	// they assumeis seconds, but because Go uses nanosecond granularity,
158	// ends up being very small. If we see a value below this threshold,
159	// we multiply by time.Second
160	lockDelayMinThreshold = 1000
161
162	// WildcardSpecifier is the string which should be used for specifying a wildcard
163	// The exact semantics of the wildcard is left up to the code where its used.
164	WildcardSpecifier = "*"
165)
166
167var allowedConsulMetaKeysForMeshGateway = map[string]struct{}{MetaWANFederationKey: {}}
168
169var (
170	NodeMaintCheckID = NewCheckID(NodeMaint, nil)
171)
172
173const (
174	TaggedAddressWAN     = "wan"
175	TaggedAddressWANIPv4 = "wan_ipv4"
176	TaggedAddressWANIPv6 = "wan_ipv6"
177	TaggedAddressLAN     = "lan"
178	TaggedAddressLANIPv4 = "lan_ipv4"
179	TaggedAddressLANIPv6 = "lan_ipv6"
180)
181
182// metaKeyFormat checks if a metadata key string is valid
183var metaKeyFormat = regexp.MustCompile(`^[a-zA-Z0-9_-]+$`).MatchString
184
185func ValidStatus(s string) bool {
186	return s == api.HealthPassing || s == api.HealthWarning || s == api.HealthCritical
187}
188
189// RPCInfo is used to describe common information about query
190type RPCInfo interface {
191	RequestDatacenter() string
192	IsRead() bool
193	AllowStaleRead() bool
194	TokenSecret() string
195	SetTokenSecret(string)
196}
197
198// QueryOptions is used to specify various flags for read queries
199type QueryOptions struct {
200	// Token is the ACL token ID. If not provided, the 'anonymous'
201	// token is assumed for backwards compatibility.
202	Token string
203
204	// If set, wait until query exceeds given index. Must be provided
205	// with MaxQueryTime.
206	MinQueryIndex uint64
207
208	// Provided with MinQueryIndex to wait for change.
209	MaxQueryTime time.Duration
210
211	// If set, any follower can service the request. Results
212	// may be arbitrarily stale.
213	AllowStale bool
214
215	// If set, the leader must verify leadership prior to
216	// servicing the request. Prevents a stale read.
217	RequireConsistent bool
218
219	// If set, the local agent may respond with an arbitrarily stale locally
220	// cached response. The semantics differ from AllowStale since the agent may
221	// be entirely partitioned from the servers and still considered "healthy" by
222	// operators. Stale responses from Servers are also arbitrarily stale, but can
223	// provide additional bounds on the last contact time from the leader. It's
224	// expected that servers that are partitioned are noticed and replaced in a
225	// timely way by operators while the same may not be true for client agents.
226	UseCache bool
227
228	// If set and AllowStale is true, will try first a stale
229	// read, and then will perform a consistent read if stale
230	// read is older than value.
231	MaxStaleDuration time.Duration
232
233	// MaxAge limits how old a cached value will be returned if UseCache is true.
234	// If there is a cached response that is older than the MaxAge, it is treated
235	// as a cache miss and a new fetch invoked. If the fetch fails, the error is
236	// returned. Clients that wish to allow for stale results on error can set
237	// StaleIfError to a longer duration to change this behavior. It is ignored
238	// if the endpoint supports background refresh caching. See
239	// https://www.consul.io/api/index.html#agent-caching for more details.
240	MaxAge time.Duration
241
242	// MustRevalidate forces the agent to fetch a fresh version of a cached
243	// resource or at least validate that the cached version is still fresh. It is
244	// implied by either max-age=0 or must-revalidate Cache-Control headers. It
245	// only makes sense when UseCache is true. We store it since MaxAge = 0 is the
246	// default unset value.
247	MustRevalidate bool
248
249	// StaleIfError specifies how stale the client will accept a cached response
250	// if the servers are unavailable to fetch a fresh one. Only makes sense when
251	// UseCache is true and MaxAge is set to a lower, non-zero value. It is
252	// ignored if the endpoint supports background refresh caching. See
253	// https://www.consul.io/api/index.html#agent-caching for more details.
254	StaleIfError time.Duration
255
256	// Filter specifies the go-bexpr filter expression to be used for
257	// filtering the data prior to returning a response
258	Filter string
259
260	// AllowNotModifiedResponse indicates that if the MinIndex matches the
261	// QueryMeta.Index, the response can be left empty and QueryMeta.NotModified
262	// will be set to true to indicate the result of the query has not changed.
263	AllowNotModifiedResponse bool
264}
265
266// IsRead is always true for QueryOption.
267func (q QueryOptions) IsRead() bool {
268	return true
269}
270
271// ConsistencyLevel display the consistency required by a request
272func (q QueryOptions) ConsistencyLevel() string {
273	if q.RequireConsistent {
274		return "consistent"
275	} else if q.AllowStale {
276		return "stale"
277	} else {
278		return "leader"
279	}
280}
281
282func (q QueryOptions) AllowStaleRead() bool {
283	return q.AllowStale
284}
285
286func (q QueryOptions) TokenSecret() string {
287	return q.Token
288}
289
290func (q *QueryOptions) SetTokenSecret(s string) {
291	q.Token = s
292}
293
294type WriteRequest struct {
295	// Token is the ACL token ID. If not provided, the 'anonymous'
296	// token is assumed for backwards compatibility.
297	Token string
298}
299
300// WriteRequest only applies to writes, always false
301func (w WriteRequest) IsRead() bool {
302	return false
303}
304
305func (w WriteRequest) AllowStaleRead() bool {
306	return false
307}
308
309func (w WriteRequest) TokenSecret() string {
310	return w.Token
311}
312
313func (w *WriteRequest) SetTokenSecret(s string) {
314	w.Token = s
315}
316
317type QueryBackend int
318
319const (
320	QueryBackendBlocking QueryBackend = iota
321	QueryBackendStreaming
322)
323
324func (q QueryBackend) String() string {
325	switch q {
326	case QueryBackendBlocking:
327		return "blocking-query"
328	case QueryBackendStreaming:
329		return "streaming"
330	default:
331		return ""
332	}
333}
334
335// QueryMeta allows a query response to include potentially
336// useful metadata about a query
337type QueryMeta struct {
338	// Index in the raft log of the latest item returned by the query.
339	Index uint64
340
341	// If AllowStale is used, this is time elapsed since
342	// last contact between the follower and leader. This
343	// can be used to gauge staleness.
344	LastContact time.Duration
345
346	// Used to indicate if there is a known leader node
347	KnownLeader bool
348
349	// Consistencylevel returns the consistency used to serve the query
350	// Having `discovery_max_stale` on the agent can affect whether
351	// the request was served by a leader.
352	ConsistencyLevel string
353
354	// NotModified is true when the Index of the query is the same value as the
355	// requested MinIndex. It indicates that the entity has not been modified.
356	// When NotModified is true, the response will not contain the result of
357	// the query.
358	NotModified bool
359
360	// Backend used to handle this query, either blocking-query or streaming.
361	Backend QueryBackend
362}
363
364// RegisterRequest is used for the Catalog.Register endpoint
365// to register a node as providing a service. If no service
366// is provided, the node is registered.
367type RegisterRequest struct {
368	Datacenter      string
369	ID              types.NodeID
370	Node            string
371	Address         string
372	TaggedAddresses map[string]string
373	NodeMeta        map[string]string
374	Service         *NodeService
375	Check           *HealthCheck
376	Checks          HealthChecks
377
378	// SkipNodeUpdate can be used when a register request is intended for
379	// updating a service and/or checks, but doesn't want to overwrite any
380	// node information if the node is already registered. If the node
381	// doesn't exist, it will still be created, but if the node exists, any
382	// node portion of this update will not apply.
383	SkipNodeUpdate bool
384
385	// EnterpriseMeta is the embedded enterprise metadata
386	EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
387
388	WriteRequest
389	RaftIndex `bexpr:"-"`
390}
391
392func (r *RegisterRequest) RequestDatacenter() string {
393	return r.Datacenter
394}
395
396// ChangesNode returns true if the given register request changes the given
397// node, which can be nil. This only looks for changes to the node record itself,
398// not any of the health checks.
399func (r *RegisterRequest) ChangesNode(node *Node) bool {
400	// This means it's creating the node.
401	if node == nil {
402		return true
403	}
404
405	// If we've been asked to skip the node update, then say there are no
406	// changes.
407	if r.SkipNodeUpdate {
408		return false
409	}
410
411	// Check if any of the node-level fields are being changed.
412	if r.ID != node.ID ||
413		r.Node != node.Node ||
414		r.Address != node.Address ||
415		r.Datacenter != node.Datacenter ||
416		!reflect.DeepEqual(r.TaggedAddresses, node.TaggedAddresses) ||
417		!reflect.DeepEqual(r.NodeMeta, node.Meta) {
418		return true
419	}
420
421	return false
422}
423
424// DeregisterRequest is used for the Catalog.Deregister endpoint
425// to deregister a node as providing a service. If no service is
426// provided the entire node is deregistered.
427type DeregisterRequest struct {
428	Datacenter     string
429	Node           string
430	ServiceID      string
431	CheckID        types.CheckID
432	EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
433	WriteRequest
434}
435
436func (r *DeregisterRequest) RequestDatacenter() string {
437	return r.Datacenter
438}
439
440func (r *DeregisterRequest) UnmarshalJSON(data []byte) error {
441	type Alias DeregisterRequest
442	aux := &struct {
443		Address string // obsolete field - but we want to explicitly allow it
444		*Alias
445	}{
446		Alias: (*Alias)(r),
447	}
448
449	if err := lib.UnmarshalJSON(data, &aux); err != nil {
450		return err
451	}
452	return nil
453}
454
455// QuerySource is used to pass along information about the source node
456// in queries so that we can adjust the response based on its network
457// coordinates.
458type QuerySource struct {
459	Datacenter string
460	Segment    string
461	Node       string
462	Ip         string
463}
464
465type DatacentersRequest struct {
466	QueryOptions
467}
468
469func (r *DatacentersRequest) CacheInfo() cache.RequestInfo {
470	return cache.RequestInfo{
471		Token:          "",
472		Datacenter:     "",
473		MinIndex:       0,
474		Timeout:        r.MaxQueryTime,
475		MaxAge:         r.MaxAge,
476		MustRevalidate: r.MustRevalidate,
477		Key:            "catalog-datacenters", // must not be empty for cache to work
478	}
479}
480
481// DCSpecificRequest is used to query about a specific DC
482type DCSpecificRequest struct {
483	Datacenter      string
484	NodeMetaFilters map[string]string
485	Source          QuerySource
486	EnterpriseMeta  `hcl:",squash" mapstructure:",squash"`
487	QueryOptions
488}
489
490func (r *DCSpecificRequest) RequestDatacenter() string {
491	return r.Datacenter
492}
493
494func (r *DCSpecificRequest) CacheInfo() cache.RequestInfo {
495	info := cache.RequestInfo{
496		Token:          r.Token,
497		Datacenter:     r.Datacenter,
498		MinIndex:       r.MinQueryIndex,
499		Timeout:        r.MaxQueryTime,
500		MaxAge:         r.MaxAge,
501		MustRevalidate: r.MustRevalidate,
502	}
503
504	// To calculate the cache key we only hash the node meta filters and the bexpr filter.
505	// The datacenter is handled by the cache framework. The other fields are
506	// not, but should not be used in any cache types.
507	v, err := hashstructure.Hash([]interface{}{
508		r.NodeMetaFilters,
509		r.Filter,
510		r.EnterpriseMeta,
511	}, nil)
512	if err == nil {
513		// If there is an error, we don't set the key. A blank key forces
514		// no cache for this request so the request is forwarded directly
515		// to the server.
516		info.Key = strconv.FormatUint(v, 10)
517	}
518
519	return info
520}
521
522func (r *DCSpecificRequest) CacheMinIndex() uint64 {
523	return r.QueryOptions.MinQueryIndex
524}
525
526type ServiceDumpRequest struct {
527	Datacenter     string
528	ServiceKind    ServiceKind
529	UseServiceKind bool
530	Source         QuerySource
531	EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
532	QueryOptions
533}
534
535func (r *ServiceDumpRequest) RequestDatacenter() string {
536	return r.Datacenter
537}
538
539func (r *ServiceDumpRequest) CacheInfo() cache.RequestInfo {
540	info := cache.RequestInfo{
541		Token:          r.Token,
542		Datacenter:     r.Datacenter,
543		MinIndex:       r.MinQueryIndex,
544		Timeout:        r.MaxQueryTime,
545		MaxAge:         r.MaxAge,
546		MustRevalidate: r.MustRevalidate,
547	}
548
549	// When we are not using the service kind we want to normalize the ServiceKind
550	keyKind := ServiceKindTypical
551	if r.UseServiceKind {
552		keyKind = r.ServiceKind
553	}
554	// To calculate the cache key we only hash the node meta filters and the bexpr filter.
555	// The datacenter is handled by the cache framework. The other fields are
556	// not, but should not be used in any cache types.
557	v, err := hashstructure.Hash([]interface{}{
558		keyKind,
559		r.UseServiceKind,
560		r.Filter,
561		r.EnterpriseMeta,
562	}, nil)
563	if err == nil {
564		// If there is an error, we don't set the key. A blank key forces
565		// no cache for this request so the request is forwarded directly
566		// to the server.
567		info.Key = strconv.FormatUint(v, 10)
568	}
569
570	return info
571}
572
573func (r *ServiceDumpRequest) CacheMinIndex() uint64 {
574	return r.QueryOptions.MinQueryIndex
575}
576
577// ServiceSpecificRequest is used to query about a specific service
578type ServiceSpecificRequest struct {
579	Datacenter      string
580	NodeMetaFilters map[string]string
581	ServiceName     string
582	ServiceKind     ServiceKind
583	// DEPRECATED (singular-service-tag) - remove this when backwards RPC compat
584	// with 1.2.x is not required.
585	ServiceTag     string
586	ServiceTags    []string
587	ServiceAddress string
588	TagFilter      bool // Controls tag filtering
589	Source         QuerySource
590
591	// Connect if true will only search for Connect-compatible services.
592	Connect bool
593
594	// Ingress if true will only search for Ingress gateways for the given service.
595	Ingress bool
596
597	EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
598	QueryOptions
599}
600
601func (r *ServiceSpecificRequest) RequestDatacenter() string {
602	return r.Datacenter
603}
604
605func (r *ServiceSpecificRequest) CacheInfo() cache.RequestInfo {
606	info := cache.RequestInfo{
607		Token:          r.Token,
608		Datacenter:     r.Datacenter,
609		MinIndex:       r.MinQueryIndex,
610		Timeout:        r.MaxQueryTime,
611		MaxAge:         r.MaxAge,
612		MustRevalidate: r.MustRevalidate,
613	}
614
615	// To calculate the cache key we hash over all the fields that affect the
616	// output other than Datacenter and Token which are dealt with in the cache
617	// framework already. Note the order here is important for the outcome - if we
618	// ever care about cache-invalidation on updates e.g. because we persist
619	// cached results, we need to be careful we maintain the same order of fields
620	// here. We could alternatively use `hash:set` struct tag on an anonymous
621	// struct to make it more robust if it becomes significant.
622	sort.Strings(r.ServiceTags)
623	v, err := hashstructure.Hash([]interface{}{
624		r.NodeMetaFilters,
625		strings.ToLower(r.ServiceName),
626		// DEPRECATED (singular-service-tag) - remove this when upgrade RPC compat
627		// with 1.2.x is not required. We still need this in because <1.3 agents
628		// might still send RPCs with singular tag set. In fact the only place we
629		// use this method is in agent cache so if the agent is new enough to have
630		// this code this should never be set, but it's safer to include it until we
631		// completely remove this field just in case it's erroneously used anywhere
632		// (e.g. until this change DNS still used it).
633		r.ServiceTag,
634		r.ServiceTags,
635		r.ServiceAddress,
636		r.TagFilter,
637		r.Connect,
638		r.Filter,
639		r.EnterpriseMeta,
640		r.Ingress,
641		r.ServiceKind,
642	}, nil)
643	if err == nil {
644		// If there is an error, we don't set the key. A blank key forces
645		// no cache for this request so the request is forwarded directly
646		// to the server.
647		info.Key = strconv.FormatUint(v, 10)
648	}
649
650	return info
651}
652
653func (r *ServiceSpecificRequest) CacheMinIndex() uint64 {
654	return r.QueryOptions.MinQueryIndex
655}
656
657// NodeSpecificRequest is used to request the information about a single node
658type NodeSpecificRequest struct {
659	Datacenter     string
660	Node           string
661	EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
662	QueryOptions
663}
664
665func (r *NodeSpecificRequest) RequestDatacenter() string {
666	return r.Datacenter
667}
668
669func (r *NodeSpecificRequest) CacheInfo() cache.RequestInfo {
670	info := cache.RequestInfo{
671		Token:          r.Token,
672		Datacenter:     r.Datacenter,
673		MinIndex:       r.MinQueryIndex,
674		Timeout:        r.MaxQueryTime,
675		MaxAge:         r.MaxAge,
676		MustRevalidate: r.MustRevalidate,
677	}
678
679	v, err := hashstructure.Hash([]interface{}{
680		r.Node,
681		r.Filter,
682		r.EnterpriseMeta,
683	}, nil)
684	if err == nil {
685		// If there is an error, we don't set the key. A blank key forces
686		// no cache for this request so the request is forwarded directly
687		// to the server.
688		info.Key = strconv.FormatUint(v, 10)
689	}
690
691	return info
692}
693
694// ChecksInStateRequest is used to query for nodes in a state
695type ChecksInStateRequest struct {
696	Datacenter      string
697	NodeMetaFilters map[string]string
698	State           string
699	Source          QuerySource
700
701	EnterpriseMeta `mapstructure:",squash"`
702	QueryOptions
703}
704
705func (r *ChecksInStateRequest) RequestDatacenter() string {
706	return r.Datacenter
707}
708
709// Used to return information about a node
710type Node struct {
711	ID              types.NodeID
712	Node            string
713	Address         string
714	Datacenter      string
715	TaggedAddresses map[string]string
716	Meta            map[string]string
717
718	RaftIndex `bexpr:"-"`
719}
720
721func (n *Node) BestAddress(wan bool) string {
722	if wan {
723		if addr, ok := n.TaggedAddresses[TaggedAddressWAN]; ok {
724			return addr
725		}
726	}
727	return n.Address
728}
729
730type Nodes []*Node
731
732// IsSame return whether nodes are similar without taking into account
733// RaftIndex fields.
734func (n *Node) IsSame(other *Node) bool {
735	return n.ID == other.ID &&
736		n.Node == other.Node &&
737		n.Address == other.Address &&
738		n.Datacenter == other.Datacenter &&
739		reflect.DeepEqual(n.TaggedAddresses, other.TaggedAddresses) &&
740		reflect.DeepEqual(n.Meta, other.Meta)
741}
742
743// ValidateNodeMetadata validates a set of key/value pairs from the agent
744// config for use on a Node.
745func ValidateNodeMetadata(meta map[string]string, allowConsulPrefix bool) error {
746	return validateMetadata(meta, allowConsulPrefix, nil)
747}
748
749// ValidateServiceMetadata validates a set of key/value pairs from the agent config for use on a Service.
750// ValidateMeta validates a set of key/value pairs from the agent config
751func ValidateServiceMetadata(kind ServiceKind, meta map[string]string, allowConsulPrefix bool) error {
752	switch kind {
753	case ServiceKindMeshGateway:
754		return validateMetadata(meta, allowConsulPrefix, allowedConsulMetaKeysForMeshGateway)
755	default:
756		return validateMetadata(meta, allowConsulPrefix, nil)
757	}
758}
759
760func validateMetadata(meta map[string]string, allowConsulPrefix bool, allowedConsulKeys map[string]struct{}) error {
761	if len(meta) > metaMaxKeyPairs {
762		return fmt.Errorf("Node metadata cannot contain more than %d key/value pairs", metaMaxKeyPairs)
763	}
764
765	for key, value := range meta {
766		if err := validateMetaPair(key, value, allowConsulPrefix, allowedConsulKeys); err != nil {
767			return fmt.Errorf("Couldn't load metadata pair ('%s', '%s'): %s", key, value, err)
768		}
769	}
770
771	return nil
772}
773
774// ValidateWeights checks the definition of DNS weight is valid
775func ValidateWeights(weights *Weights) error {
776	if weights == nil {
777		return nil
778	}
779	if weights.Passing < 1 {
780		return fmt.Errorf("Passing must be greater than 0")
781	}
782	if weights.Warning < 0 {
783		return fmt.Errorf("Warning must be greater or equal than 0")
784	}
785	if weights.Passing > 65535 || weights.Warning > 65535 {
786		return fmt.Errorf("DNS Weight must be between 0 and 65535")
787	}
788	return nil
789}
790
791// validateMetaPair checks that the given key/value pair is in a valid format
792func validateMetaPair(key, value string, allowConsulPrefix bool, allowedConsulKeys map[string]struct{}) error {
793	if key == "" {
794		return fmt.Errorf("Key cannot be blank")
795	}
796	if !metaKeyFormat(key) {
797		return fmt.Errorf("Key contains invalid characters")
798	}
799	if len(key) > metaKeyMaxLength {
800		return fmt.Errorf("Key is too long (limit: %d characters)", metaKeyMaxLength)
801	}
802	if strings.HasPrefix(key, metaKeyReservedPrefix) {
803		if _, ok := allowedConsulKeys[key]; !allowConsulPrefix && !ok {
804			return fmt.Errorf("Key prefix '%s' is reserved for internal use", metaKeyReservedPrefix)
805		}
806	}
807	if len(value) > metaValueMaxLength {
808		return fmt.Errorf("Value is too long (limit: %d characters)", metaValueMaxLength)
809	}
810	return nil
811}
812
813// SatisfiesMetaFilters returns true if the metadata map contains the given filters
814func SatisfiesMetaFilters(meta map[string]string, filters map[string]string) bool {
815	for key, value := range filters {
816		if v, ok := meta[key]; !ok || v != value {
817			return false
818		}
819	}
820	return true
821}
822
823// Used to return information about a provided services.
824// Maps service name to available tags
825type Services map[string][]string
826
827// ServiceNode represents a node that is part of a service. ID, Address,
828// TaggedAddresses, and NodeMeta are node-related fields that are always empty
829// in the state store and are filled in on the way out by parseServiceNodes().
830// This is also why PartialClone() skips them, because we know they are blank
831// already so it would be a waste of time to copy them.
832// This is somewhat complicated when the address is really a unix domain socket; technically that
833// will override the address field, but in practice the two use cases should not overlap.
834type ServiceNode struct {
835	ID                       types.NodeID
836	Node                     string
837	Address                  string
838	Datacenter               string
839	TaggedAddresses          map[string]string
840	NodeMeta                 map[string]string
841	ServiceKind              ServiceKind
842	ServiceID                string
843	ServiceName              string
844	ServiceTags              []string
845	ServiceAddress           string
846	ServiceTaggedAddresses   map[string]ServiceAddress `json:",omitempty"`
847	ServiceWeights           Weights
848	ServiceMeta              map[string]string
849	ServicePort              int
850	ServiceSocketPath        string
851	ServiceEnableTagOverride bool
852	ServiceProxy             ConnectProxyConfig
853	ServiceConnect           ServiceConnect
854
855	EnterpriseMeta `hcl:",squash" mapstructure:",squash" bexpr:"-"`
856
857	RaftIndex `bexpr:"-"`
858}
859
860func (s *ServiceNode) NodeIdentity() Identity {
861	return Identity{ID: s.Node}
862}
863
864// PartialClone() returns a clone of the given service node, minus the node-
865// related fields that get filled in later, Address and TaggedAddresses.
866func (s *ServiceNode) PartialClone() *ServiceNode {
867	tags := make([]string, len(s.ServiceTags))
868	copy(tags, s.ServiceTags)
869	nsmeta := make(map[string]string)
870	for k, v := range s.ServiceMeta {
871		nsmeta[k] = v
872	}
873
874	var svcTaggedAddrs map[string]ServiceAddress
875	if len(s.ServiceTaggedAddresses) > 0 {
876		svcTaggedAddrs = make(map[string]ServiceAddress)
877		for k, v := range s.ServiceTaggedAddresses {
878			svcTaggedAddrs[k] = v
879		}
880	}
881
882	return &ServiceNode{
883		// Skip ID, see above.
884		Node: s.Node,
885		// Skip Address, see above.
886		// Skip TaggedAddresses, see above.
887		ServiceKind:              s.ServiceKind,
888		ServiceID:                s.ServiceID,
889		ServiceName:              s.ServiceName,
890		ServiceTags:              tags,
891		ServiceAddress:           s.ServiceAddress,
892		ServiceSocketPath:        s.ServiceSocketPath,
893		ServiceTaggedAddresses:   svcTaggedAddrs,
894		ServicePort:              s.ServicePort,
895		ServiceMeta:              nsmeta,
896		ServiceWeights:           s.ServiceWeights,
897		ServiceEnableTagOverride: s.ServiceEnableTagOverride,
898		ServiceProxy:             s.ServiceProxy,
899		ServiceConnect:           s.ServiceConnect,
900		RaftIndex: RaftIndex{
901			CreateIndex: s.CreateIndex,
902			ModifyIndex: s.ModifyIndex,
903		},
904		EnterpriseMeta: s.EnterpriseMeta,
905	}
906}
907
908// ToNodeService converts the given service node to a node service.
909func (s *ServiceNode) ToNodeService() *NodeService {
910	return &NodeService{
911		Kind:              s.ServiceKind,
912		ID:                s.ServiceID,
913		Service:           s.ServiceName,
914		Tags:              s.ServiceTags,
915		Address:           s.ServiceAddress,
916		TaggedAddresses:   s.ServiceTaggedAddresses,
917		Port:              s.ServicePort,
918		SocketPath:        s.ServiceSocketPath,
919		Meta:              s.ServiceMeta,
920		Weights:           &s.ServiceWeights,
921		EnableTagOverride: s.ServiceEnableTagOverride,
922		Proxy:             s.ServiceProxy,
923		Connect:           s.ServiceConnect,
924		EnterpriseMeta:    s.EnterpriseMeta,
925		RaftIndex: RaftIndex{
926			CreateIndex: s.CreateIndex,
927			ModifyIndex: s.ModifyIndex,
928		},
929	}
930}
931
932func (sn *ServiceNode) CompoundServiceID() ServiceID {
933	id := sn.ServiceID
934	if id == "" {
935		id = sn.ServiceName
936	}
937
938	// copy the ent meta and normalize it
939	entMeta := sn.EnterpriseMeta
940	entMeta.Normalize()
941
942	return ServiceID{
943		ID:             id,
944		EnterpriseMeta: entMeta,
945	}
946}
947
948func (sn *ServiceNode) CompoundServiceName() ServiceName {
949	name := sn.ServiceName
950	if name == "" {
951		name = sn.ServiceID
952	}
953
954	// copy the ent meta and normalize it
955	entMeta := sn.EnterpriseMeta
956	entMeta.Normalize()
957
958	return ServiceName{
959		Name:           name,
960		EnterpriseMeta: entMeta,
961	}
962}
963
964// Weights represent the weight used by DNS for a given status
965type Weights struct {
966	Passing int
967	Warning int
968}
969
970type ServiceNodes []*ServiceNode
971
972// ServiceKind is the kind of service being registered.
973type ServiceKind string
974
975const (
976	// ServiceKindTypical is a typical, classic Consul service. This is
977	// represented by the absence of a value. This was chosen for ease of
978	// backwards compatibility: existing services in the catalog would
979	// default to the typical service.
980	ServiceKindTypical ServiceKind = ""
981
982	// ServiceKindConnectProxy is a proxy for the Connect feature. This
983	// service proxies another service within Consul and speaks the connect
984	// protocol.
985	ServiceKindConnectProxy ServiceKind = "connect-proxy"
986
987	// ServiceKindMeshGateway is a Mesh Gateway for the Connect feature. This
988	// service will proxy connections based off the SNI header set by other
989	// connect proxies
990	ServiceKindMeshGateway ServiceKind = "mesh-gateway"
991
992	// ServiceKindTerminatingGateway is a Terminating Gateway for the Connect
993	// feature. This service will proxy connections to services outside the mesh.
994	ServiceKindTerminatingGateway ServiceKind = "terminating-gateway"
995
996	// ServiceKindIngressGateway is an Ingress Gateway for the Connect feature.
997	// This service allows external traffic to enter the mesh based on
998	// centralized configuration.
999	ServiceKindIngressGateway ServiceKind = "ingress-gateway"
1000)
1001
1002// Type to hold a address and port of a service
1003type ServiceAddress struct {
1004	Address string
1005	Port    int
1006}
1007
1008func (a ServiceAddress) ToAPIServiceAddress() api.ServiceAddress {
1009	return api.ServiceAddress{Address: a.Address, Port: a.Port}
1010}
1011
1012// NodeService is a service provided by a node
1013type NodeService struct {
1014	// Kind is the kind of service this is. Different kinds of services may
1015	// have differing validation, DNS behavior, etc. An empty kind will default
1016	// to the Default kind. See ServiceKind for the full list of kinds.
1017	Kind ServiceKind `json:",omitempty"`
1018
1019	ID                string
1020	Service           string
1021	Tags              []string
1022	Address           string
1023	TaggedAddresses   map[string]ServiceAddress `json:",omitempty"`
1024	Meta              map[string]string
1025	Port              int    `json:",omitempty"`
1026	SocketPath        string `json:",omitempty"` // TODO This might be integrated into Address somehow, but not sure about the ergonomics. Only one of (address,port) or socketpath can be defined.
1027	Weights           *Weights
1028	EnableTagOverride bool
1029
1030	// Proxy is the configuration set for Kind = connect-proxy. It is mandatory in
1031	// that case and an error to be set for any other kind. This config is part of
1032	// a proxy service definition. ProxyConfig may be a more natural name here, but
1033	// it's confusing for the UX because one of the fields in ConnectProxyConfig is
1034	// also called just "Config"
1035	Proxy ConnectProxyConfig
1036
1037	// Connect are the Connect settings for a service. This is purposely NOT
1038	// a pointer so that we never have to nil-check this.
1039	Connect ServiceConnect
1040
1041	// LocallyRegisteredAsSidecar is private as it is only used by a local agent
1042	// state to track if the service was registered from a nested sidecar_service
1043	// block. We need to track that so we can know whether we need to deregister
1044	// it automatically too if it's removed from the service definition or if the
1045	// parent service is deregistered. Relying only on ID would cause us to
1046	// deregister regular services if they happen to be registered using the same
1047	// ID scheme as our sidecars do by default. We could use meta but that gets
1048	// unpleasant because we can't use the consul- prefix from an agent (reserved
1049	// for use internally but in practice that means within the state store or in
1050	// responses only), and it leaks the detail publicly which people might rely
1051	// on which is a bit unpleasant for something that is meant to be config-file
1052	// syntax sugar. Note this is not translated to ServiceNode and friends and
1053	// may not be set on a NodeService that isn't the one the agent registered and
1054	// keeps in it's local state. We never want this rendered in JSON as it's
1055	// internal only. Right now our agent endpoints return api structs which don't
1056	// include it but this is a safety net incase we change that or there is
1057	// somewhere this is used in API output.
1058	LocallyRegisteredAsSidecar bool `json:"-" bexpr:"-"`
1059
1060	EnterpriseMeta `hcl:",squash" mapstructure:",squash" bexpr:"-"`
1061
1062	RaftIndex `bexpr:"-"`
1063}
1064
1065func (ns *NodeService) BestAddress(wan bool) (string, int) {
1066	addr := ns.Address
1067	port := ns.Port
1068
1069	if wan {
1070		if wan, ok := ns.TaggedAddresses[TaggedAddressWAN]; ok {
1071			addr = wan.Address
1072			if wan.Port != 0 {
1073				port = wan.Port
1074			}
1075		}
1076	}
1077	return addr, port
1078}
1079
1080func (ns *NodeService) CompoundServiceID() ServiceID {
1081	id := ns.ID
1082	if id == "" {
1083		id = ns.Service
1084	}
1085
1086	// copy the ent meta and normalize it
1087	entMeta := ns.EnterpriseMeta
1088	entMeta.Normalize()
1089
1090	return ServiceID{
1091		ID:             id,
1092		EnterpriseMeta: entMeta,
1093	}
1094}
1095
1096func (ns *NodeService) CompoundServiceName() ServiceName {
1097	name := ns.Service
1098	if name == "" {
1099		name = ns.ID
1100	}
1101
1102	// copy the ent meta and normalize it
1103	entMeta := ns.EnterpriseMeta
1104	entMeta.Normalize()
1105
1106	return ServiceName{
1107		Name:           name,
1108		EnterpriseMeta: entMeta,
1109	}
1110}
1111
1112// UniqueID is a unique identifier for a service instance within a datacenter by encoding:
1113// node/namespace/service_id
1114//
1115// Note: We do not have strict character restrictions in all node names, so this should NOT be split on / to retrieve components.
1116func UniqueID(node string, compoundID string) string {
1117	return fmt.Sprintf("%s/%s", node, compoundID)
1118}
1119
1120// ServiceConnect are the shared Connect settings between all service
1121// definitions from the agent to the state store.
1122type ServiceConnect struct {
1123	// Native is true when this service can natively understand Connect.
1124	Native bool `json:",omitempty"`
1125
1126	// SidecarService is a nested Service Definition to register at the same time.
1127	// It's purely a convenience mechanism to allow specifying a sidecar service
1128	// along with the application service definition. It's nested nature allows
1129	// all of the fields to be defaulted which can reduce the amount of
1130	// boilerplate needed to register a sidecar service separately, but the end
1131	// result is identical to just making a second service registration via any
1132	// other means.
1133	SidecarService *ServiceDefinition `json:",omitempty" bexpr:"-"`
1134}
1135
1136func (t *ServiceConnect) UnmarshalJSON(data []byte) (err error) {
1137	type Alias ServiceConnect
1138	aux := &struct {
1139		SidecarServiceSnake *ServiceDefinition `json:"sidecar_service"`
1140
1141		*Alias
1142	}{
1143		Alias: (*Alias)(t),
1144	}
1145
1146	if err = json.Unmarshal(data, &aux); err != nil {
1147		return err
1148	}
1149
1150	if t.SidecarService == nil && aux != nil {
1151		t.SidecarService = aux.SidecarServiceSnake
1152	}
1153	return nil
1154}
1155
1156// IsSidecarProxy returns true if the NodeService is a sidecar proxy.
1157func (s *NodeService) IsSidecarProxy() bool {
1158	return s.Kind == ServiceKindConnectProxy && s.Proxy.DestinationServiceID != ""
1159}
1160
1161func (s *NodeService) IsGateway() bool {
1162	return s.Kind == ServiceKindMeshGateway ||
1163		s.Kind == ServiceKindTerminatingGateway ||
1164		s.Kind == ServiceKindIngressGateway
1165}
1166
1167// Validate validates the node service configuration.
1168//
1169// NOTE(mitchellh): This currently only validates fields for a ConnectProxy.
1170// Historically validation has been directly in the Catalog.Register RPC.
1171// ConnectProxy validation was moved here for easier table testing, but
1172// other validation still exists in Catalog.Register.
1173func (s *NodeService) Validate() error {
1174	var result error
1175
1176	// ConnectProxy validation
1177	if s.Kind == ServiceKindConnectProxy {
1178		if strings.TrimSpace(s.Proxy.DestinationServiceName) == "" {
1179			result = multierror.Append(result, fmt.Errorf(
1180				"Proxy.DestinationServiceName must be non-empty for Connect proxy "+
1181					"services"))
1182		}
1183		if s.Proxy.DestinationServiceName == WildcardSpecifier {
1184			result = multierror.Append(result, fmt.Errorf(
1185				"Proxy.DestinationServiceName must not be a wildcard for Connect proxy "+
1186					"services"))
1187		}
1188
1189		if s.Port == 0 && s.SocketPath == "" {
1190			result = multierror.Append(result, fmt.Errorf(
1191				"Port or SocketPath must be set for a Connect proxy"))
1192		}
1193
1194		if s.Connect.Native {
1195			result = multierror.Append(result, fmt.Errorf(
1196				"A Proxy cannot also be Connect Native, only typical services"))
1197		}
1198
1199		// ensure we don't have multiple upstreams for the same service
1200		var (
1201			upstreamKeys = make(map[UpstreamKey]struct{})
1202			bindAddrs    = make(map[string]struct{})
1203		)
1204		for _, u := range s.Proxy.Upstreams {
1205			if err := u.Validate(); err != nil {
1206				result = multierror.Append(result, err)
1207				continue
1208			}
1209
1210			uk := u.ToKey()
1211			if _, ok := upstreamKeys[uk]; ok {
1212				result = multierror.Append(result, fmt.Errorf(
1213					"upstreams cannot contain duplicates of %s", uk))
1214				continue
1215			}
1216			upstreamKeys[uk] = struct{}{}
1217
1218			addr := u.UpstreamAddressToString()
1219
1220			// Centrally configured upstreams will fail this check if there are multiple because they do not have an address/port.
1221			// Only consider non-centrally configured upstreams in this check since those are the ones we create listeners for.
1222			if _, ok := bindAddrs[addr]; ok && !u.CentrallyConfigured {
1223				result = multierror.Append(result, fmt.Errorf(
1224					"upstreams cannot contain duplicates by local bind address and port or unix path; %q is specified twice", addr))
1225				continue
1226			}
1227			bindAddrs[addr] = struct{}{}
1228		}
1229
1230		var knownListeners = make(map[int]bool)
1231		for _, path := range s.Proxy.Expose.Paths {
1232			if path.Path == "" {
1233				result = multierror.Append(result, fmt.Errorf("expose.paths: empty path exposed"))
1234			}
1235
1236			if seen := knownListeners[path.ListenerPort]; seen {
1237				result = multierror.Append(result, fmt.Errorf("expose.paths: duplicate listener ports exposed"))
1238			}
1239			knownListeners[path.ListenerPort] = true
1240
1241			if path.ListenerPort <= 0 || path.ListenerPort > 65535 {
1242				result = multierror.Append(result, fmt.Errorf("expose.paths: invalid listener port: %d", path.ListenerPort))
1243			}
1244
1245			path.Protocol = strings.ToLower(path.Protocol)
1246			if ok := allowedExposeProtocols[path.Protocol]; !ok && path.Protocol != "" {
1247				protocols := make([]string, 0)
1248				for p := range allowedExposeProtocols {
1249					protocols = append(protocols, p)
1250				}
1251
1252				result = multierror.Append(result,
1253					fmt.Errorf("protocol '%s' not supported for path: %s, must be in: %v",
1254						path.Protocol, path.Path, protocols))
1255			}
1256		}
1257	}
1258
1259	// Gateway validation
1260	if s.IsGateway() {
1261		// Non-ingress gateways must have a port
1262		if s.Port == 0 && s.Kind != ServiceKindIngressGateway {
1263			result = multierror.Append(result, fmt.Errorf("Port must be non-zero for a %s", s.Kind))
1264		}
1265
1266		// Gateways cannot have sidecars
1267		if s.Connect.SidecarService != nil {
1268			result = multierror.Append(result, fmt.Errorf("A %s cannot have a sidecar service defined", s.Kind))
1269		}
1270
1271		if s.Proxy.DestinationServiceName != "" {
1272			result = multierror.Append(result, fmt.Errorf("The Proxy.DestinationServiceName configuration is invalid for a %s", s.Kind))
1273		}
1274
1275		if s.Proxy.DestinationServiceID != "" {
1276			result = multierror.Append(result, fmt.Errorf("The Proxy.DestinationServiceID configuration is invalid for a %s", s.Kind))
1277		}
1278
1279		if s.Proxy.LocalServiceAddress != "" {
1280			result = multierror.Append(result, fmt.Errorf("The Proxy.LocalServiceAddress configuration is invalid for a %s", s.Kind))
1281		}
1282
1283		if s.Proxy.LocalServicePort != 0 {
1284			result = multierror.Append(result, fmt.Errorf("The Proxy.LocalServicePort configuration is invalid for a %s", s.Kind))
1285		}
1286
1287		if s.Proxy.LocalServiceSocketPath != "" {
1288			result = multierror.Append(result, fmt.Errorf("The Proxy.LocalServiceSocketPath configuration is invalid for a %s", s.Kind))
1289		}
1290
1291		if len(s.Proxy.Upstreams) != 0 {
1292			result = multierror.Append(result, fmt.Errorf("The Proxy.Upstreams configuration is invalid for a %s", s.Kind))
1293		}
1294	}
1295
1296	// Nested sidecar validation
1297	if s.Connect.SidecarService != nil {
1298		if s.Connect.SidecarService.ID != "" {
1299			result = multierror.Append(result, fmt.Errorf(
1300				"A SidecarService cannot specify an ID as this is managed by the "+
1301					"agent"))
1302		}
1303		if s.Connect.SidecarService.Connect != nil {
1304			if s.Connect.SidecarService.Connect.SidecarService != nil {
1305				result = multierror.Append(result, fmt.Errorf(
1306					"A SidecarService cannot have a nested SidecarService"))
1307			}
1308		}
1309	}
1310
1311	return result
1312}
1313
1314// IsSame checks if one NodeService is the same as another, without looking
1315// at the Raft information (that's why we didn't call it IsEqual). This is
1316// useful for seeing if an update would be idempotent for all the functional
1317// parts of the structure.
1318func (s *NodeService) IsSame(other *NodeService) bool {
1319	if s.ID != other.ID ||
1320		s.Service != other.Service ||
1321		!reflect.DeepEqual(s.Tags, other.Tags) ||
1322		s.Address != other.Address ||
1323		s.Port != other.Port ||
1324		s.SocketPath != other.SocketPath ||
1325		!reflect.DeepEqual(s.TaggedAddresses, other.TaggedAddresses) ||
1326		!reflect.DeepEqual(s.Weights, other.Weights) ||
1327		!reflect.DeepEqual(s.Meta, other.Meta) ||
1328		s.EnableTagOverride != other.EnableTagOverride ||
1329		s.Kind != other.Kind ||
1330		!reflect.DeepEqual(s.Proxy, other.Proxy) ||
1331		s.Connect != other.Connect ||
1332		!s.EnterpriseMeta.IsSame(&other.EnterpriseMeta) {
1333		return false
1334	}
1335
1336	return true
1337}
1338
1339// IsSameService checks if one Service of a ServiceNode is the same as another,
1340// without looking at the Raft information or Node information (that's why we
1341// didn't call it IsEqual).
1342// This is useful for seeing if an update would be idempotent for all the functional
1343// parts of the structure.
1344// In a similar fashion as ToNodeService(), fields related to Node are ignored
1345// see ServiceNode for more information.
1346func (s *ServiceNode) IsSameService(other *ServiceNode) bool {
1347	// Skip the following fields, see ServiceNode definition
1348	// Address                  string
1349	// Datacenter               string
1350	// TaggedAddresses          map[string]string
1351	// NodeMeta                 map[string]string
1352	if s.ID != other.ID ||
1353		s.Node != other.Node ||
1354		s.ServiceKind != other.ServiceKind ||
1355		s.ServiceID != other.ServiceID ||
1356		s.ServiceName != other.ServiceName ||
1357		!reflect.DeepEqual(s.ServiceTags, other.ServiceTags) ||
1358		s.ServiceAddress != other.ServiceAddress ||
1359		!reflect.DeepEqual(s.ServiceTaggedAddresses, other.ServiceTaggedAddresses) ||
1360		s.ServicePort != other.ServicePort ||
1361		!reflect.DeepEqual(s.ServiceMeta, other.ServiceMeta) ||
1362		!reflect.DeepEqual(s.ServiceWeights, other.ServiceWeights) ||
1363		s.ServiceEnableTagOverride != other.ServiceEnableTagOverride ||
1364		!reflect.DeepEqual(s.ServiceProxy, other.ServiceProxy) ||
1365		!reflect.DeepEqual(s.ServiceConnect, other.ServiceConnect) ||
1366		!s.EnterpriseMeta.IsSame(&other.EnterpriseMeta) {
1367		return false
1368	}
1369
1370	return true
1371}
1372
1373// ToServiceNode converts the given node service to a service node.
1374func (s *NodeService) ToServiceNode(node string) *ServiceNode {
1375	theWeights := Weights{
1376		Passing: 1,
1377		Warning: 1,
1378	}
1379	if s.Weights != nil {
1380		if err := ValidateWeights(s.Weights); err == nil {
1381			theWeights = *s.Weights
1382		}
1383	}
1384	return &ServiceNode{
1385		// Skip ID, see ServiceNode definition.
1386		Node: node,
1387		// Skip Address, see ServiceNode definition.
1388		// Skip TaggedAddresses, see ServiceNode definition.
1389		ServiceKind:              s.Kind,
1390		ServiceID:                s.ID,
1391		ServiceName:              s.Service,
1392		ServiceTags:              s.Tags,
1393		ServiceAddress:           s.Address,
1394		ServiceTaggedAddresses:   s.TaggedAddresses,
1395		ServicePort:              s.Port,
1396		ServiceSocketPath:        s.SocketPath,
1397		ServiceMeta:              s.Meta,
1398		ServiceWeights:           theWeights,
1399		ServiceEnableTagOverride: s.EnableTagOverride,
1400		ServiceProxy:             s.Proxy,
1401		ServiceConnect:           s.Connect,
1402		EnterpriseMeta:           s.EnterpriseMeta,
1403		RaftIndex: RaftIndex{
1404			CreateIndex: s.CreateIndex,
1405			ModifyIndex: s.ModifyIndex,
1406		},
1407	}
1408}
1409
1410type NodeServices struct {
1411	Node     *Node
1412	Services map[string]*NodeService
1413}
1414
1415type NodeServiceList struct {
1416	Node     *Node
1417	Services []*NodeService
1418}
1419
1420// HealthCheck represents a single check on a given node.
1421type HealthCheck struct {
1422	Node        string
1423	CheckID     types.CheckID // Unique per-node ID
1424	Name        string        // Check name
1425	Status      string        // The current check status
1426	Notes       string        // Additional notes with the status
1427	Output      string        // Holds output of script runs
1428	ServiceID   string        // optional associated service
1429	ServiceName string        // optional service name
1430	ServiceTags []string      // optional service tags
1431	Type        string        // Check type: http/ttl/tcp/etc
1432
1433	Interval string // from definition
1434	Timeout  string // from definition
1435
1436	// ExposedPort is the port of the exposed Envoy listener representing the
1437	// HTTP or GRPC health check of the service.
1438	ExposedPort int
1439
1440	Definition HealthCheckDefinition `bexpr:"-"`
1441
1442	EnterpriseMeta `hcl:",squash" mapstructure:",squash" bexpr:"-"`
1443
1444	RaftIndex `bexpr:"-"`
1445}
1446
1447func (hc *HealthCheck) NodeIdentity() Identity {
1448	return Identity{ID: hc.Node}
1449}
1450
1451func (hc *HealthCheck) CompoundServiceID() ServiceID {
1452	id := hc.ServiceID
1453	if id == "" {
1454		id = hc.ServiceName
1455	}
1456
1457	entMeta := hc.EnterpriseMeta
1458	entMeta.Normalize()
1459
1460	return ServiceID{
1461		ID:             id,
1462		EnterpriseMeta: entMeta,
1463	}
1464}
1465
1466func (hc *HealthCheck) CompoundCheckID() CheckID {
1467	entMeta := hc.EnterpriseMeta
1468	entMeta.Normalize()
1469
1470	return CheckID{
1471		ID:             hc.CheckID,
1472		EnterpriseMeta: entMeta,
1473	}
1474}
1475
1476type HealthCheckDefinition struct {
1477	HTTP                           string              `json:",omitempty"`
1478	TLSServerName                  string              `json:",omitempty"`
1479	TLSSkipVerify                  bool                `json:",omitempty"`
1480	Header                         map[string][]string `json:",omitempty"`
1481	Method                         string              `json:",omitempty"`
1482	Body                           string              `json:",omitempty"`
1483	TCP                            string              `json:",omitempty"`
1484	H2PING                         string              `json:",omitempty"`
1485	Interval                       time.Duration       `json:",omitempty"`
1486	OutputMaxSize                  uint                `json:",omitempty"`
1487	Timeout                        time.Duration       `json:",omitempty"`
1488	DeregisterCriticalServiceAfter time.Duration       `json:",omitempty"`
1489	ScriptArgs                     []string            `json:",omitempty"`
1490	DockerContainerID              string              `json:",omitempty"`
1491	Shell                          string              `json:",omitempty"`
1492	GRPC                           string              `json:",omitempty"`
1493	GRPCUseTLS                     bool                `json:",omitempty"`
1494	AliasNode                      string              `json:",omitempty"`
1495	AliasService                   string              `json:",omitempty"`
1496	TTL                            time.Duration       `json:",omitempty"`
1497}
1498
1499func (d *HealthCheckDefinition) MarshalJSON() ([]byte, error) {
1500	type Alias HealthCheckDefinition
1501	exported := &struct {
1502		Interval                       string `json:",omitempty"`
1503		OutputMaxSize                  uint   `json:",omitempty"`
1504		Timeout                        string `json:",omitempty"`
1505		DeregisterCriticalServiceAfter string `json:",omitempty"`
1506		*Alias
1507	}{
1508		Interval:                       d.Interval.String(),
1509		OutputMaxSize:                  d.OutputMaxSize,
1510		Timeout:                        d.Timeout.String(),
1511		DeregisterCriticalServiceAfter: d.DeregisterCriticalServiceAfter.String(),
1512		Alias:                          (*Alias)(d),
1513	}
1514	if d.Interval == 0 {
1515		exported.Interval = ""
1516	}
1517	if d.Timeout == 0 {
1518		exported.Timeout = ""
1519	}
1520	if d.DeregisterCriticalServiceAfter == 0 {
1521		exported.DeregisterCriticalServiceAfter = ""
1522	}
1523
1524	return json.Marshal(exported)
1525}
1526
1527func (t *HealthCheckDefinition) UnmarshalJSON(data []byte) (err error) {
1528	type Alias HealthCheckDefinition
1529	aux := &struct {
1530		Interval                       interface{}
1531		Timeout                        interface{}
1532		DeregisterCriticalServiceAfter interface{}
1533		TTL                            interface{}
1534		*Alias
1535	}{
1536		Alias: (*Alias)(t),
1537	}
1538	if err := json.Unmarshal(data, &aux); err != nil {
1539		return err
1540	}
1541	if aux.Interval != nil {
1542		switch v := aux.Interval.(type) {
1543		case string:
1544			if t.Interval, err = time.ParseDuration(v); err != nil {
1545				return err
1546			}
1547		case float64:
1548			t.Interval = time.Duration(v)
1549		}
1550	}
1551	if aux.Timeout != nil {
1552		switch v := aux.Timeout.(type) {
1553		case string:
1554			if t.Timeout, err = time.ParseDuration(v); err != nil {
1555				return err
1556			}
1557		case float64:
1558			t.Timeout = time.Duration(v)
1559		}
1560	}
1561	if aux.DeregisterCriticalServiceAfter != nil {
1562		switch v := aux.DeregisterCriticalServiceAfter.(type) {
1563		case string:
1564			if t.DeregisterCriticalServiceAfter, err = time.ParseDuration(v); err != nil {
1565				return err
1566			}
1567		case float64:
1568			t.DeregisterCriticalServiceAfter = time.Duration(v)
1569		}
1570	}
1571	if aux.TTL != nil {
1572		switch v := aux.TTL.(type) {
1573		case string:
1574			if t.TTL, err = time.ParseDuration(v); err != nil {
1575				return err
1576			}
1577		case float64:
1578			t.TTL = time.Duration(v)
1579		}
1580	}
1581	return nil
1582}
1583
1584// IsSame checks if one HealthCheck is the same as another, without looking
1585// at the Raft information (that's why we didn't call it IsEqual). This is
1586// useful for seeing if an update would be idempotent for all the functional
1587// parts of the structure.
1588func (c *HealthCheck) IsSame(other *HealthCheck) bool {
1589	if c.Node != other.Node ||
1590		c.CheckID != other.CheckID ||
1591		c.Name != other.Name ||
1592		c.Status != other.Status ||
1593		c.Notes != other.Notes ||
1594		c.Output != other.Output ||
1595		c.ServiceID != other.ServiceID ||
1596		c.ServiceName != other.ServiceName ||
1597		!reflect.DeepEqual(c.ServiceTags, other.ServiceTags) ||
1598		!reflect.DeepEqual(c.Definition, other.Definition) ||
1599		!c.EnterpriseMeta.IsSame(&other.EnterpriseMeta) {
1600		return false
1601	}
1602
1603	return true
1604}
1605
1606// Clone returns a distinct clone of the HealthCheck. Note that the
1607// "ServiceTags" and "Definition.Header" field are not deep copied.
1608func (c *HealthCheck) Clone() *HealthCheck {
1609	clone := new(HealthCheck)
1610	*clone = *c
1611	return clone
1612}
1613
1614func (c *HealthCheck) CheckType() *CheckType {
1615	return &CheckType{
1616		CheckID: c.CheckID,
1617		Name:    c.Name,
1618		Status:  c.Status,
1619		Notes:   c.Notes,
1620
1621		ScriptArgs:                     c.Definition.ScriptArgs,
1622		AliasNode:                      c.Definition.AliasNode,
1623		AliasService:                   c.Definition.AliasService,
1624		HTTP:                           c.Definition.HTTP,
1625		GRPC:                           c.Definition.GRPC,
1626		GRPCUseTLS:                     c.Definition.GRPCUseTLS,
1627		Header:                         c.Definition.Header,
1628		Method:                         c.Definition.Method,
1629		Body:                           c.Definition.Body,
1630		TCP:                            c.Definition.TCP,
1631		H2PING:                         c.Definition.H2PING,
1632		Interval:                       c.Definition.Interval,
1633		DockerContainerID:              c.Definition.DockerContainerID,
1634		Shell:                          c.Definition.Shell,
1635		TLSServerName:                  c.Definition.TLSServerName,
1636		TLSSkipVerify:                  c.Definition.TLSSkipVerify,
1637		Timeout:                        c.Definition.Timeout,
1638		TTL:                            c.Definition.TTL,
1639		DeregisterCriticalServiceAfter: c.Definition.DeregisterCriticalServiceAfter,
1640	}
1641}
1642
1643// HealthChecks is a collection of HealthCheck structs.
1644type HealthChecks []*HealthCheck
1645
1646// CheckServiceNode is used to provide the node, its service
1647// definition, as well as a HealthCheck that is associated.
1648type CheckServiceNode struct {
1649	Node    *Node
1650	Service *NodeService
1651	Checks  HealthChecks
1652}
1653
1654func (csn *CheckServiceNode) BestAddress(wan bool) (string, int) {
1655	// TODO (mesh-gateway) needs a test
1656	// best address
1657	// wan
1658	//   wan svc addr
1659	//   svc addr
1660	//   wan node addr
1661	//   node addr
1662	// lan
1663	//   svc addr
1664	//   node addr
1665
1666	addr, port := csn.Service.BestAddress(wan)
1667
1668	if addr == "" {
1669		addr = csn.Node.BestAddress(wan)
1670	}
1671
1672	return addr, port
1673}
1674
1675func (csn *CheckServiceNode) CanRead(authz acl.Authorizer) acl.EnforcementDecision {
1676	if csn.Node == nil || csn.Service == nil {
1677		return acl.Deny
1678	}
1679
1680	authzContext := new(acl.AuthorizerContext)
1681	csn.Service.EnterpriseMeta.FillAuthzContext(authzContext)
1682
1683	if authz.NodeRead(csn.Node.Node, authzContext) != acl.Allow {
1684		return acl.Deny
1685	}
1686
1687	if authz.ServiceRead(csn.Service.Service, authzContext) != acl.Allow {
1688		return acl.Deny
1689	}
1690	return acl.Allow
1691}
1692
1693type CheckServiceNodes []CheckServiceNode
1694
1695// Shuffle does an in-place random shuffle using the Fisher-Yates algorithm.
1696func (nodes CheckServiceNodes) Shuffle() {
1697	for i := len(nodes) - 1; i > 0; i-- {
1698		j := rand.Int31n(int32(i + 1))
1699		nodes[i], nodes[j] = nodes[j], nodes[i]
1700	}
1701}
1702
1703func (nodes CheckServiceNodes) ToServiceDump() ServiceDump {
1704	var ret ServiceDump
1705	for i := range nodes {
1706		svc := ServiceInfo{
1707			Node:           nodes[i].Node,
1708			Service:        nodes[i].Service,
1709			Checks:         nodes[i].Checks,
1710			GatewayService: nil,
1711		}
1712		ret = append(ret, &svc)
1713	}
1714	return ret
1715}
1716
1717// ShallowClone duplicates the slice and underlying array.
1718func (nodes CheckServiceNodes) ShallowClone() CheckServiceNodes {
1719	dup := make(CheckServiceNodes, len(nodes))
1720	copy(dup, nodes)
1721	return dup
1722}
1723
1724// Filter removes nodes that are failing health checks (and any non-passing
1725// check if that option is selected). Note that this returns the filtered
1726// results AND modifies the receiver for performance.
1727func (nodes CheckServiceNodes) Filter(onlyPassing bool) CheckServiceNodes {
1728	return nodes.FilterIgnore(onlyPassing, nil)
1729}
1730
1731// FilterIgnore removes nodes that are failing health checks just like Filter.
1732// It also ignores the status of any check with an ID present in ignoreCheckIDs
1733// as if that check didn't exist. Note that this returns the filtered results
1734// AND modifies the receiver for performance.
1735func (nodes CheckServiceNodes) FilterIgnore(onlyPassing bool,
1736	ignoreCheckIDs []types.CheckID) CheckServiceNodes {
1737	n := len(nodes)
1738OUTER:
1739	for i := 0; i < n; i++ {
1740		node := nodes[i]
1741	INNER:
1742		for _, check := range node.Checks {
1743			for _, ignore := range ignoreCheckIDs {
1744				if check.CheckID == ignore {
1745					// Skip this _check_ but keep looking at other checks for this node.
1746					continue INNER
1747				}
1748			}
1749			if check.Status == api.HealthCritical ||
1750				(onlyPassing && check.Status != api.HealthPassing) {
1751				nodes[i], nodes[n-1] = nodes[n-1], CheckServiceNode{}
1752				n--
1753				i--
1754				// Skip this _node_ now we've swapped it off the end of the list.
1755				continue OUTER
1756			}
1757		}
1758	}
1759	return nodes[:n]
1760}
1761
1762// NodeInfo is used to dump all associated information about
1763// a node. This is currently used for the UI only, as it is
1764// rather expensive to generate.
1765type NodeInfo struct {
1766	ID              types.NodeID
1767	Node            string
1768	Address         string
1769	TaggedAddresses map[string]string
1770	Meta            map[string]string
1771	Services        []*NodeService
1772	Checks          HealthChecks
1773}
1774
1775// NodeDump is used to dump all the nodes with all their
1776// associated data. This is currently used for the UI only,
1777// as it is rather expensive to generate.
1778type NodeDump []*NodeInfo
1779
1780type ServiceInfo struct {
1781	Node           *Node
1782	Service        *NodeService
1783	Checks         HealthChecks
1784	GatewayService *GatewayService
1785}
1786
1787type ServiceDump []*ServiceInfo
1788
1789type CheckID struct {
1790	ID types.CheckID
1791	EnterpriseMeta
1792}
1793
1794func NewCheckID(id types.CheckID, entMeta *EnterpriseMeta) CheckID {
1795	var cid CheckID
1796	cid.ID = id
1797	if entMeta == nil {
1798		entMeta = DefaultEnterpriseMeta()
1799	}
1800
1801	cid.EnterpriseMeta = *entMeta
1802	cid.EnterpriseMeta.Normalize()
1803	return cid
1804}
1805
1806// StringHash is used mainly to populate part of the filename of a check
1807// definition persisted on the local agent
1808func (cid CheckID) StringHash() string {
1809	hasher := md5.New()
1810	hasher.Write([]byte(cid.ID))
1811	cid.EnterpriseMeta.addToHash(hasher, true)
1812	return fmt.Sprintf("%x", hasher.Sum(nil))
1813}
1814
1815type ServiceID struct {
1816	ID string
1817	EnterpriseMeta
1818}
1819
1820func NewServiceID(id string, entMeta *EnterpriseMeta) ServiceID {
1821	var sid ServiceID
1822	sid.ID = id
1823	if entMeta == nil {
1824		entMeta = DefaultEnterpriseMeta()
1825	}
1826
1827	sid.EnterpriseMeta = *entMeta
1828	sid.EnterpriseMeta.Normalize()
1829	return sid
1830}
1831
1832func (sid ServiceID) Matches(other ServiceID) bool {
1833	return sid.ID == other.ID && sid.EnterpriseMeta.Matches(&other.EnterpriseMeta)
1834}
1835
1836// StringHash is used mainly to populate part of the filename of a service
1837// definition persisted on the local agent
1838func (sid ServiceID) StringHash() string {
1839	hasher := md5.New()
1840	hasher.Write([]byte(sid.ID))
1841	sid.EnterpriseMeta.addToHash(hasher, true)
1842	return fmt.Sprintf("%x", hasher.Sum(nil))
1843}
1844
1845type IndexedNodes struct {
1846	Nodes Nodes
1847	QueryMeta
1848}
1849
1850type IndexedServices struct {
1851	Services Services
1852	// In various situations we need to know the meta that the services are for - in particular
1853	// this is needed to be able to properly filter the list based on ACLs
1854	EnterpriseMeta
1855	QueryMeta
1856}
1857
1858type ServiceName struct {
1859	Name string
1860	EnterpriseMeta
1861}
1862
1863func NewServiceName(name string, entMeta *EnterpriseMeta) ServiceName {
1864	var ret ServiceName
1865	ret.Name = name
1866	if entMeta == nil {
1867		entMeta = DefaultEnterpriseMeta()
1868	}
1869
1870	ret.EnterpriseMeta = *entMeta
1871	ret.EnterpriseMeta.Normalize()
1872	return ret
1873}
1874
1875func (n ServiceName) Matches(o ServiceName) bool {
1876	return n.Name == o.Name && n.EnterpriseMeta.Matches(&o.EnterpriseMeta)
1877}
1878
1879func (n ServiceName) ToServiceID() ServiceID {
1880	return ServiceID{ID: n.Name, EnterpriseMeta: n.EnterpriseMeta}
1881}
1882
1883type ServiceList []ServiceName
1884
1885type IndexedServiceList struct {
1886	Services ServiceList
1887	QueryMeta
1888}
1889
1890type IndexedServiceNodes struct {
1891	ServiceNodes ServiceNodes
1892	QueryMeta
1893}
1894
1895type IndexedNodeServices struct {
1896	// TODO: This should not be a pointer, see comments in
1897	// agent/catalog_endpoint.go.
1898	NodeServices *NodeServices
1899	QueryMeta
1900}
1901
1902type IndexedNodeServiceList struct {
1903	NodeServices NodeServiceList
1904	QueryMeta
1905}
1906
1907type IndexedHealthChecks struct {
1908	HealthChecks HealthChecks
1909	QueryMeta
1910}
1911
1912type IndexedCheckServiceNodes struct {
1913	Nodes CheckServiceNodes
1914	QueryMeta
1915}
1916
1917type IndexedNodesWithGateways struct {
1918	Nodes    CheckServiceNodes
1919	Gateways GatewayServices
1920	QueryMeta
1921}
1922
1923type DatacenterIndexedCheckServiceNodes struct {
1924	DatacenterNodes map[string]CheckServiceNodes
1925	QueryMeta
1926}
1927
1928type IndexedNodeDump struct {
1929	Dump NodeDump
1930	QueryMeta
1931}
1932
1933type IndexedServiceDump struct {
1934	Dump ServiceDump
1935	QueryMeta
1936}
1937
1938type IndexedGatewayServices struct {
1939	Services GatewayServices
1940	QueryMeta
1941}
1942
1943type IndexedServiceTopology struct {
1944	ServiceTopology *ServiceTopology
1945	FilteredByACLs  bool
1946	QueryMeta
1947}
1948
1949type ServiceTopology struct {
1950	Upstreams   CheckServiceNodes
1951	Downstreams CheckServiceNodes
1952
1953	UpstreamDecisions   map[string]IntentionDecisionSummary
1954	DownstreamDecisions map[string]IntentionDecisionSummary
1955
1956	// MetricsProtocol is the protocol of the service being queried
1957	MetricsProtocol string
1958
1959	// TransparentProxy describes whether all instances of the proxy
1960	// service are in transparent mode.
1961	TransparentProxy bool
1962
1963	// (Up|Down)streamSources are maps with labels for why each service is being
1964	// returned. Services can be upstreams or downstreams due to
1965	// explicit upstream definition or various types of intention policies:
1966	// specific, wildcard, or default allow.
1967	UpstreamSources   map[string]string
1968	DownstreamSources map[string]string
1969}
1970
1971// IndexedConfigEntries has its own encoding logic which differs from
1972// ConfigEntryRequest as it has to send a slice of ConfigEntry.
1973type IndexedConfigEntries struct {
1974	Kind    string
1975	Entries []ConfigEntry
1976	QueryMeta
1977}
1978
1979func (c *IndexedConfigEntries) MarshalBinary() (data []byte, err error) {
1980	// bs will grow if needed but allocate enough to avoid reallocation in common
1981	// case.
1982	bs := make([]byte, 128)
1983	enc := codec.NewEncoderBytes(&bs, MsgpackHandle)
1984
1985	// Encode length.
1986	err = enc.Encode(len(c.Entries))
1987	if err != nil {
1988		return nil, err
1989	}
1990
1991	// Encode kind.
1992	err = enc.Encode(c.Kind)
1993	if err != nil {
1994		return nil, err
1995	}
1996
1997	// Then actual value using alias trick to avoid infinite recursion
1998	type Alias IndexedConfigEntries
1999	err = enc.Encode(struct {
2000		*Alias
2001	}{
2002		Alias: (*Alias)(c),
2003	})
2004	if err != nil {
2005		return nil, err
2006	}
2007	return bs, nil
2008}
2009
2010func (c *IndexedConfigEntries) UnmarshalBinary(data []byte) error {
2011	// First decode the number of entries.
2012	var numEntries int
2013	dec := codec.NewDecoderBytes(data, MsgpackHandle)
2014	if err := dec.Decode(&numEntries); err != nil {
2015		return err
2016	}
2017
2018	// Next decode the kind.
2019	var kind string
2020	if err := dec.Decode(&kind); err != nil {
2021		return err
2022	}
2023
2024	// Then decode the slice of ConfigEntries
2025	c.Entries = make([]ConfigEntry, numEntries)
2026	for i := 0; i < numEntries; i++ {
2027		entry, err := MakeConfigEntry(kind, "")
2028		if err != nil {
2029			return err
2030		}
2031		c.Entries[i] = entry
2032	}
2033
2034	// Alias juggling to prevent infinite recursive calls back to this decode
2035	// method.
2036	type Alias IndexedConfigEntries
2037	as := struct {
2038		*Alias
2039	}{
2040		Alias: (*Alias)(c),
2041	}
2042	if err := dec.Decode(&as); err != nil {
2043		return err
2044	}
2045	return nil
2046}
2047
2048type IndexedGenericConfigEntries struct {
2049	Entries []ConfigEntry
2050	QueryMeta
2051}
2052
2053func (c *IndexedGenericConfigEntries) MarshalBinary() (data []byte, err error) {
2054	// bs will grow if needed but allocate enough to avoid reallocation in common
2055	// case.
2056	bs := make([]byte, 128)
2057	enc := codec.NewEncoderBytes(&bs, MsgpackHandle)
2058
2059	if err := enc.Encode(len(c.Entries)); err != nil {
2060		return nil, err
2061	}
2062
2063	for _, entry := range c.Entries {
2064		if err := enc.Encode(entry.GetKind()); err != nil {
2065			return nil, err
2066		}
2067		if err := enc.Encode(entry); err != nil {
2068			return nil, err
2069		}
2070	}
2071
2072	if err := enc.Encode(c.QueryMeta); err != nil {
2073		return nil, err
2074	}
2075
2076	return bs, nil
2077}
2078
2079func (c *IndexedGenericConfigEntries) UnmarshalBinary(data []byte) error {
2080	// First decode the number of entries.
2081	var numEntries int
2082	dec := codec.NewDecoderBytes(data, MsgpackHandle)
2083	if err := dec.Decode(&numEntries); err != nil {
2084		return err
2085	}
2086
2087	// Then decode the slice of ConfigEntries
2088	c.Entries = make([]ConfigEntry, numEntries)
2089	for i := 0; i < numEntries; i++ {
2090		var kind string
2091		if err := dec.Decode(&kind); err != nil {
2092			return err
2093		}
2094
2095		entry, err := MakeConfigEntry(kind, "")
2096		if err != nil {
2097			return err
2098		}
2099
2100		if err := dec.Decode(entry); err != nil {
2101			return err
2102		}
2103
2104		c.Entries[i] = entry
2105	}
2106
2107	if err := dec.Decode(&c.QueryMeta); err != nil {
2108		return err
2109	}
2110
2111	return nil
2112
2113}
2114
2115// DirEntry is used to represent a directory entry. This is
2116// used for values in our Key-Value store.
2117type DirEntry struct {
2118	LockIndex uint64
2119	Key       string
2120	Flags     uint64
2121	Value     []byte
2122	Session   string `json:",omitempty"`
2123
2124	EnterpriseMeta `bexpr:"-"`
2125	RaftIndex
2126}
2127
2128// Returns a clone of the given directory entry.
2129func (d *DirEntry) Clone() *DirEntry {
2130	return &DirEntry{
2131		LockIndex: d.LockIndex,
2132		Key:       d.Key,
2133		Flags:     d.Flags,
2134		Value:     d.Value,
2135		Session:   d.Session,
2136		RaftIndex: RaftIndex{
2137			CreateIndex: d.CreateIndex,
2138			ModifyIndex: d.ModifyIndex,
2139		},
2140		EnterpriseMeta: d.EnterpriseMeta,
2141	}
2142}
2143
2144func (d *DirEntry) Equal(o *DirEntry) bool {
2145	return d.LockIndex == o.LockIndex &&
2146		d.Key == o.Key &&
2147		d.Flags == o.Flags &&
2148		bytes.Equal(d.Value, o.Value) &&
2149		d.Session == o.Session
2150}
2151
2152type DirEntries []*DirEntry
2153
2154// KVSRequest is used to operate on the Key-Value store
2155type KVSRequest struct {
2156	Datacenter string
2157	Op         api.KVOp // Which operation are we performing
2158	DirEnt     DirEntry // Which directory entry
2159	WriteRequest
2160}
2161
2162func (r *KVSRequest) RequestDatacenter() string {
2163	return r.Datacenter
2164}
2165
2166// KeyRequest is used to request a key, or key prefix
2167type KeyRequest struct {
2168	Datacenter string
2169	Key        string
2170	EnterpriseMeta
2171	QueryOptions
2172}
2173
2174func (r *KeyRequest) RequestDatacenter() string {
2175	return r.Datacenter
2176}
2177
2178// KeyListRequest is used to list keys
2179type KeyListRequest struct {
2180	Datacenter string
2181	Prefix     string
2182	Seperator  string
2183	QueryOptions
2184	EnterpriseMeta
2185}
2186
2187func (r *KeyListRequest) RequestDatacenter() string {
2188	return r.Datacenter
2189}
2190
2191type IndexedDirEntries struct {
2192	Entries DirEntries
2193	QueryMeta
2194}
2195
2196type IndexedKeyList struct {
2197	Keys []string
2198	QueryMeta
2199}
2200
2201type SessionBehavior string
2202
2203const (
2204	SessionKeysRelease SessionBehavior = "release"
2205	SessionKeysDelete                  = "delete"
2206)
2207
2208const (
2209	SessionTTLMax        = 24 * time.Hour
2210	SessionTTLMultiplier = 2
2211)
2212
2213type Sessions []*Session
2214
2215// Session is used to represent an open session in the KV store.
2216// This issued to associate node checks with acquired locks.
2217type Session struct {
2218	ID            string
2219	Name          string
2220	Node          string
2221	LockDelay     time.Duration
2222	Behavior      SessionBehavior // What to do when session is invalidated
2223	TTL           string
2224	NodeChecks    []string
2225	ServiceChecks []ServiceCheck
2226
2227	// Deprecated v1.7.0.
2228	Checks []types.CheckID `json:",omitempty"`
2229
2230	EnterpriseMeta
2231	RaftIndex
2232}
2233
2234type ServiceCheck struct {
2235	ID        string
2236	Namespace string
2237}
2238
2239func (s *Session) UnmarshalJSON(data []byte) (err error) {
2240	type Alias Session
2241	aux := &struct {
2242		LockDelay interface{}
2243		*Alias
2244	}{
2245		Alias: (*Alias)(s),
2246	}
2247	if err = json.Unmarshal(data, &aux); err != nil {
2248		return err
2249	}
2250	if aux.LockDelay != nil {
2251		var dur time.Duration
2252		switch v := aux.LockDelay.(type) {
2253		case string:
2254			if dur, err = time.ParseDuration(v); err != nil {
2255				return err
2256			}
2257		case float64:
2258			dur = time.Duration(v)
2259		}
2260		// Convert low value integers into seconds
2261		if dur < lockDelayMinThreshold {
2262			dur = dur * time.Second
2263		}
2264		s.LockDelay = dur
2265	}
2266	return nil
2267}
2268
2269type SessionOp string
2270
2271const (
2272	SessionCreate  SessionOp = "create"
2273	SessionDestroy           = "destroy"
2274)
2275
2276// SessionRequest is used to operate on sessions
2277type SessionRequest struct {
2278	Datacenter string
2279	Op         SessionOp // Which operation are we performing
2280	Session    Session   // Which session
2281	WriteRequest
2282}
2283
2284func (r *SessionRequest) RequestDatacenter() string {
2285	return r.Datacenter
2286}
2287
2288// SessionSpecificRequest is used to request a session by ID
2289type SessionSpecificRequest struct {
2290	Datacenter string
2291	SessionID  string
2292	// DEPRECATED in 1.7.0
2293	Session string
2294	EnterpriseMeta
2295	QueryOptions
2296}
2297
2298func (r *SessionSpecificRequest) RequestDatacenter() string {
2299	return r.Datacenter
2300}
2301
2302type IndexedSessions struct {
2303	Sessions Sessions
2304	QueryMeta
2305}
2306
2307// Coordinate stores a node name with its associated network coordinate.
2308type Coordinate struct {
2309	Node    string
2310	Segment string
2311	Coord   *coordinate.Coordinate
2312}
2313
2314type Coordinates []*Coordinate
2315
2316// IndexedCoordinate is used to represent a single node's coordinate from the state
2317// store.
2318type IndexedCoordinate struct {
2319	Coord *coordinate.Coordinate
2320	QueryMeta
2321}
2322
2323// IndexedCoordinates is used to represent a list of nodes and their
2324// corresponding raw coordinates.
2325type IndexedCoordinates struct {
2326	Coordinates Coordinates
2327	QueryMeta
2328}
2329
2330// DatacenterMap is used to represent a list of nodes with their raw coordinates,
2331// associated with a datacenter. Coordinates are only compatible between nodes in
2332// the same area.
2333type DatacenterMap struct {
2334	Datacenter  string
2335	AreaID      types.AreaID
2336	Coordinates Coordinates
2337}
2338
2339// CoordinateUpdateRequest is used to update the network coordinate of a given
2340// node.
2341type CoordinateUpdateRequest struct {
2342	Datacenter string
2343	Node       string
2344	Segment    string
2345	Coord      *coordinate.Coordinate
2346	WriteRequest
2347}
2348
2349// RequestDatacenter returns the datacenter for a given update request.
2350func (c *CoordinateUpdateRequest) RequestDatacenter() string {
2351	return c.Datacenter
2352}
2353
2354// EventFireRequest is used to ask a server to fire
2355// a Serf event. It is a bit odd, since it doesn't depend on
2356// the catalog or leader. Any node can respond, so it's not quite
2357// like a standard write request. This is used only internally.
2358type EventFireRequest struct {
2359	Datacenter string
2360	Name       string
2361	Payload    []byte
2362
2363	// Not using WriteRequest so that any server can process
2364	// the request. It is a bit unusual...
2365	QueryOptions
2366}
2367
2368func (r *EventFireRequest) RequestDatacenter() string {
2369	return r.Datacenter
2370}
2371
2372// EventFireResponse is used to respond to a fire request.
2373type EventFireResponse struct {
2374	QueryMeta
2375}
2376
2377type TombstoneOp string
2378
2379const (
2380	TombstoneReap TombstoneOp = "reap"
2381)
2382
2383// TombstoneRequest is used to trigger a reaping of the tombstones
2384type TombstoneRequest struct {
2385	Datacenter string
2386	Op         TombstoneOp
2387	ReapIndex  uint64
2388	WriteRequest
2389}
2390
2391func (r *TombstoneRequest) RequestDatacenter() string {
2392	return r.Datacenter
2393}
2394
2395// MsgpackHandle is a shared handle for encoding/decoding msgpack payloads
2396var MsgpackHandle = &codec.MsgpackHandle{
2397	RawToString: true,
2398	BasicHandle: codec.BasicHandle{
2399		DecodeOptions: codec.DecodeOptions{
2400			MapType: reflect.TypeOf(map[string]interface{}{}),
2401		},
2402	},
2403}
2404
2405// Decode is used to decode a MsgPack encoded object
2406func Decode(buf []byte, out interface{}) error {
2407	return codec.NewDecoder(bytes.NewReader(buf), MsgpackHandle).Decode(out)
2408}
2409
2410// Encode is used to encode a MsgPack object with type prefix
2411func Encode(t MessageType, msg interface{}) ([]byte, error) {
2412	var buf bytes.Buffer
2413	buf.WriteByte(uint8(t))
2414	err := codec.NewEncoder(&buf, MsgpackHandle).Encode(msg)
2415	return buf.Bytes(), err
2416}
2417
2418type ProtoMarshaller interface {
2419	Size() int
2420	MarshalTo([]byte) (int, error)
2421	Unmarshal([]byte) error
2422	ProtoMessage()
2423}
2424
2425func EncodeProtoInterface(t MessageType, message interface{}) ([]byte, error) {
2426	if marshaller, ok := message.(ProtoMarshaller); ok {
2427		return EncodeProto(t, marshaller)
2428	}
2429
2430	return nil, fmt.Errorf("message does not implement the ProtoMarshaller interface: %T", message)
2431}
2432
2433func EncodeProto(t MessageType, message ProtoMarshaller) ([]byte, error) {
2434	data := make([]byte, message.Size()+1)
2435	data[0] = uint8(t)
2436	if _, err := message.MarshalTo(data[1:]); err != nil {
2437		return nil, err
2438	}
2439	return data, nil
2440}
2441
2442func DecodeProto(buf []byte, out ProtoMarshaller) error {
2443	// Note that this assumes the leading byte indicating the type as already been stripped off.
2444	return out.Unmarshal(buf)
2445}
2446
2447// CompoundResponse is an interface for gathering multiple responses. It is
2448// used in cross-datacenter RPC calls where more than 1 datacenter is
2449// expected to reply.
2450type CompoundResponse interface {
2451	// Add adds a new response to the compound response
2452	Add(interface{})
2453
2454	// New returns an empty response object which can be passed around by
2455	// reference, and then passed to Add() later on.
2456	New() interface{}
2457}
2458
2459type KeyringOp string
2460
2461const (
2462	KeyringList    KeyringOp = "list"
2463	KeyringInstall           = "install"
2464	KeyringUse               = "use"
2465	KeyringRemove            = "remove"
2466)
2467
2468// KeyringRequest encapsulates a request to modify an encryption keyring.
2469// It can be used for install, remove, or use key type operations.
2470type KeyringRequest struct {
2471	Operation   KeyringOp
2472	Key         string
2473	Datacenter  string
2474	Forwarded   bool
2475	RelayFactor uint8
2476	LocalOnly   bool
2477	QueryOptions
2478}
2479
2480func (r *KeyringRequest) RequestDatacenter() string {
2481	return r.Datacenter
2482}
2483
2484// KeyringResponse is a unified key response and can be used for install,
2485// remove, use, as well as listing key queries.
2486type KeyringResponse struct {
2487	WAN         bool
2488	Datacenter  string
2489	Segment     string
2490	Messages    map[string]string `json:",omitempty"`
2491	Keys        map[string]int
2492	PrimaryKeys map[string]int
2493	NumNodes    int
2494	Error       string `json:",omitempty"`
2495}
2496
2497// KeyringResponses holds multiple responses to keyring queries. Each
2498// datacenter replies independently, and KeyringResponses is used as a
2499// container for the set of all responses.
2500type KeyringResponses struct {
2501	Responses []*KeyringResponse
2502	QueryMeta
2503}
2504
2505func (r *KeyringResponses) Add(v interface{}) {
2506	val := v.(*KeyringResponses)
2507	r.Responses = append(r.Responses, val.Responses...)
2508}
2509
2510func (r *KeyringResponses) New() interface{} {
2511	return new(KeyringResponses)
2512}
2513
2514// String converts message type int to string
2515func (m MessageType) String() string {
2516	s, ok := requestTypeStrings[m]
2517	if ok {
2518		return s
2519	}
2520
2521	s, ok = enterpriseRequestType(m)
2522	if ok {
2523		return s
2524	}
2525	return "Unknown(" + strconv.Itoa(int(m)) + ")"
2526
2527}
2528