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