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