From 277a425ef3bd2e6f0561533519f4f69abc683508 Mon Sep 17 00:00:00 2001 From: Seth Hoenig Date: Fri, 29 Jan 2021 10:04:45 -0600 Subject: [PATCH] consul: upgrade consul/api to 1.8.1 --- go.mod | 6 +- go.sum | 10 +- .../github.com/hashicorp/consul/api/agent.go | 106 +++++++++++++- vendor/github.com/hashicorp/consul/api/api.go | 19 ++- .../hashicorp/consul/api/config_entry.go | 29 +++- .../consul/api/config_entry_discoverychain.go | 110 +++++++++++++-- .../consul/api/config_entry_gateways.go | 22 ++- .../consul/api/config_entry_intentions.go | 80 +++++++++++ .../hashicorp/consul/api/connect_intention.go | 125 ++++++++++++++-- .../hashicorp/consul/api/discovery_chain.go | 3 + vendor/github.com/hashicorp/consul/api/go.mod | 4 +- vendor/github.com/hashicorp/consul/api/go.sum | 6 +- .../github.com/hashicorp/consul/api/lock.go | 6 +- .../hashicorp/consul/api/namespace.go | 6 +- .../consul/api/operator_autopilot.go | 133 ++++++++++++++++++ .../hashicorp/consul/api/operator_keyring.go | 3 + .../github.com/hashicorp/consul/api/status.go | 22 ++- .../hashicorp/consul/sdk/testutil/io.go | 50 ++++--- .../consul/sdk/testutil/retry/retry.go | 3 + .../hashicorp/consul/sdk/testutil/server.go | 71 ++++++++-- .../hashicorp/consul/sdk/testutil/testlog.go | 17 +-- .../hashicorp/consul/sdk/testutil/types.go | 11 ++ .../hashicorp/serf/coordinate/phantom.go | 4 +- .../github.com/hashicorp/serf/serf/config.go | 10 ++ .../hashicorp/serf/serf/internal_query.go | 8 +- .../hashicorp/serf/serf/keymanager.go | 17 ++- .../hashicorp/serf/serf/merge_delegate.go | 35 ++++- vendor/github.com/hashicorp/serf/serf/serf.go | 57 +++++++- vendor/modules.txt | 6 +- 29 files changed, 867 insertions(+), 112 deletions(-) create mode 100644 vendor/github.com/hashicorp/consul/api/config_entry_intentions.go create mode 100644 vendor/github.com/hashicorp/consul/sdk/testutil/types.go diff --git a/go.mod b/go.mod index 98507fe679c..8f6ed2834ee 100644 --- a/go.mod +++ b/go.mod @@ -52,8 +52,8 @@ require ( github.com/grpc-ecosystem/go-grpc-middleware v1.2.1-0.20200228141219-3ce3d519df39 github.com/hashicorp/consul v1.7.8 github.com/hashicorp/consul-template v0.25.1 - github.com/hashicorp/consul/api v1.6.0 - github.com/hashicorp/consul/sdk v0.6.0 + github.com/hashicorp/consul/api v1.8.1 + github.com/hashicorp/consul/sdk v0.7.0 github.com/hashicorp/cronexpr v1.1.1 github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de github.com/hashicorp/go-cleanhttp v0.5.1 @@ -81,7 +81,7 @@ require ( github.com/hashicorp/nomad/api v0.0.0-20200529203653-c4416b26d3eb github.com/hashicorp/raft v1.1.3-0.20200211192230-365023de17e6 github.com/hashicorp/raft-boltdb v0.0.0-20171010151810-6e5ba93211ea - github.com/hashicorp/serf v0.9.3 + github.com/hashicorp/serf v0.9.5 github.com/hashicorp/vault/api v1.0.5-0.20190730042357-746c0b111519 github.com/hashicorp/vault/sdk v0.1.14-0.20190730042320-0dc007d98cc8 github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d diff --git a/go.sum b/go.sum index 0382f78c3fe..240ef9780ff 100644 --- a/go.sum +++ b/go.sum @@ -329,11 +329,11 @@ github.com/hashicorp/consul v1.7.8/go.mod h1:urbfGaVZDmnXC6geg0LYPh/SRUk1E8nfmDH github.com/hashicorp/consul-template v0.25.1 h1:+D2s8eyRqWyX7GPNxeUi8tsyh8pRn3J6k8giEchPfKQ= github.com/hashicorp/consul-template v0.25.1/go.mod h1:/vUsrJvDuuQHcxEw0zik+YXTS7ZKWZjQeaQhshBmfH0= github.com/hashicorp/consul/api v1.4.0/go.mod h1:xc8u05kyMa3Wjr9eEAsIAo3dg8+LywT5E/Cl7cNS5nU= -github.com/hashicorp/consul/api v1.6.0 h1:SZB2hQW8AcTOpfDmiVblQbijxzsRuiyy0JpHfabvHio= -github.com/hashicorp/consul/api v1.6.0/go.mod h1:1NSuaUUkFaJzMasbfq/11wKYWSR67Xn6r2DXKhuDNFg= +github.com/hashicorp/consul/api v1.8.1 h1:BOEQaMWoGMhmQ29fC26bi0qb7/rId9JzZP2V0Xmx7m8= +github.com/hashicorp/consul/api v1.8.1/go.mod h1:sDjTOq0yUyv5G4h+BqSea7Fn6BU+XbolEz1952UB+mk= github.com/hashicorp/consul/sdk v0.4.0/go.mod h1:fY08Y9z5SvJqevyZNy6WWPXiG3KwBPAvlcdx16zZ0fM= -github.com/hashicorp/consul/sdk v0.6.0 h1:FfhMEkwvQl57CildXJyGHnwGGM4HMODGyfjGwNM1Vdw= -github.com/hashicorp/consul/sdk v0.6.0/go.mod h1:fY08Y9z5SvJqevyZNy6WWPXiG3KwBPAvlcdx16zZ0fM= +github.com/hashicorp/consul/sdk v0.7.0 h1:H6R9d008jDcHPQPAqPNuydAshJ4v5/8URdFnUvK/+sc= +github.com/hashicorp/consul/sdk v0.7.0/go.mod h1:fY08Y9z5SvJqevyZNy6WWPXiG3KwBPAvlcdx16zZ0fM= github.com/hashicorp/cronexpr v1.1.0/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= github.com/hashicorp/cronexpr v1.1.1 h1:NJZDd87hGXjoZBdvyCF9mX4DCq5Wy7+A/w+A7q0wn6c= github.com/hashicorp/cronexpr v1.1.1/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= @@ -435,6 +435,8 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J github.com/hashicorp/serf v0.8.3/go.mod h1:UpNcs7fFbpKIyZaUuSW6EPiH+eZC7OuyFD+wc1oal+k= github.com/hashicorp/serf v0.9.3 h1:AVF6JDQQens6nMHT9OGERBvK0f8rPrAGILnsKLr6lzM= github.com/hashicorp/serf v0.9.3/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= +github.com/hashicorp/serf v0.9.5 h1:EBWvyu9tcRszt3Bxp3KNssBMP1KuHWyO51lz9+786iM= +github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoIospckxBxk6Q= github.com/hashicorp/vault/api v1.0.5-0.20190730042357-746c0b111519 h1:2qdbEUXjHohC+OYHtVU5lujvPAHPKYR4IMs9rsiUTk8= github.com/hashicorp/vault/api v1.0.5-0.20190730042357-746c0b111519/go.mod h1:i9PKqwFko/s/aihU1uuHGh/FaQS+Xcgvd9dvnfAvQb0= diff --git a/vendor/github.com/hashicorp/consul/api/agent.go b/vendor/github.com/hashicorp/consul/api/agent.go index 1baa5f0b0f9..a4cc143f05b 100644 --- a/vendor/github.com/hashicorp/consul/api/agent.go +++ b/vendor/github.com/hashicorp/consul/api/agent.go @@ -93,6 +93,8 @@ type AgentService struct { // to include the Namespace in the hash. When we do, then we are in for lots of fun with tests. // For now though, ignoring it works well enough. Namespace string `json:",omitempty" bexpr:"-" hash:"ignore"` + // Datacenter is only ever returned and is ignored if presented. + Datacenter string `json:",omitempty" bexpr:"-" hash:"ignore"` } // AgentServiceChecksInfo returns information about a Service and its checks @@ -121,12 +123,84 @@ type AgentServiceConnectProxyConfig struct { Expose ExposeConfig `json:",omitempty"` } +const ( + // MemberTagKeyACLMode is the key used to indicate what ACL mode the agent is + // operating in. The values of this key will be one of the MemberACLMode constants + // with the key not being present indicating ACLModeUnknown. + MemberTagKeyACLMode = "acls" + + // MemberTagRole is the key used to indicate that the member is a server or not. + MemberTagKeyRole = "role" + + // MemberTagValueRoleServer is the value of the MemberTagKeyRole used to indicate + // that the member represents a Consul server. + MemberTagValueRoleServer = "consul" + + // MemberTagKeySegment is the key name of the tag used to indicate which network + // segment this member is in. + // Network Segments are a Consul Enterprise feature. + MemberTagKeySegment = "segment" + + // MemberTagKeyBootstrap is the key name of the tag used to indicate whether this + // agent was started with the "bootstrap" configuration enabled + MemberTagKeyBootstrap = "bootstrap" + // MemberTagValueBootstrap is the value of the MemberTagKeyBootstrap key when the + // agent was started with the "bootstrap" configuration enabled. + MemberTagValueBootstrap = "1" + + // MemberTagKeyBootstrapExpect is the key name of the tag used to indicate whether + // this agent was started with the "bootstrap_expect" configuration set to a non-zero + // value. The value of this key will be the string for of that configuration value. + MemberTagKeyBootstrapExpect = "expect" + + // MemberTagKeyUseTLS is the key name of the tag used to indicate whther this agent + // was configured to use TLS. + MemberTagKeyUseTLS = "use_tls" + // MemberTagValueUseTLS is the value of the MemberTagKeyUseTLS when the agent was + // configured to use TLS. Any other value indicates that it was not setup in + // that manner. + MemberTagValueUseTLS = "1" + + // MemberTagKeyReadReplica is the key used to indicate that the member is a read + // replica server (will remain a Raft non-voter). + // Read Replicas are a Consul Enterprise feature. + MemberTagKeyReadReplica = "read_replica" + // MemberTagValueReadReplica is the value of the MemberTagKeyReadReplica key when + // the member is in fact a read-replica. Any other value indicates that it is not. + // Read Replicas are a Consul Enterprise feature. + MemberTagValueReadReplica = "1" +) + +type MemberACLMode string + +const ( + // ACLModeDisables indicates that ACLs are disabled for this agent + ACLModeDisabled MemberACLMode = "0" + // ACLModeEnabled indicates that ACLs are enabled and operating in new ACL + // mode (v1.4.0+ ACLs) + ACLModeEnabled MemberACLMode = "1" + // ACLModeLegacy indicates that ACLs are enabled and operating in legacy mode. + ACLModeLegacy MemberACLMode = "2" + // ACLModeUnkown is used to indicate that the AgentMember.Tags didn't advertise + // an ACL mode at all. This is the case for Consul versions before v1.4.0 and + // should be treated similarly to ACLModeLegacy. + ACLModeUnknown MemberACLMode = "3" +) + // AgentMember represents a cluster member known to the agent type AgentMember struct { - Name string - Addr string - Port uint16 - Tags map[string]string + Name string + Addr string + Port uint16 + Tags map[string]string + // Status of the Member which corresponds to github.com/hashicorp/serf/serf.MemberStatus + // Value is one of: + // + // AgentMemberNone = 0 + // AgentMemberAlive = 1 + // AgentMemberLeaving = 2 + // AgentMemberLeft = 3 + // AgentMemberFailed = 4 Status int ProtocolMin uint8 ProtocolMax uint8 @@ -136,6 +210,30 @@ type AgentMember struct { DelegateCur uint8 } +// ACLMode returns the ACL mode this agent is operating in. +func (m *AgentMember) ACLMode() MemberACLMode { + mode := m.Tags[MemberTagKeyACLMode] + + // the key may not have existed but then an + // empty string will be returned and we will + // handle that in the default case of the switch + switch MemberACLMode(mode) { + case ACLModeDisabled: + return ACLModeDisabled + case ACLModeEnabled: + return ACLModeEnabled + case ACLModeLegacy: + return ACLModeLegacy + default: + return ACLModeUnknown + } +} + +// IsConsulServer returns true when this member is a Consul server. +func (m *AgentMember) IsConsulServer() bool { + return m.Tags[MemberTagKeyRole] == MemberTagValueRoleServer +} + // AllSegments is used to select for all segments in MembersOpts. const AllSegments = "_all" diff --git a/vendor/github.com/hashicorp/consul/api/api.go b/vendor/github.com/hashicorp/consul/api/api.go index 7b00be967ad..08f00c40695 100644 --- a/vendor/github.com/hashicorp/consul/api/api.go +++ b/vendor/github.com/hashicorp/consul/api/api.go @@ -254,6 +254,11 @@ type QueryMeta struct { // CacheAge is set if request was ?cached and indicates how stale the cached // response is. CacheAge time.Duration + + // DefaultACLPolicy is used to control the ACL interaction when there is no + // defined policy. This can be "allow" which means ACLs are used to + // deny-list, or "deny" which means ACLs are allow-lists. + DefaultACLPolicy string } // WriteMeta is used to return meta data about a write @@ -305,7 +310,7 @@ type Config struct { TokenFile string // Namespace is the name of the namespace to send along for the request - // when no other Namespace ispresent in the QueryOptions + // when no other Namespace is present in the QueryOptions Namespace string TLSConfig TLSConfig @@ -607,9 +612,11 @@ func NewClient(config *Config) (*Client, error) { trans.DialContext = func(_ context.Context, _, _ string) (net.Conn, error) { return net.Dial("unix", parts[1]) } - config.HttpClient = &http.Client{ - Transport: trans, + httpClient, err := NewHttpClient(trans, config.TLSConfig) + if err != nil { + return nil, err } + config.HttpClient = httpClient default: return nil, fmt.Errorf("Unknown protocol scheme: %s", parts[0]) } @@ -960,6 +967,12 @@ func parseQueryMeta(resp *http.Response, q *QueryMeta) error { q.AddressTranslationEnabled = false } + // Parse X-Consul-Default-ACL-Policy + switch v := header.Get("X-Consul-Default-ACL-Policy"); v { + case "allow", "deny": + q.DefaultACLPolicy = v + } + // Parse Cache info if cacheStr := header.Get("X-Cache"); cacheStr != "" { q.CacheHit = strings.EqualFold(cacheStr, "HIT") diff --git a/vendor/github.com/hashicorp/consul/api/config_entry.go b/vendor/github.com/hashicorp/consul/api/config_entry.go index dc31d6110f9..f5ef60e2949 100644 --- a/vendor/github.com/hashicorp/consul/api/config_entry.go +++ b/vendor/github.com/hashicorp/consul/api/config_entry.go @@ -7,6 +7,7 @@ import ( "io" "strconv" "strings" + "time" "github.com/mitchellh/mapstructure" ) @@ -19,6 +20,7 @@ const ( ServiceResolver string = "service-resolver" IngressGateway string = "ingress-gateway" TerminatingGateway string = "terminating-gateway" + ServiceIntentions string = "service-intentions" ProxyConfigGlobal string = "global" ) @@ -26,6 +28,8 @@ const ( type ConfigEntry interface { GetKind() string GetName() string + GetNamespace() string + GetMeta() map[string]string GetCreateIndex() uint64 GetModifyIndex() uint64 } @@ -95,6 +99,7 @@ type ServiceConfigEntry struct { MeshGateway MeshGatewayConfig `json:",omitempty" alias:"mesh_gateway"` Expose ExposeConfig `json:",omitempty"` ExternalSNI string `json:",omitempty" alias:"external_sni"` + Meta map[string]string `json:",omitempty"` CreateIndex uint64 ModifyIndex uint64 } @@ -107,6 +112,14 @@ func (s *ServiceConfigEntry) GetName() string { return s.Name } +func (s *ServiceConfigEntry) GetNamespace() string { + return s.Namespace +} + +func (s *ServiceConfigEntry) GetMeta() map[string]string { + return s.Meta +} + func (s *ServiceConfigEntry) GetCreateIndex() uint64 { return s.CreateIndex } @@ -122,6 +135,7 @@ type ProxyConfigEntry struct { Config map[string]interface{} `json:",omitempty"` MeshGateway MeshGatewayConfig `json:",omitempty" alias:"mesh_gateway"` Expose ExposeConfig `json:",omitempty"` + Meta map[string]string `json:",omitempty"` CreateIndex uint64 ModifyIndex uint64 } @@ -134,6 +148,14 @@ func (p *ProxyConfigEntry) GetName() string { return p.Name } +func (p *ProxyConfigEntry) GetNamespace() string { + return p.Namespace +} + +func (p *ProxyConfigEntry) GetMeta() map[string]string { + return p.Meta +} + func (p *ProxyConfigEntry) GetCreateIndex() uint64 { return p.CreateIndex } @@ -158,6 +180,8 @@ func makeConfigEntry(kind, name string) (ConfigEntry, error) { return &IngressGatewayConfigEntry{Kind: kind, Name: name}, nil case TerminatingGateway: return &TerminatingGatewayConfigEntry{Kind: kind, Name: name}, nil + case ServiceIntentions: + return &ServiceIntentionsConfigEntry{Kind: kind, Name: name}, nil default: return nil, fmt.Errorf("invalid config entry kind: %s", kind) } @@ -200,7 +224,10 @@ func DecodeConfigEntry(raw map[string]interface{}) (ConfigEntry, error) { } decodeConf := &mapstructure.DecoderConfig{ - DecodeHook: mapstructure.StringToTimeDurationHookFunc(), + DecodeHook: mapstructure.ComposeDecodeHookFunc( + mapstructure.StringToTimeDurationHookFunc(), + mapstructure.StringToTimeHookFunc(time.RFC3339), + ), Result: &entry, WeaklyTypedInput: true, } diff --git a/vendor/github.com/hashicorp/consul/api/config_entry_discoverychain.go b/vendor/github.com/hashicorp/consul/api/config_entry_discoverychain.go index f3994f0dd9b..5419292fede 100644 --- a/vendor/github.com/hashicorp/consul/api/config_entry_discoverychain.go +++ b/vendor/github.com/hashicorp/consul/api/config_entry_discoverychain.go @@ -12,14 +12,17 @@ type ServiceRouterConfigEntry struct { Routes []ServiceRoute `json:",omitempty"` + Meta map[string]string `json:",omitempty"` CreateIndex uint64 ModifyIndex uint64 } -func (e *ServiceRouterConfigEntry) GetKind() string { return e.Kind } -func (e *ServiceRouterConfigEntry) GetName() string { return e.Name } -func (e *ServiceRouterConfigEntry) GetCreateIndex() uint64 { return e.CreateIndex } -func (e *ServiceRouterConfigEntry) GetModifyIndex() uint64 { return e.ModifyIndex } +func (e *ServiceRouterConfigEntry) GetKind() string { return e.Kind } +func (e *ServiceRouterConfigEntry) GetName() string { return e.Name } +func (e *ServiceRouterConfigEntry) GetNamespace() string { return e.Namespace } +func (e *ServiceRouterConfigEntry) GetMeta() map[string]string { return e.Meta } +func (e *ServiceRouterConfigEntry) GetCreateIndex() uint64 { return e.CreateIndex } +func (e *ServiceRouterConfigEntry) GetModifyIndex() uint64 { return e.ModifyIndex } type ServiceRoute struct { Match *ServiceRouteMatch `json:",omitempty"` @@ -111,14 +114,17 @@ type ServiceSplitterConfigEntry struct { Splits []ServiceSplit `json:",omitempty"` + Meta map[string]string `json:",omitempty"` CreateIndex uint64 ModifyIndex uint64 } -func (e *ServiceSplitterConfigEntry) GetKind() string { return e.Kind } -func (e *ServiceSplitterConfigEntry) GetName() string { return e.Name } -func (e *ServiceSplitterConfigEntry) GetCreateIndex() uint64 { return e.CreateIndex } -func (e *ServiceSplitterConfigEntry) GetModifyIndex() uint64 { return e.ModifyIndex } +func (e *ServiceSplitterConfigEntry) GetKind() string { return e.Kind } +func (e *ServiceSplitterConfigEntry) GetName() string { return e.Name } +func (e *ServiceSplitterConfigEntry) GetNamespace() string { return e.Namespace } +func (e *ServiceSplitterConfigEntry) GetMeta() map[string]string { return e.Meta } +func (e *ServiceSplitterConfigEntry) GetCreateIndex() uint64 { return e.CreateIndex } +func (e *ServiceSplitterConfigEntry) GetModifyIndex() uint64 { return e.ModifyIndex } type ServiceSplit struct { Weight float32 @@ -138,6 +144,11 @@ type ServiceResolverConfigEntry struct { Failover map[string]ServiceResolverFailover `json:",omitempty"` ConnectTimeout time.Duration `json:",omitempty" alias:"connect_timeout"` + // LoadBalancer determines the load balancing policy and configuration for services + // issuing requests to this upstream service. + LoadBalancer *LoadBalancer `json:",omitempty" alias:"load_balancer"` + + Meta map[string]string `json:",omitempty"` CreateIndex uint64 ModifyIndex uint64 } @@ -178,10 +189,12 @@ func (e *ServiceResolverConfigEntry) UnmarshalJSON(data []byte) error { return nil } -func (e *ServiceResolverConfigEntry) GetKind() string { return e.Kind } -func (e *ServiceResolverConfigEntry) GetName() string { return e.Name } -func (e *ServiceResolverConfigEntry) GetCreateIndex() uint64 { return e.CreateIndex } -func (e *ServiceResolverConfigEntry) GetModifyIndex() uint64 { return e.ModifyIndex } +func (e *ServiceResolverConfigEntry) GetKind() string { return e.Kind } +func (e *ServiceResolverConfigEntry) GetName() string { return e.Name } +func (e *ServiceResolverConfigEntry) GetNamespace() string { return e.Namespace } +func (e *ServiceResolverConfigEntry) GetMeta() map[string]string { return e.Meta } +func (e *ServiceResolverConfigEntry) GetCreateIndex() uint64 { return e.CreateIndex } +func (e *ServiceResolverConfigEntry) GetModifyIndex() uint64 { return e.ModifyIndex } type ServiceResolverSubset struct { Filter string `json:",omitempty"` @@ -201,3 +214,76 @@ type ServiceResolverFailover struct { Namespace string `json:",omitempty"` Datacenters []string `json:",omitempty"` } + +// LoadBalancer determines the load balancing policy and configuration for services +// issuing requests to this upstream service. +type LoadBalancer struct { + // Policy is the load balancing policy used to select a host + Policy string `json:",omitempty"` + + // RingHashConfig contains configuration for the "ring_hash" policy type + RingHashConfig *RingHashConfig `json:",omitempty" alias:"ring_hash_config"` + + // LeastRequestConfig contains configuration for the "least_request" policy type + LeastRequestConfig *LeastRequestConfig `json:",omitempty" alias:"least_request_config"` + + // HashPolicies is a list of hash policies to use for hashing load balancing algorithms. + // Hash policies are evaluated individually and combined such that identical lists + // result in the same hash. + // If no hash policies are present, or none are successfully evaluated, + // then a random backend host will be selected. + HashPolicies []HashPolicy `json:",omitempty" alias:"hash_policies"` +} + +// RingHashConfig contains configuration for the "ring_hash" policy type +type RingHashConfig struct { + // MinimumRingSize determines the minimum number of entries in the hash ring + MinimumRingSize uint64 `json:",omitempty" alias:"minimum_ring_size"` + + // MaximumRingSize determines the maximum number of entries in the hash ring + MaximumRingSize uint64 `json:",omitempty" alias:"maximum_ring_size"` +} + +// LeastRequestConfig contains configuration for the "least_request" policy type +type LeastRequestConfig struct { + // ChoiceCount determines the number of random healthy hosts from which to select the one with the least requests. + ChoiceCount uint32 `json:",omitempty" alias:"choice_count"` +} + +// HashPolicy defines which attributes will be hashed by hash-based LB algorithms +type HashPolicy struct { + // Field is the attribute type to hash on. + // Must be one of "header","cookie", or "query_parameter". + // Cannot be specified along with SourceIP. + Field string `json:",omitempty"` + + // FieldValue is the value to hash. + // ie. header name, cookie name, URL query parameter name + // Cannot be specified along with SourceIP. + FieldValue string `json:",omitempty" alias:"field_value"` + + // CookieConfig contains configuration for the "cookie" hash policy type. + CookieConfig *CookieConfig `json:",omitempty" alias:"cookie_config"` + + // SourceIP determines whether the hash should be of the source IP rather than of a field and field value. + // Cannot be specified along with Field or FieldValue. + SourceIP bool `json:",omitempty" alias:"source_ip"` + + // Terminal will short circuit the computation of the hash when multiple hash policies are present. + // If a hash is computed when a Terminal policy is evaluated, + // then that hash will be used and subsequent hash policies will be ignored. + Terminal bool `json:",omitempty"` +} + +// CookieConfig contains configuration for the "cookie" hash policy type. +// This is specified to have Envoy generate a cookie for a client on its first request. +type CookieConfig struct { + // Generates a session cookie with no expiration. + Session bool `json:",omitempty"` + + // TTL for generated cookies. Cannot be specified for session cookies. + TTL time.Duration `json:",omitempty"` + + // The path to set for the cookie + Path string `json:",omitempty"` +} diff --git a/vendor/github.com/hashicorp/consul/api/config_entry_gateways.go b/vendor/github.com/hashicorp/consul/api/config_entry_gateways.go index 13a5ec7072f..822c093f2bd 100644 --- a/vendor/github.com/hashicorp/consul/api/config_entry_gateways.go +++ b/vendor/github.com/hashicorp/consul/api/config_entry_gateways.go @@ -21,6 +21,8 @@ type IngressGatewayConfigEntry struct { // what services to associated to those ports. Listeners []IngressListener + Meta map[string]string `json:",omitempty"` + // CreateIndex is the Raft index this entry was created at. This is a // read-only field. CreateIndex uint64 @@ -44,7 +46,7 @@ type IngressListener struct { // Protocol declares what type of traffic this listener is expected to // receive. Depending on the protocol, a listener might support multiplexing // services over a single port, or additional discovery chain features. The - // current supported values are: (tcp | http). + // current supported values are: (tcp | http | http2 | grpc). Protocol string // Services declares the set of services to which the listener forwards @@ -94,6 +96,14 @@ func (i *IngressGatewayConfigEntry) GetName() string { return i.Name } +func (i *IngressGatewayConfigEntry) GetNamespace() string { + return i.Namespace +} + +func (i *IngressGatewayConfigEntry) GetMeta() map[string]string { + return i.Meta +} + func (i *IngressGatewayConfigEntry) GetCreateIndex() uint64 { return i.CreateIndex } @@ -115,6 +125,8 @@ type TerminatingGatewayConfigEntry struct { // Services is a list of service names represented by the terminating gateway. Services []LinkedService `json:",omitempty"` + Meta map[string]string `json:",omitempty"` + // CreateIndex is the Raft index this entry was created at. This is a // read-only field. CreateIndex uint64 @@ -161,6 +173,14 @@ func (g *TerminatingGatewayConfigEntry) GetName() string { return g.Name } +func (g *TerminatingGatewayConfigEntry) GetNamespace() string { + return g.Namespace +} + +func (g *TerminatingGatewayConfigEntry) GetMeta() map[string]string { + return g.Meta +} + func (g *TerminatingGatewayConfigEntry) GetCreateIndex() uint64 { return g.CreateIndex } diff --git a/vendor/github.com/hashicorp/consul/api/config_entry_intentions.go b/vendor/github.com/hashicorp/consul/api/config_entry_intentions.go new file mode 100644 index 00000000000..187a4250610 --- /dev/null +++ b/vendor/github.com/hashicorp/consul/api/config_entry_intentions.go @@ -0,0 +1,80 @@ +package api + +import "time" + +type ServiceIntentionsConfigEntry struct { + Kind string + Name string + Namespace string `json:",omitempty"` + + Sources []*SourceIntention + + Meta map[string]string `json:",omitempty"` + + CreateIndex uint64 + ModifyIndex uint64 +} + +type SourceIntention struct { + Name string + Namespace string `json:",omitempty"` + Action IntentionAction `json:",omitempty"` + Permissions []*IntentionPermission `json:",omitempty"` + Precedence int + Type IntentionSourceType + Description string `json:",omitempty"` + + LegacyID string `json:",omitempty" alias:"legacy_id"` + LegacyMeta map[string]string `json:",omitempty" alias:"legacy_meta"` + LegacyCreateTime *time.Time `json:",omitempty" alias:"legacy_create_time"` + LegacyUpdateTime *time.Time `json:",omitempty" alias:"legacy_update_time"` +} + +func (e *ServiceIntentionsConfigEntry) GetKind() string { + return e.Kind +} + +func (e *ServiceIntentionsConfigEntry) GetName() string { + return e.Name +} + +func (e *ServiceIntentionsConfigEntry) GetNamespace() string { + return e.Namespace +} + +func (e *ServiceIntentionsConfigEntry) GetMeta() map[string]string { + return e.Meta +} + +func (e *ServiceIntentionsConfigEntry) GetCreateIndex() uint64 { + return e.CreateIndex +} + +func (e *ServiceIntentionsConfigEntry) GetModifyIndex() uint64 { + return e.ModifyIndex +} + +type IntentionPermission struct { + Action IntentionAction + HTTP *IntentionHTTPPermission `json:",omitempty"` +} + +type IntentionHTTPPermission struct { + PathExact string `json:",omitempty" alias:"path_exact"` + PathPrefix string `json:",omitempty" alias:"path_prefix"` + PathRegex string `json:",omitempty" alias:"path_regex"` + + Header []IntentionHTTPHeaderPermission `json:",omitempty"` + + Methods []string `json:",omitempty"` +} + +type IntentionHTTPHeaderPermission struct { + Name string + Present bool `json:",omitempty"` + Exact string `json:",omitempty"` + Prefix string `json:",omitempty"` + Suffix string `json:",omitempty"` + Regex string `json:",omitempty"` + Invert bool `json:",omitempty"` +} diff --git a/vendor/github.com/hashicorp/consul/api/connect_intention.go b/vendor/github.com/hashicorp/consul/api/connect_intention.go index 3db177c7b49..26fb6cc4b13 100644 --- a/vendor/github.com/hashicorp/consul/api/connect_intention.go +++ b/vendor/github.com/hashicorp/consul/api/connect_intention.go @@ -12,12 +12,12 @@ import ( // Connect. type Intention struct { // ID is the UUID-based ID for the intention, always generated by Consul. - ID string + ID string `json:",omitempty"` // Description is a human-friendly description of this intention. // It is opaque to Consul and is only stored and transferred in API // requests. - Description string + Description string `json:",omitempty"` // SourceNS, SourceName are the namespace and name, respectively, of // the source service. Either of these may be the wildcard "*", but only @@ -34,16 +34,25 @@ type Intention struct { SourceType IntentionSourceType // Action is whether this is an allowlist or denylist intention. - Action IntentionAction + Action IntentionAction `json:",omitempty"` - // DefaultAddr, DefaultPort of the local listening proxy (if any) to - // make this connection. - DefaultAddr string - DefaultPort int + // Permissions is the list of additional L7 attributes that extend the + // intention definition. + // + // NOTE: This field is not editable unless editing the underlying + // service-intentions config entry directly. + Permissions []*IntentionPermission `json:",omitempty"` + + // DefaultAddr is not used. + // Deprecated: DefaultAddr is not used and may be removed in a future version. + DefaultAddr string `json:",omitempty"` + // DefaultPort is not used. + // Deprecated: DefaultPort is not used and may be removed in a future version. + DefaultPort int `json:",omitempty"` // Meta is arbitrary metadata associated with the intention. This is // opaque to Consul but is served in API responses. - Meta map[string]string + Meta map[string]string `json:",omitempty"` // Precedence is the order that the intention will be applied, with // larger numbers being applied first. This is a read-only field, on @@ -59,7 +68,7 @@ type Intention struct { // This is needed mainly for replication purposes. When replicating from // one DC to another keeping the content Hash will allow us to detect // content changes more efficiently than checking every single field - Hash []byte + Hash []byte `json:",omitempty"` CreateIndex uint64 ModifyIndex uint64 @@ -67,10 +76,20 @@ type Intention struct { // String returns human-friendly output describing ths intention. func (i *Intention) String() string { + var detail string + switch n := len(i.Permissions); n { + case 0: + detail = string(i.Action) + case 1: + detail = "1 permission" + default: + detail = fmt.Sprintf("%d permissions", len(i.Permissions)) + } + return fmt.Sprintf("%s => %s (%s)", i.SourceString(), i.DestinationString(), - i.Action) + detail) } // SourceString returns the namespace/name format for the source, or @@ -164,7 +183,42 @@ func (h *Connect) Intentions(q *QueryOptions) ([]*Intention, *QueryMeta, error) return out, qm, nil } +// IntentionGetExact retrieves a single intention by its unique name instead of +// its ID. +func (h *Connect) IntentionGetExact(source, destination string, q *QueryOptions) (*Intention, *QueryMeta, error) { + r := h.c.newRequest("GET", "/v1/connect/intentions/exact") + r.setQueryOptions(q) + r.params.Set("source", source) + r.params.Set("destination", destination) + rtt, resp, err := h.c.doRequest(r) + if err != nil { + return nil, nil, err + } + defer resp.Body.Close() + + qm := &QueryMeta{} + parseQueryMeta(resp, qm) + qm.RequestTime = rtt + + if resp.StatusCode == 404 { + return nil, qm, nil + } else if resp.StatusCode != 200 { + var buf bytes.Buffer + io.Copy(&buf, resp.Body) + return nil, nil, fmt.Errorf( + "Unexpected response %d: %s", resp.StatusCode, buf.String()) + } + + var out Intention + if err := decodeBody(resp, &out); err != nil { + return nil, nil, err + } + return &out, qm, nil +} + // IntentionGet retrieves a single intention. +// +// Deprecated: use IntentionGetExact instead func (h *Connect) IntentionGet(id string, q *QueryOptions) (*Intention, *QueryMeta, error) { r := h.c.newRequest("GET", "/v1/connect/intentions/"+id) r.setQueryOptions(q) @@ -194,7 +248,28 @@ func (h *Connect) IntentionGet(id string, q *QueryOptions) (*Intention, *QueryMe return &out, qm, nil } +// IntentionDeleteExact deletes a single intention by its unique name instead of its ID. +func (h *Connect) IntentionDeleteExact(source, destination string, q *WriteOptions) (*WriteMeta, error) { + r := h.c.newRequest("DELETE", "/v1/connect/intentions/exact") + r.setWriteOptions(q) + r.params.Set("source", source) + r.params.Set("destination", destination) + + rtt, resp, err := requireOK(h.c.doRequest(r)) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + qm := &WriteMeta{} + qm.RequestTime = rtt + + return qm, nil +} + // IntentionDelete deletes a single intention. +// +// Deprecated: use IntentionDeleteExact instead func (h *Connect) IntentionDelete(id string, q *WriteOptions) (*WriteMeta, error) { r := h.c.newRequest("DELETE", "/v1/connect/intentions/"+id) r.setWriteOptions(q) @@ -268,9 +343,37 @@ func (h *Connect) IntentionCheck(args *IntentionCheck, q *QueryOptions) (bool, * return out.Allowed, qm, nil } +// IntentionUpsert will update an existing intention. The Source & Destination parameters +// in the structure must be non-empty. The ID must be empty. +func (c *Connect) IntentionUpsert(ixn *Intention, q *WriteOptions) (*WriteMeta, error) { + r := c.c.newRequest("PUT", "/v1/connect/intentions/exact") + r.setWriteOptions(q) + r.params.Set("source", maybePrefixNamespace(ixn.SourceNS, ixn.SourceName)) + r.params.Set("destination", maybePrefixNamespace(ixn.DestinationNS, ixn.DestinationName)) + r.obj = ixn + rtt, resp, err := requireOK(c.c.doRequest(r)) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + wm := &WriteMeta{} + wm.RequestTime = rtt + return wm, nil +} + +func maybePrefixNamespace(ns, name string) string { + if ns == "" { + return name + } + return ns + "/" + name +} + // IntentionCreate will create a new intention. The ID in the given // structure must be empty and a generate ID will be returned on // success. +// +// Deprecated: use IntentionUpsert instead func (c *Connect) IntentionCreate(ixn *Intention, q *WriteOptions) (string, *WriteMeta, error) { r := c.c.newRequest("POST", "/v1/connect/intentions") r.setWriteOptions(q) @@ -293,6 +396,8 @@ func (c *Connect) IntentionCreate(ixn *Intention, q *WriteOptions) (string, *Wri // IntentionUpdate will update an existing intention. The ID in the given // structure must be non-empty. +// +// Deprecated: use IntentionUpsert instead func (c *Connect) IntentionUpdate(ixn *Intention, q *WriteOptions) (*WriteMeta, error) { r := c.c.newRequest("PUT", "/v1/connect/intentions/"+ixn.ID) r.setWriteOptions(q) diff --git a/vendor/github.com/hashicorp/consul/api/discovery_chain.go b/vendor/github.com/hashicorp/consul/api/discovery_chain.go index 75fdbaee255..f67f881c234 100644 --- a/vendor/github.com/hashicorp/consul/api/discovery_chain.go +++ b/vendor/github.com/hashicorp/consul/api/discovery_chain.go @@ -147,6 +147,9 @@ type DiscoveryGraphNode struct { // fields for Type==resolver Resolver *DiscoveryResolver + + // shared by Type==resolver || Type==splitter + LoadBalancer *LoadBalancer `json:",omitempty"` } // compiled form of ServiceRoute diff --git a/vendor/github.com/hashicorp/consul/api/go.mod b/vendor/github.com/hashicorp/consul/api/go.mod index d9902d403e9..89e6e0c94bd 100644 --- a/vendor/github.com/hashicorp/consul/api/go.mod +++ b/vendor/github.com/hashicorp/consul/api/go.mod @@ -5,12 +5,12 @@ go 1.12 replace github.com/hashicorp/consul/sdk => ../sdk require ( - github.com/hashicorp/consul/sdk v0.6.0 + github.com/hashicorp/consul/sdk v0.7.0 github.com/hashicorp/go-cleanhttp v0.5.1 github.com/hashicorp/go-hclog v0.12.0 github.com/hashicorp/go-rootcerts v1.0.2 github.com/hashicorp/go-uuid v1.0.1 - github.com/hashicorp/serf v0.9.3 + github.com/hashicorp/serf v0.9.5 github.com/mitchellh/mapstructure v1.1.2 github.com/stretchr/testify v1.4.0 ) diff --git a/vendor/github.com/hashicorp/consul/api/go.sum b/vendor/github.com/hashicorp/consul/api/go.sum index 48301e8c8ab..57ef543992e 100644 --- a/vendor/github.com/hashicorp/consul/api/go.sum +++ b/vendor/github.com/hashicorp/consul/api/go.sum @@ -13,8 +13,6 @@ github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/hashicorp/consul/sdk v0.6.0 h1:FfhMEkwvQl57CildXJyGHnwGGM4HMODGyfjGwNM1Vdw= -github.com/hashicorp/consul/sdk v0.6.0/go.mod h1:fY08Y9z5SvJqevyZNy6WWPXiG3KwBPAvlcdx16zZ0fM= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= @@ -43,8 +41,8 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= github.com/hashicorp/memberlist v0.2.2 h1:5+RffWKwqJ71YPu9mWsF7ZOscZmwfasdA8kbdC7AO2g= github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/hashicorp/serf v0.9.3 h1:AVF6JDQQens6nMHT9OGERBvK0f8rPrAGILnsKLr6lzM= -github.com/hashicorp/serf v0.9.3/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= +github.com/hashicorp/serf v0.9.5 h1:EBWvyu9tcRszt3Bxp3KNssBMP1KuHWyO51lz9+786iM= +github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= diff --git a/vendor/github.com/hashicorp/consul/api/lock.go b/vendor/github.com/hashicorp/consul/api/lock.go index 5cacee8f7e5..221a7add3ca 100644 --- a/vendor/github.com/hashicorp/consul/api/lock.go +++ b/vendor/github.com/hashicorp/consul/api/lock.go @@ -79,6 +79,7 @@ type LockOptions struct { MonitorRetryTime time.Duration // Optional, defaults to DefaultMonitorRetryTime LockWaitTime time.Duration // Optional, defaults to DefaultLockWaitTime LockTryOnce bool // Optional, defaults to false which means try forever + LockDelay time.Duration // Optional, defaults to 15s Namespace string `json:",omitempty"` // Optional, defaults to API client config, namespace of ACL token, or "default" namespace } @@ -351,8 +352,9 @@ func (l *Lock) createSession() (string, error) { se := l.opts.SessionOpts if se == nil { se = &SessionEntry{ - Name: l.opts.SessionName, - TTL: l.opts.SessionTTL, + Name: l.opts.SessionName, + TTL: l.opts.SessionTTL, + LockDelay: l.opts.LockDelay, } } w := WriteOptions{Namespace: l.opts.Namespace} diff --git a/vendor/github.com/hashicorp/consul/api/namespace.go b/vendor/github.com/hashicorp/consul/api/namespace.go index 875af105f90..49782d2a8c2 100644 --- a/vendor/github.com/hashicorp/consul/api/namespace.go +++ b/vendor/github.com/hashicorp/consul/api/namespace.go @@ -26,7 +26,7 @@ type Namespace struct { // DeletedAt is the time when the Namespace was marked for deletion // This is nullable so that we can omit if empty when encoding in JSON - DeletedAt *time.Time `json:"DeletedAt,omitempty"` + DeletedAt *time.Time `json:"DeletedAt,omitempty" alias:"deleted_at"` // CreateIndex is the Raft index at which the Namespace was created CreateIndex uint64 `json:"CreateIndex,omitempty"` @@ -39,10 +39,10 @@ type Namespace struct { type NamespaceACLConfig struct { // PolicyDefaults is the list of policies that should be used for the parent authorizer // of all tokens in the associated namespace. - PolicyDefaults []ACLLink `json:"PolicyDefaults"` + PolicyDefaults []ACLLink `json:"PolicyDefaults" alias:"policy_defaults"` // RoleDefaults is the list of roles that should be used for the parent authorizer // of all tokens in the associated namespace. - RoleDefaults []ACLLink `json:"RoleDefaults"` + RoleDefaults []ACLLink `json:"RoleDefaults" alias:"role_defaults"` } // Namespaces can be used to manage Namespaces in Consul Enterprise.. diff --git a/vendor/github.com/hashicorp/consul/api/operator_autopilot.go b/vendor/github.com/hashicorp/consul/api/operator_autopilot.go index 0e4ef24649f..57876ee9f68 100644 --- a/vendor/github.com/hashicorp/consul/api/operator_autopilot.go +++ b/vendor/github.com/hashicorp/consul/api/operator_autopilot.go @@ -111,6 +111,122 @@ type OperatorHealthReply struct { Servers []ServerHealth } +type AutopilotState struct { + Healthy bool + FailureTolerance int + OptimisticFailureTolerance int + + Servers map[string]AutopilotServer + Leader string + Voters []string + ReadReplicas []string `json:",omitempty"` + RedundancyZones map[string]AutopilotZone `json:",omitempty"` + Upgrade *AutopilotUpgrade `json:",omitempty"` +} + +type AutopilotServer struct { + ID string + Name string + Address string + NodeStatus string + Version string + LastContact *ReadableDuration + LastTerm uint64 + LastIndex uint64 + Healthy bool + StableSince time.Time + RedundancyZone string `json:",omitempty"` + UpgradeVersion string `json:",omitempty"` + ReadReplica bool + Status AutopilotServerStatus + Meta map[string]string + NodeType AutopilotServerType +} + +type AutopilotServerStatus string + +const ( + AutopilotServerNone AutopilotServerStatus = "none" + AutopilotServerLeader AutopilotServerStatus = "leader" + AutopilotServerVoter AutopilotServerStatus = "voter" + AutopilotServerNonVoter AutopilotServerStatus = "non-voter" + AutopilotServerStaging AutopilotServerStatus = "staging" +) + +type AutopilotServerType string + +const ( + AutopilotTypeVoter AutopilotServerType = "voter" + AutopilotTypeReadReplica AutopilotServerType = "read-replica" + AutopilotTypeZoneVoter AutopilotServerType = "zone-voter" + AutopilotTypeZoneExtraVoter AutopilotServerType = "zone-extra-voter" + AutopilotTypeZoneStandby AutopilotServerType = "zone-standby" +) + +type AutopilotZone struct { + Servers []string + Voters []string + FailureTolerance int +} + +type AutopilotZoneUpgradeVersions struct { + TargetVersionVoters []string `json:",omitempty"` + TargetVersionNonVoters []string `json:",omitempty"` + OtherVersionVoters []string `json:",omitempty"` + OtherVersionNonVoters []string `json:",omitempty"` +} + +type AutopilotUpgrade struct { + Status AutopilotUpgradeStatus + TargetVersion string `json:",omitempty"` + TargetVersionVoters []string `json:",omitempty"` + TargetVersionNonVoters []string `json:",omitempty"` + TargetVersionReadReplicas []string `json:",omitempty"` + OtherVersionVoters []string `json:",omitempty"` + OtherVersionNonVoters []string `json:",omitempty"` + OtherVersionReadReplicas []string `json:",omitempty"` + RedundancyZones map[string]AutopilotZoneUpgradeVersions `json:",omitempty"` +} + +type AutopilotUpgradeStatus string + +const ( + // AutopilotUpgradeIdle is the status when no upgrade is in progress. + AutopilotUpgradeIdle AutopilotUpgradeStatus = "idle" + + // AutopilotUpgradeAwaitNewVoters is the status when more servers of + // the target version must be added in order to start the promotion + // phase of the upgrade + AutopilotUpgradeAwaitNewVoters AutopilotUpgradeStatus = "await-new-voters" + + // AutopilotUpgradePromoting is the status when autopilot is promoting + // servers of the target version. + AutopilotUpgradePromoting AutopilotUpgradeStatus = "promoting" + + // AutopilotUpgradeDemoting is the status when autopilot is demoting + // servers not on the target version + AutopilotUpgradeDemoting AutopilotUpgradeStatus = "demoting" + + // AutopilotUpgradeLeaderTransfer is the status when autopilot is transferring + // leadership from a server running an older version to a server + // using the target version. + AutopilotUpgradeLeaderTransfer AutopilotUpgradeStatus = "leader-transfer" + + // AutopilotUpgradeAwaitNewServers is the status when autpilot has finished + // transferring leadership and has demoted all the other versioned + // servers but wants to indicate that more target version servers + // are needed to replace all the existing other version servers. + AutopilotUpgradeAwaitNewServers AutopilotUpgradeStatus = "await-new-servers" + + // AutopilotUpgradeAwaitServerRemoval is the status when autopilot is waiting + // for the servers on non-target versions to be removed + AutopilotUpgradeAwaitServerRemoval AutopilotUpgradeStatus = "await-server-removal" + + // AutopilotUpgradeDisabled is the status when automated ugprades are + // disabled in the autopilot configuration + AutopilotUpgradeDisabled AutopilotUpgradeStatus = "disabled" +) + // ReadableDuration is a duration type that is serialized to JSON in human readable format. type ReadableDuration time.Duration @@ -230,3 +346,20 @@ func (op *Operator) AutopilotServerHealth(q *QueryOptions) (*OperatorHealthReply } return &out, nil } + +func (op *Operator) AutopilotState(q *QueryOptions) (*AutopilotState, error) { + r := op.c.newRequest("GET", "/v1/operator/autopilot/state") + r.setQueryOptions(q) + _, resp, err := requireOK(op.c.doRequest(r)) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + var out AutopilotState + if err := decodeBody(resp, &out); err != nil { + return nil, err + } + + return &out, nil +} diff --git a/vendor/github.com/hashicorp/consul/api/operator_keyring.go b/vendor/github.com/hashicorp/consul/api/operator_keyring.go index 038d5d5b02f..5b80f9f9143 100644 --- a/vendor/github.com/hashicorp/consul/api/operator_keyring.go +++ b/vendor/github.com/hashicorp/consul/api/operator_keyring.go @@ -22,6 +22,9 @@ type KeyringResponse struct { // A map of the encryption keys to the number of nodes they're installed on Keys map[string]int + // A map of the encryption primary keys to the number of nodes they're installed on + PrimaryKeys map[string]int + // The total number of nodes in this ring NumNodes int } diff --git a/vendor/github.com/hashicorp/consul/api/status.go b/vendor/github.com/hashicorp/consul/api/status.go index 74ef61a678f..57f379c7b1e 100644 --- a/vendor/github.com/hashicorp/consul/api/status.go +++ b/vendor/github.com/hashicorp/consul/api/status.go @@ -11,8 +11,13 @@ func (c *Client) Status() *Status { } // Leader is used to query for a known leader -func (s *Status) Leader() (string, error) { +func (s *Status) LeaderWithQueryOptions(q *QueryOptions) (string, error) { r := s.c.newRequest("GET", "/v1/status/leader") + + if q != nil { + r.setQueryOptions(q) + } + _, resp, err := requireOK(s.c.doRequest(r)) if err != nil { return "", err @@ -26,9 +31,18 @@ func (s *Status) Leader() (string, error) { return leader, nil } +func (s *Status) Leader() (string, error) { + return s.LeaderWithQueryOptions(nil) +} + // Peers is used to query for a known raft peers -func (s *Status) Peers() ([]string, error) { +func (s *Status) PeersWithQueryOptions(q *QueryOptions) ([]string, error) { r := s.c.newRequest("GET", "/v1/status/peers") + + if q != nil { + r.setQueryOptions(q) + } + _, resp, err := requireOK(s.c.doRequest(r)) if err != nil { return nil, err @@ -41,3 +55,7 @@ func (s *Status) Peers() ([]string, error) { } return peers, nil } + +func (s *Status) Peers() ([]string, error) { + return s.PeersWithQueryOptions(nil) +} diff --git a/vendor/github.com/hashicorp/consul/sdk/testutil/io.go b/vendor/github.com/hashicorp/consul/sdk/testutil/io.go index a137fc6a3f6..b3396023c53 100644 --- a/vendor/github.com/hashicorp/consul/sdk/testutil/io.go +++ b/vendor/github.com/hashicorp/consul/sdk/testutil/io.go @@ -29,40 +29,54 @@ func init() { } } -// TempDir creates a temporary directory within tmpdir -// with the name 'testname-name'. If the directory cannot -// be created t.Fatal is called. +var noCleanup = strings.ToLower(os.Getenv("TEST_NOCLEANUP")) == "true" + +// TempDir creates a temporary directory within tmpdir with the name 'testname-name'. +// If the directory cannot be created t.Fatal is called. +// The directory will be removed when the test ends. Set TEST_NOCLEANUP env var +// to prevent the directory from being removed. func TempDir(t *testing.T, name string) string { - if t != nil && t.Name() != "" { - name = t.Name() + "-" + name + if t == nil { + panic("argument t must be non-nil") } + name = t.Name() + "-" + name name = strings.Replace(name, "/", "_", -1) d, err := ioutil.TempDir(tmpdir, name) if err != nil { - if t == nil { - panic(err) - } t.Fatalf("err: %s", err) } + t.Cleanup(func() { + if noCleanup { + t.Logf("skipping cleanup because TEST_NOCLEANUP was enabled") + return + } + os.RemoveAll(d) + }) return d } -// TempFile creates a temporary file within tmpdir -// with the name 'testname-name'. If the file cannot -// be created t.Fatal is called. If a temporary directory -// has been created before consider storing the file -// inside this directory to avoid double cleanup. +// TempFile creates a temporary file within tmpdir with the name 'testname-name'. +// If the file cannot be created t.Fatal is called. If a temporary directory +// has been created before consider storing the file inside this directory to +// avoid double cleanup. +// The file will be removed when the test ends. Set TEST_NOCLEANUP env var +// to prevent the file from being removed. func TempFile(t *testing.T, name string) *os.File { - if t != nil && t.Name() != "" { - name = t.Name() + "-" + name + if t == nil { + panic("argument t must be non-nil") } + name = t.Name() + "-" + name name = strings.Replace(name, "/", "_", -1) f, err := ioutil.TempFile(tmpdir, name) if err != nil { - if t == nil { - panic(err) - } t.Fatalf("err: %s", err) } + t.Cleanup(func() { + if noCleanup { + t.Logf("skipping cleanup because TEST_NOCLEANUP was enabled") + return + } + os.Remove(f.Name()) + }) return f } diff --git a/vendor/github.com/hashicorp/consul/sdk/testutil/retry/retry.go b/vendor/github.com/hashicorp/consul/sdk/testutil/retry/retry.go index 53c05a2b05b..603c47346a1 100644 --- a/vendor/github.com/hashicorp/consul/sdk/testutil/retry/retry.go +++ b/vendor/github.com/hashicorp/consul/sdk/testutil/retry/retry.go @@ -23,6 +23,8 @@ import ( // Failer is an interface compatible with testing.T. type Failer interface { + Helper() + // Log is called for the final test output Log(args ...interface{}) @@ -116,6 +118,7 @@ func dedup(a []string) string { func run(r Retryer, t Failer, f func(r *R)) { rr := &R{} fail := func() { + t.Helper() out := dedup(rr.output) if out != "" { t.Log(out) diff --git a/vendor/github.com/hashicorp/consul/sdk/testutil/server.go b/vendor/github.com/hashicorp/consul/sdk/testutil/server.go index bf990795e95..d9fd7ac2917 100644 --- a/vendor/github.com/hashicorp/consul/sdk/testutil/server.go +++ b/vendor/github.com/hashicorp/consul/sdk/testutil/server.go @@ -25,6 +25,7 @@ import ( "runtime" "strconv" "strings" + "syscall" "testing" "time" @@ -133,7 +134,7 @@ type ServerConfigCallback func(c *TestServerConfig) // defaultServerConfig returns a new TestServerConfig struct // with all of the listen ports incremented by one. -func defaultServerConfig(t CleanupT) *TestServerConfig { +func defaultServerConfig(t TestingTB) *TestServerConfig { nodeID, err := uuid.GenerateUUID() if err != nil { panic(err) @@ -215,11 +216,11 @@ type TestServer struct { tmpdir string } -// NewTestServerConfig creates a new TestServer, and makes a call to an optional +// NewTestServerConfigT creates a new TestServer, and makes a call to an optional // callback function to modify the configuration. If there is an error // configuring or starting the server, the server will NOT be running when the // function returns (thus you do not need to stop it). -func NewTestServerConfigT(t testing.TB, cb ServerConfigCallback) (*TestServer, error) { +func NewTestServerConfigT(t TestingTB, cb ServerConfigCallback) (*TestServer, error) { path, err := exec.LookPath("consul") if err != nil || path == "" { return nil, fmt.Errorf("consul not found on $PATH - download and install " + @@ -328,9 +329,22 @@ func (s *TestServer) Stop() error { } } + waitDone := make(chan error) + go func() { + waitDone <- s.cmd.Wait() + close(waitDone) + }() + // wait for the process to exit to be sure that the data dir can be // deleted on all platforms. - return s.cmd.Wait() + select { + case err := <-waitDone: + return err + case <-time.After(10 * time.Second): + s.cmd.Process.Signal(syscall.SIGABRT) + s.cmd.Wait() + return fmt.Errorf("timeout waiting for server to stop gracefully") + } } // waitForAPI waits for the /status/leader HTTP endpoint to start @@ -351,11 +365,12 @@ func (s *TestServer) waitForAPI() error { time.Sleep(timer.Wait) url := s.url("/v1/status/leader") - _, err := s.masterGet(url) + resp, err := s.masterGet(url) if err != nil { failed = true continue } + resp.Body.Close() failed = false } @@ -378,7 +393,7 @@ func (s *TestServer) WaitForLeader(t *testing.T) { } defer resp.Body.Close() if err := s.requireOK(resp); err != nil { - r.Fatal("failed OK response", err) + r.Fatalf("failed OK response: %v", err) } // Ensure we have a leader and a node registration. @@ -387,7 +402,7 @@ func (s *TestServer) WaitForLeader(t *testing.T) { } index, err := strconv.ParseInt(resp.Header.Get("X-Consul-Index"), 10, 64) if err != nil { - r.Fatal("bad consul index", err) + r.Fatalf("bad consul index: %v", err) } if index < 2 { r.Fatal("consul index should be at least 2") @@ -418,7 +433,7 @@ func (s *TestServer) WaitForActiveCARoot(t *testing.T) { // since this is used in both `api` and consul test or duplication. The 200 // is all we really need to wait for. if err := s.requireOK(resp); err != nil { - r.Fatal("failed OK response", err) + r.Fatalf("failed OK response: %v", err) } var roots rootsResponse @@ -434,6 +449,27 @@ func (s *TestServer) WaitForActiveCARoot(t *testing.T) { }) } +// WaitForServiceIntentions waits until the server can accept config entry +// kinds of service-intentions meaning any migration bootstrapping from pre-1.9 +// intentions has completed. +func (s *TestServer) WaitForServiceIntentions(t *testing.T) { + const fakeConfigName = "Sa4ohw5raith4si0Ohwuqu3lowiethoh" + retry.Run(t, func(r *retry.R) { + // Try to delete a non-existent service-intentions config entry. The + // preflightCheck call in agent/consul/config_endpoint.go will fail if + // we aren't ready yet, vs just doing no work instead. + url := s.url("/v1/config/service-intentions/" + fakeConfigName) + resp, err := s.masterDelete(url) + if err != nil { + r.Fatalf("failed http get '%s': %v", url, err) + } + defer resp.Body.Close() + if err := s.requireOK(resp); err != nil { + r.Fatalf("failed OK response: %v", err) + } + }) +} + // WaitForSerfCheck ensures we have a node with serfHealth check registered // Behavior mirrors testrpc.WaitForTestAgent but avoids the dependency cycle in api pkg func (s *TestServer) WaitForSerfCheck(t *testing.T) { @@ -442,11 +478,11 @@ func (s *TestServer) WaitForSerfCheck(t *testing.T) { url := s.url("/v1/catalog/nodes?index=0") resp, err := s.masterGet(url) if err != nil { - r.Fatal("failed http get", err) + r.Fatalf("failed http get: %v", err) } defer resp.Body.Close() if err := s.requireOK(resp); err != nil { - r.Fatal("failed OK response", err) + r.Fatalf("failed OK response: %v", err) } // Watch for the anti-entropy sync to finish. @@ -463,11 +499,11 @@ func (s *TestServer) WaitForSerfCheck(t *testing.T) { url = s.url(fmt.Sprintf("/v1/health/node/%s", payload[0]["Node"])) resp, err = s.masterGet(url) if err != nil { - r.Fatal("failed http get", err) + r.Fatalf("failed http get: %v", err) } defer resp.Body.Close() if err := s.requireOK(resp); err != nil { - r.Fatal("failed OK response", err) + r.Fatalf("failed OK response: %v", err) } dec = json.NewDecoder(resp.Body) if err = dec.Decode(&payload); err != nil { @@ -497,3 +533,14 @@ func (s *TestServer) masterGet(url string) (*http.Response, error) { } return s.HTTPClient.Do(req) } + +func (s *TestServer) masterDelete(url string) (*http.Response, error) { + req, err := http.NewRequest("DELETE", url, nil) + if err != nil { + return nil, err + } + if s.Config.ACL.Tokens.Master != "" { + req.Header.Set("x-consul-token", s.Config.ACL.Tokens.Master) + } + return s.HTTPClient.Do(req) +} diff --git a/vendor/github.com/hashicorp/consul/sdk/testutil/testlog.go b/vendor/github.com/hashicorp/consul/sdk/testutil/testlog.go index a16396cb81d..a47298391e7 100644 --- a/vendor/github.com/hashicorp/consul/sdk/testutil/testlog.go +++ b/vendor/github.com/hashicorp/consul/sdk/testutil/testlog.go @@ -10,11 +10,11 @@ import ( "github.com/hashicorp/go-hclog" ) -func Logger(t testing.TB) hclog.InterceptLogger { +func Logger(t TestingTB) hclog.InterceptLogger { return LoggerWithOutput(t, NewLogBuffer(t)) } -func LoggerWithOutput(t testing.TB, output io.Writer) hclog.InterceptLogger { +func LoggerWithOutput(t TestingTB, output io.Writer) hclog.InterceptLogger { return hclog.NewInterceptLogger(&hclog.LoggerOptions{ Name: t.Name(), Level: hclog.Trace, @@ -25,18 +25,18 @@ func LoggerWithOutput(t testing.TB, output io.Writer) hclog.InterceptLogger { var sendTestLogsToStdout = os.Getenv("NOLOGBUFFER") == "1" // NewLogBuffer returns an io.Writer which buffers all writes. When the test -// ends, t.Failed is checked. If the test has failed all log output is printed -// to stdout. +// ends, t.Failed is checked. If the test has failed or has been run in verbose +// mode all log output is printed to stdout. // // Set the env var NOLOGBUFFER=1 to disable buffering, resulting in all log // output being written immediately to stdout. -func NewLogBuffer(t CleanupT) io.Writer { +func NewLogBuffer(t TestingTB) io.Writer { if sendTestLogsToStdout { return os.Stdout } buf := &logBuffer{buf: new(bytes.Buffer)} t.Cleanup(func() { - if t.Failed() { + if t.Failed() || testing.Verbose() { buf.Lock() defer buf.Unlock() buf.buf.WriteTo(os.Stdout) @@ -45,11 +45,6 @@ func NewLogBuffer(t CleanupT) io.Writer { return buf } -type CleanupT interface { - Cleanup(f func()) - Failed() bool -} - type logBuffer struct { buf *bytes.Buffer sync.Mutex diff --git a/vendor/github.com/hashicorp/consul/sdk/testutil/types.go b/vendor/github.com/hashicorp/consul/sdk/testutil/types.go new file mode 100644 index 00000000000..ec04e45dcc6 --- /dev/null +++ b/vendor/github.com/hashicorp/consul/sdk/testutil/types.go @@ -0,0 +1,11 @@ +package testutil + +// TestingTB is an interface that describes the implementation of the testing object. +// Using an interface that describes testing.TB instead of the actual implementation +// makes testutil usable in a wider variety of contexts (e.g. use with ginkgo : https://godoc.org/github.com/onsi/ginkgo#GinkgoT) +type TestingTB interface { + Cleanup(func()) + Failed() bool + Logf(format string, args ...interface{}) + Name() string +} diff --git a/vendor/github.com/hashicorp/serf/coordinate/phantom.go b/vendor/github.com/hashicorp/serf/coordinate/phantom.go index 6fb033c0cd3..66da4e2e92e 100644 --- a/vendor/github.com/hashicorp/serf/coordinate/phantom.go +++ b/vendor/github.com/hashicorp/serf/coordinate/phantom.go @@ -11,7 +11,7 @@ import ( // given config. func GenerateClients(nodes int, config *Config) ([]*Client, error) { clients := make([]*Client, nodes) - for i, _ := range clients { + for i := range clients { client, err := NewClient(config) if err != nil { return nil, err @@ -146,7 +146,7 @@ func Simulate(clients []*Client, truth [][]time.Duration, cycles int) { nodes := len(clients) for cycle := 0; cycle < cycles; cycle++ { - for i, _ := range clients { + for i := range clients { if j := rand.Intn(nodes); j != i { c := clients[j].GetCoordinate() rtt := truth[i][j] diff --git a/vendor/github.com/hashicorp/serf/serf/config.go b/vendor/github.com/hashicorp/serf/serf/config.go index 2875e1d60de..57b5a98e530 100644 --- a/vendor/github.com/hashicorp/serf/serf/config.go +++ b/vendor/github.com/hashicorp/serf/serf/config.go @@ -253,6 +253,15 @@ type Config struct { // // WARNING: this should ONLY be used in tests messageDropper func(typ messageType) bool + + // ReconnectTimeoutOverride is an optional interface which when present allows + // the application to cause reaping of a node to happen when it otherwise wouldn't + ReconnectTimeoutOverride ReconnectTimeoutOverrider + + // ValidateNodeNames controls whether nodenames only + // contain alphanumeric, dashes and '.'characters + // and sets maximum length to 128 characters + ValidateNodeNames bool } // Init allocates the subdata structures @@ -298,6 +307,7 @@ func DefaultConfig() *Config { QuerySizeLimit: 1024, EnableNameConflictResolution: true, DisableCoordinates: false, + ValidateNodeNames: false, UserEventSizeLimit: 512, } } diff --git a/vendor/github.com/hashicorp/serf/serf/internal_query.go b/vendor/github.com/hashicorp/serf/serf/internal_query.go index a74ebf705b5..0e71290d4c6 100644 --- a/vendor/github.com/hashicorp/serf/serf/internal_query.go +++ b/vendor/github.com/hashicorp/serf/serf/internal_query.go @@ -64,6 +64,9 @@ type nodeKeyResponse struct { // Keys is used in listing queries to relay a list of installed keys Keys []string + + // PrimaryKey is used in listing queries to relay the primary key + PrimaryKey string } // newSerfQueries is used to create a new serfQueries. We return an event @@ -346,7 +349,7 @@ SEND: func (s *serfQueries) handleListKeys(q *Query) { response := nodeKeyResponse{Result: false} keyring := s.serf.config.MemberlistConfig.Keyring - + var primaryKeyBytes []byte if !s.serf.EncryptionEnabled() { response.Message = "Keyring is empty (encryption not enabled)" s.logger.Printf("[ERR] serf: Keyring is empty (encryption not enabled)") @@ -360,6 +363,9 @@ func (s *serfQueries) handleListKeys(q *Query) { key := base64.StdEncoding.EncodeToString(keyBytes) response.Keys = append(response.Keys, key) } + primaryKeyBytes = keyring.GetPrimaryKey() + response.PrimaryKey = base64.StdEncoding.EncodeToString(primaryKeyBytes) + response.Result = true SEND: diff --git a/vendor/github.com/hashicorp/serf/serf/keymanager.go b/vendor/github.com/hashicorp/serf/serf/keymanager.go index 11aaff2aa00..106552c0f3e 100644 --- a/vendor/github.com/hashicorp/serf/serf/keymanager.go +++ b/vendor/github.com/hashicorp/serf/serf/keymanager.go @@ -31,6 +31,10 @@ type KeyResponse struct { // Keys is a mapping of the base64-encoded value of the key bytes to the // number of nodes that have the key installed. Keys map[string]int + + // PrimaryKeys is a mapping of the base64-encoded value of the primary + // key bytes to the number of nodes that have the key installed. + PrimaryKeys map[string]int } // KeyRequestOptions is used to contain optional parameters for a keyring operation @@ -76,13 +80,11 @@ func (k *KeyManager) streamKeyResp(resp *KeyResponse, ch <-chan NodeResponse) { // Currently only used for key list queries, this adds keys to a counter // and increments them for each node response which contains them. for _, key := range nodeResponse.Keys { - if _, ok := resp.Keys[key]; !ok { - resp.Keys[key] = 1 - } else { - resp.Keys[key]++ - } + resp.Keys[key]++ } + resp.PrimaryKeys[nodeResponse.PrimaryKey]++ + NEXT: // Return early if all nodes have responded. This allows us to avoid // waiting for the full timeout when there is nothing left to do. @@ -97,8 +99,9 @@ func (k *KeyManager) streamKeyResp(resp *KeyResponse, ch <-chan NodeResponse) { // KeyResponse for uniform response handling. func (k *KeyManager) handleKeyRequest(key, query string, opts *KeyRequestOptions) (*KeyResponse, error) { resp := &KeyResponse{ - Messages: make(map[string]string), - Keys: make(map[string]int), + Messages: make(map[string]string), + Keys: make(map[string]int), + PrimaryKeys: make(map[string]int), } qName := internalQueryName(query) diff --git a/vendor/github.com/hashicorp/serf/serf/merge_delegate.go b/vendor/github.com/hashicorp/serf/serf/merge_delegate.go index 36eb52f7df0..2e1e7c5b54c 100644 --- a/vendor/github.com/hashicorp/serf/serf/merge_delegate.go +++ b/vendor/github.com/hashicorp/serf/serf/merge_delegate.go @@ -1,6 +1,7 @@ package serf import ( + "fmt" "net" "github.com/hashicorp/memberlist" @@ -17,22 +18,31 @@ type mergeDelegate struct { func (m *mergeDelegate) NotifyMerge(nodes []*memberlist.Node) error { members := make([]*Member, len(nodes)) for idx, n := range nodes { - members[idx] = m.nodeToMember(n) + var err error + members[idx], err = m.nodeToMember(n) + if err != nil { + return err + } } return m.serf.config.Merge.NotifyMerge(members) } func (m *mergeDelegate) NotifyAlive(peer *memberlist.Node) error { - member := m.nodeToMember(peer) + member, err := m.nodeToMember(peer) + if err != nil { + return err + } return m.serf.config.Merge.NotifyMerge([]*Member{member}) } -func (m *mergeDelegate) nodeToMember(n *memberlist.Node) *Member { +func (m *mergeDelegate) nodeToMember(n *memberlist.Node) (*Member, error) { status := StatusNone if n.State == memberlist.StateLeft { status = StatusLeft } - + if err := m.validateMemberInfo(n); err != nil { + return nil, err + } return &Member{ Name: n.Name, Addr: net.IP(n.Addr), @@ -45,5 +55,22 @@ func (m *mergeDelegate) nodeToMember(n *memberlist.Node) *Member { DelegateMin: n.DMin, DelegateMax: n.DMax, DelegateCur: n.DCur, + }, nil +} + +// validateMemberInfo checks that the data we are sending is valid +func (m *mergeDelegate) validateMemberInfo(n *memberlist.Node) error { + if err := m.serf.validateNodeName(n.Name); err != nil { + return err + } + + if len(n.Addr) != 4 && len(n.Addr) != 16 { + return fmt.Errorf("IP byte length is invalid: %d bytes is not either 4 or 16", len(n.Addr)) + } + + if len(n.Meta) > memberlist.MetaMaxSize { + return fmt.Errorf("Encoded length of tags exceeds limit of %d bytes", + memberlist.MetaMaxSize) } + return nil } diff --git a/vendor/github.com/hashicorp/serf/serf/serf.go b/vendor/github.com/hashicorp/serf/serf/serf.go index 1c1dda1ddf5..8a7c069c2a9 100644 --- a/vendor/github.com/hashicorp/serf/serf/serf.go +++ b/vendor/github.com/hashicorp/serf/serf/serf.go @@ -11,6 +11,7 @@ import ( "math/rand" "net" "os" + "regexp" "strconv" "sync" "sync/atomic" @@ -36,6 +37,8 @@ const ( tagMagicByte uint8 = 255 ) +const MaxNodeNameLength int = 128 + var ( // FeatureNotSupported is returned if a feature cannot be used // due to an older protocol version being used. @@ -47,6 +50,12 @@ func init() { rand.Seed(time.Now().UnixNano()) } +// ReconnectTimeoutOverrider is an interface that can be implemented to allow overriding +// the reconnect timeout for individual members. +type ReconnectTimeoutOverrider interface { + ReconnectTimeout(member *Member, timeout time.Duration) time.Duration +} + // Serf is a single node that is part of a single cluster that gets // events about joins/leaves/failures/etc. It is created with the Create // method. @@ -269,6 +278,9 @@ func Create(conf *Config) (*Serf, error) { if len(serf.encodeTags(conf.Tags)) > memberlist.MetaMaxSize { return nil, fmt.Errorf("Encoded length of tags exceeds limit of %d bytes", memberlist.MetaMaxSize) } + if err := serf.ValidateNodeNames(); err != nil { + return nil, err + } // Check if serf member event coalescing is enabled if conf.CoalescePeriod > 0 && conf.QuiescentPeriod > 0 && conf.EventCh != nil { @@ -1102,8 +1114,21 @@ func (s *Serf) handleNodeLeaveIntent(leaveMsg *messageLeave) bool { return false } - // Always set the lamport time so that if we retransmit below it won't echo - // around forever! + // Always update the lamport time even when the status does not change + // (despite the variable naming implying otherwise). + // + // By updating this statusLTime here we ensure that the earlier conditional + // on "leaveMsg.LTime <= member.statusLTime" will prevent an infinite + // rebroadcast when seeing two successive leave message for the same + // member. Without this fix a leave message that arrives after a member is + // already marked as leaving/left will cause it to be rebroadcast without + // marking it locally as witnessed. If more than one serf instance in the + // cluster experiences this series of events then they will rebroadcast + // each other's messages about the affected node indefinitely. + // + // This eventually leads to overflowing serf intent queues + // - https://github.com/hashicorp/consul/issues/8179 + // - https://github.com/hashicorp/consul/issues/7960 member.statusLTime = leaveMsg.LTime // State transition depends on current state @@ -1558,8 +1583,13 @@ func (s *Serf) reap(old []*memberState, now time.Time, timeout time.Duration) [] for i := 0; i < n; i++ { m := old[i] + memberTimeout := timeout + if s.config.ReconnectTimeoutOverride != nil { + memberTimeout = s.config.ReconnectTimeoutOverride.ReconnectTimeout(&m.Member, memberTimeout) + } + // Skip if the timeout is not yet reached - if now.Sub(m.leaveTime) <= timeout { + if now.Sub(m.leaveTime) <= memberTimeout { continue } @@ -1871,3 +1901,24 @@ func (s *Serf) NumNodes() (numNodes int) { return numNodes } + +// ValidateNodeNames verifies the NodeName contains +// only alphanumeric, -, or . and is under 128 chracters +func (s *Serf) ValidateNodeNames() error { + return s.validateNodeName(s.config.NodeName) +} + +func (s *Serf) validateNodeName(name string) error { + if s.config.ValidateNodeNames { + var InvalidNameRe = regexp.MustCompile(`[^A-Za-z0-9\-\.]+`) + if InvalidNameRe.MatchString(name) { + return fmt.Errorf("Node name contains invalid characters %v , Valid characters include "+ + "all alpha-numerics and dashes and '.' ", name) + } + if len(name) > MaxNodeNameLength { + return fmt.Errorf("Node name is %v characters. "+ + "Valid length is between 1 and 128 characters", len(name)) + } + } + return nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index d4f690026e6..0ba3b8d7f81 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -364,10 +364,10 @@ github.com/hashicorp/consul-template/signals github.com/hashicorp/consul-template/template github.com/hashicorp/consul-template/version github.com/hashicorp/consul-template/watch -# github.com/hashicorp/consul/api v1.6.0 +# github.com/hashicorp/consul/api v1.8.1 ## explicit github.com/hashicorp/consul/api -# github.com/hashicorp/consul/sdk v0.6.0 +# github.com/hashicorp/consul/sdk v0.7.0 ## explicit github.com/hashicorp/consul/sdk/freeport github.com/hashicorp/consul/sdk/testutil @@ -503,7 +503,7 @@ github.com/hashicorp/raft # github.com/hashicorp/raft-boltdb v0.0.0-20171010151810-6e5ba93211ea ## explicit github.com/hashicorp/raft-boltdb -# github.com/hashicorp/serf v0.9.3 +# github.com/hashicorp/serf v0.9.5 ## explicit github.com/hashicorp/serf/coordinate github.com/hashicorp/serf/serf